agent-relay 8.3.7 → 8.5.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,
@@ -246,8 +250,11 @@ __export(index_exports, {
246
250
  normalizeChannelMember: () => normalizeChannelMember,
247
251
  normalizeChannelName: () => normalizeChannelName,
248
252
  normalizeChannelReadStatus: () => normalizeChannelReadStatus,
253
+ normalizeDeliveryTransition: () => normalizeDeliveryTransition,
249
254
  normalizeGroupDirectConversation: () => normalizeGroupDirectConversation,
250
255
  normalizeInbox: () => normalizeInbox,
256
+ normalizeInboxItem: () => normalizeInboxItem,
257
+ normalizeInboxItemState: () => normalizeInboxItemState,
251
258
  normalizeMessage: () => normalizeMessage,
252
259
  normalizeMessagingEvent: () => normalizeMessagingEvent,
253
260
  normalizeReaction: () => normalizeReaction,
@@ -33211,6 +33218,74 @@ function normalizeChannelReadStatus(input) {
33211
33218
  ...lastReadAt ? { lastReadAt } : {}
33212
33219
  };
33213
33220
  }
33221
+ var INBOX_STATE_BY_DELIVERY_STATUS = {
33222
+ accepted: "queued",
33223
+ delivered: "delivered",
33224
+ deferred: "deferred",
33225
+ failed: "failed"
33226
+ };
33227
+ function normalizeInboxItemState(value) {
33228
+ return (value !== void 0 ? INBOX_STATE_BY_DELIVERY_STATUS[value] : void 0) ?? "queued";
33229
+ }
33230
+ function normalizeDeliveryMetadata(record3) {
33231
+ const metadata = {};
33232
+ const mode = readString(record3, "mode");
33233
+ const reason = readNullableString(record3, "reason");
33234
+ const priority = readString(record3, "priority");
33235
+ const retryable = readBoolean(record3, "retryable");
33236
+ const error101 = readNullableString(record3, "error");
33237
+ const deadline = readNullableString(record3, "deadline");
33238
+ if (mode)
33239
+ metadata.mode = mode;
33240
+ if (reason)
33241
+ metadata.reason = reason;
33242
+ if (priority)
33243
+ metadata.priority = priority;
33244
+ if (retryable !== void 0)
33245
+ metadata.retryable = retryable;
33246
+ if (error101)
33247
+ metadata.error = error101;
33248
+ if (deadline)
33249
+ metadata.deadline = deadline;
33250
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
33251
+ }
33252
+ function normalizeInboxItem(input, context = {}) {
33253
+ const record3 = isRecord(input) ? input : {};
33254
+ const messageId = readString(record3, "messageId", "message_id");
33255
+ const channelId = readString(record3, "channelId", "channel_id");
33256
+ const agentId = readString(record3, "agentId", "agent_id");
33257
+ const availableAt = readNullableString(record3, "availableAt", "available_at");
33258
+ const messageRecord = readRecord(record3, "message");
33259
+ const metadata = normalizeDeliveryMetadata(record3);
33260
+ return {
33261
+ id: readString(record3, "id", "deliveryId", "delivery_id") ?? "",
33262
+ recipient: {
33263
+ name: context.recipientName ?? agentId ?? "",
33264
+ ...agentId ? { id: agentId } : {}
33265
+ },
33266
+ state: normalizeInboxItemState(readString(record3, "status")),
33267
+ // The relaycast ledger does not expose attempt counts.
33268
+ attempts: 0,
33269
+ ...availableAt ? { availableAt } : {},
33270
+ message: normalizeMessage(messageRecord ?? {
33271
+ ...messageId ? { id: messageId } : {},
33272
+ ...channelId ? { channel_id: channelId } : {}
33273
+ }),
33274
+ ...metadata ? { metadata } : {}
33275
+ };
33276
+ }
33277
+ function normalizeDeliveryTransition(action, input) {
33278
+ const record3 = isRecord(input) ? input : {};
33279
+ const availableAt = readNullableString(record3, "availableAt", "available_at");
33280
+ return {
33281
+ supported: true,
33282
+ action,
33283
+ deliveryId: readString(record3, "id", "deliveryId", "delivery_id") ?? "",
33284
+ messageId: readString(record3, "messageId", "message_id") ?? "",
33285
+ state: normalizeInboxItemState(readString(record3, "status")),
33286
+ ...action === "defer" && availableAt ? { deferUntil: availableAt } : {}
33287
+ };
33288
+ }
33214
33289
  function normalizeSearchResult(input) {
33215
33290
  const record3 = isRecord(input) ? input : {};
33216
33291
  const createdAt = readString(record3, "createdAt", "created_at");
@@ -33528,13 +33603,7 @@ function createRelaycastClient(options) {
33528
33603
  }));
33529
33604
  }
33530
33605
  var RelaycastMessagingClient = class {
33531
- capabilities = {
33532
- serverDeliveryState: false,
33533
- durableDelivery: false,
33534
- durableAck: false,
33535
- durableFail: false,
33536
- durableDefer: false
33537
- };
33606
+ capabilities;
33538
33607
  relaycast;
33539
33608
  agentClient;
33540
33609
  eventHandlers = /* @__PURE__ */ new Map();
@@ -33542,6 +33611,14 @@ var RelaycastMessagingClient = class {
33542
33611
  constructor(options) {
33543
33612
  this.relaycast = createRelaycastClient(options);
33544
33613
  this.agentClient = options.agentClient ?? (options.agentToken ? this.relaycast.as?.(options.agentToken, options.agentClientOptions) : void 0);
33614
+ const durable = this.deliverySurface() !== void 0;
33615
+ this.capabilities = {
33616
+ serverDeliveryState: durable,
33617
+ durableDelivery: durable,
33618
+ durableAck: durable,
33619
+ durableFail: durable,
33620
+ durableDefer: durable
33621
+ };
33545
33622
  }
33546
33623
  agents = {
33547
33624
  list: async (options) => {
@@ -33550,6 +33627,7 @@ var RelaycastMessagingClient = class {
33550
33627
  },
33551
33628
  get: async (name) => normalizeAgent(await this.relaycast.agents.get(name)),
33552
33629
  register: async (input) => normalizeAgentRegistration(await this.relaycast.agents.register(input)),
33630
+ registerOrRotate: async (input) => normalizeAgentRegistration(this.relaycast.agents.registerOrRotate ? await this.relaycast.agents.registerOrRotate(input) : await this.relaycast.agents.register(input)),
33553
33631
  me: async () => normalizeAgent(await this.requireAgentClient("agents.me").me()),
33554
33632
  update: async (name, input) => normalizeAgent(await this.relaycast.agents.update(name, input)),
33555
33633
  delete: async (name) => {
@@ -33676,12 +33754,55 @@ var RelaycastMessagingClient = class {
33676
33754
  };
33677
33755
  inbox = {
33678
33756
  get: async (options) => normalizeInbox(await this.requireAgentClient("inbox.get").inbox(definedOptions({ limit: options?.limit }))),
33679
- list: async (_input) => ({ items: [] }),
33680
- subscribe: (_input) => this.emptyInboxSubscription(),
33681
- ack: async (input) => this.unsupportedInboxDelivery("ack", input.inboxItemId),
33682
- fail: async (input) => this.unsupportedInboxDelivery("fail", input.inboxItemId, input.error),
33683
- defer: async (input) => this.unsupportedInboxDelivery("defer", input.inboxItemId, input.reason, input.availableAt),
33684
- markRead: async (input) => this.unsupportedInboxDelivery("ack", input.inboxItemId, "Inbox read state updates require server delivery state.")
33757
+ /**
33758
+ * List durable deliveries queued for the authenticated agent. The
33759
+ * relaycast ledger replays non-terminal items (accepted + deferred) in
33760
+ * FIFO order with the message payload embedded. The underlying API has no
33761
+ * cursor, so `nextCursor` is never set and `before`/`after` are ignored.
33762
+ */
33763
+ list: async (input) => {
33764
+ const surface = this.deliverySurface();
33765
+ if (!surface)
33766
+ return { items: [] };
33767
+ const deliveries = await surface.deliveries(definedOptions({ limit: input?.limit }));
33768
+ return {
33769
+ items: deliveries.map((delivery) => normalizeInboxItem(delivery, definedOptions({ recipientName: input?.agentName })))
33770
+ };
33771
+ },
33772
+ /**
33773
+ * Stream durable deliveries: seed from the non-terminal queue, then push
33774
+ * items announced by `delivery.accepted` WebSocket events, deduplicated by
33775
+ * delivery id. Falls back to an empty stream without an agent client.
33776
+ */
33777
+ subscribe: (input) => {
33778
+ const surface = this.deliverySurface();
33779
+ if (!surface)
33780
+ return this.emptyInboxSubscription();
33781
+ return this.createInboxSubscription(surface, input);
33782
+ },
33783
+ ack: async (input) => {
33784
+ const surface = this.deliverySurface();
33785
+ if (!surface)
33786
+ return this.unsupportedInboxDelivery("ack", input.inboxItemId);
33787
+ return normalizeDeliveryTransition("ack", await surface.ackDelivery(input.inboxItemId));
33788
+ },
33789
+ fail: async (input) => {
33790
+ const surface = this.deliverySurface();
33791
+ if (!surface)
33792
+ return this.unsupportedInboxDelivery("fail", input.inboxItemId, input.error);
33793
+ return normalizeDeliveryTransition("fail", await surface.failDelivery(input.inboxItemId, definedOptions({ error: input.error, retryable: input.retry })));
33794
+ },
33795
+ defer: async (input) => {
33796
+ const surface = this.deliverySurface();
33797
+ if (!surface) {
33798
+ return this.unsupportedInboxDelivery("defer", input.inboxItemId, input.reason, input.availableAt);
33799
+ }
33800
+ return normalizeDeliveryTransition("defer", await surface.deferDelivery(input.inboxItemId, {
33801
+ availableAt: input.availableAt,
33802
+ ...input.reason === void 0 ? {} : { reason: input.reason }
33803
+ }));
33804
+ },
33805
+ markRead: async (input) => this.unsupportedInboxDelivery("ack", input.inboxItemId, "The Relaycast delivery ledger has no read state; use inbox.ack to mark a delivery handled.")
33685
33806
  };
33686
33807
  events = {
33687
33808
  connect: () => {
@@ -33717,26 +33838,34 @@ var RelaycastMessagingClient = class {
33717
33838
  },
33718
33839
  on: (event, handler) => this.addEventListener(event, handler)
33719
33840
  };
33841
+ /**
33842
+ * Durable delivery transitions keyed by the relaycast delivery id (the
33843
+ * `InboxItem.id` returned by `inbox.list`/`inbox.subscribe`). Transitions
33844
+ * are idempotent on the server.
33845
+ */
33720
33846
  deliveries = {
33721
- ack: async (messageId) => ({
33722
- supported: false,
33723
- action: "ack",
33724
- messageId,
33725
- reason: "Durable acknowledgements are not supported by the Relaycast messaging backend yet."
33726
- }),
33727
- fail: async (messageId, reason) => ({
33728
- supported: false,
33729
- action: "fail",
33730
- messageId,
33731
- ...reason ? { reason } : { reason: "Durable failure reporting is not supported by the Relaycast messaging backend yet." }
33732
- }),
33733
- defer: async (messageId, deferUntil) => ({
33734
- supported: false,
33735
- action: "defer",
33736
- messageId,
33737
- ...deferUntil ? { deferUntil } : {},
33738
- reason: "Durable deferral is not supported by the Relaycast messaging backend yet."
33739
- })
33847
+ ack: async (deliveryId) => {
33848
+ const surface = this.deliverySurface();
33849
+ if (!surface)
33850
+ return this.unsupportedInboxDelivery("ack", deliveryId);
33851
+ return normalizeDeliveryTransition("ack", await surface.ackDelivery(deliveryId));
33852
+ },
33853
+ fail: async (deliveryId, reason) => {
33854
+ const surface = this.deliverySurface();
33855
+ if (!surface)
33856
+ return this.unsupportedInboxDelivery("fail", deliveryId, reason);
33857
+ return normalizeDeliveryTransition("fail", await surface.failDelivery(deliveryId, definedOptions({ error: reason })));
33858
+ },
33859
+ defer: async (deliveryId, deferUntil) => {
33860
+ const surface = this.deliverySurface();
33861
+ if (!surface)
33862
+ return this.unsupportedInboxDelivery("defer", deliveryId, void 0, deferUntil);
33863
+ return normalizeDeliveryTransition("defer", await surface.deferDelivery(deliveryId, {
33864
+ // The relaycast defer transition requires an explicit availability
33865
+ // time; default to a short retry window when none is given.
33866
+ availableAt: deferUntil ?? new Date(Date.now() + 3e4).toISOString()
33867
+ }));
33868
+ }
33740
33869
  };
33741
33870
  integrations = {
33742
33871
  webhooks: {
@@ -33833,6 +33962,83 @@ var RelaycastMessagingClient = class {
33833
33962
  }
33834
33963
  return this.relaycast.dmMessages;
33835
33964
  }
33965
+ /**
33966
+ * The durable delivery API of the agent client, when present. Requires an
33967
+ * agent-scoped client built from `@relaycast/sdk` 2.5+ (or a compatible
33968
+ * injected `agentClient`).
33969
+ */
33970
+ deliverySurface() {
33971
+ const agent = this.agentClient;
33972
+ if (!agent || typeof agent.deliveries !== "function" || typeof agent.ackDelivery !== "function" || typeof agent.failDelivery !== "function" || typeof agent.deferDelivery !== "function") {
33973
+ return void 0;
33974
+ }
33975
+ return agent;
33976
+ }
33977
+ async *createInboxSubscription(agent, input) {
33978
+ const signal = input?.signal;
33979
+ if (signal?.aborted)
33980
+ return;
33981
+ const recipient = definedOptions({ recipientName: input?.agentName });
33982
+ const seen = /* @__PURE__ */ new Set();
33983
+ const queue = [];
33984
+ let stopped = false;
33985
+ let notify;
33986
+ const wake = () => {
33987
+ const resolve2 = notify;
33988
+ notify = void 0;
33989
+ resolve2?.();
33990
+ };
33991
+ const push = (item) => {
33992
+ if (!item.id || seen.has(item.id))
33993
+ return;
33994
+ seen.add(item.id);
33995
+ queue.push(item);
33996
+ wake();
33997
+ };
33998
+ const stop = () => {
33999
+ stopped = true;
34000
+ wake();
34001
+ };
34002
+ const inFlight = /* @__PURE__ */ new Set();
34003
+ agent.connect();
34004
+ const unsubscribe = agent.on.any((event) => {
34005
+ const record3 = asRecord(event);
34006
+ if (record3.type !== "delivery.accepted")
34007
+ return;
34008
+ const deliveryId = readStr(record3, "deliveryId", "delivery_id");
34009
+ if (!deliveryId || seen.has(deliveryId) || inFlight.has(deliveryId))
34010
+ return;
34011
+ inFlight.add(deliveryId);
34012
+ void agent.deliveries().then((deliveries) => {
34013
+ const match = deliveries.find((raw) => readStr(asRecord(raw), "id") === deliveryId);
34014
+ if (match)
34015
+ push(normalizeInboxItem(match, recipient));
34016
+ }).catch(() => {
34017
+ }).finally(() => {
34018
+ inFlight.delete(deliveryId);
34019
+ });
34020
+ });
34021
+ signal?.addEventListener("abort", stop, { once: true });
34022
+ try {
34023
+ for (const raw of await agent.deliveries()) {
34024
+ push(normalizeInboxItem(raw, recipient));
34025
+ }
34026
+ while (!stopped) {
34027
+ const next = queue.shift();
34028
+ if (next) {
34029
+ yield next;
34030
+ continue;
34031
+ }
34032
+ await new Promise((resolve2) => {
34033
+ notify = resolve2;
34034
+ });
34035
+ }
34036
+ } finally {
34037
+ stopped = true;
34038
+ unsubscribe();
34039
+ signal?.removeEventListener("abort", stop);
34040
+ }
34041
+ }
33836
34042
  async *emptyInboxSubscription() {
33837
34043
  return;
33838
34044
  }
@@ -33842,7 +34048,7 @@ var RelaycastMessagingClient = class {
33842
34048
  action,
33843
34049
  messageId,
33844
34050
  ...reason ? { reason } : {
33845
- reason: "Relaycast messaging does not expose durable inbox delivery state in this SDK surface yet."
34051
+ reason: "Durable delivery transitions require an agent-scoped client with the Relaycast delivery API."
33846
34052
  },
33847
34053
  ...deferUntil ? { deferUntil } : {}
33848
34054
  };
@@ -33895,270 +34101,38 @@ var RelaycastMessagingClient = class {
33895
34101
  }
33896
34102
  };
33897
34103
 
33898
- // ../sdk/dist/facade.js
33899
- function resolveAgentName(ref) {
33900
- if (typeof ref === "string")
33901
- return stripSigil(ref);
33902
- const value = ref.handle ?? ref.name ?? ref.id;
33903
- if (!value) {
33904
- throw new Error("Unable to resolve agent name: reference has no name, handle, or id.");
33905
- }
33906
- return stripSigil(value);
33907
- }
33908
- function resolveAgentToken(ref) {
33909
- return ref && typeof ref !== "string" ? ref.token : void 0;
34104
+ // ../sdk/dist/listeners.js
34105
+ function logRelayHandlerError(error101, context) {
34106
+ const where = context.action ? `action "${context.action}"` : `"${context.selector ?? "unknown"}"`;
34107
+ console.warn(`[agent-relay] ${context.source} handler for ${where} threw:`, error101);
33910
34108
  }
33911
34109
  function stripSigil(value) {
33912
34110
  return value.startsWith("@") || value.startsWith("#") ? value.slice(1) : value;
33913
34111
  }
33914
- function isChannelTarget(to) {
33915
- return to.startsWith("#");
33916
- }
33917
- function deliveryToMode(delivery) {
33918
- if (!delivery)
33919
- return void 0;
33920
- return delivery === "immediate" || delivery === "next-tool-call" ? "steer" : "wait";
33921
- }
33922
- function buildText(text, mentions) {
33923
- let body = text ?? "";
33924
- if (mentions?.length) {
33925
- const handles = mentions.map((mention) => `@${resolveAgentName(mention)}`);
33926
- const missing = handles.filter((handle) => !body.includes(handle));
33927
- if (missing.length) {
33928
- body = body ? `${missing.join(" ")} ${body}` : missing.join(" ");
33929
- }
33930
- }
33931
- return body;
33932
- }
33933
- function resolveMessageId(ref) {
33934
- if (!ref) {
33935
- throw new Error("reply requires a `messageId` or `thread`.");
33936
- }
33937
- if (typeof ref === "string")
33938
- return ref;
33939
- const value = ref.id ?? ref.threadId;
33940
- if (!value) {
33941
- throw new Error("Unable to resolve a message id from the provided thread reference.");
33942
- }
33943
- return value;
33944
- }
33945
- function createEnrichedMessages(base, resolveFrom) {
33946
- const enriched = Object.create(base);
33947
- enriched.send = async (input) => {
33948
- if ("channel" in input && input.channel) {
33949
- return base.send(input);
33950
- }
33951
- const sendInput = input;
33952
- const messages = resolveFrom(sendInput.from);
33953
- const text = buildText(sendInput.text ?? sendInput.msg, sendInput.mentions);
33954
- if (Array.isArray(sendInput.to)) {
33955
- return messages.groupDirect({
33956
- participants: sendInput.to.map(resolveAgentName),
33957
- text,
33958
- attachments: sendInput.attachments,
33959
- mode: sendInput.mode,
33960
- idempotencyKey: sendInput.idempotencyKey
33961
- });
33962
- }
33963
- if (isChannelTarget(sendInput.to)) {
33964
- return messages.send({
33965
- channel: stripSigil(sendInput.to),
33966
- text,
33967
- blocks: sendInput.blocks,
33968
- attachments: sendInput.attachments,
33969
- mode: sendInput.mode,
33970
- idempotencyKey: sendInput.idempotencyKey
33971
- });
33972
- }
33973
- return messages.direct({
33974
- to: resolveAgentName(sendInput.to),
33975
- text,
33976
- attachments: sendInput.attachments,
33977
- mode: sendInput.mode,
33978
- idempotencyKey: sendInput.idempotencyKey
33979
- });
33980
- };
33981
- enriched.reply = async (input) => {
33982
- const messages = resolveFrom(input.from);
33983
- return messages.reply({
33984
- messageId: resolveMessageId(input.messageId ?? input.thread),
33985
- text: input.text,
33986
- blocks: input.blocks,
33987
- idempotencyKey: input.idempotencyKey
33988
- });
33989
- };
33990
- enriched.react = (async (arg1, arg2) => {
33991
- if (typeof arg1 === "string") {
33992
- return base.react(arg1, arg2);
33993
- }
33994
- const messageId = typeof arg1.message === "string" ? arg1.message : arg1.message.id;
33995
- const messages = resolveFrom(arg1.agent);
33996
- return messages.react(messageId, arg1.emoji);
33997
- });
33998
- enriched.dm = async (input) => {
33999
- const messages = resolveFrom(input.from);
34000
- return messages.direct({
34001
- to: resolveAgentName(input.to),
34002
- text: input.text ?? input.msg ?? "",
34003
- attachments: input.attachments,
34004
- mode: input.mode,
34005
- idempotencyKey: input.idempotencyKey
34006
- });
34007
- };
34008
- return enriched;
34009
- }
34010
- function createWorkspaceFacade(messaging, deps) {
34011
- const register = async (agents) => {
34012
- if (!deps) {
34013
- throw new Error("register() is only available on the workspace client.");
34014
- }
34015
- const list = Array.isArray(agents) ? agents : [agents];
34016
- const inputs = list.map((agent) => typeof agent === "string" ? { name: stripSigil(agent) } : {
34017
- name: resolveAgentName(agent),
34018
- type: agent.type,
34019
- persona: agent.persona,
34020
- metadata: agent.metadata
34021
- });
34022
- const seen = /* @__PURE__ */ new Set();
34023
- for (const { name } of inputs) {
34024
- if (seen.has(name)) {
34025
- throw new Error(`Duplicate agent name in register(): "${name}".`);
34026
- }
34027
- seen.add(name);
34028
- }
34029
- const clients = [];
34030
- for (const input of inputs) {
34031
- clients.push(deps.buildAgentClient(await messaging.agents.register(input)));
34032
- }
34033
- return Array.isArray(agents) ? clients : clients[0];
34034
- };
34035
- return {
34036
- info: () => messaging.workspace.info(),
34037
- register,
34038
- reconnect: async ({ apiToken }) => {
34039
- if (!deps) {
34040
- throw new Error("reconnect() is only available on the workspace client.");
34041
- }
34042
- return deps.reconnectAgent(apiToken);
34043
- }
34044
- };
34045
- }
34046
- function registerFacadeAction(actions, def, wiring) {
34047
- const allowed = def.availableTo?.map(resolveAgentName);
34048
- const policy = allowed || def.policy ? async (input, ctx) => {
34049
- if (allowed && !allowed.includes(ctx.caller.name)) {
34050
- return {
34051
- allowed: false,
34052
- reason: `${ctx.caller.name} is not permitted to call ${def.name}.`
34053
- };
34054
- }
34055
- return def.policy ? def.policy(input, ctx) : { allowed: true };
34056
- } : void 0;
34057
- const localHandle = actions.register({
34058
- name: def.name,
34059
- description: def.description,
34060
- input: def.input,
34061
- inputSchema: def.inputSchema,
34062
- output: def.output,
34063
- outputSchema: def.outputSchema,
34064
- visibility: def.visibility,
34065
- policy,
34066
- handler: (input, ctx) => def.handler({ input, agent: ctx.caller, ctx })
34067
- });
34068
- const relayUnsubscribe = wireRelayAction(actions, def, wiring, allowed);
34069
- if (!relayUnsubscribe) {
34070
- return localHandle;
34071
- }
34072
- return {
34073
- unregister: () => {
34074
- relayUnsubscribe();
34075
- localHandle.unregister();
34076
- }
34077
- };
34078
- }
34079
- function wireRelayAction(actions, def, wiring, allowed) {
34080
- const commands = wiring?.messaging.commands;
34081
- if (!wiring || !commands?.agentScoped?.() || !commands.available?.()) {
34082
- return void 0;
34083
- }
34084
- const handlerAgent = wiring.handlerAgent;
34085
- if (!handlerAgent) {
34086
- return void 0;
34087
- }
34088
- void commands.register({
34089
- command: def.name,
34090
- description: def.description ?? def.name,
34091
- handlerAgent,
34092
- inputSchema: actionSchemaToJsonSchema(def.inputSchema ?? def.input),
34093
- outputSchema: actionSchemaToJsonSchema(def.outputSchema ?? def.output),
34094
- ...allowed ? { availableTo: allowed } : {}
34095
- }).catch((error101) => {
34096
- console.error(`[agent-relay] failed to register action descriptor "${def.name}":`, error101);
34097
- });
34098
- const unsubscribe = wiring.messaging.events.on("actionInvoked", async (event) => {
34099
- if (event.actionName !== def.name) {
34100
- return;
34101
- }
34102
- await handleActionInvoked(actions, commands, def.name, event);
34103
- });
34104
- try {
34105
- wiring.messaging.events.connect();
34106
- } catch (error101) {
34107
- console.error(`[agent-relay] failed to open the event stream for action "${def.name}":`, error101);
34108
- }
34109
- return unsubscribe;
34110
- }
34111
- async function handleActionInvoked(actions, commands, actionName, event) {
34112
- let input = {};
34113
- try {
34114
- const invocation = await commands.getInvocation(actionName, event.invocationId);
34115
- input = invocation.input ?? {};
34116
- } catch (error101) {
34117
- console.error(`[agent-relay] failed to load invocation "${event.invocationId}":`, error101);
34118
- }
34119
- const result = await actions.invoke({
34120
- name: actionName,
34121
- input,
34122
- caller: { name: event.callerName, type: "agent" }
34123
- });
34112
+ function runHandler(handler, event, report) {
34124
34113
  try {
34125
- await commands.completeInvocation(actionName, event.invocationId, result.ok ? { output: toOutputRecord(result.output) } : { error: result.error?.message ?? "handler failed" });
34114
+ void Promise.resolve(handler(event)).catch(report);
34126
34115
  } catch (error101) {
34127
- console.error(`[agent-relay] failed to complete invocation "${event.invocationId}":`, error101);
34116
+ report(error101);
34128
34117
  }
34129
34118
  }
34130
- function toOutputRecord(output) {
34131
- if (output !== null && typeof output === "object" && !Array.isArray(output)) {
34132
- return output;
34133
- }
34134
- return { value: output };
34135
- }
34136
- function createNotifyHandler(messages, target, options) {
34137
- return async () => {
34138
- const subject = options.subject ? `@${resolveAgentName(options.subject)}` : void 0;
34139
- const label = options.type ?? options.action ?? "notification";
34140
- const text = options.text ?? [`[${label}]`, subject].filter(Boolean).join(" ");
34141
- await messages.dm({
34142
- to: resolveAgentName(target),
34143
- text,
34144
- mode: deliveryToMode(options.delivery)
34145
- });
34119
+ function makeReporter(context, info) {
34120
+ return (error101) => {
34121
+ if (!context.onError) {
34122
+ logRelayHandlerError(error101, info);
34123
+ return;
34124
+ }
34125
+ try {
34126
+ context.onError(error101, info);
34127
+ } catch {
34128
+ }
34146
34129
  };
34147
34130
  }
34148
-
34149
- // ../sdk/dist/listeners.js
34150
- function stripSigil2(value) {
34151
- return value.startsWith("@") || value.startsWith("#") ? value.slice(1) : value;
34152
- }
34153
- function runHandler(handler, event) {
34154
- void Promise.resolve(handler(event)).catch(() => {
34155
- });
34156
- }
34157
34131
  var MessageCreatedPredicate = class {
34158
34132
  channel;
34159
34133
  mentioned;
34160
34134
  in(channel) {
34161
- this.channel = stripSigil2(channel);
34135
+ this.channel = stripSigil(channel);
34162
34136
  return this;
34163
34137
  }
34164
34138
  mentions(agent) {
@@ -34166,30 +34140,33 @@ var MessageCreatedPredicate = class {
34166
34140
  return this;
34167
34141
  }
34168
34142
  subscribe(context, handler) {
34143
+ const report = makeReporter(context, { source: "listener", selector: "message.created" });
34169
34144
  return context.events.on("messageCreated", (event) => {
34170
- if (this.channel && stripSigil2(event.channel) !== this.channel)
34145
+ if (this.channel && stripSigil(event.channel) !== this.channel)
34171
34146
  return;
34172
34147
  if (this.mentioned && !messageMentions(event, this.mentioned))
34173
34148
  return;
34174
- runHandler(handler, event);
34149
+ runHandler(handler, event, report);
34175
34150
  });
34176
34151
  }
34177
34152
  };
34178
34153
  function messageMentions(event, name) {
34179
34154
  const mentions = event.message.mentions ?? [];
34180
- if (mentions.some((mention) => stripSigil2(mention) === name))
34155
+ if (mentions.some((mention) => stripSigil(mention) === name))
34181
34156
  return true;
34182
34157
  return event.message.text?.includes(`@${name}`) ?? false;
34183
34158
  }
34184
34159
  var MessageReadPredicate = class {
34185
34160
  subscribe(context, handler) {
34186
- return context.events.on("messageRead", (event) => runHandler(handler, event));
34161
+ const report = makeReporter(context, { source: "listener", selector: "message.read" });
34162
+ return context.events.on("messageRead", (event) => runHandler(handler, event, report));
34187
34163
  }
34188
34164
  };
34189
34165
  var MessageReactedPredicate = class {
34190
34166
  subscribe(context, handler) {
34191
- const off1 = context.events.on("reactionAdded", (event) => runHandler(handler, event));
34192
- const off2 = context.events.on("reactionRemoved", (event) => runHandler(handler, event));
34167
+ const report = makeReporter(context, { source: "listener", selector: "message.reacted" });
34168
+ const off1 = context.events.on("reactionAdded", (event) => runHandler(handler, event, report));
34169
+ const off2 = context.events.on("reactionRemoved", (event) => runHandler(handler, event, report));
34193
34170
  return () => {
34194
34171
  off1();
34195
34172
  off2();
@@ -34229,6 +34206,11 @@ var ActionPredicate = class {
34229
34206
  return this;
34230
34207
  }
34231
34208
  subscribe(context, handler) {
34209
+ const report = makeReporter(context, {
34210
+ source: "listener",
34211
+ selector: this.phase,
34212
+ action: this.action
34213
+ });
34232
34214
  return context.onActionEvent((event) => {
34233
34215
  if (event.action !== this.action)
34234
34216
  return;
@@ -34236,10 +34218,61 @@ var ActionPredicate = class {
34236
34218
  return;
34237
34219
  if (this.caller && event.caller.name !== this.caller)
34238
34220
  return;
34239
- runHandler(handler, event);
34221
+ runHandler(handler, event, report);
34240
34222
  });
34241
34223
  }
34242
34224
  };
34225
+ var TypedActionPredicate = class {
34226
+ action;
34227
+ phase;
34228
+ caller;
34229
+ constructor(action, phase) {
34230
+ this.action = action;
34231
+ this.phase = phase;
34232
+ }
34233
+ calledBy(agent) {
34234
+ this.caller = resolveAgentName(agent);
34235
+ return this;
34236
+ }
34237
+ subscribe(context, handler) {
34238
+ const report = makeReporter(context, {
34239
+ source: "listener",
34240
+ selector: this.phase,
34241
+ action: this.action
34242
+ });
34243
+ return context.onActionEvent((event) => {
34244
+ if (event.action !== this.action)
34245
+ return;
34246
+ if (event.type !== this.phase)
34247
+ return;
34248
+ if (this.caller && event.caller.name !== this.caller)
34249
+ return;
34250
+ runHandler(handler, toTypedActionEvent(event), report);
34251
+ });
34252
+ }
34253
+ };
34254
+ function toTypedActionEvent(raw) {
34255
+ return {
34256
+ type: raw.type,
34257
+ action: raw.action,
34258
+ agent: raw.caller,
34259
+ ...raw.input !== void 0 ? { input: raw.input } : {},
34260
+ ...raw.output !== void 0 ? { output: raw.output } : {},
34261
+ ...raw.error !== void 0 ? { error: raw.error } : {},
34262
+ ...raw.reason !== void 0 ? { reason: raw.reason } : {},
34263
+ at: raw.at
34264
+ };
34265
+ }
34266
+ function createTypedActionHandle(name, unregister) {
34267
+ return {
34268
+ name,
34269
+ unregister,
34270
+ invoked: () => new TypedActionPredicate(name, "action.invoked"),
34271
+ completed: () => new TypedActionPredicate(name, "action.completed"),
34272
+ failed: () => new TypedActionPredicate(name, "action.failed"),
34273
+ denied: () => new TypedActionPredicate(name, "action.denied")
34274
+ };
34275
+ }
34243
34276
  var StatusPredicate = class {
34244
34277
  agentId;
34245
34278
  status;
@@ -34248,12 +34281,16 @@ var StatusPredicate = class {
34248
34281
  this.status = status;
34249
34282
  }
34250
34283
  subscribe(context, handler) {
34284
+ const report = makeReporter(context, {
34285
+ source: "listener",
34286
+ selector: `agent.status.${this.status}`
34287
+ });
34251
34288
  return context.onSessionEvent(({ agentId, event }) => {
34252
34289
  if (agentId !== this.agentId)
34253
34290
  return;
34254
34291
  const matches = event.type === "status.changed" && event.status === this.status || event.type === `status.${this.status}`;
34255
34292
  if (matches)
34256
- runHandler(handler, event);
34293
+ runHandler(handler, event, report);
34257
34294
  });
34258
34295
  }
34259
34296
  };
@@ -34270,6 +34307,7 @@ var ToolCalledPredicate = class {
34270
34307
  return this;
34271
34308
  }
34272
34309
  subscribe(context, handler) {
34310
+ const report = makeReporter(context, { source: "listener", selector: `tool.called:${this.tool}` });
34273
34311
  return context.onSessionEvent(({ agentId, event }) => {
34274
34312
  if (agentId !== this.agentId)
34275
34313
  return;
@@ -34277,7 +34315,7 @@ var ToolCalledPredicate = class {
34277
34315
  return;
34278
34316
  if (this.filter && !this.filter(event))
34279
34317
  return;
34280
- runHandler(handler, event);
34318
+ runHandler(handler, event, report);
34281
34319
  });
34282
34320
  }
34283
34321
  };
@@ -34289,7 +34327,7 @@ var PUBLIC_MESSAGE_TYPE = {
34289
34327
  groupDmReceived: "group_dm.received"
34290
34328
  };
34291
34329
  function buildEnvelope(message, channelName) {
34292
- const channel = message.channel ?? (channelName ? { name: stripSigil2(channelName) } : void 0);
34330
+ const channel = message.channel ?? (channelName ? { name: stripSigil(channelName) } : void 0);
34293
34331
  return {
34294
34332
  ...message.from ? { from: message.from } : {},
34295
34333
  ...message.target ? { to: message.target } : {},
@@ -34352,7 +34390,7 @@ function matchesSelector(selector, type) {
34352
34390
  return type.startsWith(selector.slice(0, -1));
34353
34391
  return selector === type;
34354
34392
  }
34355
- function createListenerHub(baseEvents, actions) {
34393
+ function createListenerHub(baseEvents, actions, options) {
34356
34394
  const sessionHandlers = /* @__PURE__ */ new Set();
34357
34395
  const events = createEnrichedEvents(baseEvents);
34358
34396
  const context = {
@@ -34362,7 +34400,8 @@ function createListenerHub(baseEvents, actions) {
34362
34400
  onSessionEvent: (handler) => {
34363
34401
  sessionHandlers.add(handler);
34364
34402
  return () => sessionHandlers.delete(handler);
34365
- }
34403
+ },
34404
+ ...options?.onError ? { onError: options.onError } : {}
34366
34405
  };
34367
34406
  const addListener = (selector, handler) => {
34368
34407
  try {
@@ -34372,29 +34411,49 @@ function createListenerHub(baseEvents, actions) {
34372
34411
  if (typeof selector !== "string") {
34373
34412
  return selector.subscribe(context, handler);
34374
34413
  }
34414
+ const report = makeReporter(context, { source: "listener", selector });
34375
34415
  const offs = [
34376
34416
  context.events.on("any", (raw) => {
34377
34417
  const evt = toPublicMessagingEvent(raw);
34378
34418
  if (evt && matchesSelector(selector, evt.type))
34379
- runHandler(handler, evt);
34419
+ runHandler(handler, evt, report);
34380
34420
  }),
34381
34421
  context.onActionEvent((raw) => {
34382
34422
  const evt = toPublicActionEvent(raw);
34383
34423
  if (matchesSelector(selector, evt.type))
34384
- runHandler(handler, evt);
34424
+ runHandler(handler, evt, report);
34385
34425
  }),
34386
34426
  context.onSessionEvent(({ agentId, event }) => {
34387
34427
  const evt = toPublicSessionEvent(agentId, event);
34388
34428
  if (matchesSelector(selector, evt.type))
34389
- runHandler(handler, evt);
34429
+ runHandler(handler, evt, report);
34390
34430
  })
34391
34431
  ];
34392
34432
  return () => offs.forEach((off) => off());
34393
34433
  };
34434
+ const once = (selector, handler) => {
34435
+ let done = false;
34436
+ let off;
34437
+ const wrapped = (event) => {
34438
+ if (done)
34439
+ return;
34440
+ done = true;
34441
+ off?.();
34442
+ return handler(event);
34443
+ };
34444
+ off = addListener(selector, wrapped);
34445
+ if (done)
34446
+ off();
34447
+ return () => {
34448
+ done = true;
34449
+ off?.();
34450
+ };
34451
+ };
34394
34452
  return {
34395
34453
  events,
34396
34454
  on: (predicate, handler) => predicate.subscribe(context, handler),
34397
34455
  addListener,
34456
+ once,
34398
34457
  action: (name) => new ActionPredicate(name),
34399
34458
  agent: (input) => createAgentHandle(input),
34400
34459
  emitSessionEvent: (agentId, event) => {
@@ -34423,6 +34482,263 @@ function createAgentHandle(input) {
34423
34482
  };
34424
34483
  }
34425
34484
 
34485
+ // ../sdk/dist/facade.js
34486
+ function resolveAgentName(ref) {
34487
+ if (typeof ref === "string")
34488
+ return stripSigil2(ref);
34489
+ const value = ref.handle ?? ref.name ?? ref.id;
34490
+ if (!value) {
34491
+ throw new Error("Unable to resolve agent name: reference has no name, handle, or id.");
34492
+ }
34493
+ return stripSigil2(value);
34494
+ }
34495
+ function resolveAgentToken(ref) {
34496
+ return ref && typeof ref !== "string" ? ref.token : void 0;
34497
+ }
34498
+ function stripSigil2(value) {
34499
+ return value.startsWith("@") || value.startsWith("#") ? value.slice(1) : value;
34500
+ }
34501
+ function isChannelTarget(to) {
34502
+ return to.startsWith("#");
34503
+ }
34504
+ function deliveryToMode(delivery) {
34505
+ if (!delivery)
34506
+ return void 0;
34507
+ return delivery === "immediate" || delivery === "next-tool-call" ? "steer" : "wait";
34508
+ }
34509
+ function buildText(text, mentions) {
34510
+ let body = text ?? "";
34511
+ if (mentions?.length) {
34512
+ const handles = mentions.map((mention) => `@${resolveAgentName(mention)}`);
34513
+ const missing = handles.filter((handle) => !body.includes(handle));
34514
+ if (missing.length) {
34515
+ body = body ? `${missing.join(" ")} ${body}` : missing.join(" ");
34516
+ }
34517
+ }
34518
+ return body;
34519
+ }
34520
+ function resolveMessageId(ref) {
34521
+ if (!ref) {
34522
+ throw new Error("reply requires a `messageId` or `thread`.");
34523
+ }
34524
+ if (typeof ref === "string")
34525
+ return ref;
34526
+ const value = ref.id ?? ref.threadId;
34527
+ if (!value) {
34528
+ throw new Error("Unable to resolve a message id from the provided thread reference.");
34529
+ }
34530
+ return value;
34531
+ }
34532
+ function createEnrichedMessages(base, resolveFrom) {
34533
+ const enriched = Object.create(base);
34534
+ enriched.send = async (input) => {
34535
+ if ("channel" in input && input.channel) {
34536
+ return base.send(input);
34537
+ }
34538
+ const sendInput = input;
34539
+ const messages = resolveFrom(sendInput.from);
34540
+ const text = buildText(sendInput.text ?? sendInput.msg, sendInput.mentions);
34541
+ if (Array.isArray(sendInput.to)) {
34542
+ return messages.groupDirect({
34543
+ participants: sendInput.to.map(resolveAgentName),
34544
+ text,
34545
+ attachments: sendInput.attachments,
34546
+ mode: sendInput.mode,
34547
+ idempotencyKey: sendInput.idempotencyKey
34548
+ });
34549
+ }
34550
+ if (isChannelTarget(sendInput.to)) {
34551
+ return messages.send({
34552
+ channel: stripSigil2(sendInput.to),
34553
+ text,
34554
+ blocks: sendInput.blocks,
34555
+ attachments: sendInput.attachments,
34556
+ mode: sendInput.mode,
34557
+ idempotencyKey: sendInput.idempotencyKey
34558
+ });
34559
+ }
34560
+ return messages.direct({
34561
+ to: resolveAgentName(sendInput.to),
34562
+ text,
34563
+ attachments: sendInput.attachments,
34564
+ mode: sendInput.mode,
34565
+ idempotencyKey: sendInput.idempotencyKey
34566
+ });
34567
+ };
34568
+ enriched.reply = async (input) => {
34569
+ const messages = resolveFrom(input.from);
34570
+ return messages.reply({
34571
+ messageId: resolveMessageId(input.messageId ?? input.thread),
34572
+ text: input.text,
34573
+ blocks: input.blocks,
34574
+ idempotencyKey: input.idempotencyKey
34575
+ });
34576
+ };
34577
+ enriched.react = (async (arg1, arg2) => {
34578
+ if (typeof arg1 === "string") {
34579
+ return base.react(arg1, arg2);
34580
+ }
34581
+ const messageId = typeof arg1.message === "string" ? arg1.message : arg1.message.id;
34582
+ const messages = resolveFrom(arg1.agent);
34583
+ return messages.react(messageId, arg1.emoji);
34584
+ });
34585
+ enriched.dm = async (input) => {
34586
+ const messages = resolveFrom(input.from);
34587
+ return messages.direct({
34588
+ to: resolveAgentName(input.to),
34589
+ text: input.text ?? input.msg ?? "",
34590
+ attachments: input.attachments,
34591
+ mode: input.mode,
34592
+ idempotencyKey: input.idempotencyKey
34593
+ });
34594
+ };
34595
+ return enriched;
34596
+ }
34597
+ function createWorkspaceFacade(messaging, deps) {
34598
+ const register = async (agents, options) => {
34599
+ if (!deps) {
34600
+ throw new Error("register() is only available on the workspace client.");
34601
+ }
34602
+ const list = Array.isArray(agents) ? agents : [agents];
34603
+ const inputs = list.map((agent) => typeof agent === "string" ? { name: stripSigil2(agent) } : {
34604
+ name: resolveAgentName(agent),
34605
+ type: agent.type,
34606
+ persona: agent.persona,
34607
+ metadata: agent.metadata
34608
+ });
34609
+ const seen = /* @__PURE__ */ new Set();
34610
+ for (const { name } of inputs) {
34611
+ if (seen.has(name)) {
34612
+ throw new RelayError("name_conflict", `Duplicate agent name in register(): "${name}".`);
34613
+ }
34614
+ seen.add(name);
34615
+ }
34616
+ const registerOne = !options?.strict && messaging.agents.registerOrRotate ? messaging.agents.registerOrRotate.bind(messaging.agents) : messaging.agents.register.bind(messaging.agents);
34617
+ const clients = [];
34618
+ for (const input of inputs) {
34619
+ clients.push(deps.buildAgentClient(await registerOne(input)));
34620
+ }
34621
+ return Array.isArray(agents) ? clients : clients[0];
34622
+ };
34623
+ return {
34624
+ info: () => messaging.workspace.info(),
34625
+ register,
34626
+ reconnect: async ({ apiToken }) => {
34627
+ if (!deps) {
34628
+ throw new Error("reconnect() is only available on the workspace client.");
34629
+ }
34630
+ return deps.reconnectAgent(apiToken);
34631
+ }
34632
+ };
34633
+ }
34634
+ function reportActionError(wiring, action, operation, message, error101) {
34635
+ if (wiring.onError) {
34636
+ try {
34637
+ wiring.onError(error101, { source: "action", action, operation });
34638
+ } catch {
34639
+ }
34640
+ return;
34641
+ }
34642
+ console.error(`[agent-relay] ${message}`, error101);
34643
+ }
34644
+ function registerFacadeAction(actions, def, wiring) {
34645
+ const allowed = def.availableTo?.map(resolveAgentName);
34646
+ const policy = allowed || def.policy ? async (input, ctx) => {
34647
+ if (allowed && !allowed.includes(ctx.caller.name)) {
34648
+ return {
34649
+ allowed: false,
34650
+ reason: `${ctx.caller.name} is not permitted to call ${def.name}.`
34651
+ };
34652
+ }
34653
+ return def.policy ? def.policy(input, ctx) : { allowed: true };
34654
+ } : void 0;
34655
+ const localHandle = actions.register({
34656
+ name: def.name,
34657
+ description: def.description,
34658
+ input: def.input,
34659
+ inputSchema: def.inputSchema,
34660
+ output: def.output,
34661
+ outputSchema: def.outputSchema,
34662
+ visibility: def.visibility,
34663
+ policy,
34664
+ handler: (input, ctx) => def.handler({ input, agent: ctx.caller, ctx })
34665
+ });
34666
+ const relayUnsubscribe = wireRelayAction(actions, def, wiring, allowed);
34667
+ return createTypedActionHandle(def.name, () => {
34668
+ relayUnsubscribe?.();
34669
+ localHandle.unregister();
34670
+ });
34671
+ }
34672
+ function wireRelayAction(actions, def, wiring, allowed) {
34673
+ const commands = wiring?.messaging.commands;
34674
+ if (!wiring || !commands?.agentScoped?.() || !commands.available?.()) {
34675
+ return void 0;
34676
+ }
34677
+ const handlerAgent = wiring.handlerAgent;
34678
+ if (!handlerAgent) {
34679
+ return void 0;
34680
+ }
34681
+ void commands.register({
34682
+ command: def.name,
34683
+ description: def.description ?? def.name,
34684
+ handlerAgent,
34685
+ inputSchema: actionSchemaToJsonSchema(def.inputSchema ?? def.input),
34686
+ outputSchema: actionSchemaToJsonSchema(def.outputSchema ?? def.output),
34687
+ ...allowed ? { availableTo: allowed } : {}
34688
+ }).catch((error101) => {
34689
+ reportActionError(wiring, def.name, "register", `failed to register action descriptor "${def.name}":`, error101);
34690
+ });
34691
+ const unsubscribe = wiring.messaging.events.on("actionInvoked", async (event) => {
34692
+ if (event.actionName !== def.name) {
34693
+ return;
34694
+ }
34695
+ await handleActionInvoked(actions, commands, def.name, event, wiring);
34696
+ });
34697
+ try {
34698
+ wiring.messaging.events.connect();
34699
+ } catch (error101) {
34700
+ reportActionError(wiring, def.name, "connect", `failed to open the event stream for action "${def.name}":`, error101);
34701
+ }
34702
+ return unsubscribe;
34703
+ }
34704
+ async function handleActionInvoked(actions, commands, actionName, event, wiring) {
34705
+ let input = {};
34706
+ try {
34707
+ const invocation = await commands.getInvocation(actionName, event.invocationId);
34708
+ input = invocation.input ?? {};
34709
+ } catch (error101) {
34710
+ reportActionError(wiring, actionName, "load_invocation", `failed to load invocation "${event.invocationId}":`, error101);
34711
+ }
34712
+ const result = await actions.invoke({
34713
+ name: actionName,
34714
+ input,
34715
+ caller: { name: event.callerName, type: "agent" }
34716
+ });
34717
+ try {
34718
+ await commands.completeInvocation(actionName, event.invocationId, result.ok ? { output: toOutputRecord(result.output) } : { error: result.error?.message ?? "handler failed" });
34719
+ } catch (error101) {
34720
+ reportActionError(wiring, actionName, "complete_invocation", `failed to complete invocation "${event.invocationId}":`, error101);
34721
+ }
34722
+ }
34723
+ function toOutputRecord(output) {
34724
+ if (output !== null && typeof output === "object" && !Array.isArray(output)) {
34725
+ return output;
34726
+ }
34727
+ return { value: output };
34728
+ }
34729
+ function createNotifyHandler(messages, target, options) {
34730
+ return async () => {
34731
+ const subject = options.subject ? `@${resolveAgentName(options.subject)}` : void 0;
34732
+ const label = options.type ?? options.action ?? "notification";
34733
+ const text = options.text ?? [`[${label}]`, subject].filter(Boolean).join(" ");
34734
+ await messages.dm({
34735
+ to: resolveAgentName(target),
34736
+ text,
34737
+ mode: deliveryToMode(options.delivery)
34738
+ });
34739
+ };
34740
+ }
34741
+
34426
34742
  // ../sdk/dist/agent-relay.js
34427
34743
  var AgentRelay = class _AgentRelay {
34428
34744
  messaging;
@@ -34434,14 +34750,18 @@ var AgentRelay = class _AgentRelay {
34434
34750
  enrichedMessages;
34435
34751
  workspaceFacade;
34436
34752
  hub;
34753
+ errorHooks = /* @__PURE__ */ new Set();
34437
34754
  constructor(options = {}) {
34438
- const { messaging, actions, workspaceKey, createAgentMessaging, ...messagingOptions } = options;
34755
+ const { messaging, actions, workspaceKey, createAgentMessaging, onError, ...messagingOptions } = options;
34439
34756
  const resolvedWorkspaceKey = workspaceKey ?? messagingOptions.apiKey;
34440
34757
  this.workspaceKey = resolvedWorkspaceKey;
34441
34758
  this.messagingOptions = { ...messagingOptions, workspaceKey: resolvedWorkspaceKey };
34442
34759
  this.messaging = messaging ?? new RelaycastMessagingClient(this.messagingOptions);
34443
34760
  this.actions = actions ?? new ActionRegistry();
34444
34761
  this.createAgentMessaging = createAgentMessaging ?? ((token) => new RelaycastMessagingClient({ ...this.messagingOptions, agentToken: token }));
34762
+ if (onError) {
34763
+ this.errorHooks.add(onError);
34764
+ }
34445
34765
  }
34446
34766
  static async createWorkspace(input) {
34447
34767
  const options = typeof input === "string" ? { name: input } : input;
@@ -34452,7 +34772,7 @@ var AgentRelay = class _AgentRelay {
34452
34772
  });
34453
34773
  const workspaceKey = extractWorkspaceKey(workspace);
34454
34774
  if (!workspaceKey) {
34455
- throw new Error("Workspace created, but the response did not include a workspace key.");
34775
+ throw new RelayError("transport_error", "Workspace created, but the response did not include a workspace key.", { retryable: false });
34456
34776
  }
34457
34777
  return new _AgentRelay({
34458
34778
  workspaceKey,
@@ -34485,7 +34805,9 @@ var AgentRelay = class _AgentRelay {
34485
34805
  }
34486
34806
  get listenerHub() {
34487
34807
  if (!this.hub) {
34488
- this.hub = createListenerHub(this.messaging.events, this.actions);
34808
+ this.hub = createListenerHub(this.messaging.events, this.actions, {
34809
+ onError: (error101, context) => this.reportError(error101, context)
34810
+ });
34489
34811
  }
34490
34812
  return this.hub;
34491
34813
  }
@@ -34516,7 +34838,7 @@ var AgentRelay = class _AgentRelay {
34516
34838
  id: registration.id,
34517
34839
  name: registration.name,
34518
34840
  token: registration.token
34519
- });
34841
+ }, { onError: (error101, context) => this.reportError(error101, context) });
34520
34842
  }
34521
34843
  /** Rehydrate a live client from a persisted agent token, resolving identity from the relay. */
34522
34844
  async reconnectAgent(apiToken) {
@@ -34526,15 +34848,43 @@ var AgentRelay = class _AgentRelay {
34526
34848
  id: identity.id,
34527
34849
  name: identity.name,
34528
34850
  token: apiToken
34529
- });
34851
+ }, { onError: (error101, context) => this.reportError(error101, context) });
34530
34852
  }
34531
34853
  registerAction(def) {
34532
- return registerFacadeAction(this.actions, def, { messaging: this.messaging });
34854
+ return registerFacadeAction(this.actions, def, {
34855
+ messaging: this.messaging,
34856
+ onError: (error101, context) => this.reportError(error101, context)
34857
+ });
34533
34858
  }
34534
- /** Subscribe by dotted event name, `'*'`/prefix wildcard, or a predicate. */
34535
34859
  addListener(selector, handler) {
34536
34860
  return this.listenerHub.addListener(selector, handler);
34537
34861
  }
34862
+ once(selector, handler) {
34863
+ return this.listenerHub.once(selector, handler);
34864
+ }
34865
+ /**
34866
+ * Register a hook that receives listener and action handler errors. Returns
34867
+ * an unsubscribe callback. When no hook is registered, errors are logged as
34868
+ * console warnings.
34869
+ */
34870
+ onError(hook) {
34871
+ this.errorHooks.add(hook);
34872
+ return () => {
34873
+ this.errorHooks.delete(hook);
34874
+ };
34875
+ }
34876
+ reportError(error101, context) {
34877
+ if (this.errorHooks.size === 0) {
34878
+ logRelayHandlerError(error101, context);
34879
+ return;
34880
+ }
34881
+ for (const hook of this.errorHooks) {
34882
+ try {
34883
+ hook(error101, context);
34884
+ } catch {
34885
+ }
34886
+ }
34887
+ }
34538
34888
  action(name) {
34539
34889
  return this.listenerHub.action(name);
34540
34890
  }
@@ -34564,9 +34914,9 @@ function extractWorkspaceKey(payload) {
34564
34914
  const value = payload.workspaceKey ?? payload.workspace_key ?? payload.apiKey ?? payload.api_key ?? data.workspaceKey ?? data.workspace_key ?? data.apiKey ?? data.api_key;
34565
34915
  return typeof value === "string" && value.trim() ? value : void 0;
34566
34916
  }
34567
- function agentRelayAgent(messaging, actions, handlerAgent) {
34917
+ function agentRelayAgent(messaging, actions, handlerAgent, options) {
34568
34918
  const messages = createEnrichedMessages(messaging.messages, () => messaging.messages);
34569
- const hub = createListenerHub(messaging.events, actions);
34919
+ const hub = createListenerHub(messaging.events, actions, { onError: options?.onError });
34570
34920
  return {
34571
34921
  messaging,
34572
34922
  agents: messaging.agents,
@@ -34580,15 +34930,16 @@ function agentRelayAgent(messaging, actions, handlerAgent) {
34580
34930
  webhooks: messaging.webhooks,
34581
34931
  capabilities: messaging.commands,
34582
34932
  workspace: createWorkspaceFacade(messaging),
34583
- registerAction: (def) => registerFacadeAction(actions, def, { messaging, handlerAgent }),
34584
- addListener: (selector, handler) => hub.addListener(selector, handler),
34933
+ registerAction: (def) => registerFacadeAction(actions, def, { messaging, handlerAgent, onError: options?.onError }),
34934
+ addListener: ((selector, handler) => hub.addListener(selector, handler)),
34935
+ once: ((selector, handler) => hub.once(selector, handler)),
34585
34936
  action: (name) => hub.action(name),
34586
34937
  agent: (input) => hub.agent(input),
34587
34938
  emitSessionEvent: (agentId, event) => hub.emitSessionEvent(agentId, event)
34588
34939
  };
34589
34940
  }
34590
- function assembleAgentClient(messaging, actions, identity) {
34591
- const base = agentRelayAgent(messaging, actions, identity.name);
34941
+ function assembleAgentClient(messaging, actions, identity, options) {
34942
+ const base = agentRelayAgent(messaging, actions, identity.name, options);
34592
34943
  const handle = createAgentHandle(identity);
34593
34944
  return {
34594
34945
  ...base,
@@ -36163,11 +36514,13 @@ function generateRequestId(prefix = "") {
36163
36514
  MessageReactedPredicate,
36164
36515
  MessageReadPredicate,
36165
36516
  RelayCapabilityError,
36517
+ RelayError,
36166
36518
  RelaycastMessagingClient,
36167
36519
  StaticPatterns,
36168
36520
  StatusPredicate,
36169
36521
  ToolCalledPredicate,
36170
36522
  TracedError,
36523
+ TypedActionPredicate,
36171
36524
  actionSchemaToJsonSchema,
36172
36525
  agentRelayAgent,
36173
36526
  agentTokenRecoveryMessage,
@@ -36189,6 +36542,7 @@ function generateRequestId(prefix = "") {
36189
36542
  createRequestEnvelope,
36190
36543
  createRequestHandler,
36191
36544
  createTraceableError,
36545
+ createTypedActionHandle,
36192
36546
  createWorkspaceFacade,
36193
36547
  defineHarness,
36194
36548
  findGitRoot,
@@ -36221,6 +36575,7 @@ function generateRequestId(prefix = "") {
36221
36575
  isSpawnOrReleaseCommandFast,
36222
36576
  isValidAgentName,
36223
36577
  logAndTraceError,
36578
+ logRelayHandlerError,
36224
36579
  mapModelToCli,
36225
36580
  matchesSelector,
36226
36581
  nextHarnessName,
@@ -36234,8 +36589,11 @@ function generateRequestId(prefix = "") {
36234
36589
  normalizeChannelMember,
36235
36590
  normalizeChannelName,
36236
36591
  normalizeChannelReadStatus,
36592
+ normalizeDeliveryTransition,
36237
36593
  normalizeGroupDirectConversation,
36238
36594
  normalizeInbox,
36595
+ normalizeInboxItem,
36596
+ normalizeInboxItemState,
36239
36597
  normalizeMessage,
36240
36598
  normalizeMessagingEvent,
36241
36599
  normalizeReaction,