chat 4.23.0 → 4.25.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/{chunk-JW7GYSMH.js → chunk-OPV5U4WG.js} +8 -4
- package/dist/index.d.ts +225 -14
- package/dist/index.js +323 -34
- package/dist/{jsx-runtime-DraWieqP.d.ts → jsx-runtime-DxATbnrP.d.ts} +6 -0
- package/dist/jsx-runtime.d.ts +1 -1
- package/dist/jsx-runtime.js +1 -2
- package/docs/adapters.mdx +3 -3
- package/docs/getting-started.mdx +3 -1
- package/docs/guides/slack-nextjs.mdx +2 -0
- package/docs/index.mdx +3 -3
- package/docs/state.mdx +1 -1
- package/docs/usage.mdx +3 -3
- package/package.json +1 -1
- package/dist/chunk-JW7GYSMH.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/jsx-runtime.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -59,7 +59,7 @@ import {
|
|
|
59
59
|
toModalElement,
|
|
60
60
|
toPlainText,
|
|
61
61
|
walkAst
|
|
62
|
-
} from "./chunk-
|
|
62
|
+
} from "./chunk-OPV5U4WG.js";
|
|
63
63
|
|
|
64
64
|
// src/ai.ts
|
|
65
65
|
var TEXT_MIME_PREFIXES = [
|
|
@@ -380,6 +380,27 @@ var Message = class _Message {
|
|
|
380
380
|
}
|
|
381
381
|
};
|
|
382
382
|
|
|
383
|
+
// src/postable-object.ts
|
|
384
|
+
var POSTABLE_OBJECT = /* @__PURE__ */ Symbol.for("chat.postable");
|
|
385
|
+
function isPostableObject(value) {
|
|
386
|
+
return typeof value === "object" && value !== null && value.$$typeof === POSTABLE_OBJECT;
|
|
387
|
+
}
|
|
388
|
+
async function postPostableObject(obj, adapter, threadId, postFn, logger) {
|
|
389
|
+
const context = (raw) => ({
|
|
390
|
+
adapter,
|
|
391
|
+
logger,
|
|
392
|
+
messageId: raw.id,
|
|
393
|
+
threadId: raw.threadId ?? threadId
|
|
394
|
+
});
|
|
395
|
+
if (obj.isSupported(adapter) && adapter.postObject) {
|
|
396
|
+
const raw = await adapter.postObject(threadId, obj.kind, obj.getPostData());
|
|
397
|
+
obj.onPosted(context(raw));
|
|
398
|
+
} else {
|
|
399
|
+
const raw = await postFn(threadId, obj.getFallbackText());
|
|
400
|
+
obj.onPosted(context(raw));
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
383
404
|
// src/errors.ts
|
|
384
405
|
var ChatError = class extends Error {
|
|
385
406
|
code;
|
|
@@ -605,6 +626,10 @@ var ChannelImpl = class _ChannelImpl {
|
|
|
605
626
|
};
|
|
606
627
|
}
|
|
607
628
|
async post(message) {
|
|
629
|
+
if (isPostableObject(message)) {
|
|
630
|
+
await this.handlePostableObject(message);
|
|
631
|
+
return message;
|
|
632
|
+
}
|
|
608
633
|
if (isAsyncIterable(message)) {
|
|
609
634
|
let accumulated = "";
|
|
610
635
|
for await (const chunk of fromFullStream(message)) {
|
|
@@ -624,6 +649,14 @@ var ChannelImpl = class _ChannelImpl {
|
|
|
624
649
|
}
|
|
625
650
|
return this.postSingleMessage(postable);
|
|
626
651
|
}
|
|
652
|
+
async handlePostableObject(obj) {
|
|
653
|
+
await postPostableObject(
|
|
654
|
+
obj,
|
|
655
|
+
this.adapter,
|
|
656
|
+
this.id,
|
|
657
|
+
(threadId, message) => this.adapter.postChannelMessage ? this.adapter.postChannelMessage(threadId, message) : this.adapter.postMessage(threadId, message)
|
|
658
|
+
);
|
|
659
|
+
}
|
|
627
660
|
async postSingleMessage(postable) {
|
|
628
661
|
const rawMessage = this.adapter.postChannelMessage ? await this.adapter.postChannelMessage(this.id, postable) : await this.adapter.postMessage(this.id, postable);
|
|
629
662
|
const sent = this.createSentMessage(
|
|
@@ -877,6 +910,12 @@ var StreamingMarkdownRenderer = class {
|
|
|
877
910
|
fenceToggles = 0;
|
|
878
911
|
/** Incomplete trailing line buffer for incremental fence tracking. */
|
|
879
912
|
incompleteLine = "";
|
|
913
|
+
options;
|
|
914
|
+
constructor(options = {}) {
|
|
915
|
+
this.options = {
|
|
916
|
+
wrapTablesForAppend: options.wrapTablesForAppend ?? true
|
|
917
|
+
};
|
|
918
|
+
}
|
|
880
919
|
/** Append a chunk from the LLM stream. */
|
|
881
920
|
push(chunk) {
|
|
882
921
|
this.accumulated += chunk;
|
|
@@ -928,30 +967,31 @@ var StreamingMarkdownRenderer = class {
|
|
|
928
967
|
* Get text safe for append-only streaming (e.g. Slack native streaming).
|
|
929
968
|
*
|
|
930
969
|
* - Holds back unconfirmed table headers until separator arrives.
|
|
931
|
-
* -
|
|
932
|
-
* text
|
|
933
|
-
* the table is still streaming,
|
|
970
|
+
* - Optionally wraps confirmed tables in code fences so pipes render as
|
|
971
|
+
* literal text on append-only surfaces that lack native table support.
|
|
972
|
+
* The code fence is left OPEN while the table is still streaming,
|
|
973
|
+
* keeping output monotonic for deltas.
|
|
934
974
|
* - Holds back unclosed inline markers (**, *, ~~, `, [).
|
|
935
975
|
* - The final editMessage replaces everything with properly formatted text.
|
|
936
976
|
*/
|
|
937
977
|
getCommittableText() {
|
|
938
978
|
if (this.finished) {
|
|
939
|
-
return
|
|
979
|
+
return this.formatAppendOnlyText(this.accumulated, true);
|
|
940
980
|
}
|
|
941
981
|
let text2 = this.accumulated;
|
|
942
982
|
if (text2.length > 0 && !text2.endsWith("\n")) {
|
|
943
983
|
const lastNewline = text2.lastIndexOf("\n");
|
|
944
984
|
const withoutIncompleteLine = lastNewline >= 0 ? text2.slice(0, lastNewline + 1) : "";
|
|
945
985
|
if (isInsideCodeFence(withoutIncompleteLine)) {
|
|
946
|
-
return
|
|
986
|
+
return this.formatAppendOnlyText(text2);
|
|
947
987
|
}
|
|
948
988
|
text2 = withoutIncompleteLine;
|
|
949
989
|
}
|
|
950
990
|
if (isInsideCodeFence(text2)) {
|
|
951
|
-
return
|
|
991
|
+
return this.formatAppendOnlyText(text2);
|
|
952
992
|
}
|
|
953
993
|
const committed = getCommittablePrefix(text2);
|
|
954
|
-
const wrapped =
|
|
994
|
+
const wrapped = this.formatAppendOnlyText(committed);
|
|
955
995
|
if (isInsideCodeFence(wrapped)) {
|
|
956
996
|
return wrapped;
|
|
957
997
|
}
|
|
@@ -967,6 +1007,12 @@ var StreamingMarkdownRenderer = class {
|
|
|
967
1007
|
this.dirty = true;
|
|
968
1008
|
return this.render();
|
|
969
1009
|
}
|
|
1010
|
+
formatAppendOnlyText(text2, closeFences = false) {
|
|
1011
|
+
if (!this.options.wrapTablesForAppend) {
|
|
1012
|
+
return text2;
|
|
1013
|
+
}
|
|
1014
|
+
return wrapTablesForAppend(text2, closeFences);
|
|
1015
|
+
}
|
|
970
1016
|
};
|
|
971
1017
|
var INLINE_MARKER_CHARS = /* @__PURE__ */ new Set(["*", "~", "`", "["]);
|
|
972
1018
|
function isClean(text2) {
|
|
@@ -1308,6 +1354,10 @@ var ThreadImpl = class _ThreadImpl {
|
|
|
1308
1354
|
await this._stateAdapter.unsubscribe(this.id);
|
|
1309
1355
|
}
|
|
1310
1356
|
async post(message) {
|
|
1357
|
+
if (isPostableObject(message)) {
|
|
1358
|
+
await this.handlePostableObject(message);
|
|
1359
|
+
return message;
|
|
1360
|
+
}
|
|
1311
1361
|
if (isAsyncIterable2(message)) {
|
|
1312
1362
|
return this.handleStream(message);
|
|
1313
1363
|
}
|
|
@@ -1330,6 +1380,15 @@ var ThreadImpl = class _ThreadImpl {
|
|
|
1330
1380
|
}
|
|
1331
1381
|
return result;
|
|
1332
1382
|
}
|
|
1383
|
+
async handlePostableObject(obj) {
|
|
1384
|
+
await postPostableObject(
|
|
1385
|
+
obj,
|
|
1386
|
+
this.adapter,
|
|
1387
|
+
this.id,
|
|
1388
|
+
(threadId, message) => this.adapter.postMessage(threadId, message),
|
|
1389
|
+
this._logger
|
|
1390
|
+
);
|
|
1391
|
+
}
|
|
1333
1392
|
async postEphemeral(user, message, options) {
|
|
1334
1393
|
const { fallbackToDM } = options;
|
|
1335
1394
|
const userId = typeof user === "string" ? user : user.userId;
|
|
@@ -2243,7 +2302,7 @@ var Chat = class {
|
|
|
2243
2302
|
* Handles waitUntil registration and error catching internally.
|
|
2244
2303
|
*/
|
|
2245
2304
|
processAction(event, options) {
|
|
2246
|
-
const task = this.handleActionEvent(event).catch((err) => {
|
|
2305
|
+
const task = this.handleActionEvent(event, options).catch((err) => {
|
|
2247
2306
|
this.logger.error("Action processing error", {
|
|
2248
2307
|
error: err,
|
|
2249
2308
|
actionId: event.actionId,
|
|
@@ -2253,6 +2312,7 @@ var Chat = class {
|
|
|
2253
2312
|
if (options?.waitUntil) {
|
|
2254
2313
|
options.waitUntil(task);
|
|
2255
2314
|
}
|
|
2315
|
+
return task;
|
|
2256
2316
|
}
|
|
2257
2317
|
async processModalSubmit(event, contextId, _options) {
|
|
2258
2318
|
const { relatedThread, relatedMessage, relatedChannel } = await this.retrieveModalContext(event.adapter.name, contextId);
|
|
@@ -2307,7 +2367,7 @@ var Chat = class {
|
|
|
2307
2367
|
* Handles waitUntil registration and error catching internally.
|
|
2308
2368
|
*/
|
|
2309
2369
|
processSlashCommand(event, options) {
|
|
2310
|
-
const task = this.handleSlashCommandEvent(event).catch((err) => {
|
|
2370
|
+
const task = this.handleSlashCommandEvent(event, options).catch((err) => {
|
|
2311
2371
|
this.logger.error("Slash command processing error", {
|
|
2312
2372
|
error: err,
|
|
2313
2373
|
command: event.command,
|
|
@@ -2382,7 +2442,7 @@ var Chat = class {
|
|
|
2382
2442
|
/**
|
|
2383
2443
|
* Handle a slash command event internally.
|
|
2384
2444
|
*/
|
|
2385
|
-
async handleSlashCommandEvent(event) {
|
|
2445
|
+
async handleSlashCommandEvent(event, options) {
|
|
2386
2446
|
this.logger.debug("Incoming slash command", {
|
|
2387
2447
|
adapter: event.adapter.name,
|
|
2388
2448
|
command: event.command,
|
|
@@ -2404,11 +2464,11 @@ var Chat = class {
|
|
|
2404
2464
|
...event,
|
|
2405
2465
|
channel,
|
|
2406
2466
|
openModal: async (modal) => {
|
|
2407
|
-
if (!event.triggerId) {
|
|
2467
|
+
if (!(event.triggerId || options?.onOpenModal)) {
|
|
2408
2468
|
this.logger.warn("Cannot open modal: no triggerId available");
|
|
2409
2469
|
return void 0;
|
|
2410
2470
|
}
|
|
2411
|
-
if (!event.adapter.openModal) {
|
|
2471
|
+
if (!(options?.onOpenModal || event.adapter.openModal)) {
|
|
2412
2472
|
this.logger.warn(
|
|
2413
2473
|
`Cannot open modal: ${event.adapter.name} does not support modals`
|
|
2414
2474
|
);
|
|
@@ -2423,18 +2483,24 @@ var Chat = class {
|
|
|
2423
2483
|
modalElement = converted;
|
|
2424
2484
|
}
|
|
2425
2485
|
const contextId = crypto.randomUUID();
|
|
2426
|
-
this.storeModalContext(
|
|
2486
|
+
await this.storeModalContext(
|
|
2427
2487
|
event.adapter.name,
|
|
2428
2488
|
contextId,
|
|
2429
2489
|
void 0,
|
|
2430
2490
|
void 0,
|
|
2431
2491
|
channel
|
|
2432
2492
|
);
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2493
|
+
if (options?.onOpenModal) {
|
|
2494
|
+
return options.onOpenModal(modalElement, contextId);
|
|
2495
|
+
}
|
|
2496
|
+
if (event.triggerId && event.adapter.openModal) {
|
|
2497
|
+
return event.adapter.openModal(
|
|
2498
|
+
event.triggerId,
|
|
2499
|
+
modalElement,
|
|
2500
|
+
contextId
|
|
2501
|
+
);
|
|
2502
|
+
}
|
|
2503
|
+
return void 0;
|
|
2438
2504
|
}
|
|
2439
2505
|
};
|
|
2440
2506
|
this.logger.debug("Checking slash command handlers", {
|
|
@@ -2459,19 +2525,21 @@ var Chat = class {
|
|
|
2459
2525
|
* Store modal context server-side with a context ID.
|
|
2460
2526
|
* Called when opening a modal to preserve thread/message/channel for the submit handler.
|
|
2461
2527
|
*/
|
|
2462
|
-
storeModalContext(adapterName, contextId, thread, message, channel) {
|
|
2528
|
+
async storeModalContext(adapterName, contextId, thread, message, channel) {
|
|
2463
2529
|
const key = `modal-context:${adapterName}:${contextId}`;
|
|
2464
2530
|
const context = {
|
|
2465
2531
|
thread: thread?.toJSON(),
|
|
2466
2532
|
message: message?.toJSON(),
|
|
2467
2533
|
channel: channel?.toJSON()
|
|
2468
2534
|
};
|
|
2469
|
-
|
|
2535
|
+
try {
|
|
2536
|
+
await this._stateAdapter.set(key, context, MODAL_CONTEXT_TTL_MS);
|
|
2537
|
+
} catch (err) {
|
|
2470
2538
|
this.logger.error("Failed to store modal context", {
|
|
2471
2539
|
contextId,
|
|
2472
2540
|
error: err
|
|
2473
2541
|
});
|
|
2474
|
-
}
|
|
2542
|
+
}
|
|
2475
2543
|
}
|
|
2476
2544
|
/**
|
|
2477
2545
|
* Retrieve and delete modal context from server-side storage.
|
|
@@ -2494,6 +2562,7 @@ var Chat = class {
|
|
|
2494
2562
|
relatedChannel: void 0
|
|
2495
2563
|
};
|
|
2496
2564
|
}
|
|
2565
|
+
await this._stateAdapter.delete(key);
|
|
2497
2566
|
const adapter = this.adapters.get(adapterName);
|
|
2498
2567
|
let relatedThread;
|
|
2499
2568
|
if (stored.thread) {
|
|
@@ -2513,7 +2582,7 @@ var Chat = class {
|
|
|
2513
2582
|
/**
|
|
2514
2583
|
* Handle an action event internally.
|
|
2515
2584
|
*/
|
|
2516
|
-
async handleActionEvent(event) {
|
|
2585
|
+
async handleActionEvent(event, options) {
|
|
2517
2586
|
this.logger.debug("Incoming action", {
|
|
2518
2587
|
adapter: event.adapter.name,
|
|
2519
2588
|
actionId: event.actionId,
|
|
@@ -2549,11 +2618,11 @@ var Chat = class {
|
|
|
2549
2618
|
...event,
|
|
2550
2619
|
thread,
|
|
2551
2620
|
openModal: async (modal) => {
|
|
2552
|
-
if (!event.triggerId) {
|
|
2621
|
+
if (!(event.triggerId || options?.onOpenModal)) {
|
|
2553
2622
|
this.logger.warn("Cannot open modal: no triggerId available");
|
|
2554
2623
|
return void 0;
|
|
2555
2624
|
}
|
|
2556
|
-
if (!event.adapter.openModal) {
|
|
2625
|
+
if (!(options?.onOpenModal || event.adapter.openModal)) {
|
|
2557
2626
|
this.logger.warn(
|
|
2558
2627
|
`Cannot open modal: ${event.adapter.name} does not support modals`
|
|
2559
2628
|
);
|
|
@@ -2589,18 +2658,24 @@ var Chat = class {
|
|
|
2589
2658
|
}
|
|
2590
2659
|
const contextId = crypto.randomUUID();
|
|
2591
2660
|
const channel = thread ? thread.channel : void 0;
|
|
2592
|
-
this.storeModalContext(
|
|
2661
|
+
await this.storeModalContext(
|
|
2593
2662
|
event.adapter.name,
|
|
2594
2663
|
contextId,
|
|
2595
2664
|
thread ? thread : void 0,
|
|
2596
2665
|
message,
|
|
2597
2666
|
channel
|
|
2598
2667
|
);
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2668
|
+
if (options?.onOpenModal) {
|
|
2669
|
+
return options.onOpenModal(modalElement, contextId);
|
|
2670
|
+
}
|
|
2671
|
+
if (event.triggerId && event.adapter.openModal) {
|
|
2672
|
+
return event.adapter.openModal(
|
|
2673
|
+
event.triggerId,
|
|
2674
|
+
modalElement,
|
|
2675
|
+
contextId
|
|
2676
|
+
);
|
|
2677
|
+
}
|
|
2678
|
+
return void 0;
|
|
2604
2679
|
}
|
|
2605
2680
|
};
|
|
2606
2681
|
this.logger.debug("Checking action handlers", {
|
|
@@ -3198,8 +3273,7 @@ var Chat = class {
|
|
|
3198
3273
|
}
|
|
3199
3274
|
}
|
|
3200
3275
|
createThread(adapter, threadId, initialMessage, isSubscribedContext = false) {
|
|
3201
|
-
const
|
|
3202
|
-
const channelId = parts[1] || "";
|
|
3276
|
+
const channelId = adapter.channelIdFromThreadId(threadId);
|
|
3203
3277
|
const isDM = adapter.isDM?.(threadId) ?? false;
|
|
3204
3278
|
const channelVisibility = adapter.getChannelVisibility?.(threadId) ?? "unknown";
|
|
3205
3279
|
return new ThreadImpl({
|
|
@@ -3296,6 +3370,220 @@ var Chat = class {
|
|
|
3296
3370
|
}
|
|
3297
3371
|
};
|
|
3298
3372
|
|
|
3373
|
+
// src/plan.ts
|
|
3374
|
+
function contentToPlainText(content) {
|
|
3375
|
+
if (!content) {
|
|
3376
|
+
return "";
|
|
3377
|
+
}
|
|
3378
|
+
if (Array.isArray(content)) {
|
|
3379
|
+
return content.join(" ").trim();
|
|
3380
|
+
}
|
|
3381
|
+
if (typeof content === "string") {
|
|
3382
|
+
return content;
|
|
3383
|
+
}
|
|
3384
|
+
if ("markdown" in content) {
|
|
3385
|
+
return toPlainText(parseMarkdown(content.markdown));
|
|
3386
|
+
}
|
|
3387
|
+
if ("ast" in content) {
|
|
3388
|
+
return toPlainText(content.ast);
|
|
3389
|
+
}
|
|
3390
|
+
return "";
|
|
3391
|
+
}
|
|
3392
|
+
var Plan = class {
|
|
3393
|
+
$$typeof = POSTABLE_OBJECT;
|
|
3394
|
+
kind = "plan";
|
|
3395
|
+
_model;
|
|
3396
|
+
_bound = null;
|
|
3397
|
+
constructor(options) {
|
|
3398
|
+
const title = contentToPlainText(options.initialMessage) || "Plan";
|
|
3399
|
+
const firstTask = {
|
|
3400
|
+
id: crypto.randomUUID(),
|
|
3401
|
+
title,
|
|
3402
|
+
status: "in_progress"
|
|
3403
|
+
};
|
|
3404
|
+
this._model = { title, tasks: [firstTask] };
|
|
3405
|
+
}
|
|
3406
|
+
isSupported(adapter) {
|
|
3407
|
+
return !!adapter.postObject && !!adapter.editObject;
|
|
3408
|
+
}
|
|
3409
|
+
getPostData() {
|
|
3410
|
+
return this._model;
|
|
3411
|
+
}
|
|
3412
|
+
getFallbackText() {
|
|
3413
|
+
const lines = [];
|
|
3414
|
+
lines.push(`\u{1F4CB} ${this._model.title || "Plan"}`);
|
|
3415
|
+
for (const task of this._model.tasks) {
|
|
3416
|
+
const statusIcons = {
|
|
3417
|
+
complete: "\u2705",
|
|
3418
|
+
in_progress: "\u{1F504}",
|
|
3419
|
+
error: "\u274C"
|
|
3420
|
+
};
|
|
3421
|
+
const statusIcon = statusIcons[task.status] ?? "\u2B1C";
|
|
3422
|
+
lines.push(`${statusIcon} ${task.title}`);
|
|
3423
|
+
}
|
|
3424
|
+
return lines.join("\n");
|
|
3425
|
+
}
|
|
3426
|
+
onPosted(context) {
|
|
3427
|
+
this._bound = {
|
|
3428
|
+
adapter: context.adapter,
|
|
3429
|
+
fallback: !this.isSupported(context.adapter),
|
|
3430
|
+
logger: context.logger,
|
|
3431
|
+
messageId: context.messageId,
|
|
3432
|
+
threadId: context.threadId,
|
|
3433
|
+
updateChain: Promise.resolve()
|
|
3434
|
+
};
|
|
3435
|
+
}
|
|
3436
|
+
get id() {
|
|
3437
|
+
return this._bound?.messageId ?? "";
|
|
3438
|
+
}
|
|
3439
|
+
get threadId() {
|
|
3440
|
+
return this._bound?.threadId ?? "";
|
|
3441
|
+
}
|
|
3442
|
+
get title() {
|
|
3443
|
+
return this._model.title;
|
|
3444
|
+
}
|
|
3445
|
+
get tasks() {
|
|
3446
|
+
return this._model.tasks.map((t) => ({
|
|
3447
|
+
id: t.id,
|
|
3448
|
+
title: t.title,
|
|
3449
|
+
status: t.status
|
|
3450
|
+
}));
|
|
3451
|
+
}
|
|
3452
|
+
get currentTask() {
|
|
3453
|
+
let current;
|
|
3454
|
+
for (let i = this._model.tasks.length - 1; i >= 0; i--) {
|
|
3455
|
+
if (this._model.tasks[i].status === "in_progress") {
|
|
3456
|
+
current = this._model.tasks[i];
|
|
3457
|
+
break;
|
|
3458
|
+
}
|
|
3459
|
+
}
|
|
3460
|
+
current ??= this._model.tasks.at(-1);
|
|
3461
|
+
if (!current) {
|
|
3462
|
+
return null;
|
|
3463
|
+
}
|
|
3464
|
+
return { id: current.id, title: current.title, status: current.status };
|
|
3465
|
+
}
|
|
3466
|
+
async addTask(options) {
|
|
3467
|
+
if (!this.canMutate()) {
|
|
3468
|
+
return null;
|
|
3469
|
+
}
|
|
3470
|
+
const title = contentToPlainText(options.title) || "Task";
|
|
3471
|
+
for (const task of this._model.tasks) {
|
|
3472
|
+
if (task.status === "in_progress") {
|
|
3473
|
+
task.status = "complete";
|
|
3474
|
+
}
|
|
3475
|
+
}
|
|
3476
|
+
const nextTask = {
|
|
3477
|
+
id: crypto.randomUUID(),
|
|
3478
|
+
title,
|
|
3479
|
+
status: "in_progress",
|
|
3480
|
+
details: options.children
|
|
3481
|
+
};
|
|
3482
|
+
this._model.tasks.push(nextTask);
|
|
3483
|
+
this._model.title = title;
|
|
3484
|
+
await this.enqueueEdit();
|
|
3485
|
+
return { id: nextTask.id, title: nextTask.title, status: nextTask.status };
|
|
3486
|
+
}
|
|
3487
|
+
async updateTask(update) {
|
|
3488
|
+
if (!this.canMutate()) {
|
|
3489
|
+
return null;
|
|
3490
|
+
}
|
|
3491
|
+
let current;
|
|
3492
|
+
for (let i = this._model.tasks.length - 1; i >= 0; i--) {
|
|
3493
|
+
if (this._model.tasks[i].status === "in_progress") {
|
|
3494
|
+
current = this._model.tasks[i];
|
|
3495
|
+
break;
|
|
3496
|
+
}
|
|
3497
|
+
}
|
|
3498
|
+
current ??= this._model.tasks.at(-1);
|
|
3499
|
+
if (!current) {
|
|
3500
|
+
return null;
|
|
3501
|
+
}
|
|
3502
|
+
if (update !== void 0) {
|
|
3503
|
+
if (typeof update === "object" && update !== null && "output" in update) {
|
|
3504
|
+
if (update.output !== void 0) {
|
|
3505
|
+
current.output = update.output;
|
|
3506
|
+
}
|
|
3507
|
+
if (update.status) {
|
|
3508
|
+
current.status = update.status;
|
|
3509
|
+
}
|
|
3510
|
+
} else {
|
|
3511
|
+
current.output = update;
|
|
3512
|
+
}
|
|
3513
|
+
}
|
|
3514
|
+
await this.enqueueEdit();
|
|
3515
|
+
return { id: current.id, title: current.title, status: current.status };
|
|
3516
|
+
}
|
|
3517
|
+
async reset(options) {
|
|
3518
|
+
if (!this.canMutate()) {
|
|
3519
|
+
return null;
|
|
3520
|
+
}
|
|
3521
|
+
const title = contentToPlainText(options.initialMessage) || "Plan";
|
|
3522
|
+
const firstTask = {
|
|
3523
|
+
id: crypto.randomUUID(),
|
|
3524
|
+
title,
|
|
3525
|
+
status: "in_progress"
|
|
3526
|
+
};
|
|
3527
|
+
this._model = { title, tasks: [firstTask] };
|
|
3528
|
+
await this.enqueueEdit();
|
|
3529
|
+
return {
|
|
3530
|
+
id: firstTask.id,
|
|
3531
|
+
title: firstTask.title,
|
|
3532
|
+
status: firstTask.status
|
|
3533
|
+
};
|
|
3534
|
+
}
|
|
3535
|
+
async complete(options) {
|
|
3536
|
+
if (!this.canMutate()) {
|
|
3537
|
+
return;
|
|
3538
|
+
}
|
|
3539
|
+
for (const task of this._model.tasks) {
|
|
3540
|
+
if (task.status === "in_progress") {
|
|
3541
|
+
task.status = "complete";
|
|
3542
|
+
}
|
|
3543
|
+
}
|
|
3544
|
+
this._model.title = contentToPlainText(options.completeMessage) || this._model.title;
|
|
3545
|
+
await this.enqueueEdit();
|
|
3546
|
+
}
|
|
3547
|
+
canMutate() {
|
|
3548
|
+
return !!this._bound;
|
|
3549
|
+
}
|
|
3550
|
+
enqueueEdit() {
|
|
3551
|
+
if (!this._bound) {
|
|
3552
|
+
return Promise.resolve();
|
|
3553
|
+
}
|
|
3554
|
+
const bound = this._bound;
|
|
3555
|
+
const doEdit = async () => {
|
|
3556
|
+
if (bound.fallback) {
|
|
3557
|
+
await bound.adapter.editMessage(
|
|
3558
|
+
bound.threadId,
|
|
3559
|
+
bound.messageId,
|
|
3560
|
+
this.getFallbackText()
|
|
3561
|
+
);
|
|
3562
|
+
} else {
|
|
3563
|
+
const editObject = bound.adapter.editObject;
|
|
3564
|
+
if (!editObject) {
|
|
3565
|
+
return;
|
|
3566
|
+
}
|
|
3567
|
+
await editObject.call(
|
|
3568
|
+
bound.adapter,
|
|
3569
|
+
bound.threadId,
|
|
3570
|
+
bound.messageId,
|
|
3571
|
+
this.kind,
|
|
3572
|
+
this._model
|
|
3573
|
+
);
|
|
3574
|
+
}
|
|
3575
|
+
};
|
|
3576
|
+
const chained = bound.updateChain.then(doEdit, doEdit);
|
|
3577
|
+
bound.updateChain = chained.then(
|
|
3578
|
+
() => void 0,
|
|
3579
|
+
(err) => {
|
|
3580
|
+
bound.logger?.warn("Failed to edit plan", err);
|
|
3581
|
+
}
|
|
3582
|
+
);
|
|
3583
|
+
return chained;
|
|
3584
|
+
}
|
|
3585
|
+
};
|
|
3586
|
+
|
|
3299
3587
|
// src/emoji.ts
|
|
3300
3588
|
var emojiRegistry = /* @__PURE__ */ new Map();
|
|
3301
3589
|
function getEmoji(name) {
|
|
@@ -3734,6 +4022,7 @@ export {
|
|
|
3734
4022
|
MessageHistoryCache,
|
|
3735
4023
|
Modal2 as Modal,
|
|
3736
4024
|
NotImplementedError,
|
|
4025
|
+
Plan,
|
|
3737
4026
|
RadioSelect2 as RadioSelect,
|
|
3738
4027
|
RateLimitError,
|
|
3739
4028
|
Section2 as Section,
|
|
@@ -3772,6 +4061,7 @@ export {
|
|
|
3772
4061
|
isListNode,
|
|
3773
4062
|
isModalElement2 as isModalElement,
|
|
3774
4063
|
isParagraphNode,
|
|
4064
|
+
isPostableObject,
|
|
3775
4065
|
isStrongNode,
|
|
3776
4066
|
isTableCellNode,
|
|
3777
4067
|
isTableNode,
|
|
@@ -3794,4 +4084,3 @@ export {
|
|
|
3794
4084
|
toPlainText,
|
|
3795
4085
|
walkAst
|
|
3796
4086
|
};
|
|
3797
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -50,6 +50,8 @@ type ButtonStyle = "primary" | "danger" | "default";
|
|
|
50
50
|
type TextStyle = "plain" | "bold" | "muted";
|
|
51
51
|
/** Button element for interactive actions */
|
|
52
52
|
interface ButtonElement {
|
|
53
|
+
/** Whether this button triggers a regular action or opens a modal dialog. Default: "action" */
|
|
54
|
+
actionType?: "action" | "modal";
|
|
53
55
|
/** If true, the button is displayed in an inactive state and doesn't respond to user actions */
|
|
54
56
|
disabled?: boolean;
|
|
55
57
|
/** Unique action ID for callback routing */
|
|
@@ -237,6 +239,8 @@ declare function Section(children: CardChild[]): SectionElement;
|
|
|
237
239
|
declare function Actions(children: (ButtonElement | LinkButtonElement | SelectElement | RadioSelectElement)[]): ActionsElement;
|
|
238
240
|
/** Options for Button */
|
|
239
241
|
interface ButtonOptions {
|
|
242
|
+
/** Whether this button triggers a regular action or opens a modal dialog. Default: "action" */
|
|
243
|
+
actionType?: "action" | "modal";
|
|
240
244
|
/** If true, the button is displayed in an inactive state and doesn't respond to user actions */
|
|
241
245
|
disabled?: boolean;
|
|
242
246
|
/** Unique action ID for callback routing */
|
|
@@ -504,7 +508,9 @@ interface TextProps {
|
|
|
504
508
|
}
|
|
505
509
|
/** Props for Button component in JSX */
|
|
506
510
|
interface ButtonProps {
|
|
511
|
+
actionType?: "action" | "modal";
|
|
507
512
|
children?: string | number | (string | number | undefined)[];
|
|
513
|
+
disabled?: boolean;
|
|
508
514
|
id: string;
|
|
509
515
|
label?: string;
|
|
510
516
|
style?: ButtonStyle;
|
package/dist/jsx-runtime.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { A as ActionsComponent, B as ButtonComponent, V as ButtonProps, c as CardComponent, W as CardJSXElement, X as CardJSXProps, e as CardLinkComponent, Y as CardLinkProps, Z as CardProps, C as ChatElement, _ as ContainerProps, D as DividerComponent, $ as DividerProps, F as FieldComponent, a0 as FieldProps, f as FieldsComponent, an as Fragment, I as ImageComponent, a1 as ImageProps, ao as JSX, L as LinkButtonComponent, a2 as LinkButtonProps, n as ModalComponent, a3 as ModalProps, R as RadioSelectComponent, S as SectionComponent, o as SelectComponent, p as SelectOptionComponent, a4 as SelectOptionProps, a5 as SelectProps, ai as TableComponent, ah as TableProps, T as TextComponent, q as TextInputComponent, a6 as TextInputProps, a7 as TextProps, aj as isCardLinkProps, h as isJSX, ak as jsx, am as jsxDEV, al as jsxs, t as toCardElement, k as toModalElement } from './jsx-runtime-
|
|
1
|
+
export { A as ActionsComponent, B as ButtonComponent, V as ButtonProps, c as CardComponent, W as CardJSXElement, X as CardJSXProps, e as CardLinkComponent, Y as CardLinkProps, Z as CardProps, C as ChatElement, _ as ContainerProps, D as DividerComponent, $ as DividerProps, F as FieldComponent, a0 as FieldProps, f as FieldsComponent, an as Fragment, I as ImageComponent, a1 as ImageProps, ao as JSX, L as LinkButtonComponent, a2 as LinkButtonProps, n as ModalComponent, a3 as ModalProps, R as RadioSelectComponent, S as SectionComponent, o as SelectComponent, p as SelectOptionComponent, a4 as SelectOptionProps, a5 as SelectProps, ai as TableComponent, ah as TableProps, T as TextComponent, q as TextInputComponent, a6 as TextInputProps, a7 as TextProps, aj as isCardLinkProps, h as isJSX, ak as jsx, am as jsxDEV, al as jsxs, t as toCardElement, k as toModalElement } from './jsx-runtime-DxATbnrP.js';
|
package/dist/jsx-runtime.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
jsxs,
|
|
8
8
|
toCardElement,
|
|
9
9
|
toModalElement
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-OPV5U4WG.js";
|
|
11
11
|
export {
|
|
12
12
|
Fragment,
|
|
13
13
|
isCardLinkProps,
|
|
@@ -18,4 +18,3 @@ export {
|
|
|
18
18
|
toCardElement,
|
|
19
19
|
toModalElement
|
|
20
20
|
};
|
|
21
|
-
//# sourceMappingURL=jsx-runtime.js.map
|
package/docs/adapters.mdx
CHANGED
|
@@ -64,7 +64,7 @@ Ready to build your own? Follow the [building](/docs/contributing/building) guid
|
|
|
64
64
|
⚠️ indicates partial support — the feature works with limitations. See individual adapter pages for details.
|
|
65
65
|
</Callout>
|
|
66
66
|
|
|
67
|
-
## How adapters work
|
|
67
|
+
## How [adapters](/adapters) work
|
|
68
68
|
|
|
69
69
|
Each adapter implements a standard interface that the `Chat` class uses to route events and send messages. When a webhook arrives:
|
|
70
70
|
|
|
@@ -73,9 +73,9 @@ Each adapter implements a standard interface that the `Chat` class uses to route
|
|
|
73
73
|
3. Routes to your handlers via the `Chat` class
|
|
74
74
|
4. Converts outgoing messages from markdown/AST/cards to the platform's native format
|
|
75
75
|
|
|
76
|
-
## Using multiple adapters
|
|
76
|
+
## Using multiple [adapters](/adapters)
|
|
77
77
|
|
|
78
|
-
Register multiple adapters and your event handlers work across all of them:
|
|
78
|
+
Register multiple [adapters](/adapters) and your event handlers work across all of them:
|
|
79
79
|
|
|
80
80
|
```typescript title="lib/bot.ts" lineNumbers
|
|
81
81
|
import { Chat } from "chat";
|
package/docs/getting-started.mdx
CHANGED
|
@@ -14,7 +14,7 @@ Learn the core patterns for handling incoming events and posting messages back t
|
|
|
14
14
|
<Card title="Posting Messages" description="Different ways to render and send messages with thread.post()." href="/docs/posting-messages" />
|
|
15
15
|
</Cards>
|
|
16
16
|
|
|
17
|
-
## Adapters
|
|
17
|
+
## [Adapters](/adapters)
|
|
18
18
|
|
|
19
19
|
Connect your bot to chat platforms and persist state across restarts.
|
|
20
20
|
|
|
@@ -23,6 +23,8 @@ Connect your bot to chat platforms and persist state across restarts.
|
|
|
23
23
|
<Card title="State Adapters" description="Pluggable state adapters for thread subscriptions, distributed locking, and caching." href="/docs/state" />
|
|
24
24
|
</Cards>
|
|
25
25
|
|
|
26
|
+
Browse all official and community adapters on the [Adapters](/adapters) page.
|
|
27
|
+
|
|
26
28
|
## Guides
|
|
27
29
|
|
|
28
30
|
Step-by-step tutorials to get up and running on your platform of choice.
|
|
@@ -87,6 +87,8 @@ After creating the app:
|
|
|
87
87
|
1. Go to **OAuth & Permissions**, click **Install to Workspace**, and copy the **Bot User OAuth Token** (`xoxb-...`) — you'll need this as `SLACK_BOT_TOKEN`
|
|
88
88
|
2. Go to **Basic Information** → **App Credentials** and copy the **Signing Secret** — you'll need this as `SLACK_SIGNING_SECRET`
|
|
89
89
|
|
|
90
|
+
If you're distributing the app across multiple workspaces via OAuth instead of installing it to one workspace, configure `clientId` and `clientSecret` on the Slack adapter and pass the same redirect URI used during the authorize step into `handleOAuthCallback(request, { redirectUri })` in your callback route.
|
|
91
|
+
|
|
90
92
|
## Configure environment variables
|
|
91
93
|
|
|
92
94
|
Create a `.env.local` file in your project root:
|
package/docs/index.mdx
CHANGED
|
@@ -11,7 +11,7 @@ Chat SDK is a TypeScript library for building chat bots that work across multipl
|
|
|
11
11
|
Building a chat bot that works across multiple platforms typically means maintaining separate codebases, learning different APIs, and handling platform-specific quirks individually. Chat SDK abstracts these differences behind a unified interface.
|
|
12
12
|
|
|
13
13
|
- **Single codebase** for all platforms
|
|
14
|
-
- **Type-safe** adapters and event handlers with full TypeScript support
|
|
14
|
+
- **Type-safe** [adapters](/adapters) and event handlers with full TypeScript support
|
|
15
15
|
- **Event-driven** architecture with handlers for mentions, messages, reactions, button clicks, slash commands, and modals
|
|
16
16
|
- **Thread subscriptions** for multi-turn conversations
|
|
17
17
|
- **Rich UI** with JSX cards, buttons, and modals that render natively on each platform
|
|
@@ -22,8 +22,8 @@ Building a chat bot that works across multiple platforms typically means maintai
|
|
|
22
22
|
|
|
23
23
|
Chat SDK has three core concepts:
|
|
24
24
|
|
|
25
|
-
1. **Chat** — the main entry point that coordinates adapters and routes events to your handlers
|
|
26
|
-
2. **Adapters** — platform-specific implementations that handle webhook parsing, message formatting, and API calls
|
|
25
|
+
1. **Chat** — the main entry point that coordinates [adapters](/adapters) and routes events to your handlers
|
|
26
|
+
2. **[Adapters](/adapters)** — platform-specific implementations that handle webhook parsing, message formatting, and API calls
|
|
27
27
|
3. **State** — a pluggable persistence layer for thread subscriptions and distributed locking
|
|
28
28
|
|
|
29
29
|
```typescript title="lib/bot.ts" lineNumbers
|