spectrum-ts 2.0.0 → 3.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.
- package/dist/{attachment-B4nSrKVd.d.ts → attachment-WePAHfcH.d.ts} +1 -1
- package/dist/{authoring-BjE5BvlO.d.ts → authoring-DDh3muGT.d.ts} +61 -26
- package/dist/authoring.d.ts +3 -3
- package/dist/authoring.js +5 -5
- package/dist/{chunk-NNY6LMSC.js → chunk-77U6SH5A.js} +1 -1
- package/dist/{chunk-ATNAE7OR.js → chunk-AYCMTRVC.js} +549 -76
- package/dist/{chunk-6BI4PFTP.js → chunk-CHY5YLLV.js} +1 -1
- package/dist/{chunk-U3LXXT3W.js → chunk-EZ5SNNFS.js} +20 -8
- package/dist/{chunk-WXY5QP3M.js → chunk-FULEQIRQ.js} +27 -21
- package/dist/{chunk-2ILTJC35.js → chunk-LQMDV75O.js} +205 -11
- package/dist/{chunk-NGC4DJIX.js → chunk-LX437ZTY.js} +416 -135
- package/dist/{chunk-3B4QH4JG.js → chunk-MHGCPC2V.js} +1 -1
- package/dist/{chunk-U7AWXDH6.js → chunk-NZ5WCMTY.js} +1 -1
- package/dist/{chunk-5LT5J3NR.js → chunk-TXRWKSNH.js} +262 -30
- package/dist/{chunk-Q537JPTG.js → chunk-UXJ5OO6P.js} +10 -10
- package/dist/index.d.ts +107 -56
- package/dist/index.js +29 -182
- package/dist/providers/imessage/index.d.ts +6 -14
- package/dist/providers/imessage/index.js +6 -6
- package/dist/providers/index.d.ts +3 -3
- package/dist/providers/index.js +11 -11
- package/dist/providers/slack/index.d.ts +1 -2
- package/dist/providers/slack/index.js +3 -3
- package/dist/providers/telegram/index.d.ts +3 -5
- package/dist/providers/telegram/index.js +5 -5
- package/dist/providers/terminal/index.d.ts +2 -4
- package/dist/providers/terminal/index.js +5 -5
- package/dist/providers/whatsapp-business/index.d.ts +1 -1
- package/dist/providers/whatsapp-business/index.js +4 -4
- package/dist/{types-BD0-kKyv.d.ts → types-BujGKBin.d.ts} +1 -1
- package/dist/{types-Bje8aq1k.d.ts → types-YqCNUDIt.d.ts} +171 -23
- package/package.json +2 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { createRequire as __spectrumCreateRequire } from "node:module"; const require = __spectrumCreateRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
asVoice
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-77U6SH5A.js";
|
|
5
5
|
import {
|
|
6
6
|
asContact
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-NZ5WCMTY.js";
|
|
8
8
|
import {
|
|
9
9
|
stream
|
|
10
10
|
} from "./chunk-5XEFJBN2.js";
|
|
@@ -15,12 +15,12 @@ import {
|
|
|
15
15
|
import {
|
|
16
16
|
UnsupportedError,
|
|
17
17
|
definePlatform
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-LX437ZTY.js";
|
|
19
19
|
import {
|
|
20
20
|
asAttachment,
|
|
21
21
|
asCustom,
|
|
22
22
|
reactionSchema
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-LQMDV75O.js";
|
|
24
24
|
|
|
25
25
|
// src/providers/terminal/index.ts
|
|
26
26
|
import { spawn } from "child_process";
|
|
@@ -781,12 +781,18 @@ var terminal = definePlatform("Terminal", {
|
|
|
781
781
|
})
|
|
782
782
|
},
|
|
783
783
|
space: {
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
const id = input.params?.id ?? generateChatId(client);
|
|
784
|
+
create: async ({ client }) => {
|
|
785
|
+
const id = generateChatId(client);
|
|
787
786
|
client.knownChats.add(id);
|
|
788
787
|
await client.session.request("ensureSpace", { id });
|
|
789
788
|
return { id };
|
|
789
|
+
},
|
|
790
|
+
// Explicit (not the framework default) so targeting a known id still
|
|
791
|
+
// materializes the chat in the TUI via `ensureSpace`.
|
|
792
|
+
get: async ({ client, input }) => {
|
|
793
|
+
client.knownChats.add(input.id);
|
|
794
|
+
await client.session.request("ensureSpace", { id: input.id });
|
|
795
|
+
return { id: input.id };
|
|
790
796
|
}
|
|
791
797
|
},
|
|
792
798
|
// Return a ManagedStream (not a native async generator): a native generator
|
|
@@ -857,7 +863,13 @@ var terminal = definePlatform("Terminal", {
|
|
|
857
863
|
messageId: content.target.id,
|
|
858
864
|
reaction: content.emoji
|
|
859
865
|
});
|
|
860
|
-
|
|
866
|
+
const timestamp = /* @__PURE__ */ new Date();
|
|
867
|
+
return {
|
|
868
|
+
id: `reaction:${content.target.id}:${content.emoji}:${timestamp.toISOString()}`,
|
|
869
|
+
content,
|
|
870
|
+
space: { id: space.id },
|
|
871
|
+
timestamp
|
|
872
|
+
};
|
|
861
873
|
}
|
|
862
874
|
if (content.type === "typing") {
|
|
863
875
|
const method = content.state === "start" ? "startTyping" : "stopTyping";
|
|
@@ -9,13 +9,13 @@ import {
|
|
|
9
9
|
import {
|
|
10
10
|
UnsupportedError,
|
|
11
11
|
definePlatform
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-LX437ZTY.js";
|
|
13
13
|
import {
|
|
14
14
|
asAttachment,
|
|
15
15
|
asCustom,
|
|
16
16
|
asReaction,
|
|
17
17
|
asText
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-LQMDV75O.js";
|
|
19
19
|
|
|
20
20
|
// src/providers/slack/index.ts
|
|
21
21
|
import { createClient as createClient2, staticTokens } from "@photon-ai/slack";
|
|
@@ -340,13 +340,12 @@ var send = async (client, space, content) => {
|
|
|
340
340
|
);
|
|
341
341
|
}
|
|
342
342
|
if (content.type === "reaction") {
|
|
343
|
-
await reactToMessage(
|
|
343
|
+
return await reactToMessage(
|
|
344
344
|
client,
|
|
345
345
|
space,
|
|
346
346
|
content.target.ts ?? content.target.id,
|
|
347
|
-
content
|
|
347
|
+
content
|
|
348
348
|
);
|
|
349
|
-
return;
|
|
350
349
|
}
|
|
351
350
|
if (content.type === "typing") {
|
|
352
351
|
return;
|
|
@@ -388,15 +387,23 @@ var sendContent = async (client, space, content, threadTs) => {
|
|
|
388
387
|
throw UnsupportedError.content(content.type);
|
|
389
388
|
}
|
|
390
389
|
};
|
|
391
|
-
var reactToMessage = async (client, space, targetTs,
|
|
390
|
+
var reactToMessage = async (client, space, targetTs, content) => {
|
|
392
391
|
await client.team(space.teamId).messages.send({
|
|
393
392
|
channel: space.id,
|
|
394
393
|
reaction: {
|
|
395
|
-
emoji,
|
|
394
|
+
emoji: content.emoji,
|
|
396
395
|
itemChannel: space.id,
|
|
397
396
|
itemTs: targetTs
|
|
398
397
|
}
|
|
399
398
|
});
|
|
399
|
+
return {
|
|
400
|
+
id: `${targetTs}:reaction:self:${content.emoji}`,
|
|
401
|
+
content,
|
|
402
|
+
space: { id: space.id, teamId: space.teamId },
|
|
403
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
404
|
+
ts: targetTs,
|
|
405
|
+
isFromMe: true
|
|
406
|
+
};
|
|
400
407
|
};
|
|
401
408
|
var replyToMessage = async (client, space, targetTs, content) => await sendContent(client, space, content, targetTs);
|
|
402
409
|
|
|
@@ -424,7 +431,6 @@ var spaceSchema = z.object({
|
|
|
424
431
|
teamId: z.string()
|
|
425
432
|
});
|
|
426
433
|
var spaceParamsSchema = z.object({
|
|
427
|
-
channel: z.string().optional(),
|
|
428
434
|
teamId: z.string()
|
|
429
435
|
});
|
|
430
436
|
var messageSchema = z.object({
|
|
@@ -475,27 +481,18 @@ var slack = definePlatform("Slack", {
|
|
|
475
481
|
space: {
|
|
476
482
|
schema: spaceSchema,
|
|
477
483
|
params: spaceParamsSchema,
|
|
478
|
-
|
|
484
|
+
create: async ({ input }) => {
|
|
479
485
|
const teamId = input.params?.teamId;
|
|
480
486
|
if (!teamId) {
|
|
481
487
|
throw new Error(
|
|
482
|
-
"Slack space creation requires a teamId param. Pass it via
|
|
483
|
-
);
|
|
484
|
-
}
|
|
485
|
-
const channel = input.params?.channel;
|
|
486
|
-
if (channel) {
|
|
487
|
-
return { id: channel, teamId };
|
|
488
|
-
}
|
|
489
|
-
if (input.users.length === 0) {
|
|
490
|
-
throw new Error(
|
|
491
|
-
"Slack space creation requires either a channel param or at least one user"
|
|
488
|
+
"Slack space creation requires a teamId param. Pass it via space.create(user, { teamId })."
|
|
492
489
|
);
|
|
493
490
|
}
|
|
494
491
|
if (input.users.length > 1) {
|
|
495
492
|
throw UnsupportedError.action(
|
|
496
|
-
"
|
|
493
|
+
"space.create",
|
|
497
494
|
"Slack",
|
|
498
|
-
"group DMs require an explicit channel id (Slack's conversations.open is not exposed);
|
|
495
|
+
"group DMs require an explicit channel id (Slack's conversations.open is not exposed); use space.get(channelId, { teamId })"
|
|
499
496
|
);
|
|
500
497
|
}
|
|
501
498
|
const user = input.users[0];
|
|
@@ -503,6 +500,15 @@ var slack = definePlatform("Slack", {
|
|
|
503
500
|
throw new Error("Slack space creation requires a user");
|
|
504
501
|
}
|
|
505
502
|
return { id: user.id, teamId };
|
|
503
|
+
},
|
|
504
|
+
get: async ({ input }) => {
|
|
505
|
+
const teamId = input.params?.teamId;
|
|
506
|
+
if (!teamId) {
|
|
507
|
+
throw new Error(
|
|
508
|
+
"Slack spaces require a teamId param. Pass it via space.get(channelId, { teamId })."
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
return { id: input.id, teamId };
|
|
506
512
|
}
|
|
507
513
|
},
|
|
508
514
|
message: {
|
|
@@ -195,16 +195,202 @@ function custom(raw) {
|
|
|
195
195
|
}
|
|
196
196
|
|
|
197
197
|
// src/content/text.ts
|
|
198
|
+
import z5 from "zod";
|
|
199
|
+
|
|
200
|
+
// src/content/stream-text.ts
|
|
198
201
|
import z4 from "zod";
|
|
199
|
-
var
|
|
200
|
-
type: z4.literal("
|
|
201
|
-
text
|
|
202
|
+
var streamTextSchema = z4.object({
|
|
203
|
+
type: z4.literal("streamText"),
|
|
204
|
+
// A single-consumption producer of normalized text deltas. The builder
|
|
205
|
+
// closes over the normalized source; the platform driver calls it once.
|
|
206
|
+
// Kept opaque to Zod via `z.custom` (same approach as `attachment.read`).
|
|
207
|
+
stream: z4.custom(
|
|
208
|
+
(v) => typeof v === "function",
|
|
209
|
+
{
|
|
210
|
+
message: "streamText.stream must be a function returning AsyncIterable<string>"
|
|
211
|
+
}
|
|
212
|
+
),
|
|
213
|
+
// How platforms should interpret the accumulated text; absent = plain.
|
|
214
|
+
format: z4.enum(["plain", "markdown"]).optional()
|
|
202
215
|
});
|
|
203
|
-
var
|
|
204
|
-
|
|
216
|
+
var asRecord = (value) => typeof value === "object" && value !== null ? value : void 0;
|
|
217
|
+
var SKIP_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
218
|
+
"message_start",
|
|
219
|
+
"message_delta",
|
|
220
|
+
"message_stop",
|
|
221
|
+
"content_block_start",
|
|
222
|
+
"content_block_stop",
|
|
223
|
+
"ping"
|
|
224
|
+
]);
|
|
225
|
+
var fromOpenAIResponses = (obj) => {
|
|
226
|
+
const type = obj.type;
|
|
227
|
+
if (typeof type !== "string" || !type.startsWith("response.")) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
if (type === "response.output_text.delta" && typeof obj.delta === "string") {
|
|
231
|
+
return obj.delta;
|
|
232
|
+
}
|
|
233
|
+
return null;
|
|
234
|
+
};
|
|
235
|
+
var fromAnthropicDelta = (obj) => {
|
|
236
|
+
if (obj.type !== "content_block_delta") {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
const delta = asRecord(obj.delta);
|
|
240
|
+
if (delta?.type === "text_delta" && typeof delta.text === "string") {
|
|
241
|
+
return delta.text;
|
|
242
|
+
}
|
|
243
|
+
return null;
|
|
244
|
+
};
|
|
245
|
+
var fromAiSdkPart = (obj) => {
|
|
246
|
+
if (obj.type !== "text-delta") {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if (typeof obj.textDelta === "string") {
|
|
250
|
+
return obj.textDelta;
|
|
251
|
+
}
|
|
252
|
+
return typeof obj.text === "string" ? obj.text : null;
|
|
253
|
+
};
|
|
254
|
+
var fromOpenAIChat = (obj) => {
|
|
255
|
+
if (!Array.isArray(obj.choices)) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
const delta = asRecord(asRecord(obj.choices[0])?.delta);
|
|
259
|
+
const content = delta?.content;
|
|
260
|
+
return typeof content === "string" ? content : null;
|
|
261
|
+
};
|
|
262
|
+
var fromControlEvent = (obj) => typeof obj.type === "string" && SKIP_EVENT_TYPES.has(obj.type) ? null : void 0;
|
|
263
|
+
var OBJECT_EXTRACTORS = [
|
|
264
|
+
fromOpenAIResponses,
|
|
265
|
+
fromAnthropicDelta,
|
|
266
|
+
fromAiSdkPart,
|
|
267
|
+
fromOpenAIChat,
|
|
268
|
+
fromControlEvent
|
|
269
|
+
];
|
|
270
|
+
var defaultExtract = (chunk) => {
|
|
271
|
+
if (typeof chunk === "string") {
|
|
272
|
+
return chunk;
|
|
273
|
+
}
|
|
274
|
+
const record = asRecord(chunk);
|
|
275
|
+
if (!record) {
|
|
276
|
+
throw new Error(
|
|
277
|
+
`text stream: cannot extract a text delta from a ${typeof chunk} chunk. Pass { extract } to map your stream's chunks to text.`
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
for (const extractor of OBJECT_EXTRACTORS) {
|
|
281
|
+
const result = extractor(record);
|
|
282
|
+
if (result !== void 0) {
|
|
283
|
+
return result;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
throw new Error(
|
|
287
|
+
`text stream: unrecognized chunk shape (type=${String(record.type)}). Pass an { extract } function to map your provider's chunk to a text delta.`
|
|
288
|
+
);
|
|
289
|
+
};
|
|
290
|
+
var isReadableStream = (value) => typeof value?.getReader === "function";
|
|
291
|
+
var isAsyncIterable = (value) => typeof value?.[Symbol.asyncIterator] === "function";
|
|
292
|
+
async function* readableToAsync(source) {
|
|
293
|
+
if (isAsyncIterable(source)) {
|
|
294
|
+
yield* source;
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
const reader = source.getReader();
|
|
298
|
+
try {
|
|
299
|
+
while (true) {
|
|
300
|
+
const { done, value } = await reader.read();
|
|
301
|
+
if (done) {
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
yield value;
|
|
305
|
+
}
|
|
306
|
+
} finally {
|
|
307
|
+
reader.releaseLock();
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
var resolveChunkIterable = (source) => {
|
|
311
|
+
const textStream = source.textStream;
|
|
312
|
+
if (textStream != null) {
|
|
313
|
+
if (isReadableStream(textStream)) {
|
|
314
|
+
return readableToAsync(textStream);
|
|
315
|
+
}
|
|
316
|
+
if (isAsyncIterable(textStream)) {
|
|
317
|
+
return textStream;
|
|
318
|
+
}
|
|
319
|
+
throw new Error(
|
|
320
|
+
"text stream: `.textStream` must be an AsyncIterable or a ReadableStream."
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
if (isReadableStream(source)) {
|
|
324
|
+
return readableToAsync(source);
|
|
325
|
+
}
|
|
326
|
+
if (isAsyncIterable(source)) {
|
|
327
|
+
return source;
|
|
328
|
+
}
|
|
329
|
+
throw new Error(
|
|
330
|
+
"text stream: source must be an AsyncIterable, a ReadableStream, or an object with a `.textStream` (e.g. the AI SDK streamText() result)."
|
|
331
|
+
);
|
|
332
|
+
};
|
|
333
|
+
var StreamConsumedError = class extends Error {
|
|
334
|
+
constructor() {
|
|
335
|
+
super(
|
|
336
|
+
"text stream: this source has already been consumed \u2014 a stream can only be sent once."
|
|
337
|
+
);
|
|
338
|
+
this.name = "StreamConsumedError";
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
var normalize = (source, options) => {
|
|
342
|
+
const extract = options?.extract ? options.extract : defaultExtract;
|
|
343
|
+
let consumed = false;
|
|
344
|
+
return async function* normalized() {
|
|
345
|
+
if (consumed) {
|
|
346
|
+
throw new StreamConsumedError();
|
|
347
|
+
}
|
|
348
|
+
consumed = true;
|
|
349
|
+
for await (const chunk of resolveChunkIterable(source)) {
|
|
350
|
+
const delta = extract(chunk);
|
|
351
|
+
if (delta) {
|
|
352
|
+
yield delta;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
};
|
|
357
|
+
var asStreamText = (input) => streamTextSchema.parse({
|
|
358
|
+
type: "streamText",
|
|
359
|
+
stream: input.stream,
|
|
360
|
+
...input.format ? { format: input.format } : {}
|
|
361
|
+
});
|
|
362
|
+
var drainStreamText = async (content) => {
|
|
363
|
+
let full = "";
|
|
364
|
+
for await (const delta of content.stream()) {
|
|
365
|
+
full += delta;
|
|
366
|
+
}
|
|
367
|
+
return full;
|
|
368
|
+
};
|
|
369
|
+
var streamTextBuilder = (kind, source, options) => {
|
|
370
|
+
if (typeof source.build === "function") {
|
|
371
|
+
throw new Error(
|
|
372
|
+
`${kind}(): pass the stream source itself (an AsyncIterable, a ReadableStream, or an SDK result with .textStream), not another content builder.`
|
|
373
|
+
);
|
|
374
|
+
}
|
|
205
375
|
return {
|
|
206
|
-
build: async () =>
|
|
376
|
+
build: async () => asStreamText({
|
|
377
|
+
stream: normalize(source, options),
|
|
378
|
+
format: kind === "markdown" ? "markdown" : void 0
|
|
379
|
+
})
|
|
207
380
|
};
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
// src/content/text.ts
|
|
384
|
+
var textSchema = z5.object({
|
|
385
|
+
type: z5.literal("text"),
|
|
386
|
+
text: z5.string().nonempty()
|
|
387
|
+
});
|
|
388
|
+
var asText = (text2) => textSchema.parse({ type: "text", text: text2 });
|
|
389
|
+
function text(source, options) {
|
|
390
|
+
if (typeof source === "string") {
|
|
391
|
+
return { build: async () => asText(source) };
|
|
392
|
+
}
|
|
393
|
+
return streamTextBuilder("text", source, options);
|
|
208
394
|
}
|
|
209
395
|
|
|
210
396
|
// src/content/resolve.ts
|
|
@@ -213,12 +399,12 @@ var resolveContents = (items) => Promise.all(
|
|
|
213
399
|
);
|
|
214
400
|
|
|
215
401
|
// src/content/reaction.ts
|
|
216
|
-
import
|
|
402
|
+
import z6 from "zod";
|
|
217
403
|
var isMessage = (v) => typeof v === "object" && v !== null && "id" in v && "content" in v;
|
|
218
|
-
var reactionSchema =
|
|
219
|
-
type:
|
|
220
|
-
emoji:
|
|
221
|
-
target:
|
|
404
|
+
var reactionSchema = z6.object({
|
|
405
|
+
type: z6.literal("reaction"),
|
|
406
|
+
emoji: z6.string().min(1),
|
|
407
|
+
target: z6.custom(isMessage, {
|
|
222
408
|
message: "reaction target must be a Message"
|
|
223
409
|
})
|
|
224
410
|
});
|
|
@@ -226,6 +412,11 @@ var asReaction = (input) => reactionSchema.parse({ type: "reaction", ...input })
|
|
|
226
412
|
function reaction(emoji, target) {
|
|
227
413
|
return {
|
|
228
414
|
build: async () => {
|
|
415
|
+
if (!target) {
|
|
416
|
+
throw new Error(
|
|
417
|
+
"reaction() target is undefined \u2014 the targeted message was never sent (space.send resolves undefined when a platform skips unsupported content)"
|
|
418
|
+
);
|
|
419
|
+
}
|
|
229
420
|
if (target.content.type === "reaction") {
|
|
230
421
|
throw new Error('reaction() cannot target "reaction" content');
|
|
231
422
|
}
|
|
@@ -250,6 +441,9 @@ export {
|
|
|
250
441
|
attachment,
|
|
251
442
|
asCustom,
|
|
252
443
|
custom,
|
|
444
|
+
StreamConsumedError,
|
|
445
|
+
drainStreamText,
|
|
446
|
+
streamTextBuilder,
|
|
253
447
|
textSchema,
|
|
254
448
|
asText,
|
|
255
449
|
text,
|