spectrum-ts 1.17.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +11 -1
  2. package/dist/{attachment-DfWSZS5L.d.ts → attachment-B4nSrKVd.d.ts} +1 -1
  3. package/dist/{authoring-C9uDdZ2F.d.ts → authoring-BjE5BvlO.d.ts} +2 -2
  4. package/dist/authoring.d.ts +3 -3
  5. package/dist/authoring.js +6 -3
  6. package/dist/chunk-34FQGGD7.js +34 -0
  7. package/dist/chunk-3B4QH4JG.js +35 -0
  8. package/dist/chunk-3GEJYGZK.js +84 -0
  9. package/dist/chunk-5LT5J3NR.js +695 -0
  10. package/dist/{chunk-MC6ZKFSG.js → chunk-5XEFJBN2.js} +25 -103
  11. package/dist/{chunk-JQN6CRSC.js → chunk-6BI4PFTP.js} +10 -39
  12. package/dist/{chunk-QGJFZMD5.js → chunk-6UZFVXQF.js} +17 -101
  13. package/dist/{chunk-YJMPSD3S.js → chunk-ATNAE7OR.js} +196 -47
  14. package/dist/{chunk-IPOFBAIM.js → chunk-NGC4DJIX.js} +23 -19
  15. package/dist/{chunk-5TIF3FIE.js → chunk-Q537JPTG.js} +8 -6
  16. package/dist/{chunk-5BKZJMZV.js → chunk-U3LXXT3W.js} +61 -32
  17. package/dist/chunk-U7AWXDH6.js +91 -0
  18. package/dist/{chunk-3OTECDNH.js → chunk-WXY5QP3M.js} +5 -3
  19. package/dist/index.d.ts +71 -126
  20. package/dist/index.js +350 -90
  21. package/dist/manifest.json +6 -0
  22. package/dist/providers/imessage/index.d.ts +75 -3
  23. package/dist/providers/imessage/index.js +10 -5
  24. package/dist/providers/index.d.ts +5 -2
  25. package/dist/providers/index.js +16 -8
  26. package/dist/providers/slack/index.d.ts +1 -1
  27. package/dist/providers/slack/index.js +4 -3
  28. package/dist/providers/telegram/index.d.ts +47 -0
  29. package/dist/providers/telegram/index.js +13 -0
  30. package/dist/providers/terminal/index.d.ts +17 -419
  31. package/dist/providers/terminal/index.js +5 -3
  32. package/dist/providers/whatsapp-business/index.d.ts +1 -1
  33. package/dist/providers/whatsapp-business/index.js +6 -4
  34. package/dist/types-BD0-kKyv.d.ts +82 -0
  35. package/dist/{types-DcQ5a7PK.d.ts → types-Bje8aq1k.d.ts} +34 -4
  36. package/package.json +3 -2
@@ -1,29 +1,35 @@
1
1
  import { createRequire as __spectrumCreateRequire } from "node:module"; const require = __spectrumCreateRequire(import.meta.url);
2
+ import {
3
+ asRichlink
4
+ } from "./chunk-6BI4PFTP.js";
2
5
  import {
3
6
  asGroup,
4
- asRichlink,
5
7
  groupSchema
6
- } from "./chunk-JQN6CRSC.js";
8
+ } from "./chunk-3B4QH4JG.js";
7
9
  import {
8
10
  asPoll,
9
11
  asPollOption
10
12
  } from "./chunk-2D27WW5B.js";
11
13
  import {
12
- cloud,
14
+ cloud
15
+ } from "./chunk-3GEJYGZK.js";
16
+ import {
17
+ asContact
18
+ } from "./chunk-U7AWXDH6.js";
19
+ import {
13
20
  mergeStreams,
14
21
  stream
15
- } from "./chunk-MC6ZKFSG.js";
22
+ } from "./chunk-5XEFJBN2.js";
16
23
  import {
17
- asContact,
18
24
  fromVCard,
19
25
  toVCard
20
- } from "./chunk-QGJFZMD5.js";
26
+ } from "./chunk-6UZFVXQF.js";
21
27
  import {
22
28
  UnsupportedError,
23
29
  buildPhotoAction,
24
30
  definePlatform,
25
31
  photoActionSchema
26
- } from "./chunk-IPOFBAIM.js";
32
+ } from "./chunk-NGC4DJIX.js";
27
33
  import {
28
34
  asAttachment,
29
35
  asCustom,
@@ -60,21 +66,79 @@ function background(input, options) {
60
66
  };
61
67
  }
62
68
 
69
+ // src/providers/imessage/content/customized-mini-app.ts
70
+ import z2 from "zod";
71
+ var layoutSchema = z2.object({
72
+ caption: z2.string().nonempty().optional(),
73
+ subcaption: z2.string().nonempty().optional(),
74
+ trailingCaption: z2.string().nonempty().optional(),
75
+ trailingSubcaption: z2.string().nonempty().optional(),
76
+ image: z2.instanceof(Uint8Array).optional(),
77
+ imageTitle: z2.string().nonempty().optional(),
78
+ imageSubtitle: z2.string().nonempty().optional(),
79
+ summary: z2.string().nonempty().optional()
80
+ }).refine(
81
+ (layout) => layout.caption !== void 0 || layout.subcaption !== void 0 || layout.trailingCaption !== void 0 || layout.trailingSubcaption !== void 0 || layout.image !== void 0,
82
+ {
83
+ message: "layout must set at least one of caption, subcaption, trailingCaption, trailingSubcaption, image"
84
+ }
85
+ ).refine(
86
+ (layout) => layout.image === void 0 === (layout.imageTitle === void 0),
87
+ {
88
+ message: "layout.image and layout.imageTitle must be set together",
89
+ path: ["imageTitle"]
90
+ }
91
+ ).refine(
92
+ (layout) => layout.imageSubtitle === void 0 || layout.image !== void 0,
93
+ {
94
+ message: "layout.imageSubtitle requires layout.image",
95
+ path: ["imageSubtitle"]
96
+ }
97
+ );
98
+ var customizedMiniAppSchema = z2.object({
99
+ type: z2.literal("customized-mini-app"),
100
+ __platform: z2.literal("iMessage"),
101
+ // Display name of the owning app, shown by Messages fallback UI.
102
+ appName: z2.string().nonempty(),
103
+ // Apple App Store numeric id of the owning app. Positive when set; omit to
104
+ // send a card whose extension is not published on the App Store.
105
+ appStoreId: z2.number().int().positive().optional(),
106
+ // Bundle identifier of the iMessage extension target.
107
+ extensionBundleId: z2.string().nonempty(),
108
+ // Visible card layout.
109
+ layout: layoutSchema,
110
+ // 10-character uppercase alphanumeric Apple Team ID.
111
+ teamId: z2.string(),
112
+ // Absolute URL delivered to the installed extension on tap.
113
+ url: z2.url()
114
+ });
115
+ var isCustomizedMiniApp = (v) => customizedMiniAppSchema.safeParse(v).success;
116
+ var asCustomizedMiniApp = (input) => customizedMiniAppSchema.parse({
117
+ type: "customized-mini-app",
118
+ __platform: "iMessage",
119
+ ...input
120
+ });
121
+ function customizedMiniApp(input) {
122
+ return {
123
+ build: async () => asCustomizedMiniApp(input)
124
+ };
125
+ }
126
+
63
127
  // src/providers/imessage/content/effect.ts
64
128
  import {
65
129
  MessageEffect
66
130
  } from "@photon-ai/advanced-imessage";
67
131
 
68
132
  // src/content/effect.ts
69
- import z2 from "zod";
70
- var effectInnerSchema = z2.discriminatedUnion("type", [
133
+ import z3 from "zod";
134
+ var effectInnerSchema = z3.discriminatedUnion("type", [
71
135
  textSchema,
72
136
  attachmentSchema
73
137
  ]);
74
- var messageEffectSchema = z2.object({
75
- type: z2.literal("effect"),
138
+ var messageEffectSchema = z3.object({
139
+ type: z3.literal("effect"),
76
140
  content: effectInnerSchema,
77
- effect: z2.string().nonempty()
141
+ effect: z3.string().nonempty()
78
142
  });
79
143
 
80
144
  // src/providers/imessage/content/effect.ts
@@ -104,13 +168,13 @@ function effect(input, messageEffect) {
104
168
  }
105
169
 
106
170
  // src/providers/imessage/content/read.ts
107
- import z3 from "zod";
171
+ import z4 from "zod";
108
172
  var isMessage = (v) => typeof v === "object" && v !== null && "id" in v && "content" in v;
109
- var readSchema = z3.object({
110
- type: z3.literal("read"),
111
- __platform: z3.literal("iMessage"),
112
- __fireAndForget: z3.literal(true),
113
- target: z3.custom(isMessage, {
173
+ var readSchema = z4.object({
174
+ type: z4.literal("read"),
175
+ __platform: z4.literal("iMessage"),
176
+ __fireAndForget: z4.literal(true),
177
+ target: z4.custom(isMessage, {
114
178
  message: "read target must be a Message"
115
179
  })
116
180
  });
@@ -138,33 +202,33 @@ import { createClient } from "@photon-ai/advanced-imessage";
138
202
 
139
203
  // src/providers/imessage/types.ts
140
204
  import { IMessageSDK } from "@photon-ai/imessage-kit";
141
- import z4 from "zod";
205
+ import z5 from "zod";
142
206
  var SHARED_PHONE = "shared";
143
207
  var isLocal = (client) => client instanceof IMessageSDK;
144
- var clientEntry = z4.object({
145
- address: z4.string(),
146
- token: z4.string(),
147
- phone: z4.string()
208
+ var clientEntry = z5.object({
209
+ address: z5.string(),
210
+ token: z5.string(),
211
+ phone: z5.string()
148
212
  });
149
- var configSchema = z4.union([
150
- z4.object({ local: z4.literal(true) }),
151
- z4.object({
152
- local: z4.literal(false).optional().default(false),
153
- clients: clientEntry.or(z4.array(clientEntry)).optional()
213
+ var configSchema = z5.union([
214
+ z5.object({ local: z5.literal(true) }),
215
+ z5.object({
216
+ local: z5.literal(false).optional().default(false),
217
+ clients: clientEntry.or(z5.array(clientEntry)).optional()
154
218
  })
155
219
  ]);
156
- var userSchema = z4.object({});
157
- var spaceSchema = z4.object({
158
- id: z4.string(),
159
- type: z4.enum(["dm", "group"]),
160
- phone: z4.string()
220
+ var userSchema = z5.object({});
221
+ var spaceSchema = z5.object({
222
+ id: z5.string(),
223
+ type: z5.enum(["dm", "group"]),
224
+ phone: z5.string()
161
225
  });
162
- var spaceParamsSchema = z4.object({
163
- phone: z4.string().optional()
226
+ var spaceParamsSchema = z5.object({
227
+ phone: z5.string().optional()
164
228
  });
165
- var messageSchema = z4.object({
166
- partIndex: z4.number().int().nonnegative().optional(),
167
- parentId: z4.string().optional()
229
+ var messageSchema = z5.object({
230
+ partIndex: z5.number().int().nonnegative().optional(),
231
+ parentId: z5.string().optional()
168
232
  });
169
233
 
170
234
  // src/providers/imessage/auth.ts
@@ -549,6 +613,18 @@ var setBackground = async (remote, spaceId, content) => {
549
613
  );
550
614
  };
551
615
 
616
+ // src/providers/imessage/remote/customized-mini-app.ts
617
+ var sendCustomizedMiniApp = async (remote, spaceId, content) => {
618
+ const chat = toChatGuid(spaceId);
619
+ const message = await remote.messages.sendCustomizedMiniApp(chat, content);
620
+ return {
621
+ id: message.guid,
622
+ content,
623
+ space: { id: spaceId },
624
+ timestamp: message.dateCreated
625
+ };
626
+ };
627
+
552
628
  // src/providers/imessage/remote/inbound.ts
553
629
  import {
554
630
  NotFoundError as NotFoundError2
@@ -910,7 +986,6 @@ var getMessage3 = async (remote, spaceId, msgId, phone) => {
910
986
  if (childRef) {
911
987
  try {
912
988
  const fetched = await remote.messages.get(
913
- toChatGuid(spaceId),
914
989
  toMessageGuid(childRef.parentGuid)
915
990
  );
916
991
  const parent = await rebuildFromAppleMessage(
@@ -933,10 +1008,7 @@ var getMessage3 = async (remote, spaceId, msgId, phone) => {
933
1008
  }
934
1009
  }
935
1010
  try {
936
- const fetched = await remote.messages.get(
937
- toChatGuid(spaceId),
938
- toMessageGuid(msgId)
939
- );
1011
+ const fetched = await remote.messages.get(toMessageGuid(msgId));
940
1012
  const rebuilt = await rebuildFromAppleMessage(
941
1013
  remote,
942
1014
  fetched,
@@ -975,10 +1047,7 @@ var resolveReactionTarget = async (client, cache, chat, targetGuid, partIndex, p
975
1047
  let candidate = cache.get(targetGuid);
976
1048
  if (!candidate) {
977
1049
  try {
978
- const fetched = await client.messages.get(
979
- toChatGuid(chat),
980
- toMessageGuid(targetGuid)
981
- );
1050
+ const fetched = await client.messages.get(toMessageGuid(targetGuid));
982
1051
  candidate = await rebuildFromAppleMessage(client, fetched, phone, chat);
983
1052
  cacheMessage(cache, candidate);
984
1053
  } catch {
@@ -1976,6 +2045,55 @@ var messages3 = (clients, projectConfig) => {
1976
2045
  );
1977
2046
  };
1978
2047
 
2048
+ // src/providers/imessage/remote/stream-text.ts
2049
+ var INITIAL_THROTTLE_MS = 1e3;
2050
+ var BACKOFF_FACTOR = 2;
2051
+ var MAX_EDITS = 5;
2052
+ var sendStreamText = async (remote, spaceId, content) => {
2053
+ const chat = toChatGuid(spaceId);
2054
+ let sent;
2055
+ let full = "";
2056
+ let lastSentText = "";
2057
+ let lastEditAt = 0;
2058
+ let editCount = 0;
2059
+ const flushEdit = async (text2) => {
2060
+ if (!sent || text2 === lastSentText) {
2061
+ return;
2062
+ }
2063
+ await remote.messages.edit(chat, toMessageGuid(sent.guid), text2);
2064
+ lastSentText = text2;
2065
+ lastEditAt = Date.now();
2066
+ editCount += 1;
2067
+ };
2068
+ for await (const delta of content.stream()) {
2069
+ full += delta;
2070
+ if (!sent) {
2071
+ sent = await remote.messages.sendText(chat, full);
2072
+ lastSentText = full;
2073
+ lastEditAt = Date.now();
2074
+ continue;
2075
+ }
2076
+ const hasBudgetForInterimEdit = editCount < MAX_EDITS - 1;
2077
+ const requiredGap = INITIAL_THROTTLE_MS * BACKOFF_FACTOR ** editCount;
2078
+ if (hasBudgetForInterimEdit && Date.now() - lastEditAt >= requiredGap) {
2079
+ await flushEdit(full);
2080
+ }
2081
+ }
2082
+ if (!sent) {
2083
+ throw unsupportedRemoteContent(
2084
+ "streamText",
2085
+ "stream produced no text \u2014 nothing to send"
2086
+ );
2087
+ }
2088
+ await flushEdit(full);
2089
+ return {
2090
+ id: sent.guid,
2091
+ content: asText(full),
2092
+ space: { id: spaceId },
2093
+ timestamp: sent.dateCreated
2094
+ };
2095
+ };
2096
+
1979
2097
  // src/providers/imessage/remote/typing.ts
1980
2098
  var startTyping = async (remote, spaceId) => {
1981
2099
  await remote.chats.setTyping(toChatGuid(spaceId), true);
@@ -1987,6 +2105,7 @@ var stopTyping = async (remote, spaceId) => {
1987
2105
  // src/providers/imessage/remote/api.ts
1988
2106
  var messages4 = (clients, projectConfig) => messages3(clients, projectConfig);
1989
2107
  var setBackground2 = async (remote, spaceId, content) => setBackground(remote, spaceId, content);
2108
+ var sendCustomizedMiniApp2 = async (remote, spaceId, content) => sendCustomizedMiniApp(remote, spaceId, content);
1990
2109
  var setDisplayName2 = async (remote, spaceId, content) => setDisplayName(remote, spaceId, content);
1991
2110
  var setIcon2 = async (remote, spaceId, content) => setIcon(remote, spaceId, content);
1992
2111
  var markRead2 = async (remote, spaceId) => {
@@ -1999,6 +2118,7 @@ var stopTyping2 = async (remote, spaceId) => {
1999
2118
  await stopTyping(remote, spaceId);
2000
2119
  };
2001
2120
  var send4 = async (remote, spaceId, content) => send3(remote, spaceId, content);
2121
+ var sendStreamText2 = async (remote, spaceId, content) => sendStreamText(remote, spaceId, content);
2002
2122
  var replyToMessage2 = async (remote, spaceId, msgId, content) => replyToMessage(remote, spaceId, msgId, content);
2003
2123
  var editMessage2 = async (remote, spaceId, msgId, content) => editMessage(remote, spaceId, msgId, content);
2004
2124
  var reactToMessage2 = async (remote, spaceId, target, reaction) => {
@@ -2056,6 +2176,17 @@ var handleEdit = async (client, space, content) => {
2056
2176
  const remote = clientForPhone(client, space.phone);
2057
2177
  await editMessage2(remote, space.id, content.target.id, content.content);
2058
2178
  };
2179
+ var handleStreamText = async (client, space, content) => {
2180
+ if (isLocal(client)) {
2181
+ throw UnsupportedError.action(
2182
+ "streamText",
2183
+ "iMessage (local mode)",
2184
+ "streaming text responses require remote iMessage"
2185
+ );
2186
+ }
2187
+ const remote = clientForPhone(client, space.phone);
2188
+ return await sendStreamText2(remote, space.id, content);
2189
+ };
2059
2190
  var handleBackground = async (client, space, content) => {
2060
2191
  if (isLocal(client)) {
2061
2192
  throw UnsupportedError.action(
@@ -2067,6 +2198,17 @@ var handleBackground = async (client, space, content) => {
2067
2198
  const remote = clientForPhone(client, space.phone);
2068
2199
  await setBackground2(remote, space.id, content);
2069
2200
  };
2201
+ var handleCustomizedMiniApp = async (client, space, content) => {
2202
+ if (isLocal(client)) {
2203
+ throw UnsupportedError.action(
2204
+ "customized-mini-app",
2205
+ "iMessage (local mode)",
2206
+ "mini app cards require remote iMessage"
2207
+ );
2208
+ }
2209
+ const remote = clientForPhone(client, space.phone);
2210
+ return await sendCustomizedMiniApp2(remote, space.id, content);
2211
+ };
2070
2212
  var handleRead = async (client, space) => {
2071
2213
  if (isLocal(client)) {
2072
2214
  throw UnsupportedError.action(
@@ -2275,6 +2417,9 @@ var imessage = definePlatform("iMessage", {
2275
2417
  await handleEdit(client, space, content);
2276
2418
  return;
2277
2419
  }
2420
+ if (content.type === "streamText") {
2421
+ return await handleStreamText(client, space, content);
2422
+ }
2278
2423
  if (content.type === "rename") {
2279
2424
  await handleRename(client, space, content);
2280
2425
  return;
@@ -2291,6 +2436,9 @@ var imessage = definePlatform("iMessage", {
2291
2436
  await handleRead(client, space);
2292
2437
  return;
2293
2438
  }
2439
+ if (isCustomizedMiniApp(content)) {
2440
+ return await handleCustomizedMiniApp(client, space, content);
2441
+ }
2294
2442
  if (isLocal(client)) {
2295
2443
  return await send2(client, space.id, content);
2296
2444
  }
@@ -2351,6 +2499,7 @@ var imessage = definePlatform("iMessage", {
2351
2499
 
2352
2500
  export {
2353
2501
  background,
2502
+ customizedMiniApp,
2354
2503
  effect,
2355
2504
  read,
2356
2505
  imessage
@@ -216,7 +216,7 @@ var UnsupportedError = class _UnsupportedError extends Error {
216
216
  };
217
217
 
218
218
  // src/platform/define.ts
219
- import { withSpan as withSpan2 } from "@photon-ai/otel";
219
+ import { createLogger as createLogger2, withSpan as withSpan2 } from "@photon-ai/otel";
220
220
 
221
221
  // src/utils/identifier.ts
222
222
  import { sanitizeEmail, sanitizePhone } from "@photon-ai/otel";
@@ -747,6 +747,7 @@ function buildMessage(params) {
747
747
  }
748
748
 
749
749
  // src/platform/define.ts
750
+ var platformLog2 = createLogger2("spectrum.platform");
750
751
  function classifySpaceIdentifier(args) {
751
752
  const stringArgs = args.filter((a) => typeof a === "string");
752
753
  if (stringArgs.length > 1) {
@@ -910,13 +911,20 @@ function createPlatformInstance(def, runtime) {
910
911
  const eventProperties = {};
911
912
  const customEvents = def.events ?? {};
912
913
  for (const eventName of Object.keys(customEvents)) {
913
- const producer = customEvents[eventName];
914
- if (producer) {
914
+ const declared = customEvents[eventName];
915
+ if (typeof declared === "function") {
916
+ const producer = declared;
915
917
  eventProperties[eventName] = producer({
916
918
  client: runtime.client,
917
919
  config: runtime.config,
920
+ projectConfig: runtime.projectConfig,
918
921
  store: runtime.store
919
922
  });
923
+ continue;
924
+ }
925
+ const fusorEvents = runtime.subscribeEvent?.(eventName);
926
+ if (fusorEvents) {
927
+ eventProperties[eventName] = fusorEvents;
920
928
  }
921
929
  }
922
930
  let messagesIterable;
@@ -948,8 +956,9 @@ function createPlatformInstance(def, runtime) {
948
956
  eventProperties
949
957
  );
950
958
  }
951
- function definePlatform(name, def) {
952
- const fullDef = { name, ...def };
959
+ function definePlatform(name, rawDef) {
960
+ const def = rawDef;
961
+ const fullDef = { ...def, name };
953
962
  const platformCache = /* @__PURE__ */ new WeakMap();
954
963
  const narrowSpectrum = (spectrum) => {
955
964
  const cached = platformCache.get(spectrum);
@@ -969,17 +978,19 @@ function definePlatform(name, def) {
969
978
  };
970
979
  const narrowSpace = (input) => {
971
980
  if (input.__platform !== name) {
972
- throw new Error(
973
- `Expected space from "${name}", got "${input.__platform}"`
974
- );
981
+ platformLog2.warn("space platform mismatch; narrowing skipped", {
982
+ expected: name,
983
+ actual: input.__platform
984
+ });
975
985
  }
976
986
  return input;
977
987
  };
978
988
  const narrowMessage = (input) => {
979
989
  if (input.platform !== name) {
980
- throw new Error(
981
- `Expected message from "${name}", got "${input.platform}"`
982
- );
990
+ platformLog2.warn("message platform mismatch; narrowing skipped", {
991
+ expected: name,
992
+ actual: input.platform
993
+ });
983
994
  }
984
995
  return input;
985
996
  };
@@ -1022,12 +1033,6 @@ function definePlatform(name, def) {
1022
1033
  }
1023
1034
  return narrower;
1024
1035
  }
1025
- function defineFusorPlatform(name, def) {
1026
- return definePlatform(
1027
- name,
1028
- def
1029
- );
1030
- }
1031
1036
 
1032
1037
  export {
1033
1038
  photoActionSchema,
@@ -1042,6 +1047,5 @@ export {
1042
1047
  senderAttrs,
1043
1048
  wrapProviderMessage,
1044
1049
  buildSpace,
1045
- definePlatform,
1046
- defineFusorPlatform
1050
+ definePlatform
1047
1051
  };
@@ -3,17 +3,19 @@ import {
3
3
  asPollOption
4
4
  } from "./chunk-2D27WW5B.js";
5
5
  import {
6
- cloud,
7
- mergeStreams,
8
- stream
9
- } from "./chunk-MC6ZKFSG.js";
6
+ cloud
7
+ } from "./chunk-3GEJYGZK.js";
10
8
  import {
11
9
  asContact
12
- } from "./chunk-QGJFZMD5.js";
10
+ } from "./chunk-U7AWXDH6.js";
11
+ import {
12
+ mergeStreams,
13
+ stream
14
+ } from "./chunk-5XEFJBN2.js";
13
15
  import {
14
16
  UnsupportedError,
15
17
  definePlatform
16
- } from "./chunk-IPOFBAIM.js";
18
+ } from "./chunk-NGC4DJIX.js";
17
19
  import {
18
20
  asAttachment,
19
21
  asCustom,
@@ -3,14 +3,19 @@ import {
3
3
  asVoice
4
4
  } from "./chunk-NNY6LMSC.js";
5
5
  import {
6
- asContact,
6
+ asContact
7
+ } from "./chunk-U7AWXDH6.js";
8
+ import {
9
+ stream
10
+ } from "./chunk-5XEFJBN2.js";
11
+ import {
7
12
  fromVCard,
8
13
  toVCard
9
- } from "./chunk-QGJFZMD5.js";
14
+ } from "./chunk-6UZFVXQF.js";
10
15
  import {
11
16
  UnsupportedError,
12
17
  definePlatform
13
- } from "./chunk-IPOFBAIM.js";
18
+ } from "./chunk-NGC4DJIX.js";
14
19
  import {
15
20
  asAttachment,
16
21
  asCustom,
@@ -694,7 +699,7 @@ function protocolToSpectrum(p) {
694
699
  }
695
700
  return cached;
696
701
  };
697
- const stream = async () => {
702
+ const stream2 = async () => {
698
703
  if (path) {
699
704
  const [{ createReadStream }, { Readable }] = await Promise.all([
700
705
  import("fs"),
@@ -718,7 +723,7 @@ function protocolToSpectrum(p) {
718
723
  mimeType: p.mimeType,
719
724
  size: p.size,
720
725
  read: readBytes,
721
- stream
726
+ stream: stream2
722
727
  });
723
728
  }
724
729
  return asVoice({
@@ -726,7 +731,7 @@ function protocolToSpectrum(p) {
726
731
  mimeType: p.mimeType,
727
732
  size: p.size,
728
733
  read: readBytes,
729
- stream
734
+ stream: stream2
730
735
  });
731
736
  }
732
737
  if (p.type === "contact") {
@@ -784,33 +789,57 @@ var terminal = definePlatform("Terminal", {
784
789
  return { id };
785
790
  }
786
791
  },
787
- async *messages({ client }) {
788
- for await (const evt of client.events) {
789
- if (evt.kind === "message") {
790
- const msg = evt.value;
791
- client.knownChats.add(msg.spaceId);
792
- yield {
793
- id: msg.id,
794
- content: protocolToSpectrum(msg.content),
795
- sender: { id: msg.senderId },
796
- space: { id: msg.spaceId },
797
- timestamp: parseTimestamp(msg.timestamp),
798
- // replyTo is a terminal-specific extra — agents inspect via a
799
- // cast until Spectrum's message model grows first-class support.
800
- ...msg.replyTo ? { replyTo: msg.replyTo } : {}
801
- };
802
- continue;
803
- }
804
- const r = evt.value;
805
- client.knownChats.add(r.spaceId);
806
- yield {
807
- id: `reaction:${r.messageId}:${r.reaction}:${r.timestamp}`,
808
- content: reactionContentFromProtocol(r),
809
- sender: { id: r.senderId },
810
- space: { id: r.spaceId },
811
- timestamp: parseTimestamp(r.timestamp)
792
+ // Return a ManagedStream (not a native async generator): a native generator
793
+ // parked on an in-flight `client.events.next()` cannot be force-cancelled —
794
+ // a `.return()` queues behind the pending `next()` and never reaches the
795
+ // event queue, which would deadlock `Spectrum.stop()`. Driving the queue with
796
+ // an explicit pump lets cleanup call the queue iterator's `return()` directly
797
+ // (synchronous close + drain), so the stream tears down promptly on stop()
798
+ // without waiting for destroyClient.
799
+ messages({ client }) {
800
+ return stream((emit, end) => {
801
+ const iterator = client.events[Symbol.asyncIterator]();
802
+ const pump = (async () => {
803
+ try {
804
+ let result = await iterator.next();
805
+ while (!result.done) {
806
+ const evt = result.value;
807
+ if (evt.kind === "message") {
808
+ const msg = evt.value;
809
+ client.knownChats.add(msg.spaceId);
810
+ await emit({
811
+ id: msg.id,
812
+ content: protocolToSpectrum(msg.content),
813
+ sender: { id: msg.senderId },
814
+ space: { id: msg.spaceId },
815
+ timestamp: parseTimestamp(msg.timestamp),
816
+ // replyTo is a terminal-specific extra — agents inspect via a
817
+ // cast until Spectrum's message model grows first-class support.
818
+ ...msg.replyTo ? { replyTo: msg.replyTo } : {}
819
+ });
820
+ } else {
821
+ const r = evt.value;
822
+ client.knownChats.add(r.spaceId);
823
+ await emit({
824
+ id: `reaction:${r.messageId}:${r.reaction}:${r.timestamp}`,
825
+ content: reactionContentFromProtocol(r),
826
+ sender: { id: r.senderId },
827
+ space: { id: r.spaceId },
828
+ timestamp: parseTimestamp(r.timestamp)
829
+ });
830
+ }
831
+ result = await iterator.next();
832
+ }
833
+ end();
834
+ } catch (error) {
835
+ end(error);
836
+ }
837
+ })();
838
+ return async () => {
839
+ await iterator.return?.();
840
+ await pump.catch(() => void 0);
812
841
  };
813
- }
842
+ });
814
843
  },
815
844
  send: async ({ client, content, space }) => {
816
845
  if (content.type === "reply") {