chat 4.27.0 → 4.28.1
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/{chunk-AN7MRAVW.js → chunk-V25FKIIL.js} +5 -1
- package/dist/index.d.ts +269 -31
- package/dist/index.js +544 -88
- package/dist/{jsx-runtime-Co9uV6l7.d.ts → jsx-runtime-DxGwoLu2.d.ts} +10 -0
- package/dist/jsx-runtime.d.ts +1 -1
- package/dist/jsx-runtime.js +1 -1
- package/docs/actions.mdx +52 -1
- package/docs/adapters.mdx +42 -36
- package/docs/api/cards.mdx +4 -0
- package/docs/api/chat.mdx +91 -9
- package/docs/api/index.mdx +2 -0
- package/docs/api/markdown.mdx +28 -5
- package/docs/api/message.mdx +53 -0
- package/docs/api/meta.json +2 -0
- package/docs/api/modals.mdx +50 -0
- package/docs/api/postable-message.mdx +55 -1
- package/docs/api/thread.mdx +10 -2
- package/docs/api/transcripts.mdx +220 -0
- package/docs/cards.mdx +6 -0
- package/docs/concurrency.mdx +4 -0
- package/docs/contributing/building.mdx +73 -1
- package/docs/conversation-history.mdx +137 -0
- package/docs/direct-messages.mdx +13 -4
- package/docs/ephemeral-messages.mdx +1 -1
- package/docs/error-handling.mdx +15 -3
- package/docs/files.mdx +1 -1
- package/docs/index.mdx +7 -5
- package/docs/meta.json +14 -3
- package/docs/modals.mdx +24 -0
- package/docs/posting-messages.mdx +7 -3
- package/docs/streaming.mdx +72 -24
- package/docs/subject.mdx +53 -0
- package/docs/threads-messages-channels.mdx +9 -0
- package/docs/usage.mdx +11 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -60,7 +60,7 @@ import {
|
|
|
60
60
|
toModalElement,
|
|
61
61
|
toPlainText,
|
|
62
62
|
walkAst
|
|
63
|
-
} from "./chunk-
|
|
63
|
+
} from "./chunk-V25FKIIL.js";
|
|
64
64
|
|
|
65
65
|
// src/ai.ts
|
|
66
66
|
var TEXT_MIME_PREFIXES = [
|
|
@@ -190,6 +190,127 @@ ${linkParts}`;
|
|
|
190
190
|
// src/channel.ts
|
|
191
191
|
import { WORKFLOW_DESERIALIZE as WORKFLOW_DESERIALIZE2, WORKFLOW_SERIALIZE as WORKFLOW_SERIALIZE2 } from "@workflow/serde";
|
|
192
192
|
|
|
193
|
+
// src/callback-url.ts
|
|
194
|
+
var CALLBACK_TOKEN_PREFIX = "__cb:";
|
|
195
|
+
var CALLBACK_CACHE_KEY_PREFIX = "chat:callback:";
|
|
196
|
+
var CALLBACK_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
197
|
+
function encodeCallbackValue(token) {
|
|
198
|
+
return `${CALLBACK_TOKEN_PREFIX}${token}`;
|
|
199
|
+
}
|
|
200
|
+
function decodeCallbackValue(value) {
|
|
201
|
+
if (!value?.startsWith(CALLBACK_TOKEN_PREFIX)) {
|
|
202
|
+
return { callbackToken: void 0 };
|
|
203
|
+
}
|
|
204
|
+
return { callbackToken: value.slice(CALLBACK_TOKEN_PREFIX.length) };
|
|
205
|
+
}
|
|
206
|
+
function generateToken() {
|
|
207
|
+
return crypto.randomUUID().replace(/-/g, "").slice(0, 16);
|
|
208
|
+
}
|
|
209
|
+
async function processActionsElement(actions, stateAdapter) {
|
|
210
|
+
return {
|
|
211
|
+
type: "actions",
|
|
212
|
+
children: await Promise.all(
|
|
213
|
+
actions.children.map(async (el) => {
|
|
214
|
+
if (el.type !== "button" || !el.callbackUrl) {
|
|
215
|
+
return el;
|
|
216
|
+
}
|
|
217
|
+
const token = generateToken();
|
|
218
|
+
const stored = {
|
|
219
|
+
url: el.callbackUrl,
|
|
220
|
+
originalValue: el.value
|
|
221
|
+
};
|
|
222
|
+
await stateAdapter.set(
|
|
223
|
+
`${CALLBACK_CACHE_KEY_PREFIX}${token}`,
|
|
224
|
+
stored,
|
|
225
|
+
CALLBACK_TTL_MS
|
|
226
|
+
);
|
|
227
|
+
const processed = {
|
|
228
|
+
type: "button",
|
|
229
|
+
id: el.id,
|
|
230
|
+
label: el.label,
|
|
231
|
+
style: el.style,
|
|
232
|
+
disabled: el.disabled,
|
|
233
|
+
value: encodeCallbackValue(token),
|
|
234
|
+
actionType: el.actionType
|
|
235
|
+
};
|
|
236
|
+
return processed;
|
|
237
|
+
})
|
|
238
|
+
)
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
function hasCallbackButtons(children) {
|
|
242
|
+
for (const child of children) {
|
|
243
|
+
if (child.type === "actions") {
|
|
244
|
+
for (const el of child.children) {
|
|
245
|
+
if (el.type === "button" && el.callbackUrl) {
|
|
246
|
+
return true;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (child.type === "section" && "children" in child && hasCallbackButtons(child.children)) {
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
async function processChildren(children, stateAdapter) {
|
|
257
|
+
const result = [];
|
|
258
|
+
for (const child of children) {
|
|
259
|
+
if (child.type === "actions") {
|
|
260
|
+
result.push(await processActionsElement(child, stateAdapter));
|
|
261
|
+
} else if (child.type === "section" && "children" in child) {
|
|
262
|
+
result.push({
|
|
263
|
+
...child,
|
|
264
|
+
children: await processChildren(child.children, stateAdapter)
|
|
265
|
+
});
|
|
266
|
+
} else {
|
|
267
|
+
result.push(child);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return result;
|
|
271
|
+
}
|
|
272
|
+
async function processCardCallbackUrls(card, stateAdapter) {
|
|
273
|
+
if (!hasCallbackButtons(card.children)) {
|
|
274
|
+
return card;
|
|
275
|
+
}
|
|
276
|
+
return {
|
|
277
|
+
...card,
|
|
278
|
+
children: await processChildren(card.children, stateAdapter)
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
async function resolveCallbackUrl(token, stateAdapter) {
|
|
282
|
+
const stored = await stateAdapter.get(
|
|
283
|
+
`${CALLBACK_CACHE_KEY_PREFIX}${token}`
|
|
284
|
+
);
|
|
285
|
+
if (!stored) {
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
if (typeof stored === "string") {
|
|
289
|
+
return { url: stored };
|
|
290
|
+
}
|
|
291
|
+
return stored;
|
|
292
|
+
}
|
|
293
|
+
async function postToCallbackUrl(callbackUrl, payload) {
|
|
294
|
+
try {
|
|
295
|
+
const response = await fetch(callbackUrl, {
|
|
296
|
+
method: "POST",
|
|
297
|
+
headers: { "Content-Type": "application/json" },
|
|
298
|
+
body: JSON.stringify(payload)
|
|
299
|
+
});
|
|
300
|
+
if (!response.ok) {
|
|
301
|
+
return {
|
|
302
|
+
error: new Error(
|
|
303
|
+
`Callback URL returned ${response.status}: ${await response.text().catch(() => "")}`
|
|
304
|
+
),
|
|
305
|
+
status: response.status
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
return { status: response.status };
|
|
309
|
+
} catch (error) {
|
|
310
|
+
return { error };
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
193
314
|
// src/chat-singleton.ts
|
|
194
315
|
var _singleton = null;
|
|
195
316
|
function setChatSingleton(chat) {
|
|
@@ -245,6 +366,10 @@ async function* fromFullStream(stream) {
|
|
|
245
366
|
|
|
246
367
|
// src/message.ts
|
|
247
368
|
import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from "@workflow/serde";
|
|
369
|
+
var adapterMap = /* @__PURE__ */ new WeakMap();
|
|
370
|
+
function setMessageAdapter(message, adapter) {
|
|
371
|
+
adapterMap.set(message, adapter);
|
|
372
|
+
}
|
|
248
373
|
var Message = class _Message {
|
|
249
374
|
/** Unique message ID */
|
|
250
375
|
id;
|
|
@@ -283,8 +408,31 @@ var Message = class _Message {
|
|
|
283
408
|
* ```
|
|
284
409
|
*/
|
|
285
410
|
isMention;
|
|
411
|
+
/**
|
|
412
|
+
* Cross-platform user key for this message's author.
|
|
413
|
+
*
|
|
414
|
+
* Set by the Chat SDK before passing the message to handlers, when
|
|
415
|
+
* `ChatConfig.identity` is configured. `undefined` if no resolver is
|
|
416
|
+
* configured; `undefined` (i.e. absent) when the resolver returned null.
|
|
417
|
+
*
|
|
418
|
+
* Used by the Transcripts API to look up / append per-user transcripts.
|
|
419
|
+
*/
|
|
420
|
+
userKey;
|
|
286
421
|
/** Links found in the message */
|
|
287
422
|
links;
|
|
423
|
+
_subjectPromise;
|
|
424
|
+
get subject() {
|
|
425
|
+
if (this._subjectPromise) {
|
|
426
|
+
return this._subjectPromise;
|
|
427
|
+
}
|
|
428
|
+
const adapter = adapterMap.get(this);
|
|
429
|
+
if (!adapter?.fetchSubject) {
|
|
430
|
+
this._subjectPromise = Promise.resolve(null);
|
|
431
|
+
return this._subjectPromise;
|
|
432
|
+
}
|
|
433
|
+
this._subjectPromise = adapter.fetchSubject(this.raw).catch(() => null);
|
|
434
|
+
return this._subjectPromise;
|
|
435
|
+
}
|
|
288
436
|
constructor(data) {
|
|
289
437
|
this.id = data.id;
|
|
290
438
|
this.threadId = data.threadId;
|
|
@@ -497,7 +645,7 @@ var ChannelImpl = class _ChannelImpl {
|
|
|
497
645
|
_adapterName;
|
|
498
646
|
_stateAdapterInstance;
|
|
499
647
|
_name = null;
|
|
500
|
-
|
|
648
|
+
_threadHistory;
|
|
501
649
|
constructor(config) {
|
|
502
650
|
this.id = config.id;
|
|
503
651
|
this.isDM = config.isDM ?? false;
|
|
@@ -507,7 +655,7 @@ var ChannelImpl = class _ChannelImpl {
|
|
|
507
655
|
} else {
|
|
508
656
|
this._adapter = config.adapter;
|
|
509
657
|
this._stateAdapterInstance = config.stateAdapter;
|
|
510
|
-
this.
|
|
658
|
+
this._threadHistory = config.threadHistory;
|
|
511
659
|
}
|
|
512
660
|
}
|
|
513
661
|
get adapter() {
|
|
@@ -561,7 +709,7 @@ var ChannelImpl = class _ChannelImpl {
|
|
|
561
709
|
get messages() {
|
|
562
710
|
const adapter = this.adapter;
|
|
563
711
|
const channelId = this.id;
|
|
564
|
-
const
|
|
712
|
+
const threadHistory = this._threadHistory;
|
|
565
713
|
return {
|
|
566
714
|
async *[Symbol.asyncIterator]() {
|
|
567
715
|
let cursor;
|
|
@@ -579,8 +727,8 @@ var ChannelImpl = class _ChannelImpl {
|
|
|
579
727
|
}
|
|
580
728
|
cursor = result.nextCursor;
|
|
581
729
|
}
|
|
582
|
-
if (!yieldedAny &&
|
|
583
|
-
const cached = await
|
|
730
|
+
if (!yieldedAny && threadHistory) {
|
|
731
|
+
const cached = await threadHistory.getMessages(channelId);
|
|
584
732
|
for (let i = cached.length - 1; i >= 0; i--) {
|
|
585
733
|
yield cached[i];
|
|
586
734
|
}
|
|
@@ -637,6 +785,8 @@ var ChannelImpl = class _ChannelImpl {
|
|
|
637
785
|
for await (const chunk of fromFullStream(message)) {
|
|
638
786
|
if (typeof chunk === "string") {
|
|
639
787
|
accumulated += chunk;
|
|
788
|
+
} else if (chunk.type === "markdown_text") {
|
|
789
|
+
accumulated += chunk.text;
|
|
640
790
|
}
|
|
641
791
|
}
|
|
642
792
|
return this.postSingleMessage({ markdown: accumulated });
|
|
@@ -649,6 +799,7 @@ var ChannelImpl = class _ChannelImpl {
|
|
|
649
799
|
}
|
|
650
800
|
postable = card;
|
|
651
801
|
}
|
|
802
|
+
postable = await this.processCallbackUrls(postable);
|
|
652
803
|
return this.postSingleMessage(postable);
|
|
653
804
|
}
|
|
654
805
|
async handlePostableObject(obj) {
|
|
@@ -666,8 +817,8 @@ var ChannelImpl = class _ChannelImpl {
|
|
|
666
817
|
postable,
|
|
667
818
|
rawMessage.threadId
|
|
668
819
|
);
|
|
669
|
-
if (this.
|
|
670
|
-
await this.
|
|
820
|
+
if (this._threadHistory) {
|
|
821
|
+
await this._threadHistory.append(this.id, new Message(sent));
|
|
671
822
|
}
|
|
672
823
|
return sent;
|
|
673
824
|
}
|
|
@@ -684,6 +835,7 @@ var ChannelImpl = class _ChannelImpl {
|
|
|
684
835
|
} else {
|
|
685
836
|
postable = message;
|
|
686
837
|
}
|
|
838
|
+
postable = await this.processCallbackUrls(postable);
|
|
687
839
|
if (this.adapter.postEphemeral) {
|
|
688
840
|
return this.adapter.postEphemeral(this.id, userId, postable);
|
|
689
841
|
}
|
|
@@ -713,6 +865,7 @@ var ChannelImpl = class _ChannelImpl {
|
|
|
713
865
|
} else {
|
|
714
866
|
postable = message;
|
|
715
867
|
}
|
|
868
|
+
postable = await this.processCallbackUrls(postable);
|
|
716
869
|
if (!this.adapter.scheduleMessage) {
|
|
717
870
|
throw new NotImplementedError(
|
|
718
871
|
"Scheduled messages are not supported by this adapter",
|
|
@@ -721,6 +874,24 @@ var ChannelImpl = class _ChannelImpl {
|
|
|
721
874
|
}
|
|
722
875
|
return this.adapter.scheduleMessage(this.id, postable, options);
|
|
723
876
|
}
|
|
877
|
+
async processCallbackUrls(postable) {
|
|
878
|
+
if (typeof postable === "string") {
|
|
879
|
+
return postable;
|
|
880
|
+
}
|
|
881
|
+
if ("type" in postable && postable.type === "card") {
|
|
882
|
+
return processCardCallbackUrls(postable, this._stateAdapter);
|
|
883
|
+
}
|
|
884
|
+
if ("card" in postable && postable.card?.type === "card") {
|
|
885
|
+
const processed = await processCardCallbackUrls(
|
|
886
|
+
postable.card,
|
|
887
|
+
this._stateAdapter
|
|
888
|
+
);
|
|
889
|
+
if (processed !== postable.card) {
|
|
890
|
+
return { ...postable, card: processed };
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
return postable;
|
|
894
|
+
}
|
|
724
895
|
async startTyping(status) {
|
|
725
896
|
await this.adapter.startTyping(this.id, status);
|
|
726
897
|
}
|
|
@@ -790,6 +961,7 @@ var ChannelImpl = class _ChannelImpl {
|
|
|
790
961
|
}
|
|
791
962
|
editPostable = card;
|
|
792
963
|
}
|
|
964
|
+
editPostable = await self.processCallbackUrls(editPostable);
|
|
793
965
|
await adapter.editMessage(threadId, messageId, editPostable);
|
|
794
966
|
return self.createSentMessage(messageId, editPostable);
|
|
795
967
|
},
|
|
@@ -858,46 +1030,6 @@ function extractMessageContent(message) {
|
|
|
858
1030
|
throw new Error("Invalid PostableMessage format");
|
|
859
1031
|
}
|
|
860
1032
|
|
|
861
|
-
// src/message-history.ts
|
|
862
|
-
var DEFAULT_MAX_MESSAGES = 100;
|
|
863
|
-
var DEFAULT_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
864
|
-
var KEY_PREFIX = "msg-history:";
|
|
865
|
-
var MessageHistoryCache = class {
|
|
866
|
-
state;
|
|
867
|
-
maxMessages;
|
|
868
|
-
ttlMs;
|
|
869
|
-
constructor(state, config) {
|
|
870
|
-
this.state = state;
|
|
871
|
-
this.maxMessages = config?.maxMessages ?? DEFAULT_MAX_MESSAGES;
|
|
872
|
-
this.ttlMs = config?.ttlMs ?? DEFAULT_TTL_MS;
|
|
873
|
-
}
|
|
874
|
-
/**
|
|
875
|
-
* Atomically append a message to the history for a thread.
|
|
876
|
-
* Trims to maxMessages (keeps newest) and refreshes TTL.
|
|
877
|
-
*/
|
|
878
|
-
async append(threadId, message) {
|
|
879
|
-
const key = `${KEY_PREFIX}${threadId}`;
|
|
880
|
-
const serialized = message.toJSON();
|
|
881
|
-
serialized.raw = null;
|
|
882
|
-
await this.state.appendToList(key, serialized, {
|
|
883
|
-
maxLength: this.maxMessages,
|
|
884
|
-
ttlMs: this.ttlMs
|
|
885
|
-
});
|
|
886
|
-
}
|
|
887
|
-
/**
|
|
888
|
-
* Get messages for a thread in chronological order (oldest first).
|
|
889
|
-
*
|
|
890
|
-
* @param threadId - The thread ID
|
|
891
|
-
* @param limit - Optional limit on number of messages to return (returns newest N)
|
|
892
|
-
*/
|
|
893
|
-
async getMessages(threadId, limit) {
|
|
894
|
-
const key = `${KEY_PREFIX}${threadId}`;
|
|
895
|
-
const stored = await this.state.getList(key);
|
|
896
|
-
const sliced = limit && stored.length > limit ? stored.slice(stored.length - limit) : stored;
|
|
897
|
-
return sliced.map((s) => Message.fromJSON(s));
|
|
898
|
-
}
|
|
899
|
-
};
|
|
900
|
-
|
|
901
1033
|
// src/thread.ts
|
|
902
1034
|
import { WORKFLOW_DESERIALIZE as WORKFLOW_DESERIALIZE3, WORKFLOW_SERIALIZE as WORKFLOW_SERIALIZE3 } from "@workflow/serde";
|
|
903
1035
|
|
|
@@ -1168,8 +1300,8 @@ var ThreadImpl = class _ThreadImpl {
|
|
|
1168
1300
|
_fallbackStreamingPlaceholderText;
|
|
1169
1301
|
/** Cached channel instance */
|
|
1170
1302
|
_channel;
|
|
1171
|
-
/**
|
|
1172
|
-
|
|
1303
|
+
/** Thread history cache (set only for adapters with persistThreadHistory) */
|
|
1304
|
+
_threadHistory;
|
|
1173
1305
|
_logger;
|
|
1174
1306
|
constructor(config) {
|
|
1175
1307
|
this.id = config.id;
|
|
@@ -1186,7 +1318,7 @@ var ThreadImpl = class _ThreadImpl {
|
|
|
1186
1318
|
} else {
|
|
1187
1319
|
this._adapter = config.adapter;
|
|
1188
1320
|
this._stateAdapterInstance = config.stateAdapter;
|
|
1189
|
-
this.
|
|
1321
|
+
this._threadHistory = config.threadHistory;
|
|
1190
1322
|
}
|
|
1191
1323
|
if (config.initialMessage) {
|
|
1192
1324
|
this._recentMessages = [config.initialMessage];
|
|
@@ -1267,7 +1399,7 @@ var ThreadImpl = class _ThreadImpl {
|
|
|
1267
1399
|
stateAdapter: this._stateAdapter,
|
|
1268
1400
|
isDM: this.isDM,
|
|
1269
1401
|
channelVisibility: this.channelVisibility,
|
|
1270
|
-
|
|
1402
|
+
threadHistory: this._threadHistory
|
|
1271
1403
|
});
|
|
1272
1404
|
}
|
|
1273
1405
|
return this._channel;
|
|
@@ -1279,7 +1411,7 @@ var ThreadImpl = class _ThreadImpl {
|
|
|
1279
1411
|
get messages() {
|
|
1280
1412
|
const adapter = this.adapter;
|
|
1281
1413
|
const threadId = this.id;
|
|
1282
|
-
const
|
|
1414
|
+
const threadHistory = this._threadHistory;
|
|
1283
1415
|
return {
|
|
1284
1416
|
async *[Symbol.asyncIterator]() {
|
|
1285
1417
|
let cursor;
|
|
@@ -1299,8 +1431,8 @@ var ThreadImpl = class _ThreadImpl {
|
|
|
1299
1431
|
}
|
|
1300
1432
|
cursor = result.nextCursor;
|
|
1301
1433
|
}
|
|
1302
|
-
if (!yieldedAny &&
|
|
1303
|
-
const cached = await
|
|
1434
|
+
if (!yieldedAny && threadHistory) {
|
|
1435
|
+
const cached = await threadHistory.getMessages(threadId);
|
|
1304
1436
|
for (let i = cached.length - 1; i >= 0; i--) {
|
|
1305
1437
|
yield cached[i];
|
|
1306
1438
|
}
|
|
@@ -1311,7 +1443,7 @@ var ThreadImpl = class _ThreadImpl {
|
|
|
1311
1443
|
get allMessages() {
|
|
1312
1444
|
const adapter = this.adapter;
|
|
1313
1445
|
const threadId = this.id;
|
|
1314
|
-
const
|
|
1446
|
+
const threadHistory = this._threadHistory;
|
|
1315
1447
|
return {
|
|
1316
1448
|
async *[Symbol.asyncIterator]() {
|
|
1317
1449
|
let cursor;
|
|
@@ -1331,8 +1463,8 @@ var ThreadImpl = class _ThreadImpl {
|
|
|
1331
1463
|
}
|
|
1332
1464
|
cursor = result.nextCursor;
|
|
1333
1465
|
}
|
|
1334
|
-
if (!yieldedAny &&
|
|
1335
|
-
const cached = await
|
|
1466
|
+
if (!yieldedAny && threadHistory) {
|
|
1467
|
+
const cached = await threadHistory.getMessages(threadId);
|
|
1336
1468
|
for (const message of cached) {
|
|
1337
1469
|
yield message;
|
|
1338
1470
|
}
|
|
@@ -1394,14 +1526,15 @@ var ThreadImpl = class _ThreadImpl {
|
|
|
1394
1526
|
}
|
|
1395
1527
|
postable = card;
|
|
1396
1528
|
}
|
|
1529
|
+
postable = await this.processCallbackUrls(postable);
|
|
1397
1530
|
const rawMessage = await this.adapter.postMessage(this.id, postable);
|
|
1398
1531
|
const result = this.createSentMessage(
|
|
1399
1532
|
rawMessage.id,
|
|
1400
1533
|
postable,
|
|
1401
1534
|
rawMessage.threadId
|
|
1402
1535
|
);
|
|
1403
|
-
if (this.
|
|
1404
|
-
await this.
|
|
1536
|
+
if (this._threadHistory) {
|
|
1537
|
+
await this._threadHistory.append(this.id, new Message(result));
|
|
1405
1538
|
}
|
|
1406
1539
|
return result;
|
|
1407
1540
|
}
|
|
@@ -1427,6 +1560,7 @@ var ThreadImpl = class _ThreadImpl {
|
|
|
1427
1560
|
} else {
|
|
1428
1561
|
postable = message;
|
|
1429
1562
|
}
|
|
1563
|
+
postable = await this.processCallbackUrls(postable);
|
|
1430
1564
|
if (this.adapter.postEphemeral) {
|
|
1431
1565
|
return this.adapter.postEphemeral(this.id, userId, postable);
|
|
1432
1566
|
}
|
|
@@ -1445,6 +1579,24 @@ var ThreadImpl = class _ThreadImpl {
|
|
|
1445
1579
|
}
|
|
1446
1580
|
return null;
|
|
1447
1581
|
}
|
|
1582
|
+
async processCallbackUrls(postable) {
|
|
1583
|
+
if (typeof postable === "string") {
|
|
1584
|
+
return postable;
|
|
1585
|
+
}
|
|
1586
|
+
if ("type" in postable && postable.type === "card") {
|
|
1587
|
+
return processCardCallbackUrls(postable, this._stateAdapter);
|
|
1588
|
+
}
|
|
1589
|
+
if ("card" in postable && postable.card?.type === "card") {
|
|
1590
|
+
const processed = await processCardCallbackUrls(
|
|
1591
|
+
postable.card,
|
|
1592
|
+
this._stateAdapter
|
|
1593
|
+
);
|
|
1594
|
+
if (processed !== postable.card) {
|
|
1595
|
+
return { ...postable, card: processed };
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
return postable;
|
|
1599
|
+
}
|
|
1448
1600
|
async schedule(message, options) {
|
|
1449
1601
|
let postable;
|
|
1450
1602
|
if (isJSX(message)) {
|
|
@@ -1456,6 +1608,9 @@ var ThreadImpl = class _ThreadImpl {
|
|
|
1456
1608
|
} else {
|
|
1457
1609
|
postable = message;
|
|
1458
1610
|
}
|
|
1611
|
+
postable = await this.processCallbackUrls(
|
|
1612
|
+
postable
|
|
1613
|
+
);
|
|
1459
1614
|
if (!this.adapter.scheduleMessage) {
|
|
1460
1615
|
throw new NotImplementedError(
|
|
1461
1616
|
"Scheduled messages are not supported by this adapter",
|
|
@@ -1467,7 +1622,7 @@ var ThreadImpl = class _ThreadImpl {
|
|
|
1467
1622
|
/**
|
|
1468
1623
|
* Handle streaming from an AsyncIterable.
|
|
1469
1624
|
* Normalizes the stream (supports both textStream and fullStream from AI SDK),
|
|
1470
|
-
* then uses adapter's
|
|
1625
|
+
* then uses the adapter's stream implementation if available, otherwise falls back to post+edit.
|
|
1471
1626
|
*/
|
|
1472
1627
|
async handleStream(rawStream, callerOptions) {
|
|
1473
1628
|
const textStream = fromFullStream(rawStream);
|
|
@@ -1505,8 +1660,8 @@ var ThreadImpl = class _ThreadImpl {
|
|
|
1505
1660
|
{ markdown: accumulated },
|
|
1506
1661
|
raw.threadId
|
|
1507
1662
|
);
|
|
1508
|
-
if (this.
|
|
1509
|
-
await this.
|
|
1663
|
+
if (this._threadHistory) {
|
|
1664
|
+
await this._threadHistory.append(this.id, new Message(sent));
|
|
1510
1665
|
}
|
|
1511
1666
|
return sent;
|
|
1512
1667
|
}
|
|
@@ -1653,8 +1808,8 @@ var ThreadImpl = class _ThreadImpl {
|
|
|
1653
1808
|
{ markdown: accumulated },
|
|
1654
1809
|
threadIdForEdits
|
|
1655
1810
|
);
|
|
1656
|
-
if (this.
|
|
1657
|
-
await this.
|
|
1811
|
+
if (this._threadHistory) {
|
|
1812
|
+
await this._threadHistory.append(this.id, new Message(sent));
|
|
1658
1813
|
}
|
|
1659
1814
|
return sent;
|
|
1660
1815
|
}
|
|
@@ -1662,11 +1817,8 @@ var ThreadImpl = class _ThreadImpl {
|
|
|
1662
1817
|
const result = await this.adapter.fetchMessages(this.id, { limit: 50 });
|
|
1663
1818
|
if (result.messages.length > 0) {
|
|
1664
1819
|
this._recentMessages = result.messages;
|
|
1665
|
-
} else if (this.
|
|
1666
|
-
this._recentMessages = await this.
|
|
1667
|
-
this.id,
|
|
1668
|
-
50
|
|
1669
|
-
);
|
|
1820
|
+
} else if (this._threadHistory) {
|
|
1821
|
+
this._recentMessages = await this._threadHistory.getMessages(this.id, 50);
|
|
1670
1822
|
} else {
|
|
1671
1823
|
this._recentMessages = [];
|
|
1672
1824
|
}
|
|
@@ -1778,6 +1930,7 @@ var ThreadImpl = class _ThreadImpl {
|
|
|
1778
1930
|
}
|
|
1779
1931
|
postable2 = card;
|
|
1780
1932
|
}
|
|
1933
|
+
postable2 = await self.processCallbackUrls(postable2);
|
|
1781
1934
|
await adapter.editMessage(threadId, messageId, postable2);
|
|
1782
1935
|
return self.createSentMessage(messageId, postable2);
|
|
1783
1936
|
},
|
|
@@ -1821,6 +1974,7 @@ var ThreadImpl = class _ThreadImpl {
|
|
|
1821
1974
|
}
|
|
1822
1975
|
postable = card;
|
|
1823
1976
|
}
|
|
1977
|
+
postable = await self.processCallbackUrls(postable);
|
|
1824
1978
|
await adapter.editMessage(threadId, messageId, postable);
|
|
1825
1979
|
return self.createSentMessage(messageId, postable, threadId);
|
|
1826
1980
|
},
|
|
@@ -1902,6 +2056,177 @@ function reviver(_key, value) {
|
|
|
1902
2056
|
return value;
|
|
1903
2057
|
}
|
|
1904
2058
|
|
|
2059
|
+
// src/thread-history.ts
|
|
2060
|
+
var DEFAULT_MAX_MESSAGES = 100;
|
|
2061
|
+
var DEFAULT_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
2062
|
+
var KEY_PREFIX = "msg-history:";
|
|
2063
|
+
var ThreadHistoryCache = class {
|
|
2064
|
+
state;
|
|
2065
|
+
maxMessages;
|
|
2066
|
+
ttlMs;
|
|
2067
|
+
constructor(state, config) {
|
|
2068
|
+
this.state = state;
|
|
2069
|
+
this.maxMessages = config?.maxMessages ?? DEFAULT_MAX_MESSAGES;
|
|
2070
|
+
this.ttlMs = config?.ttlMs ?? DEFAULT_TTL_MS;
|
|
2071
|
+
}
|
|
2072
|
+
/**
|
|
2073
|
+
* Atomically append a message to the history for a thread.
|
|
2074
|
+
* Trims to maxMessages (keeps newest) and refreshes TTL.
|
|
2075
|
+
*/
|
|
2076
|
+
async append(threadId, message) {
|
|
2077
|
+
const key = `${KEY_PREFIX}${threadId}`;
|
|
2078
|
+
const serialized = message.toJSON();
|
|
2079
|
+
serialized.raw = null;
|
|
2080
|
+
await this.state.appendToList(key, serialized, {
|
|
2081
|
+
maxLength: this.maxMessages,
|
|
2082
|
+
ttlMs: this.ttlMs
|
|
2083
|
+
});
|
|
2084
|
+
}
|
|
2085
|
+
/**
|
|
2086
|
+
* Get messages for a thread in chronological order (oldest first).
|
|
2087
|
+
*
|
|
2088
|
+
* @param threadId - The thread ID
|
|
2089
|
+
* @param limit - Optional limit on number of messages to return (returns newest N)
|
|
2090
|
+
*/
|
|
2091
|
+
async getMessages(threadId, limit) {
|
|
2092
|
+
const key = `${KEY_PREFIX}${threadId}`;
|
|
2093
|
+
const stored = await this.state.getList(key);
|
|
2094
|
+
const sliced = limit && stored.length > limit ? stored.slice(stored.length - limit) : stored;
|
|
2095
|
+
return sliced.map((s) => Message.fromJSON(s));
|
|
2096
|
+
}
|
|
2097
|
+
};
|
|
2098
|
+
|
|
2099
|
+
// src/transcripts.ts
|
|
2100
|
+
var KEY_PREFIX2 = "transcripts:user:";
|
|
2101
|
+
var DEFAULT_MAX_PER_USER = 200;
|
|
2102
|
+
var DEFAULT_LIST_LIMIT = 50;
|
|
2103
|
+
var DURATION_RE = /^(\d+)([smhd])$/;
|
|
2104
|
+
var TOMBSTONE_MARKER = "__chatSdkTombstone";
|
|
2105
|
+
function isTombstone(value) {
|
|
2106
|
+
return typeof value === "object" && value !== null && value[TOMBSTONE_MARKER] === true;
|
|
2107
|
+
}
|
|
2108
|
+
var MS_PER_UNIT = {
|
|
2109
|
+
s: 1e3,
|
|
2110
|
+
m: 6e4,
|
|
2111
|
+
h: 36e5,
|
|
2112
|
+
d: 864e5
|
|
2113
|
+
};
|
|
2114
|
+
var TranscriptsApiImpl = class {
|
|
2115
|
+
state;
|
|
2116
|
+
maxPerUser;
|
|
2117
|
+
retentionMs;
|
|
2118
|
+
storeFormatted;
|
|
2119
|
+
constructor(state, config) {
|
|
2120
|
+
this.state = state;
|
|
2121
|
+
this.maxPerUser = config.maxPerUser ?? DEFAULT_MAX_PER_USER;
|
|
2122
|
+
this.retentionMs = parseDuration(config.retention);
|
|
2123
|
+
this.storeFormatted = config.storeFormatted ?? false;
|
|
2124
|
+
}
|
|
2125
|
+
async append(thread, message, options) {
|
|
2126
|
+
const isMessage = message instanceof Message;
|
|
2127
|
+
let userKey;
|
|
2128
|
+
let role;
|
|
2129
|
+
let platformMessageId;
|
|
2130
|
+
if (isMessage) {
|
|
2131
|
+
userKey = message.userKey;
|
|
2132
|
+
role = "user";
|
|
2133
|
+
platformMessageId = message.id;
|
|
2134
|
+
if (!userKey) {
|
|
2135
|
+
return null;
|
|
2136
|
+
}
|
|
2137
|
+
} else {
|
|
2138
|
+
userKey = options?.userKey;
|
|
2139
|
+
role = message.role;
|
|
2140
|
+
platformMessageId = message.platformMessageId;
|
|
2141
|
+
if (!userKey) {
|
|
2142
|
+
throw new Error(
|
|
2143
|
+
"transcripts.append: options.userKey is required when appending an AppendInput"
|
|
2144
|
+
);
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
const entry = {
|
|
2148
|
+
id: crypto.randomUUID(),
|
|
2149
|
+
userKey,
|
|
2150
|
+
role,
|
|
2151
|
+
text: message.text,
|
|
2152
|
+
platform: thread.adapter.name,
|
|
2153
|
+
threadId: thread.id,
|
|
2154
|
+
timestamp: Date.now()
|
|
2155
|
+
};
|
|
2156
|
+
if (this.storeFormatted && message.formatted) {
|
|
2157
|
+
entry.formatted = message.formatted;
|
|
2158
|
+
}
|
|
2159
|
+
if (platformMessageId !== void 0) {
|
|
2160
|
+
entry.platformMessageId = platformMessageId;
|
|
2161
|
+
}
|
|
2162
|
+
await this.state.appendToList(keyFor(userKey), entry, {
|
|
2163
|
+
maxLength: this.maxPerUser,
|
|
2164
|
+
ttlMs: this.retentionMs
|
|
2165
|
+
});
|
|
2166
|
+
return entry;
|
|
2167
|
+
}
|
|
2168
|
+
async list(query) {
|
|
2169
|
+
const raw = await this.state.getList(
|
|
2170
|
+
keyFor(query.userKey)
|
|
2171
|
+
);
|
|
2172
|
+
let filtered = raw.filter(
|
|
2173
|
+
(entry) => !isTombstone(entry)
|
|
2174
|
+
);
|
|
2175
|
+
if (query.platforms && query.platforms.length > 0) {
|
|
2176
|
+
const platforms = new Set(query.platforms);
|
|
2177
|
+
filtered = filtered.filter((m) => platforms.has(m.platform));
|
|
2178
|
+
}
|
|
2179
|
+
if (query.threadId !== void 0) {
|
|
2180
|
+
const tid = query.threadId;
|
|
2181
|
+
filtered = filtered.filter((m) => m.threadId === tid);
|
|
2182
|
+
}
|
|
2183
|
+
if (query.roles && query.roles.length > 0) {
|
|
2184
|
+
const roles = new Set(query.roles);
|
|
2185
|
+
filtered = filtered.filter((m) => roles.has(m.role));
|
|
2186
|
+
}
|
|
2187
|
+
const limit = query.limit ?? DEFAULT_LIST_LIMIT;
|
|
2188
|
+
if (filtered.length > limit) {
|
|
2189
|
+
filtered = filtered.slice(filtered.length - limit);
|
|
2190
|
+
}
|
|
2191
|
+
return filtered;
|
|
2192
|
+
}
|
|
2193
|
+
async count(query) {
|
|
2194
|
+
const raw = await this.state.getList(keyFor(query.userKey));
|
|
2195
|
+
return raw.filter((entry) => !isTombstone(entry)).length;
|
|
2196
|
+
}
|
|
2197
|
+
async delete(target) {
|
|
2198
|
+
const key = keyFor(target.userKey);
|
|
2199
|
+
const existing = await this.state.getList(key);
|
|
2200
|
+
const previous = existing.filter((entry) => !isTombstone(entry)).length;
|
|
2201
|
+
const tombstone = { [TOMBSTONE_MARKER]: true };
|
|
2202
|
+
await this.state.appendToList(key, tombstone, {
|
|
2203
|
+
maxLength: 1,
|
|
2204
|
+
ttlMs: this.retentionMs
|
|
2205
|
+
});
|
|
2206
|
+
return { deleted: previous };
|
|
2207
|
+
}
|
|
2208
|
+
};
|
|
2209
|
+
function keyFor(userKey) {
|
|
2210
|
+
return `${KEY_PREFIX2}${userKey}`;
|
|
2211
|
+
}
|
|
2212
|
+
function parseDuration(value) {
|
|
2213
|
+
if (value === void 0) {
|
|
2214
|
+
return void 0;
|
|
2215
|
+
}
|
|
2216
|
+
if (typeof value === "number") {
|
|
2217
|
+
return value;
|
|
2218
|
+
}
|
|
2219
|
+
const match = DURATION_RE.exec(value);
|
|
2220
|
+
if (!match) {
|
|
2221
|
+
throw new Error(
|
|
2222
|
+
`Invalid duration: ${value} (expected number of ms, or "<n>[smhd]")`
|
|
2223
|
+
);
|
|
2224
|
+
}
|
|
2225
|
+
const n = Number.parseInt(match[1], 10);
|
|
2226
|
+
const unit = match[2];
|
|
2227
|
+
return n * MS_PER_UNIT[unit];
|
|
2228
|
+
}
|
|
2229
|
+
|
|
1905
2230
|
// src/chat.ts
|
|
1906
2231
|
var DEFAULT_LOCK_TTL_MS = 3e4;
|
|
1907
2232
|
function sleep(ms) {
|
|
@@ -1931,6 +2256,21 @@ var Chat = class {
|
|
|
1931
2256
|
setChatSingleton(this);
|
|
1932
2257
|
return this;
|
|
1933
2258
|
}
|
|
2259
|
+
/**
|
|
2260
|
+
* Cross-platform per-user transcript store.
|
|
2261
|
+
*
|
|
2262
|
+
* Available only when `transcripts` is configured on the Chat instance
|
|
2263
|
+
* (and an `identity` resolver is set). Throws on access otherwise so
|
|
2264
|
+
* callers fail loudly rather than silently no-op'ing.
|
|
2265
|
+
*/
|
|
2266
|
+
get transcripts() {
|
|
2267
|
+
if (!this._transcripts) {
|
|
2268
|
+
throw new Error(
|
|
2269
|
+
"chat.transcripts is not configured \u2014 pass `transcripts` and `identity` to ChatConfig to enable it"
|
|
2270
|
+
);
|
|
2271
|
+
}
|
|
2272
|
+
return this._transcripts;
|
|
2273
|
+
}
|
|
1934
2274
|
/**
|
|
1935
2275
|
* Get the registered singleton Chat instance.
|
|
1936
2276
|
* Throws if no singleton has been registered.
|
|
@@ -1952,7 +2292,9 @@ var Chat = class {
|
|
|
1952
2292
|
_fallbackStreamingPlaceholderText;
|
|
1953
2293
|
_dedupeTtlMs;
|
|
1954
2294
|
_onLockConflict;
|
|
1955
|
-
|
|
2295
|
+
_threadHistory;
|
|
2296
|
+
_identity;
|
|
2297
|
+
_transcripts;
|
|
1956
2298
|
_concurrencyStrategy;
|
|
1957
2299
|
_concurrencyConfig;
|
|
1958
2300
|
_concurrentSlots = /* @__PURE__ */ new Map();
|
|
@@ -2035,10 +2377,24 @@ var Chat = class {
|
|
|
2035
2377
|
queueEntryTtlMs: 9e4
|
|
2036
2378
|
};
|
|
2037
2379
|
}
|
|
2038
|
-
this.
|
|
2380
|
+
this._threadHistory = new ThreadHistoryCache(
|
|
2039
2381
|
this._stateAdapter,
|
|
2040
|
-
config.messageHistory
|
|
2382
|
+
config.threadHistory ?? config.messageHistory
|
|
2041
2383
|
);
|
|
2384
|
+
if (config.transcripts) {
|
|
2385
|
+
if (!config.identity) {
|
|
2386
|
+
throw new Error(
|
|
2387
|
+
"ChatConfig.transcripts requires ChatConfig.identity to be set \u2014 the cross-platform user key must be resolvable"
|
|
2388
|
+
);
|
|
2389
|
+
}
|
|
2390
|
+
this._identity = config.identity;
|
|
2391
|
+
this._transcripts = new TranscriptsApiImpl(
|
|
2392
|
+
this._stateAdapter,
|
|
2393
|
+
config.transcripts
|
|
2394
|
+
);
|
|
2395
|
+
} else {
|
|
2396
|
+
this._identity = config.identity;
|
|
2397
|
+
}
|
|
2042
2398
|
const webhooks = {};
|
|
2043
2399
|
for (const [name, adapter] of Object.entries(config.adapters)) {
|
|
2044
2400
|
this.adapters.set(name, adapter);
|
|
@@ -2357,12 +2713,14 @@ var Chat = class {
|
|
|
2357
2713
|
const task = (async () => {
|
|
2358
2714
|
const message = typeof messageOrFactory === "function" ? await messageOrFactory() : messageOrFactory;
|
|
2359
2715
|
await this.handleIncomingMessage(adapter, threadId, message);
|
|
2360
|
-
})()
|
|
2716
|
+
})();
|
|
2717
|
+
const tracked = task.catch((err) => {
|
|
2361
2718
|
this.logger.error("Message processing error", { error: err, threadId });
|
|
2362
2719
|
});
|
|
2363
2720
|
if (options?.waitUntil) {
|
|
2364
|
-
options.waitUntil(
|
|
2721
|
+
options.waitUntil(tracked);
|
|
2365
2722
|
}
|
|
2723
|
+
return task;
|
|
2366
2724
|
}
|
|
2367
2725
|
/**
|
|
2368
2726
|
* Process an incoming reaction event from an adapter.
|
|
@@ -2420,20 +2778,22 @@ var Chat = class {
|
|
|
2420
2778
|
}
|
|
2421
2779
|
}
|
|
2422
2780
|
}
|
|
2423
|
-
async processModalSubmit(event, contextId,
|
|
2424
|
-
const { relatedThread, relatedMessage, relatedChannel } = await this.retrieveModalContext(event.adapter.name, contextId);
|
|
2781
|
+
async processModalSubmit(event, contextId, options) {
|
|
2782
|
+
const { callbackUrl, relatedThread, relatedMessage, relatedChannel } = await this.retrieveModalContext(event.adapter.name, contextId);
|
|
2425
2783
|
const fullEvent = {
|
|
2426
2784
|
...event,
|
|
2427
2785
|
relatedThread,
|
|
2428
2786
|
relatedMessage,
|
|
2429
2787
|
relatedChannel
|
|
2430
2788
|
};
|
|
2789
|
+
let result;
|
|
2431
2790
|
for (const { callbackIds, handler } of this.modalSubmitHandlers) {
|
|
2432
2791
|
if (callbackIds.length === 0 || callbackIds.includes(event.callbackId)) {
|
|
2433
2792
|
try {
|
|
2434
2793
|
const response = await handler(fullEvent);
|
|
2435
2794
|
if (response) {
|
|
2436
|
-
|
|
2795
|
+
result = response;
|
|
2796
|
+
break;
|
|
2437
2797
|
}
|
|
2438
2798
|
} catch (err) {
|
|
2439
2799
|
this.logger.error("Modal submit handler error", {
|
|
@@ -2443,6 +2803,30 @@ var Chat = class {
|
|
|
2443
2803
|
}
|
|
2444
2804
|
}
|
|
2445
2805
|
}
|
|
2806
|
+
if (callbackUrl && result?.action !== "errors") {
|
|
2807
|
+
const task = postToCallbackUrl(callbackUrl, {
|
|
2808
|
+
type: "modal_submit",
|
|
2809
|
+
callbackId: event.callbackId,
|
|
2810
|
+
values: event.values,
|
|
2811
|
+
user: { id: event.user.userId, name: event.user.userName }
|
|
2812
|
+
}).then(({ error }) => {
|
|
2813
|
+
if (error) {
|
|
2814
|
+
this.logger.error("Modal callbackUrl POST failed", {
|
|
2815
|
+
callbackUrl,
|
|
2816
|
+
error
|
|
2817
|
+
});
|
|
2818
|
+
}
|
|
2819
|
+
}).catch((error) => {
|
|
2820
|
+
this.logger.error("Modal callbackUrl POST failed", {
|
|
2821
|
+
callbackUrl,
|
|
2822
|
+
error
|
|
2823
|
+
});
|
|
2824
|
+
});
|
|
2825
|
+
if (options?.waitUntil) {
|
|
2826
|
+
options.waitUntil(task);
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
2829
|
+
return result;
|
|
2446
2830
|
}
|
|
2447
2831
|
processModalClose(event, contextId, options) {
|
|
2448
2832
|
const task = (async () => {
|
|
@@ -2594,7 +2978,8 @@ var Chat = class {
|
|
|
2594
2978
|
contextId,
|
|
2595
2979
|
void 0,
|
|
2596
2980
|
void 0,
|
|
2597
|
-
channel
|
|
2981
|
+
channel,
|
|
2982
|
+
modalElement.callbackUrl
|
|
2598
2983
|
);
|
|
2599
2984
|
if (options?.onOpenModal) {
|
|
2600
2985
|
return options.onOpenModal(modalElement, contextId);
|
|
@@ -2631,12 +3016,13 @@ var Chat = class {
|
|
|
2631
3016
|
* Store modal context server-side with a context ID.
|
|
2632
3017
|
* Called when opening a modal to preserve thread/message/channel for the submit handler.
|
|
2633
3018
|
*/
|
|
2634
|
-
async storeModalContext(adapterName, contextId, thread, message, channel) {
|
|
3019
|
+
async storeModalContext(adapterName, contextId, thread, message, channel, callbackUrl) {
|
|
2635
3020
|
const key = `modal-context:${adapterName}:${contextId}`;
|
|
2636
3021
|
const context = {
|
|
2637
3022
|
thread: thread?.toJSON(),
|
|
2638
3023
|
message: message?.toJSON(),
|
|
2639
|
-
channel: channel?.toJSON()
|
|
3024
|
+
channel: channel?.toJSON(),
|
|
3025
|
+
callbackUrl
|
|
2640
3026
|
};
|
|
2641
3027
|
try {
|
|
2642
3028
|
await this._stateAdapter.set(key, context, MODAL_CONTEXT_TTL_MS);
|
|
@@ -2654,6 +3040,7 @@ var Chat = class {
|
|
|
2654
3040
|
async retrieveModalContext(adapterName, contextId) {
|
|
2655
3041
|
if (!contextId) {
|
|
2656
3042
|
return {
|
|
3043
|
+
callbackUrl: void 0,
|
|
2657
3044
|
relatedThread: void 0,
|
|
2658
3045
|
relatedMessage: void 0,
|
|
2659
3046
|
relatedChannel: void 0
|
|
@@ -2663,6 +3050,7 @@ var Chat = class {
|
|
|
2663
3050
|
const stored = await this._stateAdapter.get(key);
|
|
2664
3051
|
if (!stored) {
|
|
2665
3052
|
return {
|
|
3053
|
+
callbackUrl: void 0,
|
|
2666
3054
|
relatedThread: void 0,
|
|
2667
3055
|
relatedMessage: void 0,
|
|
2668
3056
|
relatedChannel: void 0
|
|
@@ -2683,7 +3071,12 @@ var Chat = class {
|
|
|
2683
3071
|
if (stored.channel) {
|
|
2684
3072
|
relatedChannel = ChannelImpl.fromJSON(stored.channel, adapter);
|
|
2685
3073
|
}
|
|
2686
|
-
return {
|
|
3074
|
+
return {
|
|
3075
|
+
callbackUrl: stored.callbackUrl,
|
|
3076
|
+
relatedThread,
|
|
3077
|
+
relatedMessage,
|
|
3078
|
+
relatedChannel
|
|
3079
|
+
};
|
|
2687
3080
|
}
|
|
2688
3081
|
/**
|
|
2689
3082
|
* Handle an action event internally.
|
|
@@ -2703,6 +3096,33 @@ var Chat = class {
|
|
|
2703
3096
|
});
|
|
2704
3097
|
return;
|
|
2705
3098
|
}
|
|
3099
|
+
const { callbackToken } = decodeCallbackValue(event.value);
|
|
3100
|
+
let resolved = null;
|
|
3101
|
+
if (callbackToken) {
|
|
3102
|
+
resolved = await resolveCallbackUrl(callbackToken, this._stateAdapter);
|
|
3103
|
+
}
|
|
3104
|
+
const actionEvent = resolved ? { ...event, value: resolved.originalValue } : event;
|
|
3105
|
+
let callbackUrlPromise;
|
|
3106
|
+
if (resolved) {
|
|
3107
|
+
const callbackUrl = resolved.url;
|
|
3108
|
+
callbackUrlPromise = (async () => {
|
|
3109
|
+
const { error } = await postToCallbackUrl(callbackUrl, {
|
|
3110
|
+
type: "action",
|
|
3111
|
+
actionId: event.actionId,
|
|
3112
|
+
value: resolved.originalValue,
|
|
3113
|
+
user: { id: event.user.userId, name: event.user.userName },
|
|
3114
|
+
threadId: event.threadId,
|
|
3115
|
+
messageId: event.messageId
|
|
3116
|
+
});
|
|
3117
|
+
if (error) {
|
|
3118
|
+
this.logger.error("Button callbackUrl POST failed", {
|
|
3119
|
+
callbackUrl,
|
|
3120
|
+
actionId: event.actionId,
|
|
3121
|
+
error
|
|
3122
|
+
});
|
|
3123
|
+
}
|
|
3124
|
+
})();
|
|
3125
|
+
}
|
|
2706
3126
|
const isSubscribed = false;
|
|
2707
3127
|
const messageForThread = event.messageId ? new Message({
|
|
2708
3128
|
id: event.messageId,
|
|
@@ -2721,7 +3141,7 @@ var Chat = class {
|
|
|
2721
3141
|
isSubscribed
|
|
2722
3142
|
) : null;
|
|
2723
3143
|
const fullEvent = {
|
|
2724
|
-
...
|
|
3144
|
+
...actionEvent,
|
|
2725
3145
|
thread,
|
|
2726
3146
|
openModal: async (modal) => {
|
|
2727
3147
|
if (!(event.triggerId || options?.onOpenModal)) {
|
|
@@ -2769,7 +3189,8 @@ var Chat = class {
|
|
|
2769
3189
|
contextId,
|
|
2770
3190
|
thread ? thread : void 0,
|
|
2771
3191
|
message,
|
|
2772
|
-
channel
|
|
3192
|
+
channel,
|
|
3193
|
+
modalElement.callbackUrl
|
|
2773
3194
|
);
|
|
2774
3195
|
if (options?.onOpenModal) {
|
|
2775
3196
|
return options.onOpenModal(modalElement, contextId);
|
|
@@ -2801,6 +3222,9 @@ var Chat = class {
|
|
|
2801
3222
|
await handler(fullEvent);
|
|
2802
3223
|
}
|
|
2803
3224
|
}
|
|
3225
|
+
if (callbackUrlPromise) {
|
|
3226
|
+
await callbackUrlPromise;
|
|
3227
|
+
}
|
|
2804
3228
|
}
|
|
2805
3229
|
/**
|
|
2806
3230
|
* Handle a reaction event internally.
|
|
@@ -3113,6 +3537,7 @@ var Chat = class {
|
|
|
3113
3537
|
* - Concurrency: Controlled by `concurrency` config (drop, queue, debounce, concurrent)
|
|
3114
3538
|
*/
|
|
3115
3539
|
async handleIncomingMessage(adapter, threadId, message) {
|
|
3540
|
+
setMessageAdapter(message, adapter);
|
|
3116
3541
|
this.logger.debug("Incoming message", {
|
|
3117
3542
|
adapter: adapter.name,
|
|
3118
3543
|
threadId,
|
|
@@ -3144,11 +3569,11 @@ var Chat = class {
|
|
|
3144
3569
|
});
|
|
3145
3570
|
return;
|
|
3146
3571
|
}
|
|
3147
|
-
if (adapter.persistMessageHistory) {
|
|
3572
|
+
if (adapter.persistThreadHistory || adapter.persistMessageHistory) {
|
|
3148
3573
|
const channelId = adapter.channelIdFromThreadId(threadId);
|
|
3149
|
-
const appends = [this.
|
|
3574
|
+
const appends = [this._threadHistory.append(threadId, message)];
|
|
3150
3575
|
if (channelId !== threadId) {
|
|
3151
|
-
appends.push(this.
|
|
3576
|
+
appends.push(this._threadHistory.append(channelId, message));
|
|
3152
3577
|
}
|
|
3153
3578
|
await Promise.all(appends);
|
|
3154
3579
|
}
|
|
@@ -3436,6 +3861,25 @@ var Chat = class {
|
|
|
3436
3861
|
message,
|
|
3437
3862
|
isSubscribed
|
|
3438
3863
|
);
|
|
3864
|
+
if (this._identity && message.userKey === void 0) {
|
|
3865
|
+
try {
|
|
3866
|
+
const resolved = await this._identity({
|
|
3867
|
+
adapter: adapter.name,
|
|
3868
|
+
author: message.author,
|
|
3869
|
+
message
|
|
3870
|
+
});
|
|
3871
|
+
if (resolved) {
|
|
3872
|
+
message.userKey = resolved;
|
|
3873
|
+
}
|
|
3874
|
+
} catch (err) {
|
|
3875
|
+
this.logger.warn("Identity resolver threw; skipping userKey", {
|
|
3876
|
+
error: err,
|
|
3877
|
+
adapter: adapter.name,
|
|
3878
|
+
threadId,
|
|
3879
|
+
authorUserId: message.author.userId
|
|
3880
|
+
});
|
|
3881
|
+
}
|
|
3882
|
+
}
|
|
3439
3883
|
const isDM = adapter.isDM?.(threadId) ?? false;
|
|
3440
3884
|
if (isDM && this.directMessageHandlers.length > 0) {
|
|
3441
3885
|
this.logger.debug("Direct message received - calling handlers", {
|
|
@@ -3517,7 +3961,7 @@ var Chat = class {
|
|
|
3517
3961
|
logger: this.logger,
|
|
3518
3962
|
streamingUpdateIntervalMs: this._streamingUpdateIntervalMs,
|
|
3519
3963
|
fallbackStreamingPlaceholderText: this._fallbackStreamingPlaceholderText,
|
|
3520
|
-
|
|
3964
|
+
threadHistory: adapter.persistThreadHistory || adapter.persistMessageHistory ? this._threadHistory : void 0
|
|
3521
3965
|
});
|
|
3522
3966
|
}
|
|
3523
3967
|
/**
|
|
@@ -3563,6 +4007,9 @@ var Chat = class {
|
|
|
3563
4007
|
*/
|
|
3564
4008
|
rehydrateMessage(raw, adapter) {
|
|
3565
4009
|
if (raw instanceof Message) {
|
|
4010
|
+
if (adapter) {
|
|
4011
|
+
setMessageAdapter(raw, adapter);
|
|
4012
|
+
}
|
|
3566
4013
|
return raw;
|
|
3567
4014
|
}
|
|
3568
4015
|
const obj = raw;
|
|
@@ -3592,6 +4039,9 @@ var Chat = class {
|
|
|
3592
4039
|
links: obj.links ?? []
|
|
3593
4040
|
});
|
|
3594
4041
|
}
|
|
4042
|
+
if (adapter) {
|
|
4043
|
+
setMessageAdapter(msg, adapter);
|
|
4044
|
+
}
|
|
3595
4045
|
const rehydrate = adapter?.rehydrateAttachment?.bind(adapter);
|
|
3596
4046
|
if (rehydrate && msg.attachments.length > 0) {
|
|
3597
4047
|
msg.attachments = msg.attachments.map(
|
|
@@ -3607,6 +4057,9 @@ var Chat = class {
|
|
|
3607
4057
|
}
|
|
3608
4058
|
};
|
|
3609
4059
|
|
|
4060
|
+
// src/message-history.ts
|
|
4061
|
+
var MessageHistoryCache = ThreadHistoryCache;
|
|
4062
|
+
|
|
3610
4063
|
// src/plan.ts
|
|
3611
4064
|
function contentToPlainText(content) {
|
|
3612
4065
|
if (!content) {
|
|
@@ -4107,6 +4560,8 @@ function convertEmojiPlaceholders(text2, platform, resolver = defaultEmojiResolv
|
|
|
4107
4560
|
return resolver.toGChat(emojiName);
|
|
4108
4561
|
case "discord":
|
|
4109
4562
|
return resolver.toDiscord(emojiName);
|
|
4563
|
+
case "messenger":
|
|
4564
|
+
return resolver.toGChat(emojiName);
|
|
4110
4565
|
case "github":
|
|
4111
4566
|
return resolver.toGChat(emojiName);
|
|
4112
4567
|
case "linear":
|
|
@@ -4308,6 +4763,7 @@ export {
|
|
|
4308
4763
|
THREAD_STATE_TTL_MS,
|
|
4309
4764
|
Table2 as Table,
|
|
4310
4765
|
TextInput2 as TextInput,
|
|
4766
|
+
ThreadHistoryCache,
|
|
4311
4767
|
ThreadImpl,
|
|
4312
4768
|
blockquote,
|
|
4313
4769
|
cardChildToFallbackText2 as cardChildToFallbackText,
|