chat 4.4.0 → 4.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +348 -47
- package/dist/index.js +354 -5
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -22,6 +22,153 @@ import {
|
|
|
22
22
|
toModalElement
|
|
23
23
|
} from "./chunk-VJ2RPXCK.js";
|
|
24
24
|
|
|
25
|
+
// src/chat-singleton.ts
|
|
26
|
+
var _singleton = null;
|
|
27
|
+
function setChatSingleton(chat) {
|
|
28
|
+
_singleton = chat;
|
|
29
|
+
}
|
|
30
|
+
function getChatSingleton() {
|
|
31
|
+
if (!_singleton) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
"No Chat singleton registered. Call chat.registerSingleton() first."
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
return _singleton;
|
|
37
|
+
}
|
|
38
|
+
function hasChatSingleton() {
|
|
39
|
+
return _singleton !== null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// src/message.ts
|
|
43
|
+
import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from "@workflow/serde";
|
|
44
|
+
var Message = class _Message {
|
|
45
|
+
/** Unique message ID */
|
|
46
|
+
id;
|
|
47
|
+
/** Thread this message belongs to */
|
|
48
|
+
threadId;
|
|
49
|
+
/** Plain text content (all formatting stripped) */
|
|
50
|
+
text;
|
|
51
|
+
/**
|
|
52
|
+
* Structured formatting as an AST (mdast Root).
|
|
53
|
+
* This is the canonical representation - use this for processing.
|
|
54
|
+
* Use `stringifyMarkdown(message.formatted)` to get markdown string.
|
|
55
|
+
*/
|
|
56
|
+
formatted;
|
|
57
|
+
/** Platform-specific raw payload (escape hatch) */
|
|
58
|
+
raw;
|
|
59
|
+
/** Message author */
|
|
60
|
+
author;
|
|
61
|
+
/** Message metadata */
|
|
62
|
+
metadata;
|
|
63
|
+
/** Attachments */
|
|
64
|
+
attachments;
|
|
65
|
+
/**
|
|
66
|
+
* Whether the bot is @-mentioned in this message.
|
|
67
|
+
*
|
|
68
|
+
* This is set by the Chat SDK before passing the message to handlers.
|
|
69
|
+
* It checks for `@username` in the message text using the adapter's
|
|
70
|
+
* configured `userName` and optional `botUserId`.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* chat.onSubscribedMessage(async (thread, message) => {
|
|
75
|
+
* if (message.isMention) {
|
|
76
|
+
* await thread.post("You mentioned me!");
|
|
77
|
+
* }
|
|
78
|
+
* });
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
isMention;
|
|
82
|
+
constructor(data) {
|
|
83
|
+
this.id = data.id;
|
|
84
|
+
this.threadId = data.threadId;
|
|
85
|
+
this.text = data.text;
|
|
86
|
+
this.formatted = data.formatted;
|
|
87
|
+
this.raw = data.raw;
|
|
88
|
+
this.author = data.author;
|
|
89
|
+
this.metadata = data.metadata;
|
|
90
|
+
this.attachments = data.attachments;
|
|
91
|
+
this.isMention = data.isMention;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Serialize the message to a plain JSON object.
|
|
95
|
+
* Use this to pass message data to external systems like workflow engines.
|
|
96
|
+
*
|
|
97
|
+
* Note: Attachment `data` (Buffer) and `fetchData` (function) are omitted
|
|
98
|
+
* as they're not serializable.
|
|
99
|
+
*/
|
|
100
|
+
toJSON() {
|
|
101
|
+
return {
|
|
102
|
+
_type: "chat:Message",
|
|
103
|
+
id: this.id,
|
|
104
|
+
threadId: this.threadId,
|
|
105
|
+
text: this.text,
|
|
106
|
+
formatted: this.formatted,
|
|
107
|
+
raw: this.raw,
|
|
108
|
+
author: {
|
|
109
|
+
userId: this.author.userId,
|
|
110
|
+
userName: this.author.userName,
|
|
111
|
+
fullName: this.author.fullName,
|
|
112
|
+
isBot: this.author.isBot,
|
|
113
|
+
isMe: this.author.isMe
|
|
114
|
+
},
|
|
115
|
+
metadata: {
|
|
116
|
+
dateSent: this.metadata.dateSent.toISOString(),
|
|
117
|
+
edited: this.metadata.edited,
|
|
118
|
+
editedAt: this.metadata.editedAt?.toISOString()
|
|
119
|
+
},
|
|
120
|
+
attachments: this.attachments.map((att) => ({
|
|
121
|
+
type: att.type,
|
|
122
|
+
url: att.url,
|
|
123
|
+
name: att.name,
|
|
124
|
+
mimeType: att.mimeType,
|
|
125
|
+
size: att.size,
|
|
126
|
+
width: att.width,
|
|
127
|
+
height: att.height
|
|
128
|
+
})),
|
|
129
|
+
isMention: this.isMention
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Reconstruct a Message from serialized JSON data.
|
|
134
|
+
* Converts ISO date strings back to Date objects.
|
|
135
|
+
*/
|
|
136
|
+
static fromJSON(json) {
|
|
137
|
+
return new _Message({
|
|
138
|
+
id: json.id,
|
|
139
|
+
threadId: json.threadId,
|
|
140
|
+
text: json.text,
|
|
141
|
+
formatted: json.formatted,
|
|
142
|
+
raw: json.raw,
|
|
143
|
+
author: json.author,
|
|
144
|
+
metadata: {
|
|
145
|
+
dateSent: new Date(json.metadata.dateSent),
|
|
146
|
+
edited: json.metadata.edited,
|
|
147
|
+
editedAt: json.metadata.editedAt ? new Date(json.metadata.editedAt) : void 0
|
|
148
|
+
},
|
|
149
|
+
attachments: json.attachments,
|
|
150
|
+
isMention: json.isMention
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Serialize a Message instance for @workflow/serde.
|
|
155
|
+
* This static method is automatically called by workflow serialization.
|
|
156
|
+
*/
|
|
157
|
+
static [WORKFLOW_SERIALIZE](instance) {
|
|
158
|
+
return instance.toJSON();
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Deserialize a Message from @workflow/serde.
|
|
162
|
+
* This static method is automatically called by workflow deserialization.
|
|
163
|
+
*/
|
|
164
|
+
static [WORKFLOW_DESERIALIZE](data) {
|
|
165
|
+
return _Message.fromJSON(data);
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
// src/thread.ts
|
|
170
|
+
import { WORKFLOW_DESERIALIZE as WORKFLOW_DESERIALIZE2, WORKFLOW_SERIALIZE as WORKFLOW_SERIALIZE2 } from "@workflow/serde";
|
|
171
|
+
|
|
25
172
|
// src/markdown.ts
|
|
26
173
|
import { toString } from "mdast-util-to-string";
|
|
27
174
|
import remarkGfm from "remark-gfm";
|
|
@@ -301,16 +448,23 @@ var ConsoleLogger = class _ConsoleLogger {
|
|
|
301
448
|
var THREAD_STATE_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
302
449
|
|
|
303
450
|
// src/thread.ts
|
|
451
|
+
function isLazyConfig(config) {
|
|
452
|
+
return "adapterName" in config && !("adapter" in config);
|
|
453
|
+
}
|
|
304
454
|
var THREAD_STATE_KEY_PREFIX = "thread-state:";
|
|
305
455
|
function isAsyncIterable(value) {
|
|
306
456
|
return value !== null && typeof value === "object" && Symbol.asyncIterator in value;
|
|
307
457
|
}
|
|
308
|
-
var ThreadImpl = class {
|
|
458
|
+
var ThreadImpl = class _ThreadImpl {
|
|
309
459
|
id;
|
|
310
|
-
adapter;
|
|
311
460
|
channelId;
|
|
312
461
|
isDM;
|
|
313
|
-
|
|
462
|
+
/** Direct adapter instance (if provided) */
|
|
463
|
+
_adapter;
|
|
464
|
+
/** Adapter name for lazy resolution */
|
|
465
|
+
_adapterName;
|
|
466
|
+
/** Direct state adapter instance (if provided) */
|
|
467
|
+
_stateAdapterInstance;
|
|
314
468
|
_recentMessages = [];
|
|
315
469
|
_isSubscribedContext;
|
|
316
470
|
/** Current message context for streaming - provides userId/teamId */
|
|
@@ -319,17 +473,54 @@ var ThreadImpl = class {
|
|
|
319
473
|
_streamingUpdateIntervalMs;
|
|
320
474
|
constructor(config) {
|
|
321
475
|
this.id = config.id;
|
|
322
|
-
this.adapter = config.adapter;
|
|
323
476
|
this.channelId = config.channelId;
|
|
324
477
|
this.isDM = config.isDM ?? false;
|
|
325
|
-
this._stateAdapter = config.stateAdapter;
|
|
326
478
|
this._isSubscribedContext = config.isSubscribedContext ?? false;
|
|
327
479
|
this._currentMessage = config.currentMessage;
|
|
328
480
|
this._streamingUpdateIntervalMs = config.streamingUpdateIntervalMs ?? 500;
|
|
481
|
+
if (isLazyConfig(config)) {
|
|
482
|
+
this._adapterName = config.adapterName;
|
|
483
|
+
} else {
|
|
484
|
+
this._adapter = config.adapter;
|
|
485
|
+
this._stateAdapterInstance = config.stateAdapter;
|
|
486
|
+
}
|
|
329
487
|
if (config.initialMessage) {
|
|
330
488
|
this._recentMessages = [config.initialMessage];
|
|
331
489
|
}
|
|
332
490
|
}
|
|
491
|
+
/**
|
|
492
|
+
* Get the adapter for this thread.
|
|
493
|
+
* If created with lazy config, resolves from Chat singleton on first access.
|
|
494
|
+
*/
|
|
495
|
+
get adapter() {
|
|
496
|
+
if (this._adapter) {
|
|
497
|
+
return this._adapter;
|
|
498
|
+
}
|
|
499
|
+
if (!this._adapterName) {
|
|
500
|
+
throw new Error("Thread has no adapter configured");
|
|
501
|
+
}
|
|
502
|
+
const chat = getChatSingleton();
|
|
503
|
+
const adapter = chat.getAdapter(this._adapterName);
|
|
504
|
+
if (!adapter) {
|
|
505
|
+
throw new Error(
|
|
506
|
+
`Adapter "${this._adapterName}" not found in Chat singleton`
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
this._adapter = adapter;
|
|
510
|
+
return adapter;
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Get the state adapter for this thread.
|
|
514
|
+
* If created with lazy config, resolves from Chat singleton on first access.
|
|
515
|
+
*/
|
|
516
|
+
get _stateAdapter() {
|
|
517
|
+
if (this._stateAdapterInstance) {
|
|
518
|
+
return this._stateAdapterInstance;
|
|
519
|
+
}
|
|
520
|
+
const chat = getChatSingleton();
|
|
521
|
+
this._stateAdapterInstance = chat.getState();
|
|
522
|
+
return this._stateAdapterInstance;
|
|
523
|
+
}
|
|
333
524
|
get recentMessages() {
|
|
334
525
|
return this._recentMessages;
|
|
335
526
|
}
|
|
@@ -412,6 +603,37 @@ var ThreadImpl = class {
|
|
|
412
603
|
const rawMessage = await this.adapter.postMessage(this.id, postable);
|
|
413
604
|
return this.createSentMessage(rawMessage.id, postable, rawMessage.threadId);
|
|
414
605
|
}
|
|
606
|
+
async postEphemeral(user, message, options) {
|
|
607
|
+
const { fallbackToDM } = options;
|
|
608
|
+
const userId = typeof user === "string" ? user : user.userId;
|
|
609
|
+
let postable;
|
|
610
|
+
if (isJSX(message)) {
|
|
611
|
+
const card = toCardElement(message);
|
|
612
|
+
if (!card) {
|
|
613
|
+
throw new Error("Invalid JSX element: must be a Card element");
|
|
614
|
+
}
|
|
615
|
+
postable = card;
|
|
616
|
+
} else {
|
|
617
|
+
postable = message;
|
|
618
|
+
}
|
|
619
|
+
if (this.adapter.postEphemeral) {
|
|
620
|
+
return this.adapter.postEphemeral(this.id, userId, postable);
|
|
621
|
+
}
|
|
622
|
+
if (!fallbackToDM) {
|
|
623
|
+
return null;
|
|
624
|
+
}
|
|
625
|
+
if (this.adapter.openDM) {
|
|
626
|
+
const dmThreadId = await this.adapter.openDM(userId);
|
|
627
|
+
const result = await this.adapter.postMessage(dmThreadId, postable);
|
|
628
|
+
return {
|
|
629
|
+
id: result.id,
|
|
630
|
+
threadId: dmThreadId,
|
|
631
|
+
usedFallback: true,
|
|
632
|
+
raw: result.raw
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
return null;
|
|
636
|
+
}
|
|
415
637
|
/**
|
|
416
638
|
* Handle streaming from an AsyncIterable.
|
|
417
639
|
* Uses adapter's native streaming if available, otherwise falls back to post+edit.
|
|
@@ -507,6 +729,65 @@ var ThreadImpl = class {
|
|
|
507
729
|
mentionUser(userId) {
|
|
508
730
|
return `<@${userId}>`;
|
|
509
731
|
}
|
|
732
|
+
/**
|
|
733
|
+
* Serialize the thread to a plain JSON object.
|
|
734
|
+
* Use this to pass thread data to external systems like workflow engines.
|
|
735
|
+
*
|
|
736
|
+
* @example
|
|
737
|
+
* ```typescript
|
|
738
|
+
* // Pass to a workflow
|
|
739
|
+
* await workflow.start("my-workflow", {
|
|
740
|
+
* thread: thread.toJSON(),
|
|
741
|
+
* message: serializeMessage(message),
|
|
742
|
+
* });
|
|
743
|
+
* ```
|
|
744
|
+
*/
|
|
745
|
+
toJSON() {
|
|
746
|
+
return {
|
|
747
|
+
_type: "chat:Thread",
|
|
748
|
+
id: this.id,
|
|
749
|
+
channelId: this.channelId,
|
|
750
|
+
isDM: this.isDM,
|
|
751
|
+
adapterName: this.adapter.name
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* Reconstruct a Thread from serialized JSON data.
|
|
756
|
+
*
|
|
757
|
+
* Reconstructs a ThreadImpl from serialized data.
|
|
758
|
+
* Uses lazy resolution from Chat.getSingleton() for adapter and state.
|
|
759
|
+
*
|
|
760
|
+
* @param json - Serialized thread data
|
|
761
|
+
* @requires Call `chat.registerSingleton()` before deserializing threads
|
|
762
|
+
*
|
|
763
|
+
* @example
|
|
764
|
+
* ```typescript
|
|
765
|
+
* const thread = ThreadImpl.fromJSON(serializedThread);
|
|
766
|
+
* ```
|
|
767
|
+
*/
|
|
768
|
+
static fromJSON(json) {
|
|
769
|
+
return new _ThreadImpl({
|
|
770
|
+
id: json.id,
|
|
771
|
+
adapterName: json.adapterName,
|
|
772
|
+
channelId: json.channelId,
|
|
773
|
+
isDM: json.isDM
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
/**
|
|
777
|
+
* Serialize a ThreadImpl instance for @workflow/serde.
|
|
778
|
+
* This static method is automatically called by workflow serialization.
|
|
779
|
+
*/
|
|
780
|
+
static [WORKFLOW_SERIALIZE2](instance) {
|
|
781
|
+
return instance.toJSON();
|
|
782
|
+
}
|
|
783
|
+
/**
|
|
784
|
+
* Deserialize a ThreadImpl from @workflow/serde.
|
|
785
|
+
* Uses lazy adapter resolution from Chat.getSingleton().
|
|
786
|
+
* Requires chat.registerSingleton() to have been called.
|
|
787
|
+
*/
|
|
788
|
+
static [WORKFLOW_DESERIALIZE2](data) {
|
|
789
|
+
return _ThreadImpl.fromJSON(data);
|
|
790
|
+
}
|
|
510
791
|
createSentMessage(messageId, postable, threadIdOverride) {
|
|
511
792
|
const adapter = this.adapter;
|
|
512
793
|
const threadId = threadIdOverride || this.id;
|
|
@@ -531,6 +812,9 @@ var ThreadImpl = class {
|
|
|
531
812
|
edited: false
|
|
532
813
|
},
|
|
533
814
|
attachments,
|
|
815
|
+
toJSON() {
|
|
816
|
+
return new Message(this).toJSON();
|
|
817
|
+
},
|
|
534
818
|
async edit(newContent) {
|
|
535
819
|
let postable2 = newContent;
|
|
536
820
|
if (isJSX(newContent)) {
|
|
@@ -609,6 +893,36 @@ function extractMessageContent(message) {
|
|
|
609
893
|
var DEFAULT_LOCK_TTL_MS = 3e4;
|
|
610
894
|
var DEDUPE_TTL_MS = 6e4;
|
|
611
895
|
var Chat = class {
|
|
896
|
+
/**
|
|
897
|
+
* Register this Chat instance as the global singleton.
|
|
898
|
+
* Required for Thread deserialization via @workflow/serde.
|
|
899
|
+
*
|
|
900
|
+
* @example
|
|
901
|
+
* ```typescript
|
|
902
|
+
* const chat = new Chat({ ... });
|
|
903
|
+
* chat.registerSingleton();
|
|
904
|
+
*
|
|
905
|
+
* // Now threads can be deserialized without passing chat explicitly
|
|
906
|
+
* const thread = ThreadImpl.fromJSON(serializedThread);
|
|
907
|
+
* ```
|
|
908
|
+
*/
|
|
909
|
+
registerSingleton() {
|
|
910
|
+
setChatSingleton(this);
|
|
911
|
+
return this;
|
|
912
|
+
}
|
|
913
|
+
/**
|
|
914
|
+
* Get the registered singleton Chat instance.
|
|
915
|
+
* Throws if no singleton has been registered.
|
|
916
|
+
*/
|
|
917
|
+
static getSingleton() {
|
|
918
|
+
return getChatSingleton();
|
|
919
|
+
}
|
|
920
|
+
/**
|
|
921
|
+
* Check if a singleton has been registered.
|
|
922
|
+
*/
|
|
923
|
+
static hasSingleton() {
|
|
924
|
+
return hasChatSingleton();
|
|
925
|
+
}
|
|
612
926
|
adapters;
|
|
613
927
|
_stateAdapter;
|
|
614
928
|
userName;
|
|
@@ -841,6 +1155,40 @@ var Chat = class {
|
|
|
841
1155
|
getAdapter(name) {
|
|
842
1156
|
return this.adapters.get(name);
|
|
843
1157
|
}
|
|
1158
|
+
/**
|
|
1159
|
+
* Get a JSON.parse reviver function that automatically deserializes
|
|
1160
|
+
* chat:Thread and chat:Message objects.
|
|
1161
|
+
*
|
|
1162
|
+
* Use this when parsing JSON that contains serialized Thread or Message objects
|
|
1163
|
+
* (e.g., from workflow engine payloads).
|
|
1164
|
+
*
|
|
1165
|
+
* @returns A reviver function for JSON.parse
|
|
1166
|
+
*
|
|
1167
|
+
* @example
|
|
1168
|
+
* ```typescript
|
|
1169
|
+
* // Parse workflow payload with automatic deserialization
|
|
1170
|
+
* const data = JSON.parse(payload, chat.reviver());
|
|
1171
|
+
*
|
|
1172
|
+
* // data.thread is now a ThreadImpl instance
|
|
1173
|
+
* // data.message is now a Message object with Date fields restored
|
|
1174
|
+
* await data.thread.post("Hello from workflow!");
|
|
1175
|
+
* ```
|
|
1176
|
+
*/
|
|
1177
|
+
reviver() {
|
|
1178
|
+
this.registerSingleton();
|
|
1179
|
+
return function reviver(_key, value) {
|
|
1180
|
+
if (value && typeof value === "object" && "_type" in value) {
|
|
1181
|
+
const typed = value;
|
|
1182
|
+
if (typed._type === "chat:Thread") {
|
|
1183
|
+
return ThreadImpl.fromJSON(value);
|
|
1184
|
+
}
|
|
1185
|
+
if (typed._type === "chat:Message") {
|
|
1186
|
+
return Message.fromJSON(value);
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
return value;
|
|
1190
|
+
};
|
|
1191
|
+
}
|
|
844
1192
|
// ChatInstance interface implementations
|
|
845
1193
|
/**
|
|
846
1194
|
* Process an incoming message from an adapter.
|
|
@@ -1728,6 +2076,7 @@ export {
|
|
|
1728
2076
|
Fields2 as Fields,
|
|
1729
2077
|
Image2 as Image,
|
|
1730
2078
|
LockError,
|
|
2079
|
+
Message,
|
|
1731
2080
|
Modal2 as Modal,
|
|
1732
2081
|
NotImplementedError,
|
|
1733
2082
|
RateLimitError,
|