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/index.js CHANGED
@@ -59,7 +59,7 @@ import {
59
59
  toModalElement,
60
60
  toPlainText,
61
61
  walkAst
62
- } from "./chunk-JW7GYSMH.js";
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
- * - Wraps confirmed tables in code fences so pipes render as literal
932
- * text (not broken mrkdwn). The code fence is left OPEN while
933
- * the table is still streaming, keeping output monotonic for deltas.
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 wrapTablesForAppend(this.accumulated, true);
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 wrapTablesForAppend(text2);
986
+ return this.formatAppendOnlyText(text2);
947
987
  }
948
988
  text2 = withoutIncompleteLine;
949
989
  }
950
990
  if (isInsideCodeFence(text2)) {
951
- return wrapTablesForAppend(text2);
991
+ return this.formatAppendOnlyText(text2);
952
992
  }
953
993
  const committed = getCommittablePrefix(text2);
954
- const wrapped = wrapTablesForAppend(committed);
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
- return event.adapter.openModal(
2434
- event.triggerId,
2435
- modalElement,
2436
- contextId
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
- this._stateAdapter.set(key, context, MODAL_CONTEXT_TTL_MS).catch((err) => {
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
- return event.adapter.openModal(
2600
- event.triggerId,
2601
- modalElement,
2602
- contextId
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 parts = threadId.split(":");
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;
@@ -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-DraWieqP.js';
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';
@@ -7,7 +7,7 @@ import {
7
7
  jsxs,
8
8
  toCardElement,
9
9
  toModalElement
10
- } from "./chunk-JW7GYSMH.js";
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";
@@ -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