agent-relay 8.4.0 → 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.
Files changed (2) hide show
  1. package/dist/index.cjs +237 -33
  2. package/package.json +6 -6
package/dist/index.cjs CHANGED
@@ -250,8 +250,11 @@ __export(index_exports, {
250
250
  normalizeChannelMember: () => normalizeChannelMember,
251
251
  normalizeChannelName: () => normalizeChannelName,
252
252
  normalizeChannelReadStatus: () => normalizeChannelReadStatus,
253
+ normalizeDeliveryTransition: () => normalizeDeliveryTransition,
253
254
  normalizeGroupDirectConversation: () => normalizeGroupDirectConversation,
254
255
  normalizeInbox: () => normalizeInbox,
256
+ normalizeInboxItem: () => normalizeInboxItem,
257
+ normalizeInboxItemState: () => normalizeInboxItemState,
255
258
  normalizeMessage: () => normalizeMessage,
256
259
  normalizeMessagingEvent: () => normalizeMessagingEvent,
257
260
  normalizeReaction: () => normalizeReaction,
@@ -33215,6 +33218,74 @@ function normalizeChannelReadStatus(input) {
33215
33218
  ...lastReadAt ? { lastReadAt } : {}
33216
33219
  };
33217
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
+ }
33218
33289
  function normalizeSearchResult(input) {
33219
33290
  const record3 = isRecord(input) ? input : {};
33220
33291
  const createdAt = readString(record3, "createdAt", "created_at");
@@ -33532,13 +33603,7 @@ function createRelaycastClient(options) {
33532
33603
  }));
33533
33604
  }
33534
33605
  var RelaycastMessagingClient = class {
33535
- capabilities = {
33536
- serverDeliveryState: false,
33537
- durableDelivery: false,
33538
- durableAck: false,
33539
- durableFail: false,
33540
- durableDefer: false
33541
- };
33606
+ capabilities;
33542
33607
  relaycast;
33543
33608
  agentClient;
33544
33609
  eventHandlers = /* @__PURE__ */ new Map();
@@ -33546,6 +33611,14 @@ var RelaycastMessagingClient = class {
33546
33611
  constructor(options) {
33547
33612
  this.relaycast = createRelaycastClient(options);
33548
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
+ };
33549
33622
  }
33550
33623
  agents = {
33551
33624
  list: async (options) => {
@@ -33681,12 +33754,55 @@ var RelaycastMessagingClient = class {
33681
33754
  };
33682
33755
  inbox = {
33683
33756
  get: async (options) => normalizeInbox(await this.requireAgentClient("inbox.get").inbox(definedOptions({ limit: options?.limit }))),
33684
- list: async (_input) => ({ items: [] }),
33685
- subscribe: (_input) => this.emptyInboxSubscription(),
33686
- ack: async (input) => this.unsupportedInboxDelivery("ack", input.inboxItemId),
33687
- fail: async (input) => this.unsupportedInboxDelivery("fail", input.inboxItemId, input.error),
33688
- defer: async (input) => this.unsupportedInboxDelivery("defer", input.inboxItemId, input.reason, input.availableAt),
33689
- 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.")
33690
33806
  };
33691
33807
  events = {
33692
33808
  connect: () => {
@@ -33722,26 +33838,34 @@ var RelaycastMessagingClient = class {
33722
33838
  },
33723
33839
  on: (event, handler) => this.addEventListener(event, handler)
33724
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
+ */
33725
33846
  deliveries = {
33726
- ack: async (messageId) => ({
33727
- supported: false,
33728
- action: "ack",
33729
- messageId,
33730
- reason: "Durable acknowledgements are not supported by the Relaycast messaging backend yet."
33731
- }),
33732
- fail: async (messageId, reason) => ({
33733
- supported: false,
33734
- action: "fail",
33735
- messageId,
33736
- ...reason ? { reason } : { reason: "Durable failure reporting is not supported by the Relaycast messaging backend yet." }
33737
- }),
33738
- defer: async (messageId, deferUntil) => ({
33739
- supported: false,
33740
- action: "defer",
33741
- messageId,
33742
- ...deferUntil ? { deferUntil } : {},
33743
- reason: "Durable deferral is not supported by the Relaycast messaging backend yet."
33744
- })
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
+ }
33745
33869
  };
33746
33870
  integrations = {
33747
33871
  webhooks: {
@@ -33838,6 +33962,83 @@ var RelaycastMessagingClient = class {
33838
33962
  }
33839
33963
  return this.relaycast.dmMessages;
33840
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
+ }
33841
34042
  async *emptyInboxSubscription() {
33842
34043
  return;
33843
34044
  }
@@ -33847,7 +34048,7 @@ var RelaycastMessagingClient = class {
33847
34048
  action,
33848
34049
  messageId,
33849
34050
  ...reason ? { reason } : {
33850
- 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."
33851
34052
  },
33852
34053
  ...deferUntil ? { deferUntil } : {}
33853
34054
  };
@@ -36388,8 +36589,11 @@ function generateRequestId(prefix = "") {
36388
36589
  normalizeChannelMember,
36389
36590
  normalizeChannelName,
36390
36591
  normalizeChannelReadStatus,
36592
+ normalizeDeliveryTransition,
36391
36593
  normalizeGroupDirectConversation,
36392
36594
  normalizeInbox,
36595
+ normalizeInboxItem,
36596
+ normalizeInboxItemState,
36393
36597
  normalizeMessage,
36394
36598
  normalizeMessagingEvent,
36395
36599
  normalizeReaction,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-relay",
3
- "version": "8.4.0",
3
+ "version": "8.5.0",
4
4
  "description": "Real-time agent-to-agent communication system",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -43,11 +43,11 @@
43
43
  "pack:validate": "npm pack --dry-run"
44
44
  },
45
45
  "dependencies": {
46
- "@agent-relay/cloud": "8.4.0",
47
- "@agent-relay/config": "8.4.0",
48
- "@agent-relay/harness-driver": "8.4.0",
49
- "@agent-relay/sdk": "8.4.0",
50
- "@agent-relay/utils": "8.4.0",
46
+ "@agent-relay/cloud": "8.5.0",
47
+ "@agent-relay/config": "8.5.0",
48
+ "@agent-relay/harness-driver": "8.5.0",
49
+ "@agent-relay/sdk": "8.5.0",
50
+ "@agent-relay/utils": "8.5.0",
51
51
  "@modelcontextprotocol/sdk": "^1.0.0",
52
52
  "@relaycast/sdk": "^3.1.1",
53
53
  "@relayflows/cli": "^1.0.1",