agent-relay 2.1.10 → 2.1.11

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 (83) hide show
  1. package/dist/index.cjs +196 -39
  2. package/dist/src/cli/index.js +43 -0
  3. package/dist/src/cli/index.js.map +1 -1
  4. package/package.json +18 -18
  5. package/packages/api-types/package.json +1 -1
  6. package/packages/benchmark/package.json +4 -4
  7. package/packages/bridge/dist/spawner.d.ts +12 -0
  8. package/packages/bridge/dist/spawner.d.ts.map +1 -1
  9. package/packages/bridge/dist/spawner.js +108 -1
  10. package/packages/bridge/dist/spawner.js.map +1 -1
  11. package/packages/bridge/package.json +8 -8
  12. package/packages/bridge/src/spawner.ts +118 -1
  13. package/packages/cli-tester/package.json +1 -1
  14. package/packages/config/package.json +2 -2
  15. package/packages/continuity/package.json +2 -2
  16. package/packages/daemon/dist/server.js +5 -2
  17. package/packages/daemon/dist/server.js.map +1 -1
  18. package/packages/daemon/package.json +12 -12
  19. package/packages/daemon/src/server.ts +5 -2
  20. package/packages/hooks/package.json +4 -4
  21. package/packages/mcp/dist/client.d.ts +29 -1
  22. package/packages/mcp/dist/client.d.ts.map +1 -1
  23. package/packages/mcp/dist/client.js +40 -0
  24. package/packages/mcp/dist/client.js.map +1 -1
  25. package/packages/mcp/dist/file-transport.d.ts +13 -7
  26. package/packages/mcp/dist/file-transport.d.ts.map +1 -1
  27. package/packages/mcp/dist/file-transport.js +16 -9
  28. package/packages/mcp/dist/file-transport.js.map +1 -1
  29. package/packages/mcp/dist/hybrid-client.d.ts +11 -4
  30. package/packages/mcp/dist/hybrid-client.d.ts.map +1 -1
  31. package/packages/mcp/dist/hybrid-client.js +13 -5
  32. package/packages/mcp/dist/hybrid-client.js.map +1 -1
  33. package/packages/mcp/dist/index.d.ts +2 -2
  34. package/packages/mcp/dist/index.d.ts.map +1 -1
  35. package/packages/mcp/dist/index.js +5 -1
  36. package/packages/mcp/dist/index.js.map +1 -1
  37. package/packages/mcp/dist/server.d.ts.map +1 -1
  38. package/packages/mcp/dist/server.js +19 -1
  39. package/packages/mcp/dist/server.js.map +1 -1
  40. package/packages/mcp/dist/tools/index.d.ts +2 -1
  41. package/packages/mcp/dist/tools/index.d.ts.map +1 -1
  42. package/packages/mcp/dist/tools/index.js +2 -1
  43. package/packages/mcp/dist/tools/index.js.map +1 -1
  44. package/packages/mcp/dist/tools/relay-channel.d.ts +26 -0
  45. package/packages/mcp/dist/tools/relay-channel.d.ts.map +1 -1
  46. package/packages/mcp/dist/tools/relay-channel.js +48 -0
  47. package/packages/mcp/dist/tools/relay-channel.js.map +1 -1
  48. package/packages/mcp/dist/tools/relay-messages.d.ts +32 -0
  49. package/packages/mcp/dist/tools/relay-messages.d.ts.map +1 -0
  50. package/packages/mcp/dist/tools/relay-messages.js +61 -0
  51. package/packages/mcp/dist/tools/relay-messages.js.map +1 -0
  52. package/packages/mcp/package.json +4 -4
  53. package/packages/mcp/src/client.ts +91 -14
  54. package/packages/mcp/src/file-transport.ts +16 -9
  55. package/packages/mcp/src/hybrid-client.ts +13 -5
  56. package/packages/mcp/src/index.ts +15 -1
  57. package/packages/mcp/src/server.ts +30 -0
  58. package/packages/mcp/src/tools/index.ts +15 -0
  59. package/packages/mcp/src/tools/relay-channel.ts +58 -0
  60. package/packages/mcp/src/tools/relay-messages.ts +66 -0
  61. package/packages/mcp/tests/client.test.ts +75 -0
  62. package/packages/mcp/tests/tools.test.ts +154 -0
  63. package/packages/memory/package.json +2 -2
  64. package/packages/policy/package.json +2 -2
  65. package/packages/protocol/package.json +1 -1
  66. package/packages/resiliency/package.json +1 -1
  67. package/packages/sdk/package.json +3 -3
  68. package/packages/spawner/package.json +1 -1
  69. package/packages/state/package.json +1 -1
  70. package/packages/storage/package.json +2 -2
  71. package/packages/storage/src/jsonl-adapter.test.ts +4 -3
  72. package/packages/telemetry/package.json +1 -1
  73. package/packages/trajectory/package.json +2 -2
  74. package/packages/user-directory/package.json +2 -2
  75. package/packages/utils/package.json +3 -3
  76. package/packages/wrapper/dist/relay-pty-orchestrator.d.ts +5 -0
  77. package/packages/wrapper/dist/relay-pty-orchestrator.d.ts.map +1 -1
  78. package/packages/wrapper/dist/relay-pty-orchestrator.js +70 -4
  79. package/packages/wrapper/dist/relay-pty-orchestrator.js.map +1 -1
  80. package/packages/wrapper/package.json +6 -6
  81. package/packages/wrapper/src/relay-pty-orchestrator.test.ts +7 -1
  82. package/packages/wrapper/src/relay-pty-orchestrator.ts +93 -4
  83. package/relay-snippets/agent-relay-snippet.md +25 -2
package/dist/index.cjs CHANGED
@@ -3156,13 +3156,13 @@ var init_logger = __esm({
3156
3156
  });
3157
3157
 
3158
3158
  // packages/config/dist/bridge-utils.js
3159
- var import_node_child_process9, import_node_util3, execAsync2;
3159
+ var import_node_child_process10, import_node_util3, execAsync2;
3160
3160
  var init_bridge_utils = __esm({
3161
3161
  "packages/config/dist/bridge-utils.js"() {
3162
3162
  "use strict";
3163
- import_node_child_process9 = require("node:child_process");
3163
+ import_node_child_process10 = require("node:child_process");
3164
3164
  import_node_util3 = require("node:util");
3165
- execAsync2 = (0, import_node_util3.promisify)(import_node_child_process9.exec);
3165
+ execAsync2 = (0, import_node_util3.promisify)(import_node_child_process10.exec);
3166
3166
  }
3167
3167
  });
3168
3168
 
@@ -43148,6 +43148,7 @@ var import_node_net3 = require("node:net");
43148
43148
  var import_node_crypto9 = require("node:crypto");
43149
43149
  var import_node_path11 = require("node:path");
43150
43150
  var import_node_os6 = require("node:os");
43151
+ var import_node_child_process4 = require("node:child_process");
43151
43152
  var import_node_fs8 = require("node:fs");
43152
43153
  var import_node_url2 = require("node:url");
43153
43154
 
@@ -43502,9 +43503,9 @@ var AgentHealthMonitor = class extends import_events.EventEmitter {
43502
43503
  * Get memory and CPU usage for a process
43503
43504
  */
43504
43505
  async getProcessUsage(pid) {
43505
- const { execSync: execSync10 } = await import("child_process");
43506
+ const { execSync: execSync12 } = await import("child_process");
43506
43507
  try {
43507
- const output = execSync10(`ps -o rss=,pcpu= -p ${pid}`, { encoding: "utf8" }).trim();
43508
+ const output = execSync12(`ps -o rss=,pcpu= -p ${pid}`, { encoding: "utf8" }).trim();
43508
43509
  const [rss, cpu] = output.split(/\s+/);
43509
43510
  return {
43510
43511
  memory: parseInt(rss, 10) * 1024,
@@ -46429,7 +46430,7 @@ var ACTIVITY_VERIFICATION = {
46429
46430
  function hashWorkspaceId(workspaceId) {
46430
46431
  return (0, import_node_crypto9.createHash)("sha256").update(workspaceId).digest("hex").slice(0, 12);
46431
46432
  }
46432
- var RelayPtyOrchestrator = class extends BaseWrapper {
46433
+ var RelayPtyOrchestrator = class _RelayPtyOrchestrator extends BaseWrapper {
46433
46434
  config;
46434
46435
  // Process management
46435
46436
  relayPtyProcess;
@@ -46493,6 +46494,42 @@ var RelayPtyOrchestrator = class extends BaseWrapper {
46493
46494
  cgroupManager;
46494
46495
  hasCgroupSetup = false;
46495
46496
  // Note: sessionEndProcessed and lastSummaryRawContent are inherited from BaseWrapper
46497
+ /**
46498
+ * Gather system diagnostics for debugging SIGKILL/unexpected exits.
46499
+ * Returns a formatted string with memory, process, and system info.
46500
+ */
46501
+ static gatherSigkillDiagnostics(agentName, pid) {
46502
+ const lines = [];
46503
+ try {
46504
+ const free = (0, import_node_os6.freemem)();
46505
+ const total = (0, import_node_os6.totalmem)();
46506
+ const usedPercent = Math.round((1 - free / total) * 100);
46507
+ lines.push(`Memory: ${Math.round(free / 1024 / 1024)}MB free / ${Math.round(total / 1024 / 1024)}MB total (${usedPercent}% used)`);
46508
+ try {
46509
+ const psOutput = (0, import_node_child_process4.execSync)("ps aux | grep -c relay-pty || echo 0", { encoding: "utf-8", timeout: 1e3 }).trim();
46510
+ lines.push(`relay-pty processes: ${psOutput}`);
46511
+ } catch {
46512
+ }
46513
+ try {
46514
+ const dmesgOutput = (0, import_node_child_process4.execSync)('dmesg -T 2>/dev/null | grep -i "killed process" | tail -3 || true', {
46515
+ encoding: "utf-8",
46516
+ timeout: 1e3
46517
+ }).trim();
46518
+ if (dmesgOutput) {
46519
+ lines.push(`Recent OOM kills: ${dmesgOutput.replace(/\n/g, " | ")}`);
46520
+ }
46521
+ } catch {
46522
+ }
46523
+ if (pid) {
46524
+ lines.push(`Killed process PID: ${pid}`);
46525
+ }
46526
+ lines.push(`Agent name: ${agentName}`);
46527
+ lines.push(`Timestamp: ${(/* @__PURE__ */ new Date()).toISOString()}`);
46528
+ } catch (err) {
46529
+ lines.push(`Diagnostics error: ${err instanceof Error ? err.message : String(err)}`);
46530
+ }
46531
+ return lines.join("\n ");
46532
+ }
46496
46533
  constructor(config2) {
46497
46534
  super(config2);
46498
46535
  this.config = config2;
@@ -46708,6 +46745,11 @@ var RelayPtyOrchestrator = class extends BaseWrapper {
46708
46745
  if (!binaryPath) {
46709
46746
  throw new Error("relay-pty binary not found. Build with: cd relay-pty && cargo build --release");
46710
46747
  }
46748
+ try {
46749
+ (0, import_node_fs8.accessSync)(binaryPath, import_node_fs8.constants.X_OK);
46750
+ } catch (err) {
46751
+ throw new Error(`relay-pty binary not executable at ${binaryPath}: ${err?.message ?? "permission denied"}. Build with: cd relay-pty && cargo build --release, or ensure the binary has execute permissions.`);
46752
+ }
46711
46753
  this.log(` Using binary: ${binaryPath}`);
46712
46754
  await this.spawnRelayPty(binaryPath);
46713
46755
  await this.connectToSocket();
@@ -46897,6 +46939,13 @@ var RelayPtyOrchestrator = class extends BaseWrapper {
46897
46939
  if (!this.socketConnected) {
46898
46940
  this.earlyExitInfo = { code, signal, stderr: stderrBuffer };
46899
46941
  }
46942
+ if (signal === "SIGKILL" || exitCode === 137) {
46943
+ const diagnostics = _RelayPtyOrchestrator.gatherSigkillDiagnostics(this.config.name, proc.pid);
46944
+ this.logError(` SIGKILL DETECTED - gathering diagnostics:
46945
+ ${diagnostics}`);
46946
+ console.error(`[relay-pty-orchestrator] SIGKILL for ${this.config.name}:
46947
+ ${diagnostics}`);
46948
+ }
46900
46949
  this.running = false;
46901
46950
  const crashContext = this.memoryMonitor.getCrashContext(this.config.name);
46902
46951
  this.memoryMonitor.unregister(this.config.name);
@@ -47318,7 +47367,10 @@ ${line}
47318
47367
  const exitReason = exitInfo.signal ? `signal ${exitInfo.signal}` : `code ${exitInfo.code ?? "unknown"}`;
47319
47368
  const stderrHint = exitInfo.stderr ? `
47320
47369
  stderr: ${exitInfo.stderr.trim().slice(0, 500)}` : "";
47321
- throw new Error(`relay-pty process died early (${exitReason}).${stderrHint}`);
47370
+ const diagnostics = exitInfo.signal === "SIGKILL" || exitInfo.code === 137 ? `
47371
+ Diagnostics (SIGKILL often indicates OOM or resource limits):
47372
+ ${_RelayPtyOrchestrator.gatherSigkillDiagnostics(this.config.name, this.relayPtyProcess?.pid)}` : "";
47373
+ throw new Error(`relay-pty process died early (${exitReason}).${stderrHint}${diagnostics}`);
47322
47374
  }
47323
47375
  throw new Error("relay-pty process died before socket could be created");
47324
47376
  }
@@ -47338,7 +47390,10 @@ ${line}
47338
47390
  const exitReason = exitInfo.signal ? `signal ${exitInfo.signal}` : `code ${exitInfo.code ?? "unknown"}`;
47339
47391
  const stderrHint = exitInfo.stderr ? `
47340
47392
  stderr: ${exitInfo.stderr.trim().slice(0, 500)}` : "";
47341
- throw new Error(`relay-pty process died during socket connection (${exitReason}).${stderrHint}`);
47393
+ const diagnostics = exitInfo.signal === "SIGKILL" || exitInfo.code === 137 ? `
47394
+ Diagnostics (SIGKILL often indicates OOM or resource limits):
47395
+ ${_RelayPtyOrchestrator.gatherSigkillDiagnostics(this.config.name, this.relayPtyProcess?.pid)}` : "";
47396
+ throw new Error(`relay-pty process died during socket connection (${exitReason}).${stderrHint}${diagnostics}`);
47342
47397
  }
47343
47398
  throw new Error(`Failed to connect to socket after ${maxAttempts} attempts`);
47344
47399
  }
@@ -48611,7 +48666,7 @@ var OpenCodeApi = class {
48611
48666
  var openCodeApi = new OpenCodeApi();
48612
48667
 
48613
48668
  // packages/wrapper/dist/opencode-wrapper.js
48614
- var import_node_child_process4 = require("node:child_process");
48669
+ var import_node_child_process5 = require("node:child_process");
48615
48670
  var TASK_INJECTION = {
48616
48671
  /** Maximum retries when injection fails */
48617
48672
  MAX_RETRIES: 3,
@@ -48719,7 +48774,7 @@ var OpenCodeWrapper = class extends BaseWrapper {
48719
48774
  this.running = true;
48720
48775
  await this.client.connect();
48721
48776
  const args = this.config.args ?? [];
48722
- this.process = (0, import_node_child_process4.spawn)(this.config.command, args, {
48777
+ this.process = (0, import_node_child_process5.spawn)(this.config.command, args, {
48723
48778
  cwd: this.config.cwd,
48724
48779
  env: { ...process.env, ...this.config.env },
48725
48780
  stdio: ["pipe", "pipe", "pipe"]
@@ -48797,7 +48852,7 @@ var OpenCodeWrapper = class extends BaseWrapper {
48797
48852
  */
48798
48853
  async startServe() {
48799
48854
  console.log("[OpenCodeWrapper] Auto-starting opencode serve...");
48800
- this.serveProcess = (0, import_node_child_process4.spawn)("opencode", ["serve"], {
48855
+ this.serveProcess = (0, import_node_child_process5.spawn)("opencode", ["serve"], {
48801
48856
  cwd: this.config.cwd,
48802
48857
  env: { ...process.env, ...this.config.env },
48803
48858
  stdio: "ignore",
@@ -55295,26 +55350,26 @@ var Router = class _Router {
55295
55350
  };
55296
55351
 
55297
55352
  // packages/bridge/dist/utils.js
55298
- var import_node_child_process5 = require("node:child_process");
55353
+ var import_node_child_process6 = require("node:child_process");
55299
55354
  var import_node_util = require("node:util");
55300
- var execAsync = (0, import_node_util.promisify)(import_node_child_process5.exec);
55355
+ var execAsync = (0, import_node_util.promisify)(import_node_child_process6.exec);
55301
55356
  function sleep2(ms) {
55302
55357
  return new Promise((resolve5) => setTimeout(resolve5, ms));
55303
55358
  }
55304
55359
 
55305
55360
  // packages/bridge/dist/shadow-cli.js
55306
- var import_node_child_process7 = require("node:child_process");
55361
+ var import_node_child_process8 = require("node:child_process");
55307
55362
  var import_node_util2 = require("node:util");
55308
55363
 
55309
55364
  // packages/utils/dist/command-resolver.js
55310
- var import_node_child_process6 = require("node:child_process");
55365
+ var import_node_child_process7 = require("node:child_process");
55311
55366
  var import_node_fs10 = __toESM(require("node:fs"), 1);
55312
55367
  function resolveCommand(command) {
55313
55368
  if (command.startsWith("/")) {
55314
55369
  return resolveSymlinks(command);
55315
55370
  }
55316
55371
  try {
55317
- const output = (0, import_node_child_process6.execSync)(`which ${command}`, {
55372
+ const output = (0, import_node_child_process7.execSync)(`which ${command}`, {
55318
55373
  encoding: "utf-8",
55319
55374
  stdio: ["pipe", "pipe", "pipe"],
55320
55375
  // Ensure we have a reasonable PATH
@@ -55348,7 +55403,7 @@ function resolveSymlinks(filePath) {
55348
55403
  }
55349
55404
  function commandExists(command) {
55350
55405
  try {
55351
- (0, import_node_child_process6.execSync)(`which ${command}`, {
55406
+ (0, import_node_child_process7.execSync)(`which ${command}`, {
55352
55407
  encoding: "utf-8",
55353
55408
  stdio: ["pipe", "pipe", "pipe"]
55354
55409
  });
@@ -55359,7 +55414,7 @@ function commandExists(command) {
55359
55414
  }
55360
55415
 
55361
55416
  // packages/bridge/dist/shadow-cli.js
55362
- var execFileAsync = (0, import_node_util2.promisify)(import_node_child_process7.execFile);
55417
+ var execFileAsync = (0, import_node_util2.promisify)(import_node_child_process8.execFile);
55363
55418
  function normalizeCli(cli) {
55364
55419
  if (!cli)
55365
55420
  return null;
@@ -55423,13 +55478,13 @@ async function selectShadowCli(primaryCli, options) {
55423
55478
  }
55424
55479
 
55425
55480
  // packages/bridge/dist/cli-resolution.js
55426
- var import_node_child_process8 = require("node:child_process");
55481
+ var import_node_child_process9 = require("node:child_process");
55427
55482
  init_logger();
55428
55483
  var log2 = createLogger2("cli-resolution");
55429
55484
  function commandExists2(cmd) {
55430
55485
  try {
55431
55486
  const whichCmd = process.platform === "win32" ? "where" : "which";
55432
- (0, import_node_child_process8.execSync)(`${whichCmd} ${cmd}`, { stdio: "ignore" });
55487
+ (0, import_node_child_process9.execSync)(`${whichCmd} ${cmd}`, { stdio: "ignore" });
55433
55488
  return true;
55434
55489
  } catch {
55435
55490
  return false;
@@ -55470,7 +55525,7 @@ function resolveCli(rawCommand) {
55470
55525
 
55471
55526
  // packages/bridge/dist/spawner.js
55472
55527
  var import_node_fs14 = __toESM(require("node:fs"), 1);
55473
- var import_node_child_process12 = require("node:child_process");
55528
+ var import_node_child_process13 = require("node:child_process");
55474
55529
  var import_node_path17 = __toESM(require("node:path"), 1);
55475
55530
  var import_node_url3 = require("node:url");
55476
55531
 
@@ -63593,8 +63648,8 @@ var relayStatusSchema = external_exports.object({});
63593
63648
 
63594
63649
  // packages/mcp/dist/tools/relay-logs.js
63595
63650
  var import_node_util4 = require("node:util");
63596
- var import_node_child_process10 = require("node:child_process");
63597
- var execAsync3 = (0, import_node_util4.promisify)(import_node_child_process10.exec);
63651
+ var import_node_child_process11 = require("node:child_process");
63652
+ var execAsync3 = (0, import_node_util4.promisify)(import_node_child_process11.exec);
63598
63653
  var relayLogsSchema = external_exports.object({
63599
63654
  agent: external_exports.string().describe("Name of the agent to get logs for"),
63600
63655
  lines: external_exports.number().optional().default(50).describe("Number of lines to retrieve (default: 50)")
@@ -63661,6 +63716,14 @@ var relayChannelMessageSchema = external_exports.object({
63661
63716
  message: external_exports.string().describe("The message content"),
63662
63717
  thread: external_exports.string().optional().describe("Optional thread ID for threaded conversations")
63663
63718
  });
63719
+ var relayAdminChannelJoinSchema = external_exports.object({
63720
+ channel: external_exports.string().describe("The channel name"),
63721
+ member: external_exports.string().describe("The agent name to add to the channel")
63722
+ });
63723
+ var relayAdminRemoveMemberSchema = external_exports.object({
63724
+ channel: external_exports.string().describe("The channel name"),
63725
+ member: external_exports.string().describe("The agent name to remove from the channel")
63726
+ });
63664
63727
 
63665
63728
  // packages/mcp/dist/tools/relay-shadow.js
63666
63729
  var relayShadowBindSchema = external_exports.object({
@@ -63685,11 +63748,21 @@ var relayVoteSchema = external_exports.object({
63685
63748
  reason: external_exports.string().optional().describe("Optional reason for your vote")
63686
63749
  });
63687
63750
 
63751
+ // packages/mcp/dist/tools/relay-messages.js
63752
+ var relayQueryMessagesSchema = external_exports.object({
63753
+ limit: external_exports.number().optional().describe("Maximum number of messages to return (default: 100)"),
63754
+ since_ts: external_exports.number().optional().describe("Only return messages after this Unix timestamp (ms)"),
63755
+ from: external_exports.string().optional().describe("Filter by sender name"),
63756
+ to: external_exports.string().optional().describe("Filter by recipient name"),
63757
+ thread: external_exports.string().optional().describe("Filter by thread ID"),
63758
+ order: external_exports.enum(["asc", "desc"]).optional().describe("Sort order (default: desc)")
63759
+ });
63760
+
63688
63761
  // packages/mcp/dist/install.js
63689
63762
  var import_node_fs13 = require("node:fs");
63690
63763
  var import_node_path16 = require("node:path");
63691
63764
  var import_node_os9 = require("node:os");
63692
- var import_node_child_process11 = require("node:child_process");
63765
+ var import_node_child_process12 = require("node:child_process");
63693
63766
 
63694
63767
  // node_modules/smol-toml/dist/error.js
63695
63768
  function getLineColFromPtr(string3, ptr) {
@@ -64597,7 +64670,7 @@ function getConfigPaths() {
64597
64670
  }
64598
64671
  function isUsingNvm() {
64599
64672
  try {
64600
- const nodePath = (0, import_node_child_process11.execSync)("which node", { encoding: "utf-8" }).trim();
64673
+ const nodePath = (0, import_node_child_process12.execSync)("which node", { encoding: "utf-8" }).trim();
64601
64674
  return nodePath.includes(".nvm");
64602
64675
  } catch {
64603
64676
  return false;
@@ -64605,14 +64678,14 @@ function isUsingNvm() {
64605
64678
  }
64606
64679
  function getNodePath() {
64607
64680
  try {
64608
- return (0, import_node_child_process11.execSync)("which node", { encoding: "utf-8" }).trim();
64681
+ return (0, import_node_child_process12.execSync)("which node", { encoding: "utf-8" }).trim();
64609
64682
  } catch {
64610
64683
  return null;
64611
64684
  }
64612
64685
  }
64613
64686
  function getGlobalMcpBinPath() {
64614
64687
  try {
64615
- const npmPrefix = (0, import_node_child_process11.execSync)("npm prefix -g", { encoding: "utf-8" }).trim();
64688
+ const npmPrefix = (0, import_node_child_process12.execSync)("npm prefix -g", { encoding: "utf-8" }).trim();
64616
64689
  const binPath = (0, import_node_path16.join)(npmPrefix, "lib", "node_modules", "@agent-relay", "mcp", "dist", "bin.js");
64617
64690
  if ((0, import_node_fs13.existsSync)(binPath)) {
64618
64691
  return binPath;
@@ -65021,6 +65094,12 @@ var AgentSpawner = class _AgentSpawner {
65021
65094
  policyEnforcementEnabled = false;
65022
65095
  onMarkSpawning;
65023
65096
  onClearSpawning;
65097
+ /**
65098
+ * Set of agent names currently being spawned.
65099
+ * Prevents race conditions where concurrent spawn requests for the same agent
65100
+ * could both pass the activeWorkers.has() check before either completes.
65101
+ */
65102
+ spawningAgents = /* @__PURE__ */ new Set();
65024
65103
  constructor(projectRootOrOptions, _tmuxSession, dashboardPort) {
65025
65104
  const options = typeof projectRootOrOptions === "string" ? { projectRoot: projectRootOrOptions, tmuxSession: _tmuxSession, dashboardPort } : projectRootOrOptions;
65026
65105
  const paths = getProjectPaths(options.projectRoot);
@@ -65045,6 +65124,72 @@ var AgentSpawner = class _AgentSpawner {
65045
65124
  });
65046
65125
  log3.info("Policy enforcement enabled");
65047
65126
  }
65127
+ this.cleanupOrphanedWorkers();
65128
+ }
65129
+ /**
65130
+ * Clean up orphaned relay-pty processes from a previous daemon run.
65131
+ * Reads workers.json to find PIDs from the previous session and kills any
65132
+ * that are still running. This ensures a clean slate after daemon restarts.
65133
+ */
65134
+ cleanupOrphanedWorkers() {
65135
+ if (!import_node_fs14.default.existsSync(this.workersPath)) {
65136
+ return;
65137
+ }
65138
+ try {
65139
+ const raw = JSON.parse(import_node_fs14.default.readFileSync(this.workersPath, "utf-8"));
65140
+ const workers = Array.isArray(raw?.workers) ? raw.workers : [];
65141
+ if (workers.length === 0) {
65142
+ return;
65143
+ }
65144
+ log3.info(`Checking for orphaned workers from previous run (${workers.length} entries)`);
65145
+ let orphansKilled = 0;
65146
+ for (const worker of workers) {
65147
+ if (!worker.pid) {
65148
+ continue;
65149
+ }
65150
+ let isRunning = false;
65151
+ try {
65152
+ process.kill(worker.pid, 0);
65153
+ isRunning = true;
65154
+ } catch {
65155
+ }
65156
+ if (isRunning) {
65157
+ try {
65158
+ const psOutput = (0, import_node_child_process13.execSync)(`ps -p ${worker.pid} -o comm= 2>/dev/null || true`, {
65159
+ encoding: "utf-8",
65160
+ timeout: 1e3
65161
+ }).trim();
65162
+ if (psOutput.includes("relay-pty") || psOutput.includes(worker.cli)) {
65163
+ log3.warn(`Killing orphaned worker "${worker.name}" (PID: ${worker.pid})`);
65164
+ try {
65165
+ process.kill(worker.pid, "SIGTERM");
65166
+ } catch {
65167
+ }
65168
+ const pid = worker.pid;
65169
+ setTimeout(() => {
65170
+ try {
65171
+ process.kill(pid, 0);
65172
+ process.kill(pid, "SIGKILL");
65173
+ log3.warn(`Force killed orphaned worker "${worker.name}" (PID: ${pid})`);
65174
+ } catch {
65175
+ }
65176
+ }, 500);
65177
+ orphansKilled++;
65178
+ } else {
65179
+ log3.debug(`PID ${worker.pid} is running but not relay-pty (${psOutput}), skipping`);
65180
+ }
65181
+ } catch (err) {
65182
+ log3.debug(`Could not verify PID ${worker.pid}, skipping: ${err}`);
65183
+ }
65184
+ }
65185
+ }
65186
+ import_node_fs14.default.writeFileSync(this.workersPath, JSON.stringify({ workers: [] }, null, 2));
65187
+ if (orphansKilled > 0) {
65188
+ log3.info(`Cleaned up ${orphansKilled} orphaned worker(s) from previous run`);
65189
+ }
65190
+ } catch (err) {
65191
+ log3.warn(`Failed to clean up orphaned workers: ${err}`);
65192
+ }
65048
65193
  }
65049
65194
  /**
65050
65195
  * Set cloud policy fetcher for workspace-level policies
@@ -65142,7 +65287,7 @@ var AgentSpawner = class _AgentSpawner {
65142
65287
  return null;
65143
65288
  }
65144
65289
  return await new Promise((resolve5) => {
65145
- (0, import_node_child_process12.execFile)(ghPath, ["auth", "token", "--hostname", "github.com"], { timeout: 5e3 }, (err, stdout) => {
65290
+ (0, import_node_child_process13.execFile)(ghPath, ["auth", "token", "--hostname", "github.com"], { timeout: 5e3 }, (err, stdout) => {
65146
65291
  if (err) {
65147
65292
  resolve5(null);
65148
65293
  return;
@@ -65268,6 +65413,13 @@ var AgentSpawner = class _AgentSpawner {
65268
65413
  error: `Agent "${name}" is already running. Use a different name or release the existing agent first.`
65269
65414
  };
65270
65415
  }
65416
+ if (this.spawningAgents.has(name)) {
65417
+ return {
65418
+ success: false,
65419
+ name,
65420
+ error: `Agent "${name}" spawn is already in progress. Wait for it to complete or use a different name.`
65421
+ };
65422
+ }
65271
65423
  if (this.isAgentConnected(name)) {
65272
65424
  return {
65273
65425
  success: false,
@@ -65300,6 +65452,8 @@ var AgentSpawner = class _AgentSpawner {
65300
65452
  log3.debug(`Policy allowed spawn: ${spawnerName} -> ${name} (source: ${decision.policySource})`);
65301
65453
  }
65302
65454
  }
65455
+ this.spawningAgents.add(name);
65456
+ log3.info(`Spawn lock acquired for ${name} (concurrent spawns: ${this.spawningAgents.size})`);
65303
65457
  try {
65304
65458
  const cliParts = cli.split(" ");
65305
65459
  const rawCommandName = cliParts[0];
@@ -65850,6 +66004,9 @@ ${relayInstructions}`;
65850
66004
  error: tracedError.userMessage,
65851
66005
  errorId: tracedError.errorId
65852
66006
  };
66007
+ } finally {
66008
+ this.spawningAgents.delete(name);
66009
+ log3.info(`Spawn lock released for ${name} (remaining: ${this.spawningAgents.size})`);
65853
66010
  }
65854
66011
  }
65855
66012
  /** Role presets for shadow agents */
@@ -71120,7 +71277,7 @@ var SyncQueue = class {
71120
71277
  // packages/utils/dist/git-remote.js
71121
71278
  var fs26 = __toESM(require("node:fs"), 1);
71122
71279
  var path27 = __toESM(require("node:path"), 1);
71123
- var import_node_child_process13 = require("node:child_process");
71280
+ var import_node_child_process14 = require("node:child_process");
71124
71281
  function parseGitRemoteUrl(url) {
71125
71282
  if (!url)
71126
71283
  return null;
@@ -71140,7 +71297,7 @@ function getGitRemoteUrl(workingDirectory, remoteName = "origin") {
71140
71297
  if (!fs26.existsSync(gitDir)) {
71141
71298
  return null;
71142
71299
  }
71143
- const result = (0, import_node_child_process13.execSync)(`git remote get-url ${remoteName}`, {
71300
+ const result = (0, import_node_child_process14.execSync)(`git remote get-url ${remoteName}`, {
71144
71301
  cwd: workingDirectory,
71145
71302
  encoding: "utf-8",
71146
71303
  timeout: 5e3,
@@ -73994,7 +74151,7 @@ var Daemon = class _Daemon {
73994
74151
  return this.consensus;
73995
74152
  }
73996
74153
  };
73997
- var isMainModule = import_meta_url === `file://${process.argv[1]}`;
74154
+ var isMainModule = import_meta_url === `file://${process.argv[1]}` && !process.env.AGENT_RELAY_VERSION;
73998
74155
  if (isMainModule) {
73999
74156
  const daemon = new Daemon();
74000
74157
  process.on("SIGINT", async () => {
@@ -74748,15 +74905,15 @@ var Orchestrator = class extends import_events8.EventEmitter {
74748
74905
  */
74749
74906
  getGitInfo(workspacePath) {
74750
74907
  try {
74751
- const { execSync: execSync10 } = require("child_process");
74752
- const branch = execSync10("git branch --show-current", {
74908
+ const { execSync: execSync12 } = require("child_process");
74909
+ const branch = execSync12("git branch --show-current", {
74753
74910
  cwd: workspacePath,
74754
74911
  encoding: "utf8",
74755
74912
  stdio: ["pipe", "pipe", "pipe"]
74756
74913
  }).trim();
74757
74914
  let remote;
74758
74915
  try {
74759
- remote = execSync10("git remote get-url origin", {
74916
+ remote = execSync12("git remote get-url origin", {
74760
74917
  cwd: workspacePath,
74761
74918
  encoding: "utf8",
74762
74919
  stdio: ["pipe", "pipe", "pipe"]
@@ -77240,7 +77397,7 @@ function manageContext(compactor, messages) {
77240
77397
  }
77241
77398
 
77242
77399
  // packages/daemon/dist/cli-auth.js
77243
- var import_node_child_process14 = require("node:child_process");
77400
+ var import_node_child_process15 = require("node:child_process");
77244
77401
  var fs33 = __toESM(require("fs/promises"), 1);
77245
77402
  var os15 = __toESM(require("os"), 1);
77246
77403
  var import_node_path27 = require("node:path");
@@ -77357,7 +77514,7 @@ async function startCLIAuth(provider, options = {}) {
77357
77514
  config2.command,
77358
77515
  ...args
77359
77516
  ];
77360
- const proc = (0, import_node_child_process14.spawn)(relayPtyPath, relayArgs, {
77517
+ const proc = (0, import_node_child_process15.spawn)(relayPtyPath, relayArgs, {
77361
77518
  cwd: process.cwd(),
77362
77519
  env: {
77363
77520
  ...process.env,
@@ -78599,11 +78756,11 @@ var HookRegistry = class {
78599
78756
  };
78600
78757
 
78601
78758
  // packages/trajectory/dist/integration.js
78602
- var import_node_child_process15 = require("node:child_process");
78759
+ var import_node_child_process16 = require("node:child_process");
78603
78760
  async function runTrail2(args) {
78604
78761
  return new Promise((resolve5) => {
78605
78762
  const trajectoryEnv = getTrajectoryEnvVars();
78606
- const proc = (0, import_node_child_process15.spawn)("trail", args, {
78763
+ const proc = (0, import_node_child_process16.spawn)("trail", args, {
78607
78764
  cwd: getProjectPaths().projectRoot,
78608
78765
  env: { ...process.env, ...trajectoryEnv },
78609
78766
  stdio: ["pipe", "pipe", "pipe"]
@@ -78874,7 +79031,7 @@ var TrajectoryIntegration2 = class {
78874
79031
  */
78875
79032
  isTrailInstalledSync() {
78876
79033
  try {
78877
- (0, import_node_child_process15.execSync)("which trail", { stdio: "pipe" });
79034
+ (0, import_node_child_process16.execSync)("which trail", { stdio: "pipe" });
78878
79035
  return true;
78879
79036
  } catch {
78880
79037
  return false;
@@ -2140,6 +2140,49 @@ program
2140
2140
  process.exit(1);
2141
2141
  }
2142
2142
  });
2143
+ // send - Send a message to an agent via the local daemon
2144
+ program
2145
+ .command('send')
2146
+ .description('Send a message to an agent')
2147
+ .argument('<agent>', 'Target agent name (or * for broadcast, #channel for channel)')
2148
+ .argument('<message>', 'Message to send')
2149
+ .option('--from <name>', 'Sender name', 'cli')
2150
+ .option('--thread <id>', 'Thread identifier')
2151
+ .action(async (agent, message, options) => {
2152
+ const paths = getProjectPaths();
2153
+ const client = new RelayClient({
2154
+ socketPath: paths.socketPath,
2155
+ agentName: options.from,
2156
+ entityType: 'user',
2157
+ quiet: true,
2158
+ reconnect: false,
2159
+ maxReconnectAttempts: 0,
2160
+ reconnectDelayMs: 0,
2161
+ reconnectMaxDelayMs: 0,
2162
+ });
2163
+ try {
2164
+ await client.connect();
2165
+ const sent = client.sendMessage(agent, message, 'message', undefined, options.thread);
2166
+ if (sent) {
2167
+ console.log(`Message sent to ${agent}`);
2168
+ }
2169
+ else {
2170
+ console.error('Failed to send message');
2171
+ process.exit(1);
2172
+ }
2173
+ client.disconnect();
2174
+ }
2175
+ catch (err) {
2176
+ if (err.code === 'ECONNREFUSED' || err.code === 'ENOENT' || err.message?.includes('Cannot connect')) {
2177
+ console.error(`Cannot connect to daemon. Is it running?`);
2178
+ console.log(`Run 'agent-relay up' to start the daemon.`);
2179
+ }
2180
+ else {
2181
+ console.error(`Failed to send message: ${err.message}`);
2182
+ }
2183
+ process.exit(1);
2184
+ }
2185
+ });
2143
2186
  // agents:kill - Kill a spawned agent by PID
2144
2187
  program
2145
2188
  .command('agents:kill')