agent-relay 8.3.6 → 8.4.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.
package/dist/index.cjs CHANGED
@@ -175,11 +175,13 @@ __export(index_exports, {
175
175
  MessageReactedPredicate: () => MessageReactedPredicate,
176
176
  MessageReadPredicate: () => MessageReadPredicate,
177
177
  RelayCapabilityError: () => RelayCapabilityError,
178
+ RelayError: () => RelayError,
178
179
  RelaycastMessagingClient: () => RelaycastMessagingClient,
179
180
  StaticPatterns: () => StaticPatterns,
180
181
  StatusPredicate: () => StatusPredicate,
181
182
  ToolCalledPredicate: () => ToolCalledPredicate,
182
183
  TracedError: () => TracedError,
184
+ TypedActionPredicate: () => TypedActionPredicate,
183
185
  actionSchemaToJsonSchema: () => actionSchemaToJsonSchema,
184
186
  agentRelayAgent: () => agentRelayAgent,
185
187
  agentTokenRecoveryMessage: () => agentTokenRecoveryMessage,
@@ -201,6 +203,7 @@ __export(index_exports, {
201
203
  createRequestEnvelope: () => createRequestEnvelope,
202
204
  createRequestHandler: () => createRequestHandler,
203
205
  createTraceableError: () => createTraceableError,
206
+ createTypedActionHandle: () => createTypedActionHandle,
204
207
  createWorkspaceFacade: () => createWorkspaceFacade,
205
208
  defineHarness: () => defineHarness,
206
209
  findGitRoot: () => findGitRoot,
@@ -233,6 +236,7 @@ __export(index_exports, {
233
236
  isSpawnOrReleaseCommandFast: () => isSpawnOrReleaseCommandFast,
234
237
  isValidAgentName: () => isValidAgentName,
235
238
  logAndTraceError: () => logAndTraceError,
239
+ logRelayHandlerError: () => logRelayHandlerError,
236
240
  mapModelToCli: () => mapModelToCli,
237
241
  matchesSelector: () => matchesSelector,
238
242
  nextHarnessName: () => nextHarnessName,
@@ -275,7 +279,7 @@ __export(index_exports, {
275
279
  module.exports = __toCommonJS(index_exports);
276
280
 
277
281
  // ../../node_modules/@relaycast/sdk/dist/version.js
278
- var SDK_VERSION = "2.6.0";
282
+ var SDK_VERSION = "3.1.1";
279
283
 
280
284
  // ../../node_modules/@relaycast/types/node_modules/zod/v4/classic/external.js
281
285
  var external_exports = {};
@@ -15313,10 +15317,16 @@ var UnsubscribeEventSchema = external_exports.object({
15313
15317
  var PingEventSchema = external_exports.object({
15314
15318
  type: external_exports.literal("ping")
15315
15319
  });
15320
+ var ResyncEventSchema = external_exports.object({
15321
+ type: external_exports.literal("resync"),
15322
+ last_seen_seq: external_exports.number(),
15323
+ since: external_exports.string().optional()
15324
+ });
15316
15325
  var ClientEventSchema = external_exports.discriminatedUnion("type", [
15317
15326
  SubscribeEventSchema,
15318
15327
  UnsubscribeEventSchema,
15319
- PingEventSchema
15328
+ PingEventSchema,
15329
+ ResyncEventSchema
15320
15330
  ]);
15321
15331
  var ChannelMessagePayloadSchema = CoreMessagePayloadSchema.extend({
15322
15332
  attachments: external_exports.array(FileAttachmentSchema)
@@ -15450,6 +15460,13 @@ var FileUploadedEventSchema = external_exports.object({
15450
15460
  var PongEventSchema = external_exports.object({
15451
15461
  type: external_exports.literal("pong")
15452
15462
  });
15463
+ var ResyncAckEventSchema = external_exports.object({
15464
+ type: external_exports.literal("resync_ack"),
15465
+ last_seen_seq: external_exports.number(),
15466
+ current_seq: external_exports.number(),
15467
+ replayed: external_exports.number(),
15468
+ gap_detected: external_exports.boolean()
15469
+ });
15453
15470
  var WebhookReceivedEventSchema = external_exports.object({
15454
15471
  type: external_exports.literal("webhook.received"),
15455
15472
  webhook_id: external_exports.string(),
@@ -15542,6 +15559,11 @@ var WsPermanentlyDisconnectedEventSchema = external_exports.object({
15542
15559
  var WsCloseEventSchema = external_exports.object({
15543
15560
  type: external_exports.literal("close")
15544
15561
  });
15562
+ var WsResyncedEventSchema = external_exports.object({
15563
+ type: external_exports.literal("resynced"),
15564
+ replayed: external_exports.number(),
15565
+ gap_detected: external_exports.boolean()
15566
+ });
15545
15567
  var ServerEventSchema = external_exports.discriminatedUnion("type", [
15546
15568
  MessageCreatedEventSchema,
15547
15569
  MessageUpdatedEventSchema,
@@ -15576,7 +15598,8 @@ var ServerEventSchema = external_exports.discriminatedUnion("type", [
15576
15598
  DeliveryDeliveredEventSchema,
15577
15599
  DeliveryDeferredEventSchema,
15578
15600
  DeliveryFailedEventSchema,
15579
- PongEventSchema
15601
+ PongEventSchema,
15602
+ ResyncAckEventSchema
15580
15603
  ]);
15581
15604
  var RelaycastMessageEventSchema = external_exports.discriminatedUnion("type", [
15582
15605
  MessageCreatedEventSchema,
@@ -15620,12 +15643,14 @@ var WsClientEventSchema = external_exports.discriminatedUnion("type", [
15620
15643
  DeliveryDeferredEventSchema,
15621
15644
  DeliveryFailedEventSchema,
15622
15645
  PongEventSchema,
15646
+ ResyncAckEventSchema,
15623
15647
  // Client-only events
15624
15648
  WsOpenEventSchema,
15625
15649
  WsErrorEventSchema,
15626
15650
  WsReconnectingEventSchema,
15627
15651
  WsPermanentlyDisconnectedEventSchema,
15628
- WsCloseEventSchema
15652
+ WsCloseEventSchema,
15653
+ WsResyncedEventSchema
15629
15654
  ]);
15630
15655
 
15631
15656
  // ../../node_modules/@relaycast/types/dist/api.js
@@ -15923,22 +15948,22 @@ var SDK_ORIGIN = Object.freeze({
15923
15948
  client: "@relaycast/sdk",
15924
15949
  version: SDK_VERSION
15925
15950
  });
15926
- var HARNESS_HEADER = "X-Relaycast-Harness";
15951
+ var ORIGIN_ACTOR_HEADER = "X-Relaycast-Origin-Actor";
15927
15952
  var AGENT_RELAY_DISTINCT_ID_HEADER = "X-Agent-Relay-Distinct-Id";
15928
15953
  var AGENT_RELAY_DISTINCT_ID_QUERY = "agent_relay_distinct_id";
15929
- var HARNESS_MAX_LENGTH = 120;
15954
+ var ORIGIN_ACTOR_MAX_LENGTH = 128;
15930
15955
  var AGENT_RELAY_DISTINCT_ID_MAX_LENGTH = 128;
15931
- var HARNESS_ALLOWED = /^[a-z0-9 ._\-/():=;,+]+$/i;
15956
+ var ORIGIN_ACTOR_ALLOWED = /^[a-z0-9 ._\-/():=;,+@]+$/i;
15932
15957
  var AGENT_RELAY_DISTINCT_ID_ALLOWED = /^[a-z0-9._:-]+$/i;
15933
- function sanitizeHarness(raw) {
15958
+ function sanitizeOriginActor(raw) {
15934
15959
  if (!raw)
15935
15960
  return void 0;
15936
15961
  const trimmed = raw.trim();
15937
15962
  if (!trimmed)
15938
15963
  return void 0;
15939
- if (!HARNESS_ALLOWED.test(trimmed))
15964
+ if (!ORIGIN_ACTOR_ALLOWED.test(trimmed))
15940
15965
  return void 0;
15941
- return trimmed.slice(0, HARNESS_MAX_LENGTH).toLowerCase();
15966
+ return trimmed.slice(0, ORIGIN_ACTOR_MAX_LENGTH).toLowerCase();
15942
15967
  }
15943
15968
  function sanitizeAgentRelayDistinctId(raw) {
15944
15969
  if (!raw)
@@ -15995,6 +16020,7 @@ function decamelizeKey(key) {
15995
16020
  }
15996
16021
 
15997
16022
  // ../../node_modules/@relaycast/sdk/dist/ws.js
16023
+ var SEEN_EVENT_IDS_MAX = 2048;
15998
16024
  var INTERNAL_WS_ORIGIN = /* @__PURE__ */ Symbol("relaycast.internal.ws-origin");
15999
16025
  function readInternalWsOrigin(options) {
16000
16026
  return options[INTERNAL_WS_ORIGIN];
@@ -16031,8 +16057,14 @@ var WsClient = class {
16031
16057
  originSurface;
16032
16058
  originClient;
16033
16059
  originVersion;
16034
- originHarness;
16060
+ originActor;
16035
16061
  agentRelayDistinctId;
16062
+ /** Highest `agent_seq` observed across delivered events; null until the first stamped event. */
16063
+ lastSeenSeq = null;
16064
+ /** Receive time of the last seq-stamped event, used as `since` for DB-backed replay. */
16065
+ lastEventAt = null;
16066
+ /** Recently dispatched event ids (insertion-ordered for LRU eviction) for replay dedupe. */
16067
+ seenEventIds = /* @__PURE__ */ new Set();
16036
16068
  constructor(options) {
16037
16069
  const origin = readInternalWsOrigin(options) ?? SDK_ORIGIN;
16038
16070
  this.token = options.token;
@@ -16047,7 +16079,7 @@ var WsClient = class {
16047
16079
  this.originSurface = origin.surface;
16048
16080
  this.originClient = origin.client;
16049
16081
  this.originVersion = origin.version;
16050
- this.originHarness = sanitizeHarness(origin.harness ?? options.harness);
16082
+ this.originActor = sanitizeOriginActor(origin.originActor ?? options.originActor);
16051
16083
  this.agentRelayDistinctId = sanitizeAgentRelayDistinctId(origin.agentRelayDistinctId ?? options.agentRelayDistinctId);
16052
16084
  }
16053
16085
  connect() {
@@ -16060,8 +16092,8 @@ var WsClient = class {
16060
16092
  wsUrl.searchParams.set(decamelizeKey("originSurface"), this.originSurface);
16061
16093
  wsUrl.searchParams.set(decamelizeKey("originClient"), this.originClient);
16062
16094
  wsUrl.searchParams.set(decamelizeKey("originVersion"), this.originVersion);
16063
- if (this.originHarness) {
16064
- wsUrl.searchParams.set("harness", this.originHarness);
16095
+ if (this.originActor) {
16096
+ wsUrl.searchParams.set("origin_actor", this.originActor);
16065
16097
  }
16066
16098
  if (this.agentRelayDistinctId) {
16067
16099
  wsUrl.searchParams.set(AGENT_RELAY_DISTINCT_ID_QUERY, this.agentRelayDistinctId);
@@ -16089,12 +16121,34 @@ var WsClient = class {
16089
16121
  this.startPing();
16090
16122
  const openEvent = { type: "open" };
16091
16123
  this.emit("open", openEvent);
16124
+ this.sendResync();
16092
16125
  };
16093
16126
  ws.onmessage = (event) => {
16094
16127
  if (this.ws !== ws)
16095
16128
  return;
16096
16129
  try {
16097
16130
  const parsed = JSON.parse(String(event.data));
16131
+ if (parsed !== null && typeof parsed === "object") {
16132
+ if (typeof parsed.agent_seq === "number" && Number.isFinite(parsed.agent_seq)) {
16133
+ this.lastSeenSeq = parsed.agent_seq;
16134
+ this.lastEventAt = (/* @__PURE__ */ new Date()).toISOString();
16135
+ }
16136
+ if (parsed.type === "resync_ack") {
16137
+ const resyncedEvent = {
16138
+ type: "resynced",
16139
+ replayed: typeof parsed.replayed === "number" ? parsed.replayed : 0,
16140
+ gapDetected: parsed.gap_detected === true
16141
+ };
16142
+ this.emit("resynced", resyncedEvent);
16143
+ return;
16144
+ }
16145
+ if (typeof parsed.id === "string" && parsed.id.length > 0 && typeof parsed.type === "string") {
16146
+ const dedupeKey = `${parsed.type}:${parsed.id}`;
16147
+ if (this.seenEventIds.has(dedupeKey))
16148
+ return;
16149
+ this.rememberEventId(dedupeKey);
16150
+ }
16151
+ }
16098
16152
  const result = ServerEventSchema.safeParse(parsed);
16099
16153
  if (result.success) {
16100
16154
  this.emit(result.data.type, camelizeKeys(result.data));
@@ -16214,6 +16268,29 @@ var WsClient = class {
16214
16268
  this.ws.send(JSON.stringify(data));
16215
16269
  }
16216
16270
  }
16271
+ /**
16272
+ * Request replay of events missed while disconnected. The server replays its
16273
+ * resync ring past `last_seen_seq`, falls back to a DB-backed replay from
16274
+ * `since` when the gap exceeds the ring, and answers with `resync_ack`.
16275
+ * No-op until at least one seq-stamped event has been received.
16276
+ */
16277
+ sendResync() {
16278
+ if (this.lastSeenSeq === null)
16279
+ return;
16280
+ this.sendJson({
16281
+ type: "resync",
16282
+ last_seen_seq: this.lastSeenSeq,
16283
+ ...this.lastEventAt ? { since: this.lastEventAt } : {}
16284
+ });
16285
+ }
16286
+ rememberEventId(key) {
16287
+ this.seenEventIds.add(key);
16288
+ if (this.seenEventIds.size > SEEN_EVENT_IDS_MAX) {
16289
+ const oldest = this.seenEventIds.values().next().value;
16290
+ if (oldest !== void 0)
16291
+ this.seenEventIds.delete(oldest);
16292
+ }
16293
+ }
16217
16294
  startPing() {
16218
16295
  this.stopPing();
16219
16296
  this.pingTimer = setInterval(() => {
@@ -16425,7 +16502,7 @@ var AgentClient = class {
16425
16502
  surface: this.client.originSurface,
16426
16503
  client: this.client.originClient,
16427
16504
  version: this.client.originVersion,
16428
- ...this.client.originHarness ? { harness: this.client.originHarness } : {},
16505
+ ...this.client.originActor ? { originActor: this.client.originActor } : {},
16429
16506
  ...this.client.agentRelayDistinctId ? { agentRelayDistinctId: this.client.agentRelayDistinctId } : {}
16430
16507
  }));
16431
16508
  this.ws.on("open", () => {
@@ -16581,6 +16658,15 @@ var AgentClient = class {
16581
16658
  }
16582
16659
  return this.ws.on("permanently_disconnected", (e) => handler(e.attempt));
16583
16660
  },
16661
+ resynced: (handler) => {
16662
+ if (!this.ws) {
16663
+ throw new Error("WebSocket not connected. Call connect() first.");
16664
+ }
16665
+ return this.ws.on("resynced", (e) => {
16666
+ const event = e;
16667
+ handler({ replayed: event.replayed, gapDetected: event.gapDetected });
16668
+ });
16669
+ },
16584
16670
  // Wildcard
16585
16671
  any: (handler) => {
16586
16672
  if (!this.ws) {
@@ -31494,7 +31580,7 @@ var HttpClient = class _HttpClient {
31494
31580
  _originSurface;
31495
31581
  _originClient;
31496
31582
  _originVersion;
31497
- _originHarness;
31583
+ _originActor;
31498
31584
  _agentRelayDistinctId;
31499
31585
  _retryPolicy;
31500
31586
  constructor(options) {
@@ -31504,7 +31590,7 @@ var HttpClient = class _HttpClient {
31504
31590
  this._originSurface = origin.surface;
31505
31591
  this._originClient = origin.client;
31506
31592
  this._originVersion = origin.version;
31507
- this._originHarness = sanitizeHarness(origin.harness ?? options.harness);
31593
+ this._originActor = sanitizeOriginActor(origin.originActor ?? options.originActor);
31508
31594
  this._agentRelayDistinctId = sanitizeAgentRelayDistinctId(origin.agentRelayDistinctId ?? options.agentRelayDistinctId);
31509
31595
  this._retryPolicy = normalizeRetryPolicy(options.retryPolicy);
31510
31596
  }
@@ -31523,9 +31609,9 @@ var HttpClient = class _HttpClient {
31523
31609
  get originVersion() {
31524
31610
  return this._originVersion;
31525
31611
  }
31526
- /** Sanitized harness identifier, or `undefined` when none was supplied. */
31527
- get originHarness() {
31528
- return this._originHarness;
31612
+ /** Sanitized originActor identifier, or `undefined` when none was supplied. */
31613
+ get originActor() {
31614
+ return this._originActor;
31529
31615
  }
31530
31616
  /** Sanitized Agent Relay distinct id, or `undefined` when none was supplied. */
31531
31617
  get agentRelayDistinctId() {
@@ -31539,7 +31625,7 @@ var HttpClient = class _HttpClient {
31539
31625
  surface: this._originSurface,
31540
31626
  client: this._originClient,
31541
31627
  version: this._originVersion,
31542
- ...this._originHarness ? { harness: this._originHarness } : {},
31628
+ ...this._originActor ? { originActor: this._originActor } : {},
31543
31629
  ...this._agentRelayDistinctId ? { agentRelayDistinctId: this._agentRelayDistinctId } : {}
31544
31630
  }));
31545
31631
  }
@@ -31557,7 +31643,7 @@ var HttpClient = class _HttpClient {
31557
31643
  "X-Relaycast-Origin-Surface": this._originSurface,
31558
31644
  "X-Relaycast-Origin-Client": this._originClient,
31559
31645
  "X-Relaycast-Origin-Version": this._originVersion,
31560
- ...this._originHarness ? { [HARNESS_HEADER]: this._originHarness } : {},
31646
+ ...this._originActor ? { [ORIGIN_ACTOR_HEADER]: this._originActor } : {},
31561
31647
  ...this._agentRelayDistinctId ? { [AGENT_RELAY_DISTINCT_ID_HEADER]: this._agentRelayDistinctId } : {},
31562
31648
  ...options?.headers || {}
31563
31649
  };
@@ -31687,7 +31773,7 @@ var RelayCast = class _RelayCast {
31687
31773
  surface: this.client.originSurface,
31688
31774
  client: this.client.originClient,
31689
31775
  version: this.client.originVersion,
31690
- ...this.client.originHarness ? { harness: this.client.originHarness } : {}
31776
+ ...this.client.originActor ? { originActor: this.client.originActor } : {}
31691
31777
  }));
31692
31778
  }
31693
31779
  connect() {
@@ -31732,6 +31818,10 @@ var RelayCast = class _RelayCast {
31732
31818
  error: (handler) => this.onEvent("error", handler),
31733
31819
  reconnecting: (handler) => this.ws.on("reconnecting", (e) => handler(e.attempt)),
31734
31820
  permanentlyDisconnected: (handler) => this.ws.on("permanently_disconnected", (e) => handler(e.attempt)),
31821
+ resynced: (handler) => this.ws.on("resynced", (e) => {
31822
+ const event = e;
31823
+ handler({ replayed: event.replayed, gapDetected: event.gapDetected });
31824
+ }),
31735
31825
  any: (handler) => this.ws.on("*", handler)
31736
31826
  };
31737
31827
  static async createWorkspace(name, options) {
@@ -32724,6 +32814,7 @@ function actionContext(input) {
32724
32814
  }
32725
32815
 
32726
32816
  // ../sdk/dist/relaycast-telemetry.js
32817
+ var ORIGIN_ACTOR_ENV = "AGENT_RELAY_ORIGIN_ACTOR";
32727
32818
  var HARNESS_ENV_KEYS = [
32728
32819
  "AGENT_RELAY_HARNESS",
32729
32820
  "AGENT_RELAY_ORCHESTRATOR_HARNESS",
@@ -32737,11 +32828,18 @@ function nonEmpty(value) {
32737
32828
  const trimmed = value?.trim();
32738
32829
  return trimmed ? trimmed : void 0;
32739
32830
  }
32831
+ function resolveOriginActor(env) {
32832
+ const explicit = nonEmpty(env[ORIGIN_ACTOR_ENV]);
32833
+ if (explicit)
32834
+ return explicit;
32835
+ const harness = HARNESS_ENV_KEYS.map((key) => nonEmpty(env[key])).find((value) => Boolean(value));
32836
+ return harness ? `agent-relay-cli/agent/${harness}` : void 0;
32837
+ }
32740
32838
  function relaycastTelemetryOptions(explicit = {}, env = defaultEnv()) {
32741
- const harness = nonEmpty(explicit.harness) ?? HARNESS_ENV_KEYS.map((key) => nonEmpty(env[key])).find((value) => Boolean(value));
32839
+ const originActor = nonEmpty(explicit.originActor) ?? resolveOriginActor(env);
32742
32840
  const agentRelayDistinctId = nonEmpty(explicit.agentRelayDistinctId) ?? nonEmpty(env.AGENT_RELAY_DISTINCT_ID);
32743
32841
  return {
32744
- ...harness ? { harness } : {},
32842
+ ...originActor ? { originActor } : {},
32745
32843
  ...agentRelayDistinctId ? { agentRelayDistinctId } : {}
32746
32844
  };
32747
32845
  }
@@ -33428,7 +33526,7 @@ function createRelaycastClient(options) {
33428
33526
  baseUrl: options.baseUrl,
33429
33527
  retryPolicy: options.retryPolicy,
33430
33528
  ...relaycastTelemetryOptions({
33431
- harness: options.harness,
33529
+ originActor: options.originActor,
33432
33530
  agentRelayDistinctId: options.agentRelayDistinctId
33433
33531
  })
33434
33532
  }));
@@ -33456,6 +33554,7 @@ var RelaycastMessagingClient = class {
33456
33554
  },
33457
33555
  get: async (name) => normalizeAgent(await this.relaycast.agents.get(name)),
33458
33556
  register: async (input) => normalizeAgentRegistration(await this.relaycast.agents.register(input)),
33557
+ registerOrRotate: async (input) => normalizeAgentRegistration(this.relaycast.agents.registerOrRotate ? await this.relaycast.agents.registerOrRotate(input) : await this.relaycast.agents.register(input)),
33459
33558
  me: async () => normalizeAgent(await this.requireAgentClient("agents.me").me()),
33460
33559
  update: async (name, input) => normalizeAgent(await this.relaycast.agents.update(name, input)),
33461
33560
  delete: async (name) => {
@@ -33801,270 +33900,38 @@ var RelaycastMessagingClient = class {
33801
33900
  }
33802
33901
  };
33803
33902
 
33804
- // ../sdk/dist/facade.js
33805
- function resolveAgentName(ref) {
33806
- if (typeof ref === "string")
33807
- return stripSigil(ref);
33808
- const value = ref.handle ?? ref.name ?? ref.id;
33809
- if (!value) {
33810
- throw new Error("Unable to resolve agent name: reference has no name, handle, or id.");
33811
- }
33812
- return stripSigil(value);
33813
- }
33814
- function resolveAgentToken(ref) {
33815
- return ref && typeof ref !== "string" ? ref.token : void 0;
33903
+ // ../sdk/dist/listeners.js
33904
+ function logRelayHandlerError(error101, context) {
33905
+ const where = context.action ? `action "${context.action}"` : `"${context.selector ?? "unknown"}"`;
33906
+ console.warn(`[agent-relay] ${context.source} handler for ${where} threw:`, error101);
33816
33907
  }
33817
33908
  function stripSigil(value) {
33818
33909
  return value.startsWith("@") || value.startsWith("#") ? value.slice(1) : value;
33819
33910
  }
33820
- function isChannelTarget(to) {
33821
- return to.startsWith("#");
33822
- }
33823
- function deliveryToMode(delivery) {
33824
- if (!delivery)
33825
- return void 0;
33826
- return delivery === "immediate" || delivery === "next-tool-call" ? "steer" : "wait";
33827
- }
33828
- function buildText(text, mentions) {
33829
- let body = text ?? "";
33830
- if (mentions?.length) {
33831
- const handles = mentions.map((mention) => `@${resolveAgentName(mention)}`);
33832
- const missing = handles.filter((handle) => !body.includes(handle));
33833
- if (missing.length) {
33834
- body = body ? `${missing.join(" ")} ${body}` : missing.join(" ");
33835
- }
33836
- }
33837
- return body;
33838
- }
33839
- function resolveMessageId(ref) {
33840
- if (!ref) {
33841
- throw new Error("reply requires a `messageId` or `thread`.");
33842
- }
33843
- if (typeof ref === "string")
33844
- return ref;
33845
- const value = ref.id ?? ref.threadId;
33846
- if (!value) {
33847
- throw new Error("Unable to resolve a message id from the provided thread reference.");
33848
- }
33849
- return value;
33850
- }
33851
- function createEnrichedMessages(base, resolveFrom) {
33852
- const enriched = Object.create(base);
33853
- enriched.send = async (input) => {
33854
- if ("channel" in input && input.channel) {
33855
- return base.send(input);
33856
- }
33857
- const sendInput = input;
33858
- const messages = resolveFrom(sendInput.from);
33859
- const text = buildText(sendInput.text ?? sendInput.msg, sendInput.mentions);
33860
- if (Array.isArray(sendInput.to)) {
33861
- return messages.groupDirect({
33862
- participants: sendInput.to.map(resolveAgentName),
33863
- text,
33864
- attachments: sendInput.attachments,
33865
- mode: sendInput.mode,
33866
- idempotencyKey: sendInput.idempotencyKey
33867
- });
33868
- }
33869
- if (isChannelTarget(sendInput.to)) {
33870
- return messages.send({
33871
- channel: stripSigil(sendInput.to),
33872
- text,
33873
- blocks: sendInput.blocks,
33874
- attachments: sendInput.attachments,
33875
- mode: sendInput.mode,
33876
- idempotencyKey: sendInput.idempotencyKey
33877
- });
33878
- }
33879
- return messages.direct({
33880
- to: resolveAgentName(sendInput.to),
33881
- text,
33882
- attachments: sendInput.attachments,
33883
- mode: sendInput.mode,
33884
- idempotencyKey: sendInput.idempotencyKey
33885
- });
33886
- };
33887
- enriched.reply = async (input) => {
33888
- const messages = resolveFrom(input.from);
33889
- return messages.reply({
33890
- messageId: resolveMessageId(input.messageId ?? input.thread),
33891
- text: input.text,
33892
- blocks: input.blocks,
33893
- idempotencyKey: input.idempotencyKey
33894
- });
33895
- };
33896
- enriched.react = (async (arg1, arg2) => {
33897
- if (typeof arg1 === "string") {
33898
- return base.react(arg1, arg2);
33899
- }
33900
- const messageId = typeof arg1.message === "string" ? arg1.message : arg1.message.id;
33901
- const messages = resolveFrom(arg1.agent);
33902
- return messages.react(messageId, arg1.emoji);
33903
- });
33904
- enriched.dm = async (input) => {
33905
- const messages = resolveFrom(input.from);
33906
- return messages.direct({
33907
- to: resolveAgentName(input.to),
33908
- text: input.text ?? input.msg ?? "",
33909
- attachments: input.attachments,
33910
- mode: input.mode,
33911
- idempotencyKey: input.idempotencyKey
33912
- });
33913
- };
33914
- return enriched;
33915
- }
33916
- function createWorkspaceFacade(messaging, deps) {
33917
- const register = async (agents) => {
33918
- if (!deps) {
33919
- throw new Error("register() is only available on the workspace client.");
33920
- }
33921
- const list = Array.isArray(agents) ? agents : [agents];
33922
- const inputs = list.map((agent) => typeof agent === "string" ? { name: stripSigil(agent) } : {
33923
- name: resolveAgentName(agent),
33924
- type: agent.type,
33925
- persona: agent.persona,
33926
- metadata: agent.metadata
33927
- });
33928
- const seen = /* @__PURE__ */ new Set();
33929
- for (const { name } of inputs) {
33930
- if (seen.has(name)) {
33931
- throw new Error(`Duplicate agent name in register(): "${name}".`);
33932
- }
33933
- seen.add(name);
33934
- }
33935
- const clients = [];
33936
- for (const input of inputs) {
33937
- clients.push(deps.buildAgentClient(await messaging.agents.register(input)));
33938
- }
33939
- return Array.isArray(agents) ? clients : clients[0];
33940
- };
33941
- return {
33942
- info: () => messaging.workspace.info(),
33943
- register,
33944
- reconnect: async ({ apiToken }) => {
33945
- if (!deps) {
33946
- throw new Error("reconnect() is only available on the workspace client.");
33947
- }
33948
- return deps.reconnectAgent(apiToken);
33949
- }
33950
- };
33951
- }
33952
- function registerFacadeAction(actions, def, wiring) {
33953
- const allowed = def.availableTo?.map(resolveAgentName);
33954
- const policy = allowed || def.policy ? async (input, ctx) => {
33955
- if (allowed && !allowed.includes(ctx.caller.name)) {
33956
- return {
33957
- allowed: false,
33958
- reason: `${ctx.caller.name} is not permitted to call ${def.name}.`
33959
- };
33960
- }
33961
- return def.policy ? def.policy(input, ctx) : { allowed: true };
33962
- } : void 0;
33963
- const localHandle = actions.register({
33964
- name: def.name,
33965
- description: def.description,
33966
- input: def.input,
33967
- inputSchema: def.inputSchema,
33968
- output: def.output,
33969
- outputSchema: def.outputSchema,
33970
- visibility: def.visibility,
33971
- policy,
33972
- handler: (input, ctx) => def.handler({ input, agent: ctx.caller, ctx })
33973
- });
33974
- const relayUnsubscribe = wireRelayAction(actions, def, wiring, allowed);
33975
- if (!relayUnsubscribe) {
33976
- return localHandle;
33977
- }
33978
- return {
33979
- unregister: () => {
33980
- relayUnsubscribe();
33981
- localHandle.unregister();
33982
- }
33983
- };
33984
- }
33985
- function wireRelayAction(actions, def, wiring, allowed) {
33986
- const commands = wiring?.messaging.commands;
33987
- if (!wiring || !commands?.agentScoped?.() || !commands.available?.()) {
33988
- return void 0;
33989
- }
33990
- const handlerAgent = wiring.handlerAgent;
33991
- if (!handlerAgent) {
33992
- return void 0;
33993
- }
33994
- void commands.register({
33995
- command: def.name,
33996
- description: def.description ?? def.name,
33997
- handlerAgent,
33998
- inputSchema: actionSchemaToJsonSchema(def.inputSchema ?? def.input),
33999
- outputSchema: actionSchemaToJsonSchema(def.outputSchema ?? def.output),
34000
- ...allowed ? { availableTo: allowed } : {}
34001
- }).catch((error101) => {
34002
- console.error(`[agent-relay] failed to register action descriptor "${def.name}":`, error101);
34003
- });
34004
- const unsubscribe = wiring.messaging.events.on("actionInvoked", async (event) => {
34005
- if (event.actionName !== def.name) {
34006
- return;
34007
- }
34008
- await handleActionInvoked(actions, commands, def.name, event);
34009
- });
34010
- try {
34011
- wiring.messaging.events.connect();
34012
- } catch (error101) {
34013
- console.error(`[agent-relay] failed to open the event stream for action "${def.name}":`, error101);
34014
- }
34015
- return unsubscribe;
34016
- }
34017
- async function handleActionInvoked(actions, commands, actionName, event) {
34018
- let input = {};
33911
+ function runHandler(handler, event, report) {
34019
33912
  try {
34020
- const invocation = await commands.getInvocation(actionName, event.invocationId);
34021
- input = invocation.input ?? {};
33913
+ void Promise.resolve(handler(event)).catch(report);
34022
33914
  } catch (error101) {
34023
- console.error(`[agent-relay] failed to load invocation "${event.invocationId}":`, error101);
33915
+ report(error101);
34024
33916
  }
34025
- const result = await actions.invoke({
34026
- name: actionName,
34027
- input,
34028
- caller: { name: event.callerName, type: "agent" }
34029
- });
34030
- try {
34031
- await commands.completeInvocation(actionName, event.invocationId, result.ok ? { output: toOutputRecord(result.output) } : { error: result.error?.message ?? "handler failed" });
34032
- } catch (error101) {
34033
- console.error(`[agent-relay] failed to complete invocation "${event.invocationId}":`, error101);
34034
- }
34035
- }
34036
- function toOutputRecord(output) {
34037
- if (output !== null && typeof output === "object" && !Array.isArray(output)) {
34038
- return output;
34039
- }
34040
- return { value: output };
34041
33917
  }
34042
- function createNotifyHandler(messages, target, options) {
34043
- return async () => {
34044
- const subject = options.subject ? `@${resolveAgentName(options.subject)}` : void 0;
34045
- const label = options.type ?? options.action ?? "notification";
34046
- const text = options.text ?? [`[${label}]`, subject].filter(Boolean).join(" ");
34047
- await messages.dm({
34048
- to: resolveAgentName(target),
34049
- text,
34050
- mode: deliveryToMode(options.delivery)
34051
- });
33918
+ function makeReporter(context, info) {
33919
+ return (error101) => {
33920
+ if (!context.onError) {
33921
+ logRelayHandlerError(error101, info);
33922
+ return;
33923
+ }
33924
+ try {
33925
+ context.onError(error101, info);
33926
+ } catch {
33927
+ }
34052
33928
  };
34053
33929
  }
34054
-
34055
- // ../sdk/dist/listeners.js
34056
- function stripSigil2(value) {
34057
- return value.startsWith("@") || value.startsWith("#") ? value.slice(1) : value;
34058
- }
34059
- function runHandler(handler, event) {
34060
- void Promise.resolve(handler(event)).catch(() => {
34061
- });
34062
- }
34063
33930
  var MessageCreatedPredicate = class {
34064
33931
  channel;
34065
33932
  mentioned;
34066
33933
  in(channel) {
34067
- this.channel = stripSigil2(channel);
33934
+ this.channel = stripSigil(channel);
34068
33935
  return this;
34069
33936
  }
34070
33937
  mentions(agent) {
@@ -34072,30 +33939,33 @@ var MessageCreatedPredicate = class {
34072
33939
  return this;
34073
33940
  }
34074
33941
  subscribe(context, handler) {
33942
+ const report = makeReporter(context, { source: "listener", selector: "message.created" });
34075
33943
  return context.events.on("messageCreated", (event) => {
34076
- if (this.channel && stripSigil2(event.channel) !== this.channel)
33944
+ if (this.channel && stripSigil(event.channel) !== this.channel)
34077
33945
  return;
34078
33946
  if (this.mentioned && !messageMentions(event, this.mentioned))
34079
33947
  return;
34080
- runHandler(handler, event);
33948
+ runHandler(handler, event, report);
34081
33949
  });
34082
33950
  }
34083
33951
  };
34084
33952
  function messageMentions(event, name) {
34085
33953
  const mentions = event.message.mentions ?? [];
34086
- if (mentions.some((mention) => stripSigil2(mention) === name))
33954
+ if (mentions.some((mention) => stripSigil(mention) === name))
34087
33955
  return true;
34088
33956
  return event.message.text?.includes(`@${name}`) ?? false;
34089
33957
  }
34090
33958
  var MessageReadPredicate = class {
34091
33959
  subscribe(context, handler) {
34092
- return context.events.on("messageRead", (event) => runHandler(handler, event));
33960
+ const report = makeReporter(context, { source: "listener", selector: "message.read" });
33961
+ return context.events.on("messageRead", (event) => runHandler(handler, event, report));
34093
33962
  }
34094
33963
  };
34095
33964
  var MessageReactedPredicate = class {
34096
33965
  subscribe(context, handler) {
34097
- const off1 = context.events.on("reactionAdded", (event) => runHandler(handler, event));
34098
- const off2 = context.events.on("reactionRemoved", (event) => runHandler(handler, event));
33966
+ const report = makeReporter(context, { source: "listener", selector: "message.reacted" });
33967
+ const off1 = context.events.on("reactionAdded", (event) => runHandler(handler, event, report));
33968
+ const off2 = context.events.on("reactionRemoved", (event) => runHandler(handler, event, report));
34099
33969
  return () => {
34100
33970
  off1();
34101
33971
  off2();
@@ -34135,6 +34005,11 @@ var ActionPredicate = class {
34135
34005
  return this;
34136
34006
  }
34137
34007
  subscribe(context, handler) {
34008
+ const report = makeReporter(context, {
34009
+ source: "listener",
34010
+ selector: this.phase,
34011
+ action: this.action
34012
+ });
34138
34013
  return context.onActionEvent((event) => {
34139
34014
  if (event.action !== this.action)
34140
34015
  return;
@@ -34142,10 +34017,61 @@ var ActionPredicate = class {
34142
34017
  return;
34143
34018
  if (this.caller && event.caller.name !== this.caller)
34144
34019
  return;
34145
- runHandler(handler, event);
34020
+ runHandler(handler, event, report);
34146
34021
  });
34147
34022
  }
34148
34023
  };
34024
+ var TypedActionPredicate = class {
34025
+ action;
34026
+ phase;
34027
+ caller;
34028
+ constructor(action, phase) {
34029
+ this.action = action;
34030
+ this.phase = phase;
34031
+ }
34032
+ calledBy(agent) {
34033
+ this.caller = resolveAgentName(agent);
34034
+ return this;
34035
+ }
34036
+ subscribe(context, handler) {
34037
+ const report = makeReporter(context, {
34038
+ source: "listener",
34039
+ selector: this.phase,
34040
+ action: this.action
34041
+ });
34042
+ return context.onActionEvent((event) => {
34043
+ if (event.action !== this.action)
34044
+ return;
34045
+ if (event.type !== this.phase)
34046
+ return;
34047
+ if (this.caller && event.caller.name !== this.caller)
34048
+ return;
34049
+ runHandler(handler, toTypedActionEvent(event), report);
34050
+ });
34051
+ }
34052
+ };
34053
+ function toTypedActionEvent(raw) {
34054
+ return {
34055
+ type: raw.type,
34056
+ action: raw.action,
34057
+ agent: raw.caller,
34058
+ ...raw.input !== void 0 ? { input: raw.input } : {},
34059
+ ...raw.output !== void 0 ? { output: raw.output } : {},
34060
+ ...raw.error !== void 0 ? { error: raw.error } : {},
34061
+ ...raw.reason !== void 0 ? { reason: raw.reason } : {},
34062
+ at: raw.at
34063
+ };
34064
+ }
34065
+ function createTypedActionHandle(name, unregister) {
34066
+ return {
34067
+ name,
34068
+ unregister,
34069
+ invoked: () => new TypedActionPredicate(name, "action.invoked"),
34070
+ completed: () => new TypedActionPredicate(name, "action.completed"),
34071
+ failed: () => new TypedActionPredicate(name, "action.failed"),
34072
+ denied: () => new TypedActionPredicate(name, "action.denied")
34073
+ };
34074
+ }
34149
34075
  var StatusPredicate = class {
34150
34076
  agentId;
34151
34077
  status;
@@ -34154,12 +34080,16 @@ var StatusPredicate = class {
34154
34080
  this.status = status;
34155
34081
  }
34156
34082
  subscribe(context, handler) {
34083
+ const report = makeReporter(context, {
34084
+ source: "listener",
34085
+ selector: `agent.status.${this.status}`
34086
+ });
34157
34087
  return context.onSessionEvent(({ agentId, event }) => {
34158
34088
  if (agentId !== this.agentId)
34159
34089
  return;
34160
34090
  const matches = event.type === "status.changed" && event.status === this.status || event.type === `status.${this.status}`;
34161
34091
  if (matches)
34162
- runHandler(handler, event);
34092
+ runHandler(handler, event, report);
34163
34093
  });
34164
34094
  }
34165
34095
  };
@@ -34176,6 +34106,7 @@ var ToolCalledPredicate = class {
34176
34106
  return this;
34177
34107
  }
34178
34108
  subscribe(context, handler) {
34109
+ const report = makeReporter(context, { source: "listener", selector: `tool.called:${this.tool}` });
34179
34110
  return context.onSessionEvent(({ agentId, event }) => {
34180
34111
  if (agentId !== this.agentId)
34181
34112
  return;
@@ -34183,7 +34114,7 @@ var ToolCalledPredicate = class {
34183
34114
  return;
34184
34115
  if (this.filter && !this.filter(event))
34185
34116
  return;
34186
- runHandler(handler, event);
34117
+ runHandler(handler, event, report);
34187
34118
  });
34188
34119
  }
34189
34120
  };
@@ -34195,7 +34126,7 @@ var PUBLIC_MESSAGE_TYPE = {
34195
34126
  groupDmReceived: "group_dm.received"
34196
34127
  };
34197
34128
  function buildEnvelope(message, channelName) {
34198
- const channel = message.channel ?? (channelName ? { name: stripSigil2(channelName) } : void 0);
34129
+ const channel = message.channel ?? (channelName ? { name: stripSigil(channelName) } : void 0);
34199
34130
  return {
34200
34131
  ...message.from ? { from: message.from } : {},
34201
34132
  ...message.target ? { to: message.target } : {},
@@ -34258,7 +34189,7 @@ function matchesSelector(selector, type) {
34258
34189
  return type.startsWith(selector.slice(0, -1));
34259
34190
  return selector === type;
34260
34191
  }
34261
- function createListenerHub(baseEvents, actions) {
34192
+ function createListenerHub(baseEvents, actions, options) {
34262
34193
  const sessionHandlers = /* @__PURE__ */ new Set();
34263
34194
  const events = createEnrichedEvents(baseEvents);
34264
34195
  const context = {
@@ -34268,7 +34199,8 @@ function createListenerHub(baseEvents, actions) {
34268
34199
  onSessionEvent: (handler) => {
34269
34200
  sessionHandlers.add(handler);
34270
34201
  return () => sessionHandlers.delete(handler);
34271
- }
34202
+ },
34203
+ ...options?.onError ? { onError: options.onError } : {}
34272
34204
  };
34273
34205
  const addListener = (selector, handler) => {
34274
34206
  try {
@@ -34278,29 +34210,49 @@ function createListenerHub(baseEvents, actions) {
34278
34210
  if (typeof selector !== "string") {
34279
34211
  return selector.subscribe(context, handler);
34280
34212
  }
34213
+ const report = makeReporter(context, { source: "listener", selector });
34281
34214
  const offs = [
34282
34215
  context.events.on("any", (raw) => {
34283
34216
  const evt = toPublicMessagingEvent(raw);
34284
34217
  if (evt && matchesSelector(selector, evt.type))
34285
- runHandler(handler, evt);
34218
+ runHandler(handler, evt, report);
34286
34219
  }),
34287
34220
  context.onActionEvent((raw) => {
34288
34221
  const evt = toPublicActionEvent(raw);
34289
34222
  if (matchesSelector(selector, evt.type))
34290
- runHandler(handler, evt);
34223
+ runHandler(handler, evt, report);
34291
34224
  }),
34292
34225
  context.onSessionEvent(({ agentId, event }) => {
34293
34226
  const evt = toPublicSessionEvent(agentId, event);
34294
34227
  if (matchesSelector(selector, evt.type))
34295
- runHandler(handler, evt);
34228
+ runHandler(handler, evt, report);
34296
34229
  })
34297
34230
  ];
34298
34231
  return () => offs.forEach((off) => off());
34299
34232
  };
34233
+ const once = (selector, handler) => {
34234
+ let done = false;
34235
+ let off;
34236
+ const wrapped = (event) => {
34237
+ if (done)
34238
+ return;
34239
+ done = true;
34240
+ off?.();
34241
+ return handler(event);
34242
+ };
34243
+ off = addListener(selector, wrapped);
34244
+ if (done)
34245
+ off();
34246
+ return () => {
34247
+ done = true;
34248
+ off?.();
34249
+ };
34250
+ };
34300
34251
  return {
34301
34252
  events,
34302
34253
  on: (predicate, handler) => predicate.subscribe(context, handler),
34303
34254
  addListener,
34255
+ once,
34304
34256
  action: (name) => new ActionPredicate(name),
34305
34257
  agent: (input) => createAgentHandle(input),
34306
34258
  emitSessionEvent: (agentId, event) => {
@@ -34329,6 +34281,263 @@ function createAgentHandle(input) {
34329
34281
  };
34330
34282
  }
34331
34283
 
34284
+ // ../sdk/dist/facade.js
34285
+ function resolveAgentName(ref) {
34286
+ if (typeof ref === "string")
34287
+ return stripSigil2(ref);
34288
+ const value = ref.handle ?? ref.name ?? ref.id;
34289
+ if (!value) {
34290
+ throw new Error("Unable to resolve agent name: reference has no name, handle, or id.");
34291
+ }
34292
+ return stripSigil2(value);
34293
+ }
34294
+ function resolveAgentToken(ref) {
34295
+ return ref && typeof ref !== "string" ? ref.token : void 0;
34296
+ }
34297
+ function stripSigil2(value) {
34298
+ return value.startsWith("@") || value.startsWith("#") ? value.slice(1) : value;
34299
+ }
34300
+ function isChannelTarget(to) {
34301
+ return to.startsWith("#");
34302
+ }
34303
+ function deliveryToMode(delivery) {
34304
+ if (!delivery)
34305
+ return void 0;
34306
+ return delivery === "immediate" || delivery === "next-tool-call" ? "steer" : "wait";
34307
+ }
34308
+ function buildText(text, mentions) {
34309
+ let body = text ?? "";
34310
+ if (mentions?.length) {
34311
+ const handles = mentions.map((mention) => `@${resolveAgentName(mention)}`);
34312
+ const missing = handles.filter((handle) => !body.includes(handle));
34313
+ if (missing.length) {
34314
+ body = body ? `${missing.join(" ")} ${body}` : missing.join(" ");
34315
+ }
34316
+ }
34317
+ return body;
34318
+ }
34319
+ function resolveMessageId(ref) {
34320
+ if (!ref) {
34321
+ throw new Error("reply requires a `messageId` or `thread`.");
34322
+ }
34323
+ if (typeof ref === "string")
34324
+ return ref;
34325
+ const value = ref.id ?? ref.threadId;
34326
+ if (!value) {
34327
+ throw new Error("Unable to resolve a message id from the provided thread reference.");
34328
+ }
34329
+ return value;
34330
+ }
34331
+ function createEnrichedMessages(base, resolveFrom) {
34332
+ const enriched = Object.create(base);
34333
+ enriched.send = async (input) => {
34334
+ if ("channel" in input && input.channel) {
34335
+ return base.send(input);
34336
+ }
34337
+ const sendInput = input;
34338
+ const messages = resolveFrom(sendInput.from);
34339
+ const text = buildText(sendInput.text ?? sendInput.msg, sendInput.mentions);
34340
+ if (Array.isArray(sendInput.to)) {
34341
+ return messages.groupDirect({
34342
+ participants: sendInput.to.map(resolveAgentName),
34343
+ text,
34344
+ attachments: sendInput.attachments,
34345
+ mode: sendInput.mode,
34346
+ idempotencyKey: sendInput.idempotencyKey
34347
+ });
34348
+ }
34349
+ if (isChannelTarget(sendInput.to)) {
34350
+ return messages.send({
34351
+ channel: stripSigil2(sendInput.to),
34352
+ text,
34353
+ blocks: sendInput.blocks,
34354
+ attachments: sendInput.attachments,
34355
+ mode: sendInput.mode,
34356
+ idempotencyKey: sendInput.idempotencyKey
34357
+ });
34358
+ }
34359
+ return messages.direct({
34360
+ to: resolveAgentName(sendInput.to),
34361
+ text,
34362
+ attachments: sendInput.attachments,
34363
+ mode: sendInput.mode,
34364
+ idempotencyKey: sendInput.idempotencyKey
34365
+ });
34366
+ };
34367
+ enriched.reply = async (input) => {
34368
+ const messages = resolveFrom(input.from);
34369
+ return messages.reply({
34370
+ messageId: resolveMessageId(input.messageId ?? input.thread),
34371
+ text: input.text,
34372
+ blocks: input.blocks,
34373
+ idempotencyKey: input.idempotencyKey
34374
+ });
34375
+ };
34376
+ enriched.react = (async (arg1, arg2) => {
34377
+ if (typeof arg1 === "string") {
34378
+ return base.react(arg1, arg2);
34379
+ }
34380
+ const messageId = typeof arg1.message === "string" ? arg1.message : arg1.message.id;
34381
+ const messages = resolveFrom(arg1.agent);
34382
+ return messages.react(messageId, arg1.emoji);
34383
+ });
34384
+ enriched.dm = async (input) => {
34385
+ const messages = resolveFrom(input.from);
34386
+ return messages.direct({
34387
+ to: resolveAgentName(input.to),
34388
+ text: input.text ?? input.msg ?? "",
34389
+ attachments: input.attachments,
34390
+ mode: input.mode,
34391
+ idempotencyKey: input.idempotencyKey
34392
+ });
34393
+ };
34394
+ return enriched;
34395
+ }
34396
+ function createWorkspaceFacade(messaging, deps) {
34397
+ const register = async (agents, options) => {
34398
+ if (!deps) {
34399
+ throw new Error("register() is only available on the workspace client.");
34400
+ }
34401
+ const list = Array.isArray(agents) ? agents : [agents];
34402
+ const inputs = list.map((agent) => typeof agent === "string" ? { name: stripSigil2(agent) } : {
34403
+ name: resolveAgentName(agent),
34404
+ type: agent.type,
34405
+ persona: agent.persona,
34406
+ metadata: agent.metadata
34407
+ });
34408
+ const seen = /* @__PURE__ */ new Set();
34409
+ for (const { name } of inputs) {
34410
+ if (seen.has(name)) {
34411
+ throw new RelayError("name_conflict", `Duplicate agent name in register(): "${name}".`);
34412
+ }
34413
+ seen.add(name);
34414
+ }
34415
+ const registerOne = !options?.strict && messaging.agents.registerOrRotate ? messaging.agents.registerOrRotate.bind(messaging.agents) : messaging.agents.register.bind(messaging.agents);
34416
+ const clients = [];
34417
+ for (const input of inputs) {
34418
+ clients.push(deps.buildAgentClient(await registerOne(input)));
34419
+ }
34420
+ return Array.isArray(agents) ? clients : clients[0];
34421
+ };
34422
+ return {
34423
+ info: () => messaging.workspace.info(),
34424
+ register,
34425
+ reconnect: async ({ apiToken }) => {
34426
+ if (!deps) {
34427
+ throw new Error("reconnect() is only available on the workspace client.");
34428
+ }
34429
+ return deps.reconnectAgent(apiToken);
34430
+ }
34431
+ };
34432
+ }
34433
+ function reportActionError(wiring, action, operation, message, error101) {
34434
+ if (wiring.onError) {
34435
+ try {
34436
+ wiring.onError(error101, { source: "action", action, operation });
34437
+ } catch {
34438
+ }
34439
+ return;
34440
+ }
34441
+ console.error(`[agent-relay] ${message}`, error101);
34442
+ }
34443
+ function registerFacadeAction(actions, def, wiring) {
34444
+ const allowed = def.availableTo?.map(resolveAgentName);
34445
+ const policy = allowed || def.policy ? async (input, ctx) => {
34446
+ if (allowed && !allowed.includes(ctx.caller.name)) {
34447
+ return {
34448
+ allowed: false,
34449
+ reason: `${ctx.caller.name} is not permitted to call ${def.name}.`
34450
+ };
34451
+ }
34452
+ return def.policy ? def.policy(input, ctx) : { allowed: true };
34453
+ } : void 0;
34454
+ const localHandle = actions.register({
34455
+ name: def.name,
34456
+ description: def.description,
34457
+ input: def.input,
34458
+ inputSchema: def.inputSchema,
34459
+ output: def.output,
34460
+ outputSchema: def.outputSchema,
34461
+ visibility: def.visibility,
34462
+ policy,
34463
+ handler: (input, ctx) => def.handler({ input, agent: ctx.caller, ctx })
34464
+ });
34465
+ const relayUnsubscribe = wireRelayAction(actions, def, wiring, allowed);
34466
+ return createTypedActionHandle(def.name, () => {
34467
+ relayUnsubscribe?.();
34468
+ localHandle.unregister();
34469
+ });
34470
+ }
34471
+ function wireRelayAction(actions, def, wiring, allowed) {
34472
+ const commands = wiring?.messaging.commands;
34473
+ if (!wiring || !commands?.agentScoped?.() || !commands.available?.()) {
34474
+ return void 0;
34475
+ }
34476
+ const handlerAgent = wiring.handlerAgent;
34477
+ if (!handlerAgent) {
34478
+ return void 0;
34479
+ }
34480
+ void commands.register({
34481
+ command: def.name,
34482
+ description: def.description ?? def.name,
34483
+ handlerAgent,
34484
+ inputSchema: actionSchemaToJsonSchema(def.inputSchema ?? def.input),
34485
+ outputSchema: actionSchemaToJsonSchema(def.outputSchema ?? def.output),
34486
+ ...allowed ? { availableTo: allowed } : {}
34487
+ }).catch((error101) => {
34488
+ reportActionError(wiring, def.name, "register", `failed to register action descriptor "${def.name}":`, error101);
34489
+ });
34490
+ const unsubscribe = wiring.messaging.events.on("actionInvoked", async (event) => {
34491
+ if (event.actionName !== def.name) {
34492
+ return;
34493
+ }
34494
+ await handleActionInvoked(actions, commands, def.name, event, wiring);
34495
+ });
34496
+ try {
34497
+ wiring.messaging.events.connect();
34498
+ } catch (error101) {
34499
+ reportActionError(wiring, def.name, "connect", `failed to open the event stream for action "${def.name}":`, error101);
34500
+ }
34501
+ return unsubscribe;
34502
+ }
34503
+ async function handleActionInvoked(actions, commands, actionName, event, wiring) {
34504
+ let input = {};
34505
+ try {
34506
+ const invocation = await commands.getInvocation(actionName, event.invocationId);
34507
+ input = invocation.input ?? {};
34508
+ } catch (error101) {
34509
+ reportActionError(wiring, actionName, "load_invocation", `failed to load invocation "${event.invocationId}":`, error101);
34510
+ }
34511
+ const result = await actions.invoke({
34512
+ name: actionName,
34513
+ input,
34514
+ caller: { name: event.callerName, type: "agent" }
34515
+ });
34516
+ try {
34517
+ await commands.completeInvocation(actionName, event.invocationId, result.ok ? { output: toOutputRecord(result.output) } : { error: result.error?.message ?? "handler failed" });
34518
+ } catch (error101) {
34519
+ reportActionError(wiring, actionName, "complete_invocation", `failed to complete invocation "${event.invocationId}":`, error101);
34520
+ }
34521
+ }
34522
+ function toOutputRecord(output) {
34523
+ if (output !== null && typeof output === "object" && !Array.isArray(output)) {
34524
+ return output;
34525
+ }
34526
+ return { value: output };
34527
+ }
34528
+ function createNotifyHandler(messages, target, options) {
34529
+ return async () => {
34530
+ const subject = options.subject ? `@${resolveAgentName(options.subject)}` : void 0;
34531
+ const label = options.type ?? options.action ?? "notification";
34532
+ const text = options.text ?? [`[${label}]`, subject].filter(Boolean).join(" ");
34533
+ await messages.dm({
34534
+ to: resolveAgentName(target),
34535
+ text,
34536
+ mode: deliveryToMode(options.delivery)
34537
+ });
34538
+ };
34539
+ }
34540
+
34332
34541
  // ../sdk/dist/agent-relay.js
34333
34542
  var AgentRelay = class _AgentRelay {
34334
34543
  messaging;
@@ -34340,14 +34549,18 @@ var AgentRelay = class _AgentRelay {
34340
34549
  enrichedMessages;
34341
34550
  workspaceFacade;
34342
34551
  hub;
34552
+ errorHooks = /* @__PURE__ */ new Set();
34343
34553
  constructor(options = {}) {
34344
- const { messaging, actions, workspaceKey, createAgentMessaging, ...messagingOptions } = options;
34554
+ const { messaging, actions, workspaceKey, createAgentMessaging, onError, ...messagingOptions } = options;
34345
34555
  const resolvedWorkspaceKey = workspaceKey ?? messagingOptions.apiKey;
34346
34556
  this.workspaceKey = resolvedWorkspaceKey;
34347
34557
  this.messagingOptions = { ...messagingOptions, workspaceKey: resolvedWorkspaceKey };
34348
34558
  this.messaging = messaging ?? new RelaycastMessagingClient(this.messagingOptions);
34349
34559
  this.actions = actions ?? new ActionRegistry();
34350
34560
  this.createAgentMessaging = createAgentMessaging ?? ((token) => new RelaycastMessagingClient({ ...this.messagingOptions, agentToken: token }));
34561
+ if (onError) {
34562
+ this.errorHooks.add(onError);
34563
+ }
34351
34564
  }
34352
34565
  static async createWorkspace(input) {
34353
34566
  const options = typeof input === "string" ? { name: input } : input;
@@ -34358,7 +34571,7 @@ var AgentRelay = class _AgentRelay {
34358
34571
  });
34359
34572
  const workspaceKey = extractWorkspaceKey(workspace);
34360
34573
  if (!workspaceKey) {
34361
- throw new Error("Workspace created, but the response did not include a workspace key.");
34574
+ throw new RelayError("transport_error", "Workspace created, but the response did not include a workspace key.", { retryable: false });
34362
34575
  }
34363
34576
  return new _AgentRelay({
34364
34577
  workspaceKey,
@@ -34391,7 +34604,9 @@ var AgentRelay = class _AgentRelay {
34391
34604
  }
34392
34605
  get listenerHub() {
34393
34606
  if (!this.hub) {
34394
- this.hub = createListenerHub(this.messaging.events, this.actions);
34607
+ this.hub = createListenerHub(this.messaging.events, this.actions, {
34608
+ onError: (error101, context) => this.reportError(error101, context)
34609
+ });
34395
34610
  }
34396
34611
  return this.hub;
34397
34612
  }
@@ -34422,7 +34637,7 @@ var AgentRelay = class _AgentRelay {
34422
34637
  id: registration.id,
34423
34638
  name: registration.name,
34424
34639
  token: registration.token
34425
- });
34640
+ }, { onError: (error101, context) => this.reportError(error101, context) });
34426
34641
  }
34427
34642
  /** Rehydrate a live client from a persisted agent token, resolving identity from the relay. */
34428
34643
  async reconnectAgent(apiToken) {
@@ -34432,15 +34647,43 @@ var AgentRelay = class _AgentRelay {
34432
34647
  id: identity.id,
34433
34648
  name: identity.name,
34434
34649
  token: apiToken
34435
- });
34650
+ }, { onError: (error101, context) => this.reportError(error101, context) });
34436
34651
  }
34437
34652
  registerAction(def) {
34438
- return registerFacadeAction(this.actions, def, { messaging: this.messaging });
34653
+ return registerFacadeAction(this.actions, def, {
34654
+ messaging: this.messaging,
34655
+ onError: (error101, context) => this.reportError(error101, context)
34656
+ });
34439
34657
  }
34440
- /** Subscribe by dotted event name, `'*'`/prefix wildcard, or a predicate. */
34441
34658
  addListener(selector, handler) {
34442
34659
  return this.listenerHub.addListener(selector, handler);
34443
34660
  }
34661
+ once(selector, handler) {
34662
+ return this.listenerHub.once(selector, handler);
34663
+ }
34664
+ /**
34665
+ * Register a hook that receives listener and action handler errors. Returns
34666
+ * an unsubscribe callback. When no hook is registered, errors are logged as
34667
+ * console warnings.
34668
+ */
34669
+ onError(hook) {
34670
+ this.errorHooks.add(hook);
34671
+ return () => {
34672
+ this.errorHooks.delete(hook);
34673
+ };
34674
+ }
34675
+ reportError(error101, context) {
34676
+ if (this.errorHooks.size === 0) {
34677
+ logRelayHandlerError(error101, context);
34678
+ return;
34679
+ }
34680
+ for (const hook of this.errorHooks) {
34681
+ try {
34682
+ hook(error101, context);
34683
+ } catch {
34684
+ }
34685
+ }
34686
+ }
34444
34687
  action(name) {
34445
34688
  return this.listenerHub.action(name);
34446
34689
  }
@@ -34470,9 +34713,9 @@ function extractWorkspaceKey(payload) {
34470
34713
  const value = payload.workspaceKey ?? payload.workspace_key ?? payload.apiKey ?? payload.api_key ?? data.workspaceKey ?? data.workspace_key ?? data.apiKey ?? data.api_key;
34471
34714
  return typeof value === "string" && value.trim() ? value : void 0;
34472
34715
  }
34473
- function agentRelayAgent(messaging, actions, handlerAgent) {
34716
+ function agentRelayAgent(messaging, actions, handlerAgent, options) {
34474
34717
  const messages = createEnrichedMessages(messaging.messages, () => messaging.messages);
34475
- const hub = createListenerHub(messaging.events, actions);
34718
+ const hub = createListenerHub(messaging.events, actions, { onError: options?.onError });
34476
34719
  return {
34477
34720
  messaging,
34478
34721
  agents: messaging.agents,
@@ -34486,15 +34729,16 @@ function agentRelayAgent(messaging, actions, handlerAgent) {
34486
34729
  webhooks: messaging.webhooks,
34487
34730
  capabilities: messaging.commands,
34488
34731
  workspace: createWorkspaceFacade(messaging),
34489
- registerAction: (def) => registerFacadeAction(actions, def, { messaging, handlerAgent }),
34490
- addListener: (selector, handler) => hub.addListener(selector, handler),
34732
+ registerAction: (def) => registerFacadeAction(actions, def, { messaging, handlerAgent, onError: options?.onError }),
34733
+ addListener: ((selector, handler) => hub.addListener(selector, handler)),
34734
+ once: ((selector, handler) => hub.once(selector, handler)),
34491
34735
  action: (name) => hub.action(name),
34492
34736
  agent: (input) => hub.agent(input),
34493
34737
  emitSessionEvent: (agentId, event) => hub.emitSessionEvent(agentId, event)
34494
34738
  };
34495
34739
  }
34496
- function assembleAgentClient(messaging, actions, identity) {
34497
- const base = agentRelayAgent(messaging, actions, identity.name);
34740
+ function assembleAgentClient(messaging, actions, identity, options) {
34741
+ const base = agentRelayAgent(messaging, actions, identity.name, options);
34498
34742
  const handle = createAgentHandle(identity);
34499
34743
  return {
34500
34744
  ...base,
@@ -36069,11 +36313,13 @@ function generateRequestId(prefix = "") {
36069
36313
  MessageReactedPredicate,
36070
36314
  MessageReadPredicate,
36071
36315
  RelayCapabilityError,
36316
+ RelayError,
36072
36317
  RelaycastMessagingClient,
36073
36318
  StaticPatterns,
36074
36319
  StatusPredicate,
36075
36320
  ToolCalledPredicate,
36076
36321
  TracedError,
36322
+ TypedActionPredicate,
36077
36323
  actionSchemaToJsonSchema,
36078
36324
  agentRelayAgent,
36079
36325
  agentTokenRecoveryMessage,
@@ -36095,6 +36341,7 @@ function generateRequestId(prefix = "") {
36095
36341
  createRequestEnvelope,
36096
36342
  createRequestHandler,
36097
36343
  createTraceableError,
36344
+ createTypedActionHandle,
36098
36345
  createWorkspaceFacade,
36099
36346
  defineHarness,
36100
36347
  findGitRoot,
@@ -36127,6 +36374,7 @@ function generateRequestId(prefix = "") {
36127
36374
  isSpawnOrReleaseCommandFast,
36128
36375
  isValidAgentName,
36129
36376
  logAndTraceError,
36377
+ logRelayHandlerError,
36130
36378
  mapModelToCli,
36131
36379
  matchesSelector,
36132
36380
  nextHarnessName,