@rk0429/agentic-relay 2.0.9 → 2.0.10

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 +199 -57
  2. package/package.json +1 -1
package/dist/relay.mjs CHANGED
@@ -1267,7 +1267,10 @@ ${input.prompt}`;
1267
1267
  } else {
1268
1268
  effectiveNativeSessionId = input.resumeSessionId;
1269
1269
  }
1270
- return adapter.continueSession(effectiveNativeSessionId, effectivePrompt);
1270
+ return adapter.continueSession(effectiveNativeSessionId, effectivePrompt, {
1271
+ ...input.model ? { model: input.model } : {},
1272
+ ...signal ? { signal } : {}
1273
+ });
1271
1274
  } else {
1272
1275
  let mcpServers;
1273
1276
  if (childHttpUrl) {
@@ -8310,7 +8313,7 @@ var init_server = __esm({
8310
8313
  this.agentEventStore
8311
8314
  );
8312
8315
  this.server = new McpServer(
8313
- { name: "agentic-relay", version: "2.0.9" }
8316
+ { name: "agentic-relay", version: "2.0.10" }
8314
8317
  );
8315
8318
  this.registerTools(this.server);
8316
8319
  this.sessionHealthMonitor.start();
@@ -8742,7 +8745,7 @@ var init_server = __esm({
8742
8745
  sessionIdGenerator: () => randomUUID()
8743
8746
  });
8744
8747
  const server = new McpServer(
8745
- { name: "agentic-relay", version: "2.0.9" }
8748
+ { name: "agentic-relay", version: "2.0.10" }
8746
8749
  );
8747
8750
  this.registerTools(server, childRelayContext, abortController.signal);
8748
8751
  transport.onclose = () => {
@@ -10080,6 +10083,18 @@ import { homedir as homedir7 } from "os";
10080
10083
  // src/infrastructure/process-manager.ts
10081
10084
  init_logger();
10082
10085
  import { execa } from "execa";
10086
+ function toProcessResultFromError(error) {
10087
+ const exitCode = typeof error === "object" && error !== null && "exitCode" in error && typeof error.exitCode === "number" ? error.exitCode : 1;
10088
+ const stdout = typeof error === "object" && error !== null && "stdout" in error && typeof error.stdout === "string" ? error.stdout : "";
10089
+ const stderr = typeof error === "object" && error !== null && "stderr" in error && typeof error.stderr === "string" && error.stderr.length > 0 ? error.stderr : error instanceof Error ? error.message : String(error);
10090
+ return { exitCode, stdout, stderr };
10091
+ }
10092
+ function getCanceledMessage(signal) {
10093
+ if (!signal?.aborted) {
10094
+ return "";
10095
+ }
10096
+ return signal.reason instanceof Error ? signal.reason.message : typeof signal.reason === "string" ? signal.reason : "";
10097
+ }
10083
10098
  var ProcessManager = class {
10084
10099
  activeProcesses = /* @__PURE__ */ new Set();
10085
10100
  constructor() {
@@ -10093,6 +10108,7 @@ var ProcessManager = class {
10093
10108
  env: options?.env,
10094
10109
  extendEnv: options?.env ? false : true,
10095
10110
  timeout: options?.timeout,
10111
+ cancelSignal: options?.signal,
10096
10112
  reject: false
10097
10113
  };
10098
10114
  const proc = execa(command, args, execaOptions);
@@ -10113,6 +10129,7 @@ var ProcessManager = class {
10113
10129
  env: options?.env,
10114
10130
  extendEnv: options?.env ? false : true,
10115
10131
  timeout: options?.timeout,
10132
+ cancelSignal: options?.signal,
10116
10133
  reject: false
10117
10134
  };
10118
10135
  const proc = execa(command, args, execaOptions);
@@ -10122,8 +10139,10 @@ var ProcessManager = class {
10122
10139
  return {
10123
10140
  exitCode: result.exitCode ?? 1,
10124
10141
  stdout: result.stdout?.toString() ?? "",
10125
- stderr: result.stderr?.toString() ?? ""
10142
+ stderr: result.stderr?.toString() || getCanceledMessage(options?.signal)
10126
10143
  };
10144
+ } catch (error) {
10145
+ return toProcessResultFromError(error);
10127
10146
  } finally {
10128
10147
  this.activeProcesses.delete(proc);
10129
10148
  }
@@ -10135,7 +10154,9 @@ var ProcessManager = class {
10135
10154
  input: stdinData,
10136
10155
  cwd: options?.cwd,
10137
10156
  env: options?.env,
10157
+ extendEnv: options?.env ? false : true,
10138
10158
  timeout: options?.timeout,
10159
+ cancelSignal: options?.signal,
10139
10160
  reject: false
10140
10161
  };
10141
10162
  const proc = execa(command, args, execaOptions);
@@ -10145,8 +10166,10 @@ var ProcessManager = class {
10145
10166
  return {
10146
10167
  exitCode: result.exitCode ?? 1,
10147
10168
  stdout: result.stdout?.toString() ?? "",
10148
- stderr: result.stderr?.toString() ?? ""
10169
+ stderr: result.stderr?.toString() || getCanceledMessage(options?.signal)
10149
10170
  };
10171
+ } catch (error) {
10172
+ return toProcessResultFromError(error);
10150
10173
  } finally {
10151
10174
  this.activeProcesses.delete(proc);
10152
10175
  }
@@ -10235,7 +10258,7 @@ var BaseAdapter = class _BaseAdapter {
10235
10258
  }
10236
10259
  return result.stdout.trim();
10237
10260
  }
10238
- async continueSession(_nativeSessionId, _prompt) {
10261
+ async continueSession(_nativeSessionId, _prompt, _options) {
10239
10262
  return {
10240
10263
  exitCode: 1,
10241
10264
  stdout: "",
@@ -10624,9 +10647,11 @@ var ClaudeAdapter = class extends BaseAdapter {
10624
10647
  if (timer !== void 0) clearTimeout(timer);
10625
10648
  }
10626
10649
  }
10627
- async continueSession(nativeSessionId, prompt) {
10650
+ async continueSession(nativeSessionId, prompt, options) {
10628
10651
  const timeoutMs = resolveClaudeSdkTimeoutMs();
10629
10652
  const abortController = new AbortController();
10653
+ const onAbort = () => abortController.abort();
10654
+ options?.signal?.addEventListener("abort", onAbort, { once: true });
10630
10655
  const timer = timeoutMs !== void 0 ? setTimeout(() => abortController.abort(), timeoutMs) : void 0;
10631
10656
  try {
10632
10657
  const { query } = await loadClaudeSDK();
@@ -10675,6 +10700,7 @@ var ClaudeAdapter = class extends BaseAdapter {
10675
10700
  stderr: error instanceof Error ? error.message : String(error)
10676
10701
  };
10677
10702
  } finally {
10703
+ options?.signal?.removeEventListener("abort", onAbort);
10678
10704
  if (timer !== void 0) clearTimeout(timer);
10679
10705
  }
10680
10706
  }
@@ -10777,8 +10803,8 @@ var ClaudeAdapter = class extends BaseAdapter {
10777
10803
 
10778
10804
  // src/adapters/codex-adapter.ts
10779
10805
  init_logger();
10780
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, mkdtemp, copyFile } from "fs/promises";
10781
- import { homedir as homedir2 } from "os";
10806
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, mkdtemp, copyFile, rm } from "fs/promises";
10807
+ import { homedir as homedir2, tmpdir } from "os";
10782
10808
  import { join as join2, dirname as dirname2 } from "path";
10783
10809
  async function loadCodexSDK() {
10784
10810
  return await import("@openai/codex-sdk");
@@ -10964,6 +10990,70 @@ async function copyIfExists(from, to) {
10964
10990
  throw error;
10965
10991
  }
10966
10992
  }
10993
+ async function readFileIfExists(path3) {
10994
+ try {
10995
+ return await readFile2(path3, "utf-8");
10996
+ } catch (error) {
10997
+ if (isFileNotFoundError(error)) {
10998
+ return void 0;
10999
+ }
11000
+ throw error;
11001
+ }
11002
+ }
11003
+ function parseCodexExecJson(stdout) {
11004
+ const parsed = {
11005
+ completedMessages: [],
11006
+ errorMessages: []
11007
+ };
11008
+ for (const rawLine of stdout.split(/\r?\n/)) {
11009
+ const line = rawLine.trim();
11010
+ if (!line) continue;
11011
+ let event;
11012
+ try {
11013
+ event = JSON.parse(line);
11014
+ } catch {
11015
+ continue;
11016
+ }
11017
+ if (typeof event !== "object" || event === null || !("type" in event) || typeof event.type !== "string") {
11018
+ continue;
11019
+ }
11020
+ switch (event.type) {
11021
+ case "thread.started":
11022
+ if ("thread_id" in event && typeof event.thread_id === "string") {
11023
+ parsed.threadId = event.thread_id;
11024
+ }
11025
+ break;
11026
+ case "item.completed":
11027
+ if ("item" in event && typeof event.item === "object" && event.item !== null && "type" in event.item && event.item.type === "agent_message" && "text" in event.item && typeof event.item.text === "string") {
11028
+ parsed.completedMessages.push(event.item.text);
11029
+ }
11030
+ break;
11031
+ case "turn.completed":
11032
+ if ("usage" in event && typeof event.usage === "object" && event.usage !== null) {
11033
+ const cachedInputTokens = "cached_input_tokens" in event.usage && typeof event.usage.cached_input_tokens === "number" ? event.usage.cached_input_tokens : 0;
11034
+ parsed.tokenUsage = {
11035
+ inputTokens: ("input_tokens" in event.usage && typeof event.usage.input_tokens === "number" ? event.usage.input_tokens : 0) + cachedInputTokens,
11036
+ outputTokens: "output_tokens" in event.usage && typeof event.usage.output_tokens === "number" ? event.usage.output_tokens : 0,
11037
+ ...cachedInputTokens > 0 ? { cachedInputTokens } : {}
11038
+ };
11039
+ }
11040
+ break;
11041
+ case "turn.failed":
11042
+ if ("error" in event && typeof event.error === "object" && event.error !== null && "message" in event.error && typeof event.error.message === "string") {
11043
+ parsed.failureMessage = event.error.message;
11044
+ }
11045
+ break;
11046
+ case "error":
11047
+ if ("message" in event && typeof event.message === "string") {
11048
+ parsed.errorMessages.push(event.message);
11049
+ }
11050
+ break;
11051
+ default:
11052
+ break;
11053
+ }
11054
+ }
11055
+ return parsed;
11056
+ }
10967
11057
  var CodexAdapter = class extends BaseAdapter {
10968
11058
  id = "codex";
10969
11059
  command = "codex";
@@ -11097,6 +11187,62 @@ ${systemPrompt}
11097
11187
  [User Request]
11098
11188
  ${prompt}`;
11099
11189
  }
11190
+ shouldHardenRelayChild(flags) {
11191
+ return Boolean(
11192
+ flags.mcpContext || flags.mcpServers && Object.keys(flags.mcpServers).length > 0
11193
+ );
11194
+ }
11195
+ buildCodexExecBaseArgs(model, hardenRelayChild) {
11196
+ const args = [
11197
+ "-a",
11198
+ "never",
11199
+ "-C",
11200
+ process.cwd()
11201
+ ];
11202
+ if (model) {
11203
+ args.unshift(model);
11204
+ args.unshift("--model");
11205
+ }
11206
+ if (hardenRelayChild) {
11207
+ args.push("-c", "project_root_markers=[]");
11208
+ }
11209
+ return args;
11210
+ }
11211
+ async runCodexExecCommand(args, prompt, env, signal, isolatedCodexHome, fallbackNativeSessionId) {
11212
+ const execTempDir = await mkdtemp(join2(tmpdir(), "relay-codex-exec-"));
11213
+ const outputPath = join2(execTempDir, "last-message.txt");
11214
+ try {
11215
+ const result = await this.processManager.executeWithInput(
11216
+ this.command,
11217
+ [...args, "--json", "-o", outputPath, "-"],
11218
+ prompt,
11219
+ { env, signal }
11220
+ );
11221
+ const parsed = parseCodexExecJson(result.stdout);
11222
+ const outputFileContent = await readFileIfExists(outputPath);
11223
+ const finalResponse = outputFileContent ?? parsed.completedMessages.join("\n");
11224
+ const nativeSessionId = parsed.threadId ? encodeNativeSessionId(parsed.threadId, isolatedCodexHome) : fallbackNativeSessionId;
11225
+ if (result.exitCode === 0) {
11226
+ return {
11227
+ exitCode: 0,
11228
+ stdout: finalResponse,
11229
+ stderr: "",
11230
+ ...nativeSessionId ? { nativeSessionId } : {},
11231
+ ...parsed.tokenUsage ? { tokenUsage: parsed.tokenUsage } : {}
11232
+ };
11233
+ }
11234
+ const stderr = signal?.aborted ? "Codex execution cancelled: parent session disconnected" : parsed.failureMessage ?? parsed.errorMessages.at(-1) ?? (result.stderr || "Codex execution failed");
11235
+ return {
11236
+ exitCode: result.exitCode || 1,
11237
+ stdout: finalResponse,
11238
+ stderr,
11239
+ ...nativeSessionId ? { nativeSessionId } : {},
11240
+ ...parsed.tokenUsage ? { tokenUsage: parsed.tokenUsage } : {}
11241
+ };
11242
+ } finally {
11243
+ await rm(execTempDir, { recursive: true, force: true }).catch(() => void 0);
11244
+ }
11245
+ }
11100
11246
  async execute(flags) {
11101
11247
  if (!flags.prompt) {
11102
11248
  throw new Error("execute requires a prompt (-p flag)");
@@ -11107,28 +11253,22 @@ ${prompt}`;
11107
11253
  systemPrompt
11108
11254
  );
11109
11255
  try {
11110
- const { Codex } = await loadCodexSDK();
11111
11256
  const { options, isolatedCodexHome } = await this.buildCodexOptions(flags);
11112
- const codex = new Codex(options);
11113
- const thread = codex.startThread({
11114
- ...flags.model ? { model: flags.model } : {},
11115
- workingDirectory: process.cwd(),
11116
- approvalPolicy: "never"
11117
- });
11118
- const result = await thread.run(effectivePrompt, {
11119
- ...flags.signal ? { signal: flags.signal } : {}
11120
- });
11121
- return {
11122
- exitCode: 0,
11123
- stdout: result.finalResponse,
11124
- stderr: "",
11125
- ...thread.id ? {
11126
- nativeSessionId: encodeNativeSessionId(
11127
- thread.id,
11128
- isolatedCodexHome
11129
- )
11130
- } : {}
11131
- };
11257
+ const args = this.buildCodexExecBaseArgs(
11258
+ flags.model,
11259
+ this.shouldHardenRelayChild(flags)
11260
+ );
11261
+ args.push("exec");
11262
+ if (this.shouldHardenRelayChild(flags)) {
11263
+ args.push("--disable", "multi_agent");
11264
+ }
11265
+ return await this.runCodexExecCommand(
11266
+ args,
11267
+ effectivePrompt,
11268
+ options.env,
11269
+ flags.signal,
11270
+ isolatedCodexHome
11271
+ );
11132
11272
  } catch (error) {
11133
11273
  if (flags.signal?.aborted) {
11134
11274
  return {
@@ -11248,34 +11388,36 @@ ${prompt}`;
11248
11388
  };
11249
11389
  }
11250
11390
  }
11251
- async continueSession(nativeSessionId, prompt) {
11391
+ async continueSession(nativeSessionId, prompt, options) {
11252
11392
  try {
11253
- const { Codex } = await loadCodexSDK();
11254
11393
  const { threadId, codexHome } = decodeNativeSessionId(nativeSessionId);
11255
- const codex = new Codex(
11256
- codexHome ? {
11257
- env: {
11258
- ...Object.fromEntries(
11259
- Object.entries(process.env).filter(
11260
- ([, value]) => value !== void 0
11261
- )
11262
- ),
11263
- CODEX_HOME: codexHome
11264
- }
11265
- } : {}
11266
- );
11267
- const thread = codex.resumeThread(threadId, {
11268
- workingDirectory: process.cwd(),
11269
- approvalPolicy: "never"
11270
- });
11271
- const result = await thread.run(prompt);
11272
- return {
11273
- exitCode: 0,
11274
- stdout: result.finalResponse,
11275
- stderr: "",
11394
+ const env = codexHome ? {
11395
+ ...Object.fromEntries(
11396
+ Object.entries(process.env).filter(
11397
+ ([, value]) => value !== void 0
11398
+ )
11399
+ ),
11400
+ CODEX_HOME: codexHome
11401
+ } : void 0;
11402
+ const args = this.buildCodexExecBaseArgs(options?.model, true);
11403
+ args.push("exec", "resume", "--disable", "multi_agent", threadId);
11404
+ return await this.runCodexExecCommand(
11405
+ args,
11406
+ prompt,
11407
+ env,
11408
+ options?.signal,
11409
+ codexHome,
11276
11410
  nativeSessionId
11277
- };
11411
+ );
11278
11412
  } catch (error) {
11413
+ if (options?.signal?.aborted) {
11414
+ return {
11415
+ exitCode: 1,
11416
+ stdout: "",
11417
+ stderr: "Codex execution cancelled: parent session disconnected",
11418
+ nativeSessionId
11419
+ };
11420
+ }
11279
11421
  return {
11280
11422
  exitCode: 1,
11281
11423
  stdout: "",
@@ -13386,7 +13528,7 @@ function createMCPCommand(configManager2, registry2, sessionManager2, hooksEngin
13386
13528
  responseOutputDir,
13387
13529
  relayConfig
13388
13530
  );
13389
- await server.start({ transport, port, currentVersion: "2.0.9" });
13531
+ await server.start({ transport, port, currentVersion: "2.0.10" });
13390
13532
  }
13391
13533
  })
13392
13534
  },
@@ -13546,7 +13688,7 @@ function createVersionCommand(registry2) {
13546
13688
  description: "Show relay and backend versions"
13547
13689
  },
13548
13690
  async run() {
13549
- const relayVersion = "2.0.9";
13691
+ const relayVersion = "2.0.10";
13550
13692
  console.log(`agentic-relay v${relayVersion}`);
13551
13693
  console.log("");
13552
13694
  console.log("Backends:");
@@ -13943,7 +14085,7 @@ var subCommandNames = /* @__PURE__ */ new Set(["claude", "codex", "gemini", "upd
13943
14085
  var main = defineCommand11({
13944
14086
  meta: {
13945
14087
  name: "relay",
13946
- version: "2.0.9",
14088
+ version: "2.0.10",
13947
14089
  description: "Unified CLI proxy for Claude Code, Codex CLI, and Gemini CLI"
13948
14090
  },
13949
14091
  args: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rk0429/agentic-relay",
3
- "version": "2.0.9",
3
+ "version": "2.0.10",
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",