agent-relay 6.3.5 → 7.0.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/index.cjs +300 -104
  2. package/package.json +9 -9
package/dist/index.cjs CHANGED
@@ -43503,6 +43503,7 @@ __export(index_exports, {
43503
43503
  DEFAULT_PROXY_ENV_REGISTRY: () => DEFAULT_PROXY_ENV_REGISTRY,
43504
43504
  DefaultModels: () => DefaultModels,
43505
43505
  ERROR_SEARCH_HINT: () => ERROR_SEARCH_HINT,
43506
+ EventBus: () => EventBus,
43506
43507
  GEMINI_MODEL_OPTIONS: () => GEMINI_MODEL_OPTIONS,
43507
43508
  GeminiModels: () => GeminiModels,
43508
43509
  GitHubClient: () => GitHubClient,
@@ -44423,6 +44424,69 @@ function formatBrokerNotFoundError() {
44423
44424
  return `@agent-relay/sdk couldn't find an agent-relay-broker binary for ${process.platform}-${process.arch}. The optional dependency ${pkgName} is expected to be installed alongside @agent-relay/sdk. Try reinstalling with --include=optional, or set BROKER_BINARY_PATH to point at a broker binary you've downloaded manually.`;
44424
44425
  }
44425
44426
 
44427
+ // packages/sdk/dist/event-bus.js
44428
+ var EventBus = class {
44429
+ handlers = /* @__PURE__ */ new Map();
44430
+ /**
44431
+ * Register a handler for `event`. Returns an unsubscribe function that
44432
+ * removes the handler when called.
44433
+ */
44434
+ addListener(event, handler) {
44435
+ let set2 = this.handlers.get(event);
44436
+ if (!set2) {
44437
+ set2 = /* @__PURE__ */ new Set();
44438
+ this.handlers.set(event, set2);
44439
+ }
44440
+ set2.add(handler);
44441
+ return () => {
44442
+ const current = this.handlers.get(event);
44443
+ if (current !== set2)
44444
+ return;
44445
+ current.delete(handler);
44446
+ if (current.size === 0) {
44447
+ this.handlers.delete(event);
44448
+ }
44449
+ };
44450
+ }
44451
+ /** Remove a previously-registered handler. Idempotent. */
44452
+ removeListener(event, handler) {
44453
+ const set2 = this.handlers.get(event);
44454
+ if (!set2)
44455
+ return;
44456
+ set2.delete(handler);
44457
+ if (set2.size === 0) {
44458
+ this.handlers.delete(event);
44459
+ }
44460
+ }
44461
+ /** Number of currently-registered handlers for `event`. Useful for tests. */
44462
+ listenerCount(event) {
44463
+ return this.handlers.get(event)?.size ?? 0;
44464
+ }
44465
+ /** Snapshot the handlers for `event` so iteration is safe under concurrent mutation. */
44466
+ listeners(event) {
44467
+ const set2 = this.handlers.get(event);
44468
+ return set2 ? Array.from(set2) : [];
44469
+ }
44470
+ /**
44471
+ * Fire `event` with `args`, awaiting each handler sequentially in
44472
+ * registration order. Handler exceptions are caught and logged; they
44473
+ * never abort the dispatch chain.
44474
+ *
44475
+ * Return value is intentionally `void`; consumers that need to collect
44476
+ * patches (e.g. `beforeAgentSpawn`'s shallow-merge contract) iterate
44477
+ * `listeners()` directly so they can capture each handler's return.
44478
+ */
44479
+ async emit(event, ...args) {
44480
+ for (const handler of this.listeners(event)) {
44481
+ try {
44482
+ await handler(...args);
44483
+ } catch (err) {
44484
+ console.error(`[agent-relay] listener for "${String(event)}" threw:`, err);
44485
+ }
44486
+ }
44487
+ }
44488
+ };
44489
+
44426
44490
  // packages/sdk/dist/client.js
44427
44491
  function isHeadlessProvider(value) {
44428
44492
  return value === "claude" || value === "opencode";
@@ -44430,6 +44494,45 @@ function isHeadlessProvider(value) {
44430
44494
  function resolveSpawnTransport(input) {
44431
44495
  return input.transport ?? (input.provider === "opencode" ? "headless" : "pty");
44432
44496
  }
44497
+ function buildSpawnPtyBody(input) {
44498
+ return {
44499
+ name: input.name,
44500
+ cli: input.cli,
44501
+ ...input.model !== void 0 ? { model: input.model } : {},
44502
+ args: input.args ?? [],
44503
+ ...input.task !== void 0 ? { task: input.task } : {},
44504
+ channels: input.channels ?? [],
44505
+ ...input.cwd !== void 0 ? { cwd: input.cwd } : {},
44506
+ ...input.team !== void 0 ? { team: input.team } : {},
44507
+ ...input.agentToken !== void 0 ? { agentToken: input.agentToken } : {},
44508
+ ...input.shadowOf !== void 0 ? { shadowOf: input.shadowOf } : {},
44509
+ ...input.shadowMode !== void 0 ? { shadowMode: input.shadowMode } : {},
44510
+ ...input.continueFrom !== void 0 ? { continueFrom: input.continueFrom } : {},
44511
+ ...input.idleThresholdSecs !== void 0 ? { idleThresholdSecs: input.idleThresholdSecs } : {},
44512
+ ...input.restartPolicy !== void 0 ? { restartPolicy: input.restartPolicy } : {},
44513
+ ...input.skipRelayPrompt !== void 0 ? { skipRelayPrompt: input.skipRelayPrompt } : {}
44514
+ };
44515
+ }
44516
+ function buildSpawnProviderBody(input, transport) {
44517
+ return {
44518
+ name: input.name,
44519
+ cli: input.provider,
44520
+ ...input.model !== void 0 ? { model: input.model } : {},
44521
+ args: input.args ?? [],
44522
+ ...input.task !== void 0 ? { task: input.task } : {},
44523
+ channels: input.channels ?? [],
44524
+ ...input.cwd !== void 0 ? { cwd: input.cwd } : {},
44525
+ ...input.team !== void 0 ? { team: input.team } : {},
44526
+ ...input.agentToken !== void 0 ? { agentToken: input.agentToken } : {},
44527
+ ...input.shadowOf !== void 0 ? { shadowOf: input.shadowOf } : {},
44528
+ ...input.shadowMode !== void 0 ? { shadowMode: input.shadowMode } : {},
44529
+ ...input.continueFrom !== void 0 ? { continueFrom: input.continueFrom } : {},
44530
+ ...input.idleThresholdSecs !== void 0 ? { idleThresholdSecs: input.idleThresholdSecs } : {},
44531
+ ...input.restartPolicy !== void 0 ? { restartPolicy: input.restartPolicy } : {},
44532
+ ...input.skipRelayPrompt !== void 0 ? { skipRelayPrompt: input.skipRelayPrompt } : {},
44533
+ transport
44534
+ };
44535
+ }
44433
44536
  function isProcessRunning(pid) {
44434
44537
  if (!Number.isInteger(pid) || pid <= 0) {
44435
44538
  return false;
@@ -44467,7 +44570,13 @@ var AgentRelayClient = class _AgentRelayClient {
44467
44570
  /** Lease renewal timer (only for spawned brokers). */
44468
44571
  leaseTimer = null;
44469
44572
  workspaceKey;
44573
+ /** Resolved broker URL — captured so call-site lifecycle contexts can surface it. */
44574
+ baseUrl;
44575
+ /** Shared multi-listener registry. Created bare when no `eventBus` is passed in. */
44576
+ eventBus;
44470
44577
  constructor(options) {
44578
+ this.baseUrl = options.baseUrl;
44579
+ this.eventBus = options.eventBus ?? new EventBus();
44471
44580
  this.transport = new BrokerTransport({
44472
44581
  baseUrl: options.baseUrl,
44473
44582
  apiKey: options.apiKey,
@@ -44475,6 +44584,39 @@ var AgentRelayClient = class _AgentRelayClient {
44475
44584
  requestTimeoutMs: options.requestTimeoutMs
44476
44585
  });
44477
44586
  }
44587
+ /**
44588
+ * Register a listener on the client's event bus. Returns an unsubscribe
44589
+ * function. Equivalent to `client.eventBus.addListener(...)` but mirrors
44590
+ * the `AgentRelay` facade API so direct-client callers don't need to
44591
+ * reach through `.eventBus`.
44592
+ */
44593
+ addListener(event, handler) {
44594
+ return this.eventBus.addListener(event, handler);
44595
+ }
44596
+ /** Remove a previously-registered listener. */
44597
+ removeListener(event, handler) {
44598
+ this.eventBus.removeListener(event, handler);
44599
+ }
44600
+ /**
44601
+ * Fold `beforeAgentSpawn` patches into the input. Listeners run in
44602
+ * registration order; each may return a {@link SpawnPatch} that is
44603
+ * shallow-merged over the running result. Handler exceptions are caught
44604
+ * and logged but do not abort the chain.
44605
+ */
44606
+ async runBeforeSpawn(ctx) {
44607
+ let resolved = { ...ctx.input };
44608
+ for (const handler of this.eventBus.listeners("beforeAgentSpawn")) {
44609
+ try {
44610
+ const patch = await handler({ ...ctx, input: resolved });
44611
+ if (patch && typeof patch === "object") {
44612
+ resolved = { ...resolved, ...patch };
44613
+ }
44614
+ } catch (err) {
44615
+ console.error("[agent-relay] beforeAgentSpawn listener threw:", err);
44616
+ }
44617
+ }
44618
+ return resolved;
44619
+ }
44478
44620
  /**
44479
44621
  * Connect to an already-running broker by reading its connection file.
44480
44622
  *
@@ -44504,7 +44646,11 @@ var AgentRelayClient = class _AgentRelayClient {
44504
44646
  if (!isProcessRunning(conn.pid)) {
44505
44647
  throw new Error(`Stale broker connection file (${connPath}) points to dead pid ${conn.pid}. Start the broker with 'agent-relay up' or use AgentRelayClient.spawn().`);
44506
44648
  }
44507
- return new _AgentRelayClient({ baseUrl: conn.url, apiKey: conn.api_key });
44649
+ return new _AgentRelayClient({
44650
+ baseUrl: conn.url,
44651
+ apiKey: conn.api_key,
44652
+ ...options?.eventBus ? { eventBus: options.eventBus } : {}
44653
+ });
44508
44654
  }
44509
44655
  /**
44510
44656
  * Spawn a local broker process and return a connected client.
@@ -44564,7 +44710,8 @@ var AgentRelayClient = class _AgentRelayClient {
44564
44710
  const client = new _AgentRelayClient({
44565
44711
  baseUrl,
44566
44712
  apiKey,
44567
- requestTimeoutMs: options?.requestTimeoutMs
44713
+ requestTimeoutMs: options?.requestTimeoutMs,
44714
+ ...options?.eventBus ? { eventBus: options.eventBus } : {}
44568
44715
  });
44569
44716
  client.child = child;
44570
44717
  const brokerExited = new Promise((_2, reject) => {
@@ -44632,53 +44779,52 @@ var AgentRelayClient = class _AgentRelayClient {
44632
44779
  }
44633
44780
  // ── Agent lifecycle ────────────────────────────────────────────────
44634
44781
  async spawnPty(input) {
44635
- return this.transport.request("/api/spawn", {
44636
- method: "POST",
44637
- body: JSON.stringify({
44638
- name: input.name,
44639
- cli: input.cli,
44640
- ...input.model !== void 0 ? { model: input.model } : {},
44641
- args: input.args ?? [],
44642
- ...input.task !== void 0 ? { task: input.task } : {},
44643
- channels: input.channels ?? [],
44644
- ...input.cwd !== void 0 ? { cwd: input.cwd } : {},
44645
- ...input.team !== void 0 ? { team: input.team } : {},
44646
- ...input.agentToken !== void 0 ? { agentToken: input.agentToken } : {},
44647
- ...input.shadowOf !== void 0 ? { shadowOf: input.shadowOf } : {},
44648
- ...input.shadowMode !== void 0 ? { shadowMode: input.shadowMode } : {},
44649
- ...input.continueFrom !== void 0 ? { continueFrom: input.continueFrom } : {},
44650
- ...input.idleThresholdSecs !== void 0 ? { idleThresholdSecs: input.idleThresholdSecs } : {},
44651
- ...input.restartPolicy !== void 0 ? { restartPolicy: input.restartPolicy } : {},
44652
- ...input.skipRelayPrompt !== void 0 ? { skipRelayPrompt: input.skipRelayPrompt } : {}
44653
- })
44654
- });
44782
+ const beforeCtx = {
44783
+ kind: "pty",
44784
+ input,
44785
+ spawnerPid: process.pid,
44786
+ spawnStartTs: (/* @__PURE__ */ new Date()).toISOString(),
44787
+ baseUrl: this.baseUrl
44788
+ };
44789
+ const t0 = Date.now();
44790
+ const resolvedInput = await this.runBeforeSpawn(beforeCtx);
44791
+ try {
44792
+ const result = await this.transport.request("/api/spawn", {
44793
+ method: "POST",
44794
+ body: JSON.stringify(buildSpawnPtyBody(resolvedInput))
44795
+ });
44796
+ await this.emitAfterSpawn(beforeCtx, resolvedInput, t0, result, void 0);
44797
+ return result;
44798
+ } catch (err) {
44799
+ await this.emitAfterSpawn(beforeCtx, resolvedInput, t0, void 0, err);
44800
+ throw err;
44801
+ }
44655
44802
  }
44656
44803
  async spawnProvider(input) {
44657
44804
  const transport = resolveSpawnTransport(input);
44658
44805
  if (transport === "headless" && !isHeadlessProvider(input.provider)) {
44659
44806
  throw new Error(`provider '${input.provider}' does not support headless transport (supported: claude, opencode)`);
44660
44807
  }
44661
- return this.transport.request("/api/spawn", {
44662
- method: "POST",
44663
- body: JSON.stringify({
44664
- name: input.name,
44665
- cli: input.provider,
44666
- ...input.model !== void 0 ? { model: input.model } : {},
44667
- args: input.args ?? [],
44668
- ...input.task !== void 0 ? { task: input.task } : {},
44669
- channels: input.channels ?? [],
44670
- ...input.cwd !== void 0 ? { cwd: input.cwd } : {},
44671
- ...input.team !== void 0 ? { team: input.team } : {},
44672
- ...input.agentToken !== void 0 ? { agentToken: input.agentToken } : {},
44673
- ...input.shadowOf !== void 0 ? { shadowOf: input.shadowOf } : {},
44674
- ...input.shadowMode !== void 0 ? { shadowMode: input.shadowMode } : {},
44675
- ...input.continueFrom !== void 0 ? { continueFrom: input.continueFrom } : {},
44676
- ...input.idleThresholdSecs !== void 0 ? { idleThresholdSecs: input.idleThresholdSecs } : {},
44677
- ...input.restartPolicy !== void 0 ? { restartPolicy: input.restartPolicy } : {},
44678
- ...input.skipRelayPrompt !== void 0 ? { skipRelayPrompt: input.skipRelayPrompt } : {},
44679
- transport
44680
- })
44681
- });
44808
+ const beforeCtx = {
44809
+ kind: "provider",
44810
+ input,
44811
+ spawnerPid: process.pid,
44812
+ spawnStartTs: (/* @__PURE__ */ new Date()).toISOString(),
44813
+ baseUrl: this.baseUrl
44814
+ };
44815
+ const t0 = Date.now();
44816
+ const resolvedInput = await this.runBeforeSpawn(beforeCtx);
44817
+ try {
44818
+ const result = await this.transport.request("/api/spawn", {
44819
+ method: "POST",
44820
+ body: JSON.stringify(buildSpawnProviderBody(resolvedInput, transport))
44821
+ });
44822
+ await this.emitAfterSpawn(beforeCtx, resolvedInput, t0, result, void 0);
44823
+ return result;
44824
+ } catch (err) {
44825
+ await this.emitAfterSpawn(beforeCtx, resolvedInput, t0, void 0, err);
44826
+ throw err;
44827
+ }
44682
44828
  }
44683
44829
  async spawnHeadless(input) {
44684
44830
  return this.spawnProvider({ ...input, transport: "headless" });
@@ -44690,10 +44836,39 @@ var AgentRelayClient = class _AgentRelayClient {
44690
44836
  return this.spawnProvider({ ...input, provider: "opencode" });
44691
44837
  }
44692
44838
  async release(name, reason) {
44693
- return this.transport.request(`/api/spawned/${encodeURIComponent(name)}`, {
44694
- method: "DELETE",
44695
- ...reason ? { body: JSON.stringify({ reason }) } : {}
44696
- });
44839
+ const beforeCtx = { name, reason, baseUrl: this.baseUrl };
44840
+ const t0 = Date.now();
44841
+ await this.eventBus.emit("beforeAgentRelease", beforeCtx);
44842
+ try {
44843
+ const result = await this.transport.request(`/api/spawned/${encodeURIComponent(name)}`, {
44844
+ method: "DELETE",
44845
+ ...reason ? { body: JSON.stringify({ reason }) } : {}
44846
+ });
44847
+ const afterCtx = {
44848
+ ...beforeCtx,
44849
+ durationMs: Date.now() - t0
44850
+ };
44851
+ await this.eventBus.emit("afterAgentRelease", afterCtx);
44852
+ return result;
44853
+ } catch (err) {
44854
+ const afterCtx = {
44855
+ ...beforeCtx,
44856
+ error: err instanceof Error ? err : new Error(String(err)),
44857
+ durationMs: Date.now() - t0
44858
+ };
44859
+ await this.eventBus.emit("afterAgentRelease", afterCtx);
44860
+ throw err;
44861
+ }
44862
+ }
44863
+ async emitAfterSpawn(beforeCtx, resolvedInput, startMs, result, error51) {
44864
+ const afterCtx = {
44865
+ ...beforeCtx,
44866
+ resolvedInput,
44867
+ ...result ? { result } : {},
44868
+ ...error51 !== void 0 ? { error: error51 instanceof Error ? error51 : new Error(String(error51)) } : {},
44869
+ durationMs: Date.now() - startMs
44870
+ };
44871
+ await this.eventBus.emit("afterAgentSpawn", afterCtx);
44697
44872
  }
44698
44873
  async listAgents() {
44699
44874
  const result = await this.transport.request("/api/spawned");
@@ -70529,20 +70704,40 @@ function toWorkspaceRegistryEntry(value) {
70529
70704
  };
70530
70705
  }
70531
70706
  var AgentRelay = class {
70532
- // Event hooks — assign a callback or null to clear.
70533
- onMessageReceived = null;
70534
- onMessageSent = null;
70535
- onAgentSpawned = null;
70536
- onAgentReleased = null;
70537
- onAgentExited = null;
70538
- onAgentReady = null;
70539
- onWorkerOutput = null;
70540
- onDeliveryUpdate = null;
70541
- onAgentExitRequested = null;
70542
- onAgentIdle = null;
70543
- onAgentActivityChanged = null;
70544
- onChannelSubscribed = null;
70545
- onChannelUnsubscribed = null;
70707
+ /**
70708
+ * Multi-listener event registry. Subscribe via {@link addListener} or
70709
+ * `bus.addListener` directly; emit happens internally as broker events
70710
+ * arrive and at SDK call sites for the spawn / release lifecycle hooks.
70711
+ *
70712
+ * The bus is shared with the underlying `AgentRelayClient` (created via
70713
+ * {@link ensureStarted}) so listeners registered on either object see
70714
+ * the same events.
70715
+ */
70716
+ bus = new EventBus();
70717
+ // ── Listener registration ───────────────────────────────────────────────
70718
+ /**
70719
+ * Register a listener for a relay lifecycle event. Returns an
70720
+ * unsubscribe function.
70721
+ *
70722
+ * Example:
70723
+ * ```ts
70724
+ * const off = relay.addListener('agentSpawned', (agent) => console.log(agent.name));
70725
+ * // later:
70726
+ * off();
70727
+ * ```
70728
+ *
70729
+ * Replaces the pre-2.x single-callback `on*` fields. Multiple listeners
70730
+ * can register for the same event; they fire sequentially in
70731
+ * registration order. Async handlers are awaited. Handler exceptions
70732
+ * are caught and logged; one bad listener never blocks the others.
70733
+ */
70734
+ addListener(event, handler) {
70735
+ return this.bus.addListener(event, handler);
70736
+ }
70737
+ /** Remove a previously-registered listener. Idempotent. */
70738
+ removeListener(event, handler) {
70739
+ this.bus.removeListener(event, handler);
70740
+ }
70546
70741
  // ── Public accessors ────────────────────────────────────────────────────
70547
70742
  /** The resolved Relaycast workspace API key (available after first spawn). */
70548
70743
  get workspaceKey() {
@@ -70893,7 +71088,7 @@ var AgentRelay = class {
70893
71088
  data: input.data,
70894
71089
  mode: input.mode
70895
71090
  };
70896
- this.onMessageSent?.(msg);
71091
+ void this.bus.emit("messageSent", msg);
70897
71092
  return msg;
70898
71093
  }
70899
71094
  };
@@ -71268,7 +71463,7 @@ var AgentRelay = class {
71268
71463
  return;
71269
71464
  }
71270
71465
  state.active = active;
71271
- this.onAgentActivityChanged?.({
71466
+ void this.bus.emit("agentActivityChanged", {
71272
71467
  name,
71273
71468
  active,
71274
71469
  pendingDeliveries: state.pendingDeliveries.size,
@@ -71385,6 +71580,7 @@ var AgentRelay = class {
71385
71580
  return this.startPromise;
71386
71581
  this.startPromise = this.ensureRelaycastApiKey().then(() => AgentRelayClient.spawn({
71387
71582
  ...this.clientOptions,
71583
+ eventBus: this.bus,
71388
71584
  onStderr: (line) => {
71389
71585
  for (const listener of this.stderrListeners) {
71390
71586
  try {
@@ -71434,7 +71630,7 @@ var AgentRelay = class {
71434
71630
  threadId: event.thread_id,
71435
71631
  mode: event.injection_mode ?? event.mode
71436
71632
  };
71437
- this.onMessageReceived?.(msg);
71633
+ void this.bus.emit("messageReceived", msg);
71438
71634
  break;
71439
71635
  }
71440
71636
  case "agent_spawned": {
@@ -71443,7 +71639,7 @@ var AgentRelay = class {
71443
71639
  this.messageReadyAgents.delete(event.name);
71444
71640
  this.exitedAgents.delete(event.name);
71445
71641
  this.idleAgents.delete(event.name);
71446
- this.onAgentSpawned?.(agent);
71642
+ void this.bus.emit("agentSpawned", agent);
71447
71643
  break;
71448
71644
  }
71449
71645
  case "agent_released": {
@@ -71453,7 +71649,7 @@ var AgentRelay = class {
71453
71649
  this.readyAgents.delete(event.name);
71454
71650
  this.messageReadyAgents.delete(event.name);
71455
71651
  this.idleAgents.delete(event.name);
71456
- this.onAgentReleased?.(agent);
71652
+ void this.bus.emit("agentReleased", agent);
71457
71653
  this.knownAgents.delete(event.name);
71458
71654
  this.outputListeners.delete(event.name);
71459
71655
  this.exitResolvers.get(event.name)?.resolve("released");
@@ -71474,7 +71670,7 @@ var AgentRelay = class {
71474
71670
  if (event.reason !== void 0) {
71475
71671
  agent.exitReason = event.reason;
71476
71672
  }
71477
- this.onAgentExited?.(agent);
71673
+ void this.bus.emit("agentExited", agent);
71478
71674
  this.knownAgents.delete(event.name);
71479
71675
  this.outputListeners.delete(event.name);
71480
71676
  this.exitResolvers.get(event.name)?.resolve("exited");
@@ -71486,7 +71682,7 @@ var AgentRelay = class {
71486
71682
  case "agent_exit": {
71487
71683
  const agent = this.knownAgents.get(event.name) ?? this.ensureAgentHandle(event.name, "pty", []);
71488
71684
  agent.exitReason = event.reason;
71489
- this.onAgentExitRequested?.({ name: event.name, reason: event.reason });
71685
+ void this.bus.emit("agentExitRequested", { name: event.name, reason: event.reason });
71490
71686
  break;
71491
71687
  }
71492
71688
  case "worker_ready": {
@@ -71494,17 +71690,17 @@ var AgentRelay = class {
71494
71690
  this.readyAgents.add(event.name);
71495
71691
  this.exitedAgents.delete(event.name);
71496
71692
  this.idleAgents.delete(event.name);
71497
- this.onAgentReady?.(agent);
71693
+ void this.bus.emit("agentReady", agent);
71498
71694
  break;
71499
71695
  }
71500
71696
  case "channel_subscribed": {
71501
71697
  this.addAgentChannels(event.name, event.channels);
71502
- this.onChannelSubscribed?.(event.name, event.channels);
71698
+ void this.bus.emit("channelSubscribed", { agent: event.name, channels: event.channels });
71503
71699
  break;
71504
71700
  }
71505
71701
  case "channel_unsubscribed": {
71506
71702
  this.removeAgentChannels(event.name, event.channels);
71507
- this.onChannelUnsubscribed?.(event.name, event.channels);
71703
+ void this.bus.emit("channelUnsubscribed", { agent: event.name, channels: event.channels });
71508
71704
  break;
71509
71705
  }
71510
71706
  case "delivery_queued": {
@@ -71550,7 +71746,7 @@ var AgentRelay = class {
71550
71746
  }
71551
71747
  case "worker_stream": {
71552
71748
  this.idleAgents.delete(event.name);
71553
- this.onWorkerOutput?.({
71749
+ void this.bus.emit("workerOutput", {
71554
71750
  name: event.name,
71555
71751
  stream: event.stream,
71556
71752
  chunk: event.chunk
@@ -71561,7 +71757,7 @@ var AgentRelay = class {
71561
71757
  case "agent_idle": {
71562
71758
  this.clearAgentDeliveries(event.name, "agent_idle");
71563
71759
  this.idleAgents.add(event.name);
71564
- this.onAgentIdle?.({
71760
+ void this.bus.emit("agentIdle", {
71565
71761
  name: event.name,
71566
71762
  idleSecs: event.idle_secs
71567
71763
  });
@@ -71571,7 +71767,7 @@ var AgentRelay = class {
71571
71767
  }
71572
71768
  }
71573
71769
  if (event.kind.startsWith("delivery_") || event.kind.startsWith("message_delivery_")) {
71574
- this.onDeliveryUpdate?.(event);
71770
+ void this.bus.emit("deliveryUpdate", event);
71575
71771
  }
71576
71772
  });
71577
71773
  }
@@ -71730,7 +71926,7 @@ var AgentRelay = class {
71730
71926
  data: input.data,
71731
71927
  mode: input.mode
71732
71928
  };
71733
- relay.onMessageSent?.(msg);
71929
+ void relay.bus.emit("messageSent", msg);
71734
71930
  return msg;
71735
71931
  },
71736
71932
  async subscribe(channelsToAdd) {
@@ -72394,7 +72590,7 @@ var ShadowManager = class {
72394
72590
  * Determine which shadows should receive a copy of a message
72395
72591
  * between `from` and `to`. Returns a list of shadow copies to deliver.
72396
72592
  *
72397
- * Call this from your `onMessageReceived` / `onMessageSent` hooks
72593
+ * Call this from your `messageReceived` / `messageSent` listeners
72398
72594
  * to fan out shadow copies.
72399
72595
  */
72400
72596
  intercept(from, to2) {
@@ -72882,15 +73078,15 @@ async function spawnFromEnv(options = {}) {
72882
73078
  cwd: policy.cwd ?? process.cwd(),
72883
73079
  env
72884
73080
  });
72885
- relay.onAgentSpawned = (agent) => {
73081
+ relay.addListener("agentSpawned", (agent) => {
72886
73082
  console.log(`[spawn-from-env] Agent spawned: ${agent.name}`);
72887
- };
72888
- relay.onAgentReady = (agent) => {
73083
+ });
73084
+ relay.addListener("agentReady", (agent) => {
72889
73085
  console.log(`[spawn-from-env] Agent ready: ${agent.name}`);
72890
- };
72891
- relay.onAgentExited = (agent) => {
73086
+ });
73087
+ relay.addListener("agentExited", (agent) => {
72892
73088
  console.log(`[spawn-from-env] Agent exited: ${agent.name} code=${agent.exitCode ?? "none"} signal=${agent.exitSignal ?? "none"}`);
72893
- };
73089
+ });
72894
73090
  try {
72895
73091
  const agent = await relay.spawnPty({
72896
73092
  name: policy.name,
@@ -82414,6 +82610,7 @@ var WorkflowRunner = class _WorkflowRunner {
82414
82610
  runStartTime;
82415
82611
  /** Unsubscribe handle for broker stderr listener wired during a run. */
82416
82612
  unsubBrokerStderr;
82613
+ unsubRelayListeners = [];
82417
82614
  /** Tracks last idle log time per agent to debounce idle warnings (30s multiples). */
82418
82615
  lastIdleLog = /* @__PURE__ */ new Map();
82419
82616
  /** Tracks last logged activity type per agent to avoid duplicate status lines. */
@@ -84405,7 +84602,7 @@ ${err.suggestion}`);
84405
84602
  // long-running PTY processes from earlier steps. 120s gives room to breathe.
84406
84603
  requestTimeoutMs: this.relayOptions.requestTimeoutMs ?? 12e4
84407
84604
  });
84408
- this.relay.onWorkerOutput = ({ name, chunk }) => {
84605
+ this.unsubRelayListeners.push(this.relay.addListener("workerOutput", ({ name, chunk }) => {
84409
84606
  const listener = this.ptyListeners.get(name);
84410
84607
  if (listener)
84411
84608
  listener(chunk);
@@ -84444,8 +84641,8 @@ ${err.suggestion}`);
84444
84641
  this.lastActivity.set(name, activity);
84445
84642
  this.log(`[${shortName}] ${activity}`);
84446
84643
  }
84447
- };
84448
- this.relay.onMessageReceived = (msg) => {
84644
+ }));
84645
+ this.unsubRelayListeners.push(this.relay.addListener("messageReceived", (msg) => {
84449
84646
  this.emit({
84450
84647
  type: "broker:event",
84451
84648
  runId,
@@ -84482,8 +84679,8 @@ ${err.suggestion}`);
84482
84679
  });
84483
84680
  void this.trajectory?.ownerMonitoringEvent(supervision.stepName, supervision.logicalName, `Messaged ${msg.to}: ${msg.text.slice(0, 120)}`, { to: msg.to, text: msg.text });
84484
84681
  }
84485
- };
84486
- this.relay.onAgentSpawned = (agent) => {
84682
+ }));
84683
+ this.unsubRelayListeners.push(this.relay.addListener("agentSpawned", (agent) => {
84487
84684
  this.emit({
84488
84685
  type: "broker:event",
84489
84686
  runId,
@@ -84496,8 +84693,8 @@ ${err.suggestion}`);
84496
84693
  if (!this.activeAgentHandles.has(agent.name)) {
84497
84694
  this.log(`[spawned] ${agent.name} (${agent.runtime})`);
84498
84695
  }
84499
- };
84500
- this.relay.onAgentReleased = (agent) => {
84696
+ }));
84697
+ this.unsubRelayListeners.push(this.relay.addListener("agentReleased", (agent) => {
84501
84698
  this.emit({
84502
84699
  type: "broker:event",
84503
84700
  runId,
@@ -84506,8 +84703,8 @@ ${err.suggestion}`);
84506
84703
  name: agent.name
84507
84704
  }
84508
84705
  });
84509
- };
84510
- this.relay.onAgentExited = (agent) => {
84706
+ }));
84707
+ this.unsubRelayListeners.push(this.relay.addListener("agentExited", (agent) => {
84511
84708
  this.emit({
84512
84709
  type: "broker:event",
84513
84710
  runId,
@@ -84523,11 +84720,11 @@ ${err.suggestion}`);
84523
84720
  if (!this.activeAgentHandles.has(agent.name)) {
84524
84721
  this.log(`[exited] ${agent.name} (code: ${agent.exitCode ?? "?"})`);
84525
84722
  }
84526
- };
84527
- this.relay.onDeliveryUpdate = (event) => {
84723
+ }));
84724
+ this.unsubRelayListeners.push(this.relay.addListener("deliveryUpdate", (event) => {
84528
84725
  this.emit({ type: "broker:event", runId, event });
84529
- };
84530
- this.relay.onAgentIdle = ({ name, idleSecs }) => {
84726
+ }));
84727
+ this.unsubRelayListeners.push(this.relay.addListener("agentIdle", ({ name, idleSecs }) => {
84531
84728
  this.emit({
84532
84729
  type: "broker:event",
84533
84730
  runId,
@@ -84543,7 +84740,7 @@ ${err.suggestion}`);
84543
84740
  const shortName = name.replace(/-[a-f0-9]{6,}$/, "");
84544
84741
  this.log(`[idle] ${shortName} silent for ${bucket}s`);
84545
84742
  }
84546
- };
84743
+ }));
84547
84744
  this.relaycast = void 0;
84548
84745
  this.relaycastAgent = void 0;
84549
84746
  this.unsubBrokerStderr = this.relay.onBrokerStderr((line) => {
@@ -84654,15 +84851,13 @@ ${err.suggestion}`);
84654
84851
  this.ptyListeners.clear();
84655
84852
  this.unsubBrokerStderr?.();
84656
84853
  this.unsubBrokerStderr = void 0;
84657
- if (this.relay) {
84658
- this.relay.onMessageReceived = null;
84659
- this.relay.onAgentSpawned = null;
84660
- this.relay.onAgentReleased = null;
84661
- this.relay.onAgentExited = null;
84662
- this.relay.onAgentIdle = null;
84663
- this.relay.onWorkerOutput = null;
84664
- this.relay.onDeliveryUpdate = null;
84854
+ for (const off of this.unsubRelayListeners) {
84855
+ try {
84856
+ off();
84857
+ } catch {
84858
+ }
84665
84859
  }
84860
+ this.unsubRelayListeners = [];
84666
84861
  this.lastIdleLog.clear();
84667
84862
  this.lastActivity.clear();
84668
84863
  this.supervisedRuntimeAgents.clear();
@@ -94695,6 +94890,7 @@ init_dist2();
94695
94890
  DEFAULT_PROXY_ENV_REGISTRY,
94696
94891
  DefaultModels,
94697
94892
  ERROR_SEARCH_HINT,
94893
+ EventBus,
94698
94894
  GEMINI_MODEL_OPTIONS,
94699
94895
  GeminiModels,
94700
94896
  GitHubClient,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-relay",
3
- "version": "6.3.5",
3
+ "version": "7.0.0",
4
4
  "description": "Real-time agent-to-agent communication system",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -132,14 +132,14 @@
132
132
  },
133
133
  "homepage": "https://github.com/AgentWorkforce/relay#readme",
134
134
  "dependencies": {
135
- "@agent-relay/cloud": "6.3.5",
136
- "@agent-relay/config": "6.3.5",
137
- "@agent-relay/hooks": "6.3.5",
138
- "@agent-relay/sdk": "6.3.5",
139
- "@agent-relay/telemetry": "6.3.5",
140
- "@agent-relay/trajectory": "6.3.5",
141
- "@agent-relay/user-directory": "6.3.5",
142
- "@agent-relay/utils": "6.3.5",
135
+ "@agent-relay/cloud": "7.0.0",
136
+ "@agent-relay/config": "7.0.0",
137
+ "@agent-relay/hooks": "7.0.0",
138
+ "@agent-relay/sdk": "7.0.0",
139
+ "@agent-relay/telemetry": "7.0.0",
140
+ "@agent-relay/trajectory": "7.0.0",
141
+ "@agent-relay/user-directory": "7.0.0",
142
+ "@agent-relay/utils": "7.0.0",
143
143
  "@aws-sdk/client-s3": "3.1020.0",
144
144
  "@modelcontextprotocol/sdk": "^1.0.0",
145
145
  "@relayauth/core": "^0.1.2",