spectrum-ts 1.18.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/README.md +11 -1
- package/dist/{attachment-DfWSZS5L.d.ts → attachment-WePAHfcH.d.ts} +1 -1
- package/dist/{authoring-C9uDdZ2F.d.ts → authoring-DDh3muGT.d.ts} +61 -26
- package/dist/authoring.d.ts +3 -3
- package/dist/authoring.js +8 -5
- package/dist/chunk-34FQGGD7.js +34 -0
- package/dist/chunk-3GEJYGZK.js +84 -0
- package/dist/{chunk-MC6ZKFSG.js → chunk-5XEFJBN2.js} +25 -103
- package/dist/{chunk-QGJFZMD5.js → chunk-6UZFVXQF.js} +17 -101
- package/dist/{chunk-NNY6LMSC.js → chunk-77U6SH5A.js} +1 -1
- package/dist/{chunk-YN6WOTBF.js → chunk-AYCMTRVC.js} +622 -79
- package/dist/{chunk-JQN6CRSC.js → chunk-CHY5YLLV.js} +11 -40
- package/dist/{chunk-5BKZJMZV.js → chunk-EZ5SNNFS.js} +79 -38
- package/dist/{chunk-3OTECDNH.js → chunk-FULEQIRQ.js} +31 -23
- package/dist/{chunk-2ILTJC35.js → chunk-LQMDV75O.js} +205 -11
- package/dist/{chunk-IPOFBAIM.js → chunk-LX437ZTY.js} +439 -154
- package/dist/chunk-MHGCPC2V.js +35 -0
- package/dist/chunk-NZ5WCMTY.js +91 -0
- package/dist/chunk-TXRWKSNH.js +927 -0
- package/dist/{chunk-5TIF3FIE.js → chunk-UXJ5OO6P.js} +16 -14
- package/dist/index.d.ts +125 -129
- package/dist/index.js +180 -73
- package/dist/manifest.json +6 -0
- package/dist/providers/imessage/index.d.ts +6 -14
- package/dist/providers/imessage/index.js +9 -6
- package/dist/providers/index.d.ts +5 -2
- package/dist/providers/index.js +18 -10
- package/dist/providers/slack/index.d.ts +1 -2
- package/dist/providers/slack/index.js +5 -4
- package/dist/providers/telegram/index.d.ts +45 -0
- package/dist/providers/telegram/index.js +13 -0
- package/dist/providers/terminal/index.d.ts +18 -422
- package/dist/providers/terminal/index.js +7 -5
- package/dist/providers/whatsapp-business/index.d.ts +1 -1
- package/dist/providers/whatsapp-business/index.js +7 -5
- package/dist/types-BujGKBin.d.ts +82 -0
- package/dist/{types-DcQ5a7PK.d.ts → types-YqCNUDIt.d.ts} +204 -26
- package/package.json +3 -1
|
@@ -3,37 +3,11 @@ import {
|
|
|
3
3
|
bufferToStream,
|
|
4
4
|
fetchUrlBytes,
|
|
5
5
|
readSchema,
|
|
6
|
-
resolveContents,
|
|
7
6
|
streamSchema
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
|
|
10
|
-
// src/content/group.ts
|
|
11
|
-
import z from "zod";
|
|
12
|
-
var isMessage = (v) => typeof v === "object" && v !== null && "id" in v && "content" in v;
|
|
13
|
-
var groupSchema = z.object({
|
|
14
|
-
type: z.literal("group"),
|
|
15
|
-
items: z.array(z.custom(isMessage)).min(2)
|
|
16
|
-
});
|
|
17
|
-
var asGroup = (input) => groupSchema.parse({ type: "group", items: input.items });
|
|
18
|
-
var stubOutboundMessage = (content) => ({ id: "", content });
|
|
19
|
-
function group(...items) {
|
|
20
|
-
return {
|
|
21
|
-
build: async () => {
|
|
22
|
-
const resolved = await resolveContents(items);
|
|
23
|
-
const members = [];
|
|
24
|
-
for (const item of resolved) {
|
|
25
|
-
if (item.type === "group" || item.type === "reaction") {
|
|
26
|
-
throw new Error(`group() cannot contain "${item.type}" items`);
|
|
27
|
-
}
|
|
28
|
-
members.push(stubOutboundMessage(item));
|
|
29
|
-
}
|
|
30
|
-
return asGroup({ items: members });
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
}
|
|
7
|
+
} from "./chunk-LQMDV75O.js";
|
|
34
8
|
|
|
35
9
|
// src/content/richlink.ts
|
|
36
|
-
import
|
|
10
|
+
import z from "zod";
|
|
37
11
|
|
|
38
12
|
// src/utils/link-metadata.ts
|
|
39
13
|
import ogs from "open-graph-scraper";
|
|
@@ -90,22 +64,22 @@ var fetchImage = (url) => fetchUrlBytes(new URL(url), {
|
|
|
90
64
|
});
|
|
91
65
|
|
|
92
66
|
// src/content/richlink.ts
|
|
93
|
-
var richlinkCoverSchema =
|
|
94
|
-
mimeType:
|
|
67
|
+
var richlinkCoverSchema = z.object({
|
|
68
|
+
mimeType: z.string().min(1).optional(),
|
|
95
69
|
read: readSchema,
|
|
96
70
|
stream: streamSchema
|
|
97
71
|
});
|
|
98
|
-
var optionalStringAccessor =
|
|
72
|
+
var optionalStringAccessor = z.function({
|
|
99
73
|
input: [],
|
|
100
|
-
output:
|
|
74
|
+
output: z.promise(z.string().min(1).optional())
|
|
101
75
|
});
|
|
102
|
-
var coverAccessor =
|
|
76
|
+
var coverAccessor = z.function({
|
|
103
77
|
input: [],
|
|
104
|
-
output:
|
|
78
|
+
output: z.promise(richlinkCoverSchema.optional())
|
|
105
79
|
});
|
|
106
|
-
var richlinkSchema =
|
|
107
|
-
type:
|
|
108
|
-
url:
|
|
80
|
+
var richlinkSchema = z.object({
|
|
81
|
+
type: z.literal("richlink"),
|
|
82
|
+
url: z.url(),
|
|
109
83
|
title: optionalStringAccessor,
|
|
110
84
|
summary: optionalStringAccessor,
|
|
111
85
|
cover: coverAccessor
|
|
@@ -150,9 +124,6 @@ function richlink(url) {
|
|
|
150
124
|
}
|
|
151
125
|
|
|
152
126
|
export {
|
|
153
|
-
groupSchema,
|
|
154
|
-
asGroup,
|
|
155
|
-
group,
|
|
156
127
|
asRichlink,
|
|
157
128
|
richlink
|
|
158
129
|
};
|
|
@@ -1,21 +1,26 @@
|
|
|
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
|
+
import {
|
|
6
|
+
asContact
|
|
7
|
+
} from "./chunk-NZ5WCMTY.js";
|
|
8
|
+
import {
|
|
9
|
+
stream
|
|
10
|
+
} from "./chunk-5XEFJBN2.js";
|
|
5
11
|
import {
|
|
6
|
-
asContact,
|
|
7
12
|
fromVCard,
|
|
8
13
|
toVCard
|
|
9
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-6UZFVXQF.js";
|
|
10
15
|
import {
|
|
11
16
|
UnsupportedError,
|
|
12
17
|
definePlatform
|
|
13
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-LX437ZTY.js";
|
|
14
19
|
import {
|
|
15
20
|
asAttachment,
|
|
16
21
|
asCustom,
|
|
17
22
|
reactionSchema
|
|
18
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-LQMDV75O.js";
|
|
19
24
|
|
|
20
25
|
// src/providers/terminal/index.ts
|
|
21
26
|
import { spawn } from "child_process";
|
|
@@ -694,7 +699,7 @@ function protocolToSpectrum(p) {
|
|
|
694
699
|
}
|
|
695
700
|
return cached;
|
|
696
701
|
};
|
|
697
|
-
const
|
|
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") {
|
|
@@ -776,41 +781,71 @@ var terminal = definePlatform("Terminal", {
|
|
|
776
781
|
})
|
|
777
782
|
},
|
|
778
783
|
space: {
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
const id = input.params?.id ?? generateChatId(client);
|
|
784
|
+
create: async ({ client }) => {
|
|
785
|
+
const id = generateChatId(client);
|
|
782
786
|
client.knownChats.add(id);
|
|
783
787
|
await client.session.request("ensureSpace", { id });
|
|
784
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 };
|
|
785
796
|
}
|
|
786
797
|
},
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
798
|
+
// Return a ManagedStream (not a native async generator): a native generator
|
|
799
|
+
// parked on an in-flight `client.events.next()` cannot be force-cancelled —
|
|
800
|
+
// a `.return()` queues behind the pending `next()` and never reaches the
|
|
801
|
+
// event queue, which would deadlock `Spectrum.stop()`. Driving the queue with
|
|
802
|
+
// an explicit pump lets cleanup call the queue iterator's `return()` directly
|
|
803
|
+
// (synchronous close + drain), so the stream tears down promptly on stop()
|
|
804
|
+
// without waiting for destroyClient.
|
|
805
|
+
messages({ client }) {
|
|
806
|
+
return stream((emit, end) => {
|
|
807
|
+
const iterator = client.events[Symbol.asyncIterator]();
|
|
808
|
+
const pump = (async () => {
|
|
809
|
+
try {
|
|
810
|
+
let result = await iterator.next();
|
|
811
|
+
while (!result.done) {
|
|
812
|
+
const evt = result.value;
|
|
813
|
+
if (evt.kind === "message") {
|
|
814
|
+
const msg = evt.value;
|
|
815
|
+
client.knownChats.add(msg.spaceId);
|
|
816
|
+
await emit({
|
|
817
|
+
id: msg.id,
|
|
818
|
+
content: protocolToSpectrum(msg.content),
|
|
819
|
+
sender: { id: msg.senderId },
|
|
820
|
+
space: { id: msg.spaceId },
|
|
821
|
+
timestamp: parseTimestamp(msg.timestamp),
|
|
822
|
+
// replyTo is a terminal-specific extra — agents inspect via a
|
|
823
|
+
// cast until Spectrum's message model grows first-class support.
|
|
824
|
+
...msg.replyTo ? { replyTo: msg.replyTo } : {}
|
|
825
|
+
});
|
|
826
|
+
} else {
|
|
827
|
+
const r = evt.value;
|
|
828
|
+
client.knownChats.add(r.spaceId);
|
|
829
|
+
await emit({
|
|
830
|
+
id: `reaction:${r.messageId}:${r.reaction}:${r.timestamp}`,
|
|
831
|
+
content: reactionContentFromProtocol(r),
|
|
832
|
+
sender: { id: r.senderId },
|
|
833
|
+
space: { id: r.spaceId },
|
|
834
|
+
timestamp: parseTimestamp(r.timestamp)
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
result = await iterator.next();
|
|
838
|
+
}
|
|
839
|
+
end();
|
|
840
|
+
} catch (error) {
|
|
841
|
+
end(error);
|
|
842
|
+
}
|
|
843
|
+
})();
|
|
844
|
+
return async () => {
|
|
845
|
+
await iterator.return?.();
|
|
846
|
+
await pump.catch(() => void 0);
|
|
812
847
|
};
|
|
813
|
-
}
|
|
848
|
+
});
|
|
814
849
|
},
|
|
815
850
|
send: async ({ client, content, space }) => {
|
|
816
851
|
if (content.type === "reply") {
|
|
@@ -828,7 +863,13 @@ var terminal = definePlatform("Terminal", {
|
|
|
828
863
|
messageId: content.target.id,
|
|
829
864
|
reaction: content.emoji
|
|
830
865
|
});
|
|
831
|
-
|
|
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
|
+
};
|
|
832
873
|
}
|
|
833
874
|
if (content.type === "typing") {
|
|
834
875
|
const method = content.state === "start" ? "startTyping" : "stopTyping";
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import { createRequire as __spectrumCreateRequire } from "node:module"; const require = __spectrumCreateRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
|
-
cloud
|
|
3
|
+
cloud
|
|
4
|
+
} from "./chunk-3GEJYGZK.js";
|
|
5
|
+
import {
|
|
4
6
|
mergeStreams,
|
|
5
7
|
stream
|
|
6
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-5XEFJBN2.js";
|
|
7
9
|
import {
|
|
8
10
|
UnsupportedError,
|
|
9
11
|
definePlatform
|
|
10
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-LX437ZTY.js";
|
|
11
13
|
import {
|
|
12
14
|
asAttachment,
|
|
13
15
|
asCustom,
|
|
14
16
|
asReaction,
|
|
15
17
|
asText
|
|
16
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-LQMDV75O.js";
|
|
17
19
|
|
|
18
20
|
// src/providers/slack/index.ts
|
|
19
21
|
import { createClient as createClient2, staticTokens } from "@photon-ai/slack";
|
|
@@ -338,13 +340,12 @@ var send = async (client, space, content) => {
|
|
|
338
340
|
);
|
|
339
341
|
}
|
|
340
342
|
if (content.type === "reaction") {
|
|
341
|
-
await reactToMessage(
|
|
343
|
+
return await reactToMessage(
|
|
342
344
|
client,
|
|
343
345
|
space,
|
|
344
346
|
content.target.ts ?? content.target.id,
|
|
345
|
-
content
|
|
347
|
+
content
|
|
346
348
|
);
|
|
347
|
-
return;
|
|
348
349
|
}
|
|
349
350
|
if (content.type === "typing") {
|
|
350
351
|
return;
|
|
@@ -386,15 +387,23 @@ var sendContent = async (client, space, content, threadTs) => {
|
|
|
386
387
|
throw UnsupportedError.content(content.type);
|
|
387
388
|
}
|
|
388
389
|
};
|
|
389
|
-
var reactToMessage = async (client, space, targetTs,
|
|
390
|
+
var reactToMessage = async (client, space, targetTs, content) => {
|
|
390
391
|
await client.team(space.teamId).messages.send({
|
|
391
392
|
channel: space.id,
|
|
392
393
|
reaction: {
|
|
393
|
-
emoji,
|
|
394
|
+
emoji: content.emoji,
|
|
394
395
|
itemChannel: space.id,
|
|
395
396
|
itemTs: targetTs
|
|
396
397
|
}
|
|
397
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
|
+
};
|
|
398
407
|
};
|
|
399
408
|
var replyToMessage = async (client, space, targetTs, content) => await sendContent(client, space, content, targetTs);
|
|
400
409
|
|
|
@@ -422,7 +431,6 @@ var spaceSchema = z.object({
|
|
|
422
431
|
teamId: z.string()
|
|
423
432
|
});
|
|
424
433
|
var spaceParamsSchema = z.object({
|
|
425
|
-
channel: z.string().optional(),
|
|
426
434
|
teamId: z.string()
|
|
427
435
|
});
|
|
428
436
|
var messageSchema = z.object({
|
|
@@ -473,27 +481,18 @@ var slack = definePlatform("Slack", {
|
|
|
473
481
|
space: {
|
|
474
482
|
schema: spaceSchema,
|
|
475
483
|
params: spaceParamsSchema,
|
|
476
|
-
|
|
484
|
+
create: async ({ input }) => {
|
|
477
485
|
const teamId = input.params?.teamId;
|
|
478
486
|
if (!teamId) {
|
|
479
487
|
throw new Error(
|
|
480
|
-
"Slack space creation requires a teamId param. Pass it via
|
|
481
|
-
);
|
|
482
|
-
}
|
|
483
|
-
const channel = input.params?.channel;
|
|
484
|
-
if (channel) {
|
|
485
|
-
return { id: channel, teamId };
|
|
486
|
-
}
|
|
487
|
-
if (input.users.length === 0) {
|
|
488
|
-
throw new Error(
|
|
489
|
-
"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 })."
|
|
490
489
|
);
|
|
491
490
|
}
|
|
492
491
|
if (input.users.length > 1) {
|
|
493
492
|
throw UnsupportedError.action(
|
|
494
|
-
"
|
|
493
|
+
"space.create",
|
|
495
494
|
"Slack",
|
|
496
|
-
"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 })"
|
|
497
496
|
);
|
|
498
497
|
}
|
|
499
498
|
const user = input.users[0];
|
|
@@ -501,6 +500,15 @@ var slack = definePlatform("Slack", {
|
|
|
501
500
|
throw new Error("Slack space creation requires a user");
|
|
502
501
|
}
|
|
503
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 };
|
|
504
512
|
}
|
|
505
513
|
},
|
|
506
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,
|