chat 4.14.0 → 4.16.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 +12 -0
- package/dist/{chunk-THM4ACIE.js → chunk-7S5DLTN2.js} +372 -9
- package/dist/chunk-7S5DLTN2.js.map +1 -0
- package/dist/index.d.ts +122 -5
- package/dist/index.js +367 -235
- package/dist/index.js.map +1 -1
- package/dist/{jsx-runtime-Bdt1Dwzf.d.ts → jsx-runtime-Bokk9xw5.d.ts} +80 -5
- package/dist/jsx-runtime.d.ts +1 -1
- package/dist/jsx-runtime.js +3 -1
- package/docs/adapters/discord.mdx +1 -0
- package/docs/adapters/index.mdx +51 -34
- package/docs/adapters/meta.json +1 -0
- package/docs/adapters/slack.mdx +16 -0
- package/docs/adapters/telegram.mdx +161 -0
- package/docs/api/cards.mdx +57 -1
- package/docs/api/channel.mdx +4 -1
- package/docs/api/chat.mdx +5 -0
- package/docs/api/markdown.mdx +42 -0
- package/docs/api/thread.mdx +4 -1
- package/docs/cards.mdx +44 -1
- package/docs/contributing/building.mdx +626 -0
- package/docs/contributing/documenting.mdx +218 -0
- package/docs/contributing/meta.json +4 -0
- package/docs/contributing/publishing.mdx +161 -0
- package/docs/contributing/testing.mdx +494 -0
- package/docs/error-handling.mdx +1 -1
- package/docs/getting-started.mdx +23 -1
- package/docs/handling-events.mdx +375 -0
- package/docs/index.mdx +4 -2
- package/docs/meta.json +6 -1
- package/docs/posting-messages.mdx +7 -7
- package/docs/slash-commands.mdx +23 -0
- package/docs/state/meta.json +1 -6
- package/docs/streaming.mdx +32 -0
- package/docs/threads-messages-channels.mdx +237 -0
- package/docs/usage.mdx +82 -276
- package/package.json +4 -3
- package/dist/chunk-THM4ACIE.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Actions,
|
|
3
|
+
BaseFormatConverter,
|
|
3
4
|
Button,
|
|
4
5
|
Card,
|
|
6
|
+
CardLink,
|
|
5
7
|
CardText,
|
|
6
8
|
Divider,
|
|
7
9
|
Field,
|
|
@@ -13,16 +15,51 @@ import {
|
|
|
13
15
|
Section,
|
|
14
16
|
Select,
|
|
15
17
|
SelectOption,
|
|
18
|
+
Table,
|
|
16
19
|
TextInput,
|
|
20
|
+
blockquote,
|
|
21
|
+
cardChildToFallbackText,
|
|
17
22
|
cardToFallbackText,
|
|
23
|
+
codeBlock,
|
|
24
|
+
emphasis,
|
|
18
25
|
fromReactElement,
|
|
19
26
|
fromReactModalElement,
|
|
27
|
+
getNodeChildren,
|
|
28
|
+
getNodeValue,
|
|
29
|
+
inlineCode,
|
|
30
|
+
isBlockquoteNode,
|
|
20
31
|
isCardElement,
|
|
32
|
+
isCodeNode,
|
|
33
|
+
isDeleteNode,
|
|
34
|
+
isEmphasisNode,
|
|
35
|
+
isInlineCodeNode,
|
|
21
36
|
isJSX,
|
|
37
|
+
isLinkNode,
|
|
38
|
+
isListItemNode,
|
|
39
|
+
isListNode,
|
|
22
40
|
isModalElement,
|
|
41
|
+
isParagraphNode,
|
|
42
|
+
isStrongNode,
|
|
43
|
+
isTableCellNode,
|
|
44
|
+
isTableNode,
|
|
45
|
+
isTableRowNode,
|
|
46
|
+
isTextNode,
|
|
47
|
+
link,
|
|
48
|
+
markdownToPlainText,
|
|
49
|
+
paragraph,
|
|
50
|
+
parseMarkdown,
|
|
51
|
+
root,
|
|
52
|
+
strikethrough,
|
|
53
|
+
stringifyMarkdown,
|
|
54
|
+
strong,
|
|
55
|
+
tableElementToAscii,
|
|
56
|
+
tableToAscii,
|
|
57
|
+
text,
|
|
23
58
|
toCardElement,
|
|
24
|
-
toModalElement
|
|
25
|
-
|
|
59
|
+
toModalElement,
|
|
60
|
+
toPlainText,
|
|
61
|
+
walkAst
|
|
62
|
+
} from "./chunk-7S5DLTN2.js";
|
|
26
63
|
|
|
27
64
|
// src/channel.ts
|
|
28
65
|
import { WORKFLOW_DESERIALIZE as WORKFLOW_DESERIALIZE2, WORKFLOW_SERIALIZE as WORKFLOW_SERIALIZE2 } from "@workflow/serde";
|
|
@@ -44,217 +81,6 @@ function hasChatSingleton() {
|
|
|
44
81
|
return _singleton !== null;
|
|
45
82
|
}
|
|
46
83
|
|
|
47
|
-
// src/markdown.ts
|
|
48
|
-
import { toString as mdastToString } from "mdast-util-to-string";
|
|
49
|
-
import remarkGfm from "remark-gfm";
|
|
50
|
-
import remarkParse from "remark-parse";
|
|
51
|
-
import remarkStringify from "remark-stringify";
|
|
52
|
-
import { unified } from "unified";
|
|
53
|
-
function isTextNode(node) {
|
|
54
|
-
return node.type === "text";
|
|
55
|
-
}
|
|
56
|
-
function isParagraphNode(node) {
|
|
57
|
-
return node.type === "paragraph";
|
|
58
|
-
}
|
|
59
|
-
function isStrongNode(node) {
|
|
60
|
-
return node.type === "strong";
|
|
61
|
-
}
|
|
62
|
-
function isEmphasisNode(node) {
|
|
63
|
-
return node.type === "emphasis";
|
|
64
|
-
}
|
|
65
|
-
function isDeleteNode(node) {
|
|
66
|
-
return node.type === "delete";
|
|
67
|
-
}
|
|
68
|
-
function isInlineCodeNode(node) {
|
|
69
|
-
return node.type === "inlineCode";
|
|
70
|
-
}
|
|
71
|
-
function isCodeNode(node) {
|
|
72
|
-
return node.type === "code";
|
|
73
|
-
}
|
|
74
|
-
function isLinkNode(node) {
|
|
75
|
-
return node.type === "link";
|
|
76
|
-
}
|
|
77
|
-
function isBlockquoteNode(node) {
|
|
78
|
-
return node.type === "blockquote";
|
|
79
|
-
}
|
|
80
|
-
function isListNode(node) {
|
|
81
|
-
return node.type === "list";
|
|
82
|
-
}
|
|
83
|
-
function isListItemNode(node) {
|
|
84
|
-
return node.type === "listItem";
|
|
85
|
-
}
|
|
86
|
-
function getNodeChildren(node) {
|
|
87
|
-
if ("children" in node && Array.isArray(node.children)) {
|
|
88
|
-
return node.children;
|
|
89
|
-
}
|
|
90
|
-
return [];
|
|
91
|
-
}
|
|
92
|
-
function getNodeValue(node) {
|
|
93
|
-
if ("value" in node && typeof node.value === "string") {
|
|
94
|
-
return node.value;
|
|
95
|
-
}
|
|
96
|
-
return "";
|
|
97
|
-
}
|
|
98
|
-
function parseMarkdown(markdown) {
|
|
99
|
-
const processor = unified().use(remarkParse).use(remarkGfm);
|
|
100
|
-
return processor.parse(markdown);
|
|
101
|
-
}
|
|
102
|
-
function stringifyMarkdown(ast) {
|
|
103
|
-
const processor = unified().use(remarkStringify).use(remarkGfm);
|
|
104
|
-
return processor.stringify(ast);
|
|
105
|
-
}
|
|
106
|
-
function toPlainText(ast) {
|
|
107
|
-
return mdastToString(ast);
|
|
108
|
-
}
|
|
109
|
-
function markdownToPlainText(markdown) {
|
|
110
|
-
const ast = parseMarkdown(markdown);
|
|
111
|
-
return mdastToString(ast);
|
|
112
|
-
}
|
|
113
|
-
function walkAst(node, visitor) {
|
|
114
|
-
if ("children" in node && Array.isArray(node.children)) {
|
|
115
|
-
node.children = node.children.map((child) => {
|
|
116
|
-
const result = visitor(child);
|
|
117
|
-
if (result === null) {
|
|
118
|
-
return null;
|
|
119
|
-
}
|
|
120
|
-
return walkAst(result, visitor);
|
|
121
|
-
}).filter((n) => n !== null);
|
|
122
|
-
}
|
|
123
|
-
return node;
|
|
124
|
-
}
|
|
125
|
-
function text(value) {
|
|
126
|
-
return { type: "text", value };
|
|
127
|
-
}
|
|
128
|
-
function strong(children) {
|
|
129
|
-
return { type: "strong", children };
|
|
130
|
-
}
|
|
131
|
-
function emphasis(children) {
|
|
132
|
-
return { type: "emphasis", children };
|
|
133
|
-
}
|
|
134
|
-
function strikethrough(children) {
|
|
135
|
-
return { type: "delete", children };
|
|
136
|
-
}
|
|
137
|
-
function inlineCode(value) {
|
|
138
|
-
return { type: "inlineCode", value };
|
|
139
|
-
}
|
|
140
|
-
function codeBlock(value, lang) {
|
|
141
|
-
return { type: "code", value, lang };
|
|
142
|
-
}
|
|
143
|
-
function link(url, children, title) {
|
|
144
|
-
return { type: "link", url, children, title };
|
|
145
|
-
}
|
|
146
|
-
function blockquote(children) {
|
|
147
|
-
return { type: "blockquote", children };
|
|
148
|
-
}
|
|
149
|
-
function paragraph(children) {
|
|
150
|
-
return { type: "paragraph", children };
|
|
151
|
-
}
|
|
152
|
-
function root(children) {
|
|
153
|
-
return { type: "root", children };
|
|
154
|
-
}
|
|
155
|
-
var BaseFormatConverter = class {
|
|
156
|
-
/**
|
|
157
|
-
* Template method for implementing fromAst with a node converter.
|
|
158
|
-
* Iterates through AST children and converts each using the provided function.
|
|
159
|
-
* Joins results with double newlines (standard paragraph separation).
|
|
160
|
-
*
|
|
161
|
-
* @param ast - The AST to convert
|
|
162
|
-
* @param nodeConverter - Function to convert each Content node to string
|
|
163
|
-
* @returns Platform-formatted string
|
|
164
|
-
*/
|
|
165
|
-
fromAstWithNodeConverter(ast, nodeConverter) {
|
|
166
|
-
const parts = [];
|
|
167
|
-
for (const node of ast.children) {
|
|
168
|
-
parts.push(nodeConverter(node));
|
|
169
|
-
}
|
|
170
|
-
return parts.join("\n\n");
|
|
171
|
-
}
|
|
172
|
-
extractPlainText(platformText) {
|
|
173
|
-
return toPlainText(this.toAst(platformText));
|
|
174
|
-
}
|
|
175
|
-
// Convenience methods for markdown string I/O
|
|
176
|
-
fromMarkdown(markdown) {
|
|
177
|
-
return this.fromAst(parseMarkdown(markdown));
|
|
178
|
-
}
|
|
179
|
-
toMarkdown(platformText) {
|
|
180
|
-
return stringifyMarkdown(this.toAst(platformText));
|
|
181
|
-
}
|
|
182
|
-
/** @deprecated Use extractPlainText instead */
|
|
183
|
-
toPlainText(platformText) {
|
|
184
|
-
return this.extractPlainText(platformText);
|
|
185
|
-
}
|
|
186
|
-
/**
|
|
187
|
-
* Convert a PostableMessage to platform format (text only).
|
|
188
|
-
* - string: passed through as raw text (no conversion)
|
|
189
|
-
* - { raw: string }: passed through as raw text (no conversion)
|
|
190
|
-
* - { markdown: string }: converted from markdown to platform format
|
|
191
|
-
* - { ast: Root }: converted from AST to platform format
|
|
192
|
-
* - { card: CardElement }: returns fallback text (cards should be handled by adapter)
|
|
193
|
-
* - CardElement: returns fallback text (cards should be handled by adapter)
|
|
194
|
-
*
|
|
195
|
-
* Note: For cards, adapters should check for card content first and render
|
|
196
|
-
* them using platform-specific card APIs, using this method only for fallback.
|
|
197
|
-
*/
|
|
198
|
-
renderPostable(message) {
|
|
199
|
-
if (typeof message === "string") {
|
|
200
|
-
return message;
|
|
201
|
-
}
|
|
202
|
-
if ("raw" in message) {
|
|
203
|
-
return message.raw;
|
|
204
|
-
}
|
|
205
|
-
if ("markdown" in message) {
|
|
206
|
-
return this.fromMarkdown(message.markdown);
|
|
207
|
-
}
|
|
208
|
-
if ("ast" in message) {
|
|
209
|
-
return this.fromAst(message.ast);
|
|
210
|
-
}
|
|
211
|
-
if ("card" in message) {
|
|
212
|
-
return message.fallbackText || this.cardToFallbackText(message.card);
|
|
213
|
-
}
|
|
214
|
-
if ("type" in message && message.type === "card") {
|
|
215
|
-
return this.cardToFallbackText(message);
|
|
216
|
-
}
|
|
217
|
-
throw new Error("Invalid PostableMessage format");
|
|
218
|
-
}
|
|
219
|
-
/**
|
|
220
|
-
* Generate fallback text from a card element.
|
|
221
|
-
* Override in subclasses for platform-specific formatting.
|
|
222
|
-
*/
|
|
223
|
-
cardToFallbackText(card) {
|
|
224
|
-
const parts = [];
|
|
225
|
-
if (card.title) {
|
|
226
|
-
parts.push(`**${card.title}**`);
|
|
227
|
-
}
|
|
228
|
-
if (card.subtitle) {
|
|
229
|
-
parts.push(card.subtitle);
|
|
230
|
-
}
|
|
231
|
-
for (const child of card.children) {
|
|
232
|
-
const text2 = this.cardChildToFallbackText(child);
|
|
233
|
-
if (text2) {
|
|
234
|
-
parts.push(text2);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
return parts.join("\n");
|
|
238
|
-
}
|
|
239
|
-
/**
|
|
240
|
-
* Convert card child element to fallback text.
|
|
241
|
-
*/
|
|
242
|
-
cardChildToFallbackText(child) {
|
|
243
|
-
switch (child.type) {
|
|
244
|
-
case "text":
|
|
245
|
-
return child.content;
|
|
246
|
-
case "fields":
|
|
247
|
-
return child.children.map((f) => `**${f.label}**: ${f.value}`).join("\n");
|
|
248
|
-
case "actions":
|
|
249
|
-
return null;
|
|
250
|
-
case "section":
|
|
251
|
-
return child.children.map((c) => this.cardChildToFallbackText(c)).filter(Boolean).join("\n");
|
|
252
|
-
default:
|
|
253
|
-
return null;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
};
|
|
257
|
-
|
|
258
84
|
// src/message.ts
|
|
259
85
|
import { WORKFLOW_DESERIALIZE, WORKFLOW_SERIALIZE } from "@workflow/serde";
|
|
260
86
|
var Message = class _Message {
|
|
@@ -786,6 +612,233 @@ function extractMessageContent(message) {
|
|
|
786
612
|
|
|
787
613
|
// src/thread.ts
|
|
788
614
|
import { WORKFLOW_DESERIALIZE as WORKFLOW_DESERIALIZE3, WORKFLOW_SERIALIZE as WORKFLOW_SERIALIZE3 } from "@workflow/serde";
|
|
615
|
+
|
|
616
|
+
// src/streaming-markdown.ts
|
|
617
|
+
import remend from "remend";
|
|
618
|
+
var StreamingMarkdownRenderer = class {
|
|
619
|
+
accumulated = "";
|
|
620
|
+
dirty = true;
|
|
621
|
+
cachedRender = "";
|
|
622
|
+
finished = false;
|
|
623
|
+
/** Number of code fence toggles from completed lines (odd = inside). */
|
|
624
|
+
fenceToggles = 0;
|
|
625
|
+
/** Incomplete trailing line buffer for incremental fence tracking. */
|
|
626
|
+
incompleteLine = "";
|
|
627
|
+
/** Append a chunk from the LLM stream. */
|
|
628
|
+
push(chunk) {
|
|
629
|
+
this.accumulated += chunk;
|
|
630
|
+
this.dirty = true;
|
|
631
|
+
this.incompleteLine += chunk;
|
|
632
|
+
const parts = this.incompleteLine.split("\n");
|
|
633
|
+
this.incompleteLine = parts.pop() ?? "";
|
|
634
|
+
for (const line of parts) {
|
|
635
|
+
const trimmed = line.trimStart();
|
|
636
|
+
if (trimmed.startsWith("```") || trimmed.startsWith("~~~")) {
|
|
637
|
+
this.fenceToggles++;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
/** O(1) check if accumulated text is inside an unclosed code fence. */
|
|
642
|
+
isAccumulatedInsideFence() {
|
|
643
|
+
let inside = this.fenceToggles % 2 === 1;
|
|
644
|
+
const trimmed = this.incompleteLine.trimStart();
|
|
645
|
+
if (trimmed.startsWith("```") || trimmed.startsWith("~~~")) {
|
|
646
|
+
inside = !inside;
|
|
647
|
+
}
|
|
648
|
+
return inside;
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Get renderable markdown for an intermediate edit.
|
|
652
|
+
* - Holds back trailing lines that look like a table header (|...|)
|
|
653
|
+
* until a separator line (|---|---|) confirms or the next line denies.
|
|
654
|
+
* - Applies remend() to close incomplete inline markers.
|
|
655
|
+
* - Idempotent: returns cached result if no push() since last call.
|
|
656
|
+
*/
|
|
657
|
+
render() {
|
|
658
|
+
if (!this.dirty) {
|
|
659
|
+
return this.cachedRender;
|
|
660
|
+
}
|
|
661
|
+
this.dirty = false;
|
|
662
|
+
if (this.finished) {
|
|
663
|
+
this.cachedRender = remend(this.accumulated);
|
|
664
|
+
return this.cachedRender;
|
|
665
|
+
}
|
|
666
|
+
if (this.isAccumulatedInsideFence()) {
|
|
667
|
+
this.cachedRender = remend(this.accumulated);
|
|
668
|
+
return this.cachedRender;
|
|
669
|
+
}
|
|
670
|
+
const committable = getCommittablePrefix(this.accumulated);
|
|
671
|
+
this.cachedRender = remend(committable);
|
|
672
|
+
return this.cachedRender;
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Get text safe for append-only streaming (e.g. Slack native streaming).
|
|
676
|
+
*
|
|
677
|
+
* - Holds back unconfirmed table headers until separator arrives.
|
|
678
|
+
* - Wraps confirmed tables in code fences so pipes render as literal
|
|
679
|
+
* text (not broken mrkdwn). The code fence is left OPEN while
|
|
680
|
+
* the table is still streaming, keeping output monotonic for deltas.
|
|
681
|
+
* - Holds back unclosed inline markers (**, *, ~~, `, [).
|
|
682
|
+
* - The final editMessage replaces everything with properly formatted text.
|
|
683
|
+
*/
|
|
684
|
+
getCommittableText() {
|
|
685
|
+
if (this.finished) {
|
|
686
|
+
return wrapTablesForAppend(this.accumulated, true);
|
|
687
|
+
}
|
|
688
|
+
let text2 = this.accumulated;
|
|
689
|
+
if (text2.length > 0 && !text2.endsWith("\n")) {
|
|
690
|
+
const lastNewline = text2.lastIndexOf("\n");
|
|
691
|
+
const withoutIncompleteLine = lastNewline >= 0 ? text2.slice(0, lastNewline + 1) : "";
|
|
692
|
+
if (isInsideCodeFence(withoutIncompleteLine)) {
|
|
693
|
+
return wrapTablesForAppend(text2);
|
|
694
|
+
}
|
|
695
|
+
text2 = withoutIncompleteLine;
|
|
696
|
+
}
|
|
697
|
+
if (isInsideCodeFence(text2)) {
|
|
698
|
+
return wrapTablesForAppend(text2);
|
|
699
|
+
}
|
|
700
|
+
const committed = getCommittablePrefix(text2);
|
|
701
|
+
const wrapped = wrapTablesForAppend(committed);
|
|
702
|
+
if (isInsideCodeFence(wrapped)) {
|
|
703
|
+
return wrapped;
|
|
704
|
+
}
|
|
705
|
+
return findCleanPrefix(wrapped);
|
|
706
|
+
}
|
|
707
|
+
/** Raw accumulated text (no remend, no buffering). For the final edit. */
|
|
708
|
+
getText() {
|
|
709
|
+
return this.accumulated;
|
|
710
|
+
}
|
|
711
|
+
/** Signal stream end. Flushes held-back lines. Returns final render. */
|
|
712
|
+
finish() {
|
|
713
|
+
this.finished = true;
|
|
714
|
+
this.dirty = true;
|
|
715
|
+
return this.render();
|
|
716
|
+
}
|
|
717
|
+
};
|
|
718
|
+
var INLINE_MARKER_CHARS = /* @__PURE__ */ new Set(["*", "~", "`", "["]);
|
|
719
|
+
function isClean(text2) {
|
|
720
|
+
return remend(text2).length <= text2.length;
|
|
721
|
+
}
|
|
722
|
+
function findCleanPrefix(text2) {
|
|
723
|
+
if (text2.length === 0 || isClean(text2)) {
|
|
724
|
+
return text2;
|
|
725
|
+
}
|
|
726
|
+
for (let i = text2.length - 1; i >= 0; i--) {
|
|
727
|
+
if (INLINE_MARKER_CHARS.has(text2[i])) {
|
|
728
|
+
while (i > 0 && text2[i - 1] === text2[i]) {
|
|
729
|
+
i--;
|
|
730
|
+
}
|
|
731
|
+
const candidate = text2.slice(0, i);
|
|
732
|
+
if (isClean(candidate)) {
|
|
733
|
+
return candidate;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
return "";
|
|
738
|
+
}
|
|
739
|
+
var TABLE_ROW_RE = /^\|.*\|$/;
|
|
740
|
+
var TABLE_SEPARATOR_RE = /^\|[\s:]*-{1,}[\s:]*(\|[\s:]*-{1,}[\s:]*)*\|$/;
|
|
741
|
+
function isInsideCodeFence(text2) {
|
|
742
|
+
let inside = false;
|
|
743
|
+
for (const line of text2.split("\n")) {
|
|
744
|
+
const trimmed = line.trimStart();
|
|
745
|
+
if (trimmed.startsWith("```") || trimmed.startsWith("~~~")) {
|
|
746
|
+
inside = !inside;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
return inside;
|
|
750
|
+
}
|
|
751
|
+
function getCommittablePrefix(text2) {
|
|
752
|
+
const endsWithNewline = text2.endsWith("\n");
|
|
753
|
+
const lines = text2.split("\n");
|
|
754
|
+
if (!endsWithNewline && lines.length > 0) {
|
|
755
|
+
lines.pop();
|
|
756
|
+
}
|
|
757
|
+
if (endsWithNewline && lines.length > 0 && lines.at(-1) === "") {
|
|
758
|
+
lines.pop();
|
|
759
|
+
}
|
|
760
|
+
let heldCount = 0;
|
|
761
|
+
let separatorFound = false;
|
|
762
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
763
|
+
const trimmed = lines[i].trim();
|
|
764
|
+
if (trimmed === "") {
|
|
765
|
+
break;
|
|
766
|
+
}
|
|
767
|
+
if (TABLE_SEPARATOR_RE.test(trimmed)) {
|
|
768
|
+
separatorFound = true;
|
|
769
|
+
break;
|
|
770
|
+
}
|
|
771
|
+
if (TABLE_ROW_RE.test(trimmed)) {
|
|
772
|
+
heldCount++;
|
|
773
|
+
} else {
|
|
774
|
+
break;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
if (separatorFound || heldCount === 0) {
|
|
778
|
+
return text2;
|
|
779
|
+
}
|
|
780
|
+
const commitLineCount = lines.length - heldCount;
|
|
781
|
+
const committedLines = lines.slice(0, commitLineCount);
|
|
782
|
+
let result = committedLines.join("\n");
|
|
783
|
+
if (committedLines.length > 0) {
|
|
784
|
+
result += "\n";
|
|
785
|
+
}
|
|
786
|
+
return result;
|
|
787
|
+
}
|
|
788
|
+
function wrapTablesForAppend(text2, closeFences = false) {
|
|
789
|
+
const hadTrailingNewline = text2.endsWith("\n");
|
|
790
|
+
const lines = text2.split("\n");
|
|
791
|
+
if (hadTrailingNewline && lines.length > 0 && lines.at(-1) === "") {
|
|
792
|
+
lines.pop();
|
|
793
|
+
}
|
|
794
|
+
const result = [];
|
|
795
|
+
let inTable = false;
|
|
796
|
+
let inUserCodeFence = false;
|
|
797
|
+
for (let i = 0; i < lines.length; i++) {
|
|
798
|
+
const trimmed = lines[i].trim();
|
|
799
|
+
if (!inTable && (trimmed.startsWith("```") || trimmed.startsWith("~~~"))) {
|
|
800
|
+
inUserCodeFence = !inUserCodeFence;
|
|
801
|
+
result.push(lines[i]);
|
|
802
|
+
continue;
|
|
803
|
+
}
|
|
804
|
+
if (inUserCodeFence) {
|
|
805
|
+
result.push(lines[i]);
|
|
806
|
+
continue;
|
|
807
|
+
}
|
|
808
|
+
const isTableLine = trimmed !== "" && (TABLE_ROW_RE.test(trimmed) || TABLE_SEPARATOR_RE.test(trimmed));
|
|
809
|
+
if (isTableLine && !inTable) {
|
|
810
|
+
let hasSeparator = false;
|
|
811
|
+
for (let j = i; j < lines.length; j++) {
|
|
812
|
+
const t = lines[j].trim();
|
|
813
|
+
if (TABLE_SEPARATOR_RE.test(t)) {
|
|
814
|
+
hasSeparator = true;
|
|
815
|
+
break;
|
|
816
|
+
}
|
|
817
|
+
if (t === "" || !TABLE_ROW_RE.test(t)) {
|
|
818
|
+
break;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
if (hasSeparator) {
|
|
822
|
+
result.push("```");
|
|
823
|
+
inTable = true;
|
|
824
|
+
}
|
|
825
|
+
} else if (!isTableLine && inTable) {
|
|
826
|
+
result.push("```");
|
|
827
|
+
inTable = false;
|
|
828
|
+
}
|
|
829
|
+
result.push(lines[i]);
|
|
830
|
+
}
|
|
831
|
+
if (inTable && closeFences) {
|
|
832
|
+
result.push("```");
|
|
833
|
+
}
|
|
834
|
+
let output = result.join("\n");
|
|
835
|
+
if (hadTrailingNewline) {
|
|
836
|
+
output += "\n";
|
|
837
|
+
}
|
|
838
|
+
return output;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
// src/thread.ts
|
|
789
842
|
function isLazyConfig2(config) {
|
|
790
843
|
return "adapterName" in config && !("adapter" in config);
|
|
791
844
|
}
|
|
@@ -809,6 +862,8 @@ var ThreadImpl = class _ThreadImpl {
|
|
|
809
862
|
_currentMessage;
|
|
810
863
|
/** Update interval for fallback streaming */
|
|
811
864
|
_streamingUpdateIntervalMs;
|
|
865
|
+
/** Placeholder text for fallback streaming (post + edit) */
|
|
866
|
+
_fallbackStreamingPlaceholderText;
|
|
812
867
|
/** Cached channel instance */
|
|
813
868
|
_channel;
|
|
814
869
|
constructor(config) {
|
|
@@ -818,6 +873,7 @@ var ThreadImpl = class _ThreadImpl {
|
|
|
818
873
|
this._isSubscribedContext = config.isSubscribedContext ?? false;
|
|
819
874
|
this._currentMessage = config.currentMessage;
|
|
820
875
|
this._streamingUpdateIntervalMs = config.streamingUpdateIntervalMs ?? 500;
|
|
876
|
+
this._fallbackStreamingPlaceholderText = config.fallbackStreamingPlaceholderText !== void 0 ? config.fallbackStreamingPlaceholderText : "...";
|
|
821
877
|
if (isLazyConfig2(config)) {
|
|
822
878
|
this._adapterName = config.adapterName;
|
|
823
879
|
} else {
|
|
@@ -1050,7 +1106,11 @@ var ThreadImpl = class _ThreadImpl {
|
|
|
1050
1106
|
}
|
|
1051
1107
|
};
|
|
1052
1108
|
const raw = await this.adapter.stream(this.id, wrappedStream, options);
|
|
1053
|
-
return this.createSentMessage(
|
|
1109
|
+
return this.createSentMessage(
|
|
1110
|
+
raw.id,
|
|
1111
|
+
{ markdown: accumulated },
|
|
1112
|
+
raw.threadId
|
|
1113
|
+
);
|
|
1054
1114
|
}
|
|
1055
1115
|
return this.fallbackStream(textStream, options);
|
|
1056
1116
|
}
|
|
@@ -1065,37 +1125,56 @@ var ThreadImpl = class _ThreadImpl {
|
|
|
1065
1125
|
*/
|
|
1066
1126
|
async fallbackStream(textStream, options) {
|
|
1067
1127
|
const intervalMs = options?.updateIntervalMs ?? this._streamingUpdateIntervalMs;
|
|
1068
|
-
const
|
|
1069
|
-
|
|
1070
|
-
let
|
|
1071
|
-
|
|
1128
|
+
const placeholderText = this._fallbackStreamingPlaceholderText;
|
|
1129
|
+
let msg = placeholderText === null ? null : await this.adapter.postMessage(this.id, placeholderText);
|
|
1130
|
+
let threadIdForEdits = this.id;
|
|
1131
|
+
const renderer = new StreamingMarkdownRenderer();
|
|
1132
|
+
let lastEditContent = "";
|
|
1072
1133
|
let stopped = false;
|
|
1073
1134
|
let pendingEdit = null;
|
|
1074
1135
|
let timerId = null;
|
|
1136
|
+
if (msg) {
|
|
1137
|
+
threadIdForEdits = msg.threadId || this.id;
|
|
1138
|
+
lastEditContent = placeholderText ?? "";
|
|
1139
|
+
}
|
|
1140
|
+
const scheduleNextEdit = () => {
|
|
1141
|
+
timerId = setTimeout(() => {
|
|
1142
|
+
pendingEdit = doEditAndReschedule();
|
|
1143
|
+
}, intervalMs);
|
|
1144
|
+
};
|
|
1075
1145
|
const doEditAndReschedule = async () => {
|
|
1076
|
-
if (stopped) {
|
|
1146
|
+
if (stopped || !msg) {
|
|
1077
1147
|
return;
|
|
1078
1148
|
}
|
|
1079
|
-
|
|
1080
|
-
|
|
1149
|
+
const content = renderer.render();
|
|
1150
|
+
if (content !== lastEditContent) {
|
|
1081
1151
|
try {
|
|
1082
|
-
await this.adapter.editMessage(threadIdForEdits, msg.id,
|
|
1152
|
+
await this.adapter.editMessage(threadIdForEdits, msg.id, {
|
|
1153
|
+
markdown: content
|
|
1154
|
+
});
|
|
1083
1155
|
lastEditContent = content;
|
|
1084
1156
|
} catch {
|
|
1085
1157
|
}
|
|
1086
1158
|
}
|
|
1087
1159
|
if (!stopped) {
|
|
1088
|
-
|
|
1089
|
-
pendingEdit = doEditAndReschedule();
|
|
1090
|
-
}, intervalMs);
|
|
1160
|
+
scheduleNextEdit();
|
|
1091
1161
|
}
|
|
1092
1162
|
};
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
}
|
|
1163
|
+
if (msg) {
|
|
1164
|
+
scheduleNextEdit();
|
|
1165
|
+
}
|
|
1096
1166
|
try {
|
|
1097
1167
|
for await (const chunk of textStream) {
|
|
1098
|
-
|
|
1168
|
+
renderer.push(chunk);
|
|
1169
|
+
if (!msg) {
|
|
1170
|
+
const content = renderer.render();
|
|
1171
|
+
msg = await this.adapter.postMessage(this.id, {
|
|
1172
|
+
markdown: content
|
|
1173
|
+
});
|
|
1174
|
+
threadIdForEdits = msg.threadId || this.id;
|
|
1175
|
+
lastEditContent = content;
|
|
1176
|
+
scheduleNextEdit();
|
|
1177
|
+
}
|
|
1099
1178
|
}
|
|
1100
1179
|
} finally {
|
|
1101
1180
|
stopped = true;
|
|
@@ -1107,10 +1186,25 @@ var ThreadImpl = class _ThreadImpl {
|
|
|
1107
1186
|
if (pendingEdit) {
|
|
1108
1187
|
await pendingEdit;
|
|
1109
1188
|
}
|
|
1110
|
-
|
|
1111
|
-
|
|
1189
|
+
const accumulated = renderer.getText();
|
|
1190
|
+
const finalContent = renderer.finish();
|
|
1191
|
+
if (!msg) {
|
|
1192
|
+
msg = await this.adapter.postMessage(this.id, {
|
|
1193
|
+
markdown: accumulated
|
|
1194
|
+
});
|
|
1195
|
+
threadIdForEdits = msg.threadId || this.id;
|
|
1196
|
+
lastEditContent = accumulated;
|
|
1112
1197
|
}
|
|
1113
|
-
|
|
1198
|
+
if (finalContent !== lastEditContent) {
|
|
1199
|
+
await this.adapter.editMessage(threadIdForEdits, msg.id, {
|
|
1200
|
+
markdown: accumulated
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1203
|
+
return this.createSentMessage(
|
|
1204
|
+
msg.id,
|
|
1205
|
+
{ markdown: accumulated },
|
|
1206
|
+
threadIdForEdits
|
|
1207
|
+
);
|
|
1114
1208
|
}
|
|
1115
1209
|
async refresh() {
|
|
1116
1210
|
const result = await this.adapter.fetchMessages(this.id, { limit: 50 });
|
|
@@ -1330,7 +1424,7 @@ function extractMessageContent2(message) {
|
|
|
1330
1424
|
var DEFAULT_LOCK_TTL_MS = 3e4;
|
|
1331
1425
|
var SLACK_USER_ID_REGEX = /^U[A-Z0-9]+$/i;
|
|
1332
1426
|
var DISCORD_SNOWFLAKE_REGEX = /^\d{17,19}$/;
|
|
1333
|
-
var DEDUPE_TTL_MS =
|
|
1427
|
+
var DEDUPE_TTL_MS = 5 * 60 * 1e3;
|
|
1334
1428
|
var MODAL_CONTEXT_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
1335
1429
|
var Chat = class {
|
|
1336
1430
|
/**
|
|
@@ -1368,6 +1462,8 @@ var Chat = class {
|
|
|
1368
1462
|
userName;
|
|
1369
1463
|
logger;
|
|
1370
1464
|
_streamingUpdateIntervalMs;
|
|
1465
|
+
_fallbackStreamingPlaceholderText;
|
|
1466
|
+
_dedupeTtlMs;
|
|
1371
1467
|
mentionHandlers = [];
|
|
1372
1468
|
messagePatterns = [];
|
|
1373
1469
|
subscribedMessageHandlers = [];
|
|
@@ -1379,6 +1475,7 @@ var Chat = class {
|
|
|
1379
1475
|
assistantThreadStartedHandlers = [];
|
|
1380
1476
|
assistantContextChangedHandlers = [];
|
|
1381
1477
|
appHomeOpenedHandlers = [];
|
|
1478
|
+
memberJoinedChannelHandlers = [];
|
|
1382
1479
|
/** Initialization state */
|
|
1383
1480
|
initPromise = null;
|
|
1384
1481
|
initialized = false;
|
|
@@ -1393,6 +1490,8 @@ var Chat = class {
|
|
|
1393
1490
|
this._stateAdapter = config.state;
|
|
1394
1491
|
this.adapters = /* @__PURE__ */ new Map();
|
|
1395
1492
|
this._streamingUpdateIntervalMs = config.streamingUpdateIntervalMs ?? 500;
|
|
1493
|
+
this._fallbackStreamingPlaceholderText = config.fallbackStreamingPlaceholderText !== void 0 ? config.fallbackStreamingPlaceholderText : "...";
|
|
1494
|
+
this._dedupeTtlMs = config.dedupeTtlMs ?? DEDUPE_TTL_MS;
|
|
1396
1495
|
if (typeof config.logger === "string") {
|
|
1397
1496
|
this.logger = new ConsoleLogger(config.logger);
|
|
1398
1497
|
} else {
|
|
@@ -1623,6 +1722,10 @@ var Chat = class {
|
|
|
1623
1722
|
this.appHomeOpenedHandlers.push(handler);
|
|
1624
1723
|
this.logger.debug("Registered app home opened handler");
|
|
1625
1724
|
}
|
|
1725
|
+
onMemberJoinedChannel(handler) {
|
|
1726
|
+
this.memberJoinedChannelHandlers.push(handler);
|
|
1727
|
+
this.logger.debug("Registered member joined channel handler");
|
|
1728
|
+
}
|
|
1626
1729
|
/**
|
|
1627
1730
|
* Get an adapter by name with type safety.
|
|
1628
1731
|
*/
|
|
@@ -1824,6 +1927,22 @@ var Chat = class {
|
|
|
1824
1927
|
options.waitUntil(task);
|
|
1825
1928
|
}
|
|
1826
1929
|
}
|
|
1930
|
+
processMemberJoinedChannel(event, options) {
|
|
1931
|
+
const task = (async () => {
|
|
1932
|
+
for (const handler of this.memberJoinedChannelHandlers) {
|
|
1933
|
+
await handler(event);
|
|
1934
|
+
}
|
|
1935
|
+
})().catch((err) => {
|
|
1936
|
+
this.logger.error("Member joined channel handler error", {
|
|
1937
|
+
error: err,
|
|
1938
|
+
channelId: event.channelId,
|
|
1939
|
+
userId: event.userId
|
|
1940
|
+
});
|
|
1941
|
+
});
|
|
1942
|
+
if (options?.waitUntil) {
|
|
1943
|
+
options.waitUntil(task);
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1827
1946
|
/**
|
|
1828
1947
|
* Handle a slash command event internally.
|
|
1829
1948
|
*/
|
|
@@ -2299,7 +2418,7 @@ var Chat = class {
|
|
|
2299
2418
|
});
|
|
2300
2419
|
return;
|
|
2301
2420
|
}
|
|
2302
|
-
await this._stateAdapter.set(dedupeKey, true,
|
|
2421
|
+
await this._stateAdapter.set(dedupeKey, true, this._dedupeTtlMs);
|
|
2303
2422
|
const lock = await this._stateAdapter.acquireLock(
|
|
2304
2423
|
threadId,
|
|
2305
2424
|
DEFAULT_LOCK_TTL_MS
|
|
@@ -2386,7 +2505,8 @@ var Chat = class {
|
|
|
2386
2505
|
isSubscribedContext,
|
|
2387
2506
|
isDM,
|
|
2388
2507
|
currentMessage: initialMessage,
|
|
2389
|
-
streamingUpdateIntervalMs: this._streamingUpdateIntervalMs
|
|
2508
|
+
streamingUpdateIntervalMs: this._streamingUpdateIntervalMs,
|
|
2509
|
+
fallbackStreamingPlaceholderText: this._fallbackStreamingPlaceholderText
|
|
2390
2510
|
});
|
|
2391
2511
|
}
|
|
2392
2512
|
/**
|
|
@@ -2822,6 +2942,8 @@ var emoji = createEmoji();
|
|
|
2822
2942
|
var Actions2 = Actions;
|
|
2823
2943
|
var Button2 = Button;
|
|
2824
2944
|
var Card2 = Card;
|
|
2945
|
+
var cardChildToFallbackText2 = cardChildToFallbackText;
|
|
2946
|
+
var CardLink2 = CardLink;
|
|
2825
2947
|
var CardText2 = CardText;
|
|
2826
2948
|
var Divider2 = Divider;
|
|
2827
2949
|
var Field2 = Field;
|
|
@@ -2832,6 +2954,7 @@ var isCardElement2 = isCardElement;
|
|
|
2832
2954
|
var isJSX2 = isJSX;
|
|
2833
2955
|
var LinkButton2 = LinkButton;
|
|
2834
2956
|
var Section2 = Section;
|
|
2957
|
+
var Table2 = Table;
|
|
2835
2958
|
var toCardElement2 = toCardElement;
|
|
2836
2959
|
var toModalElement2 = toModalElement;
|
|
2837
2960
|
var fromReactModalElement2 = fromReactModalElement;
|
|
@@ -2846,6 +2969,7 @@ export {
|
|
|
2846
2969
|
BaseFormatConverter,
|
|
2847
2970
|
Button2 as Button,
|
|
2848
2971
|
Card2 as Card,
|
|
2972
|
+
CardLink2 as CardLink,
|
|
2849
2973
|
CardText2 as CardText,
|
|
2850
2974
|
ChannelImpl,
|
|
2851
2975
|
Chat,
|
|
@@ -2867,10 +2991,13 @@ export {
|
|
|
2867
2991
|
Section2 as Section,
|
|
2868
2992
|
Select2 as Select,
|
|
2869
2993
|
SelectOption2 as SelectOption,
|
|
2994
|
+
StreamingMarkdownRenderer,
|
|
2870
2995
|
THREAD_STATE_TTL_MS,
|
|
2996
|
+
Table2 as Table,
|
|
2871
2997
|
TextInput2 as TextInput,
|
|
2872
2998
|
ThreadImpl,
|
|
2873
2999
|
blockquote,
|
|
3000
|
+
cardChildToFallbackText2 as cardChildToFallbackText,
|
|
2874
3001
|
codeBlock,
|
|
2875
3002
|
convertEmojiPlaceholders,
|
|
2876
3003
|
createEmoji,
|
|
@@ -2897,6 +3024,9 @@ export {
|
|
|
2897
3024
|
isModalElement2 as isModalElement,
|
|
2898
3025
|
isParagraphNode,
|
|
2899
3026
|
isStrongNode,
|
|
3027
|
+
isTableCellNode,
|
|
3028
|
+
isTableNode,
|
|
3029
|
+
isTableRowNode,
|
|
2900
3030
|
isTextNode,
|
|
2901
3031
|
link,
|
|
2902
3032
|
markdownToPlainText,
|
|
@@ -2906,6 +3036,8 @@ export {
|
|
|
2906
3036
|
strikethrough,
|
|
2907
3037
|
stringifyMarkdown,
|
|
2908
3038
|
strong,
|
|
3039
|
+
tableElementToAscii,
|
|
3040
|
+
tableToAscii,
|
|
2909
3041
|
text,
|
|
2910
3042
|
toCardElement2 as toCardElement,
|
|
2911
3043
|
toModalElement2 as toModalElement,
|