Commit Graph

523 Commits

Author SHA1 Message Date
github-actions[bot] a8dc522c4c Version Packages (#310)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@chat-adapter/whatsapp@4.24.0 @chat-adapter/slack@4.24.0 @chat-adapter/state-ioredis@4.24.0 @chat-adapter/state-memory@4.24.0 @chat-adapter/telegram@4.24.0 chat@4.24.0 @chat-adapter/state-redis@4.24.0 @chat-adapter/discord@4.24.0 @chat-adapter/gchat@4.24.0 @chat-adapter/teams@4.24.0 @chat-adapter/state-pg@4.24.0 @chat-adapter/shared@4.24.0 @chat-adapter/linear@4.24.0 @chat-adapter/github@4.24.0
2026-04-06 20:21:57 -07:00
Aamir Jawaid 4f5d20029f Add modal support for Teams (#325)
* feat(teams): add dialog (task module) support

Teams dialogs require modal content to be returned inline in the HTTP
response when a task/fetch invoke fires. This adds:

- `actionType: "modal"` on buttons to emit msteams task/fetch hint
- `onOpenModal` hook on WebhookOptions for inline modal interception
- dialog.open/dialog.submit handlers in Teams adapter with Promise.race
- Modal-to-AdaptiveCard converter (modals.ts)
- Bridge adapter sends empty body (not "{}") for dialog close responses

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(teams): use @microsoft/teams.cards builders for Adaptive Cards

Replace hand-rolled plain JSON objects and local type definitions with
typed builder classes from @microsoft/teams.cards. This gives compile-time
type safety and eliminates the local AdaptiveCard/AdaptiveCardElement/
AdaptiveCardAction interfaces.

Also fix ephemeral modal button missing actionType="modal", which
prevented the dialog from opening on Teams.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(teams): preserve contextId in modal update/push responses

Pass the original contextId through to re-rendered modals so subsequent
submissions can still retrieve the stored thread/message/channel context.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: address review feedback across dialog support

- Clean up timeout timer in handleDialogOpen to prevent resource leak
- Also race on actionPromise so errors surface instead of silently timing out
- Extract buildContinueResponse helper to deduplicate update/push cases
- Use typed TextInputOptions/ChoiceSetInputOptions instead of Record<string, unknown>
- Make processSlashCommand options parameter explicit (WebhookOptions | undefined)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(chat): await storeModalContext before opening modal

The state write was fire-and-forget, so a fast dialog.submit could
arrive before the context was persisted, causing retrieveModalContext
to return empty. Also adds changeset for the new public API surface.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(chat): add missing disabled prop to ButtonProps JSX interface

The ButtonElement and ButtonOptions already supported disabled, but the
JSX ButtonProps interface was missing it, causing <Button disabled> to
silently drop the prop.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address PR review feedback for dialog support

- Add unit tests for modals.ts (16 tests) and modal button actionType in cards.test.ts
- Make dialog open timeout configurable via dialogOpenTimeoutMs in TeamsAdapterConfig
- Fix ModalSubmitHandler type to accept Promise<void> returns, remove @ts-expect-error from example
- Delete stored modal context after retrieval to prevent state adapter leaks

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: lint fixes and add X-User-Agent header to Teams adapter

- Fix import ordering, formatting, and non-null assertions in modals.test.ts
- Sort interface members in TeamsAdapterConfig
- Add X-User-Agent: Vercel.ChatSDK header to Teams SDK App client

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 13:09:25 +10:00
Nhat Bui 4304de82b7 feat: add Zalo adapter and update documentation (#336) 2026-04-07 09:02:59 +10:00
josh 8d8927411d fix: disable source maps in published packages (#337)
* fix: disable source maps in published packages

* chore: add changeset
2026-04-07 09:02:36 +10:00
Yejun Su e8dbef2bab fix(adapter-slack): replace empty table cells with single space (#327)
* fix(adapter-slack): replace empty table cells with single space

Slack's Block Kit API rejects cells with empty text fields. Fall back
to a single space in both mdastTableToSlackBlock (markdown.ts) and
convertTableToBlocks (cards.ts) to satisfy the API constraint.

* chore: add changeset for slack empty table cells fix

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-04-07 08:52:23 +10:00
josh d3f04ec92b docs: add missing JSDoc and adapter-shared README (#339)
* docs(chat): add JSDoc to onModalSubmit and onModalClose

* docs(shared): add README for adapter-shared package
2026-04-06 12:46:13 -07:00
josh cb1deebd67 fix(github): handle webhook events without repository in multi-tenant mode (#335) 2026-04-06 11:39:54 -07:00
Malte Ubl 9215740312 Add back links to /adapters from adapter detail pages (#333)
* feat: add adapters section and integrate into home page

Slack-Thread: https://vercel.slack.com/archives/C08077A6JDB/p1775418562324169?thread_ts=1775418562.324169&cid=C08077A6JDB
Co-authored-by: Malte Ubl <89679+cramforce@users.noreply.github.com>

* feat: update adapters section with new messenger links and layout changes

Slack-Thread: https://vercel.slack.com/archives/C08077A6JDB/p1775418562324169?thread_ts=1775418562.324169&cid=C08077A6JDB
Co-authored-by: Malte Ubl <89679+cramforce@users.noreply.github.com>

* feat: add navigation links and icons for adapter pages

Slack-Thread: https://vercel.slack.com/archives/C08077A6JDB/p1775418562324169?thread_ts=1775418562.324169&cid=C08077A6JDB
Co-authored-by: Malte Ubl <89679+cramforce@users.noreply.github.com>

---------

Co-authored-by: v0 <v0[bot]@users.noreply.github.com>
Co-authored-by: Malte Ubl <89679+cramforce@users.noreply.github.com>
2026-04-05 13:19:46 -07:00
Malte Ubl 332c84ac02 feat: add rewrite rule for sitemap.xml and remove sitemap.ts file (#334)
Slack-Thread: https://vercel.slack.com/archives/C08077A6JDB/p1775419801951169?thread_ts=1775419801.951169&cid=C08077A6JDB

Co-authored-by: v0 <v0[bot]@users.noreply.github.com>
Co-authored-by: Malte Ubl <89679+cramforce@users.noreply.github.com>
2026-04-05 13:17:23 -07:00
Malte Ubl 1fcab01302 Move messenger pages to /adapters/for/ and add browse section (#332)
* feat: refine adapter categorization and display logic in AdaptersGrid component

Slack-Thread: https://vercel.slack.com/archives/C08077A6JDB/p1775414281367229?thread_ts=1775414281.367229&cid=C08077A6JDB
Co-authored-by: Malte Ubl <89679+cramforce@users.noreply.github.com>

* feat: rename messenger to for in adapter paths and add messenger links

Slack-Thread: https://vercel.slack.com/archives/C08077A6JDB/p1775414281367229?thread_ts=1775414281.367229&cid=C08077A6JDB
Co-authored-by: Malte Ubl <89679+cramforce@users.noreply.github.com>

---------

Co-authored-by: v0 <v0[bot]@users.noreply.github.com>
Co-authored-by: Malte Ubl <89679+cramforce@users.noreply.github.com>
2026-04-05 12:32:23 -07:00
Malte Ubl f93228c4ef Add Sendblue iMessage adapter and strip HTML images from READMEs (#329)
* feat: add chat-adapter-sendblue community adapter

Introduce iMessage via Sendblue adapter for community integration.

Slack-Thread: https://vercel.slack.com/archives/C08077A6JDB/p1775398069006459?thread_ts=1775398069.006459&cid=C08077A6JDB
Co-authored-by: Malte Ubl <89679+cramforce@users.noreply.github.com>

* feat: enhance readme-content.tsx to strip images from markdown

Improved markdown image removal, added HTML img tag cleanup

Slack-Thread: https://vercel.slack.com/archives/C08077A6JDB/p1775398069006459?thread_ts=1775398069.006459&cid=C08077A6JDB
Co-authored-by: Malte Ubl <89679+cramforce@users.noreply.github.com>

* fix: update SKILL.md for Sendblue adapter order

Sync SKILL.md with adapters.json for correct adapter listing.

Slack-Thread: https://vercel.slack.com/archives/C08077A6JDB/p1775398069006459?thread_ts=1775398069.006459&cid=C08077A6JDB
Co-authored-by: Malte Ubl <89679+cramforce@users.noreply.github.com>

---------

Co-authored-by: v0 <v0[bot]@users.noreply.github.com>
Co-authored-by: Malte Ubl <89679+cramforce@users.noreply.github.com>
2026-04-05 11:34:48 -07:00
Ben Sabic 6743465218 Add Liveblocks vendor official adapter (#321)
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-04-03 14:32:35 +11:00
Rich Haines 11872eae01 Send siteId as label on feedback GitHub issues (#319) 2026-04-01 23:42:31 +11:00
Aamir Jawaid a0f508eee6 Migrate from BotFramework to TeamsSDK (#302)
* Migrate Teams adapter from botbuilder to @microsoft/teams.apps

Replace botbuilder/botframework-connector with @microsoft/teams.apps (TeamsSDK v2.0.6).

Breaking changes:
- Remove certificate auth (TeamsAuthCertificate) — use token or managedIdentityClientId instead
- Env vars changed: TEAMS_APP_ID → CLIENT_ID, TEAMS_APP_PASSWORD → CLIENT_SECRET, TEAMS_APP_TENANT_ID → TENANT_ID
- Reactions now work (addReaction/removeReaction) instead of throwing NotImplementedError
- Graph API calls use @microsoft/teams.graph-endpoints typed endpoints

Key changes:
- BridgeHttpAdapter captures TeamsSDK route handler for serverless dispatch
- Event handlers registered via app.on() (message, messageReaction, card.action, conversationUpdate, installationUpdate)
- Outbound calls use app.send() and app.api.conversations.activities()
- Stream support via post+edit (native HttpStream when SDK exports it)
- Graph API uses typed endpoints from @microsoft/teams.graph-endpoints
- isMention detection via activity entities instead of bot name matching

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Align TeamsAdapterConfig with AppOptions from TeamsSDK

- Replace custom config fields with Pick<AppOptions, ...>
  clientId (was appId), clientSecret (was appPassword), tenantId (was appTenantId)
- Remove TeamsAuthFederated, appType — use managedIdentityClientId directly
- Constructor passes config through to App (App handles env var resolution)
- Use this.app.id instead of this.config.appId for bot identity checks
- 3 replay-fetch-messages tests have known failures (type shape mismatch)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Remove api() getter, add handler type safety

- Remove useless private api() getter, use this.app.api directly
- Type handlers with specific activity types:
  handleMessageActivity(ctx: IActivityContext<IMessageActivity>)
  handleAdaptiveCardAction(ctx: IActivityContext<IAdaptiveCardActionInvokeActivity>)
  handleReactionFromContext(ctx: IActivityContext<IMessageReactionActivity>)
- Remove unnecessary casts now that ctx.activity is properly typed

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Revert bot.tsx example changes from migration branch

Keep Azure OpenAI and DM handler changes as local-only, not part of the migration.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Revert example package.json from migration branch

Keep only adapters.ts config changes, revert package.json dep changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Remove unrelated skill files from migration branch

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Fix

* Update README for TeamsSDK migration

- New env vars: CLIENT_ID, CLIENT_SECRET, TENANT_ID
- New config: clientId, clientSecret, tenantId, token, managedIdentityClientId
- Remove certificate auth docs (dropped)
- Reactions now supported (add/remove)
- Typing indicator now supported
- Graph API uses @microsoft/teams.graph-endpoints
- Note streaming and modals status

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Revert "Update README for TeamsSDK migration"

This reverts commit 729fe463c1.

* update readme

* Refactor adapter-teams: extract types, errors, and graph-api modules

Split the 2,114-line index.ts into focused modules:
- types.ts: TeamsAdapterConfig, TeamsThreadId, TeamsChannelContext
- errors.ts: handleTeamsError as standalone pure function
- graph-api.ts: TeamsGraphReader class with all Graph API read methods

index.ts shrinks to ~1,133 lines, delegating graph reads to TeamsGraphReader
which receives dependencies via constructor injection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Clean up tests

* Do not break public facing config.

* Use previous .env vars

* Fix tests

* Fix webhook concurrency issue

* Remove support for reactions

* Fetch aadgroup id if it doesn't exist

* Fix lint and formatting issues in Teams adapter

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Fix README to match public API, fix 403 error mapping, correct NotImplementedError args, remove stale test aliases, and add changeset

* Fix remaining mockBotAdapter references and trailing whitespace

* fix graph pagination

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-03-31 15:31:50 +11:00
Sascha Buehrle 09f6230a51 fix(discord): recover existing thread on 160004 error (#280)
* fix(discord): recover existing thread on 160004 error (fixes #279)

* Harden 160004 error matching, add tests, and add changeset

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-03-31 13:28:00 +11:00
Samy Pessé 90cfb6a98a feat(github-adapter): Add a method getInstallationId to get the installation id for a thread (#312)
* Implement getInstallationId

* Add tests

* Format

* Fix duplicate test to cover Thread object input path

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-03-31 13:02:56 +11:00
XLor 27b34e153b fix(chat): use adapter to parse channel id (#286)
* fix(chat): use adapter to parse channel id

* test: fix cases use adapter to parse channel id

* chore: update changeset
2026-03-30 09:16:23 -07:00
James 2d6a7e6a40 feat(docs): add inline logo chips to hero description (#315)
* feat(docs): add inline logo chips to hero description

Add brand logos (Slack, Microsoft Teams, Google Chat, Discord) as
inline chips prefixed before each product name in the hero section.

- Import existing SVG logos from lib/logos.tsx
- Create LogoChip component with whitespace-nowrap to prevent
  wrapping between logo and its corresponding product name
- Update Hero component description type from string to ReactNode
- Keep plain text description for metadata/SEO

* fix(docs): logo chips line-height, comma wrapping, and spacing

- Use absolute positioning inside a zero-height inline-block container
  so the logo doesn't increase line height
- Move trailing commas inside the whitespace-nowrap span via a suffix
  prop to prevent hanging commas at line start
- Add 2px extra spacing on the right side of logos before names

* fix(docs): vertically center logos and add 2px left spacing

- Use align-middle + top-1/2 -translate-y-1/2 for true vertical
  centering relative to the text line
- Add ml-[2px] on the outer nowrap span for left-side spacing

* fix(docs): widen hero description max-width on desktop

Bump from max-w-3xl (48rem) to sm:max-w-[52rem] so the first
product (Slack) stays on the first line at typical desktop widths.

* fix(docs): resolve lint issues

- Fix import order (react type import after next)
- Replace namespace import with named imports for logos
- Sort CSS classes per biome nursery rule
- Fix formatter line wrapping

---------

Co-authored-by: jamesvclements <jamesvclements@users.noreply.github.com>
2026-03-30 11:47:33 -04:00
Miki Palet 96cdef5877 Add Zernio vendor official adapter (#314)
* feat: add Zernio vendor official adapter to adapters directory

* fix: add @zernio/chat-sdk-adapter to community adapters list in SKILL.md
2026-03-30 22:31:32 +11:00
Hayden Bleasel 67e0e1ba23 Fix Redis state typings for url and client options (#303)
* Fix Redis state typings for url and client options

* Wait for injected Redis clients to become ready

* Simplify Redis readiness guard

* Stabilize memory state TTL test with fake timers

* Handle Redis reconnect errors until client is ready

* Handle injected Redis client reconnects correctly
2026-03-28 11:18:03 +11:00
github-actions[bot] 91820173f4 Version Packages (#300) @chat-adapter/telegram@4.23.0 @chat-adapter/gchat@4.23.0 @chat-adapter/whatsapp@4.23.0 @chat-adapter/discord@4.23.0 @chat-adapter/teams@4.23.0 @chat-adapter/state-redis@4.23.0 @chat-adapter/state-pg@4.23.0 @chat-adapter/state-memory@4.23.0 @chat-adapter/state-ioredis@4.23.0 @chat-adapter/slack@4.23.0 @chat-adapter/shared@4.23.0 @chat-adapter/linear@4.23.0 @chat-adapter/github@4.23.0 chat@4.23.0 2026-03-25 21:36:16 -04:00
Fernando Rojo 4166e09dad Add channelVisibility support to Thread, Channel, and Slack adapter (#51) 2026-03-25 17:21:46 -04:00
Malte Ubl acc4a336a0 Minor docs fix (#296) 2026-03-24 14:25:29 -07:00
github-actions[bot] 434421c82f Version Packages (#295)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@chat-adapter/slack@4.22.0 chat@4.22.0 @chat-adapter/whatsapp@4.22.0 @chat-adapter/telegram@4.22.0 @chat-adapter/teams@4.22.0 @chat-adapter/state-redis@4.22.0 @chat-adapter/state-pg@4.22.0 @chat-adapter/state-memory@4.22.0 @chat-adapter/state-ioredis@4.22.0 @chat-adapter/shared@4.22.0 @chat-adapter/linear@4.22.0 @chat-adapter/github@4.22.0 @chat-adapter/gchat@4.22.0 @chat-adapter/discord@4.22.0
2026-03-24 14:22:44 -07:00
Malte Ubl f2d8957797 Docs for the new concurrency strategies (#294) 2026-03-24 14:15:44 -07:00
Malte Ubl c674284885 [RFC] add concurrency strategies for overlapping messages (queue, debounce, concurrent) (#277)
* feat: add concurrency strategies for overlapping messages (queue, debounce, concurrent)

## Problem

When multiple messages arrive on the same thread while a handler is still
processing, the SDK has only one behavior: **lock-and-drop**. The incoming
message is silently discarded (or force-released, which creates uncontrolled
concurrency). This is insufficient for most real-world use cases:

- **AI chatbots** lose user follow-up messages sent while the model is streaming
- **Customer support bots** miss messages entirely, breaking conversation flow
- **Collaborative editing bots** need to coalesce rapid corrections into one action

## Solution

Introduce a new `concurrency` option on `ChatConfig` with four strategies:

### `'drop'` (default, backward-compatible)

Existing behavior. Lock acquired or `LockError` thrown. No changes.

### `'queue'`

Messages that arrive while a handler is running are enqueued in the state
adapter. When the current handler finishes, the queue is drained: **only the
latest message is dispatched**, with all intermediate messages provided as
`context.skipped`. This gives the handler full visibility into what happened
while it was busy, without forcing it to re-process every message sequentially.

```typescript
const chat = new Chat({
  concurrency: 'queue',
  // ...
});

chat.onNewMention(async (thread, message, context) => {
  if (context && context.skipped.length > 0) {
    // "You sent 4 messages while I was thinking. Responding to your latest."
    const allMessages = [...context.skipped, message];
    // Pass all messages to the LLM for full context
  }
});
```

Flow:
```
A arrives  → acquire lock → process A
B arrives  → lock busy → enqueue B
C arrives  → lock busy → enqueue C
D arrives  → lock busy → enqueue D
A done     → drain: [B, C, D] → handler(D, { skipped: [B, C] })
D done     → queue empty → release lock
```

### `'debounce'`

Every message (including the first) starts or resets a debounce timer. Only the
**final message in a burst** is processed. The lock-holding function stays alive
through `waitUntil` during the debounce window.

```typescript
const chat = new Chat({
  concurrency: { strategy: 'debounce', debounceMs: 1500 },
  // ...
});
```

Flow:
```
A arrives  → acquire lock → store A as pending → sleep(debounceMs)
B arrives  → lock busy → overwrite pending with B (A dropped)
C arrives  → lock busy → overwrite pending with C (B dropped)
             ... debounceMs elapses with no new message ...
           → process C → release lock
```

### `'concurrent'`

No locking at all. Every message is processed immediately in its own handler
invocation. Suitable for stateless handlers (lookups, translations) where
thread ordering doesn't matter.

```typescript
const chat = new Chat({
  concurrency: 'concurrent',
  // ...
});
```

## API Surface

### ChatConfig

```typescript
interface ChatConfig {
  concurrency?: ConcurrencyStrategy | ConcurrencyConfig;
  /** @deprecated Use `concurrency` instead */
  onLockConflict?: 'force' | 'drop' | ((threadId, message) => ...);
}

type ConcurrencyStrategy = 'drop' | 'queue' | 'debounce' | 'concurrent';

interface ConcurrencyConfig {
  strategy: ConcurrencyStrategy;
  maxQueueSize?: number;           // Default: 10
  onQueueFull?: 'drop-oldest' | 'drop-newest';  // Default: 'drop-oldest'
  queueEntryTtlMs?: number;        // Default: 90_000 (90s)
  debounceMs?: number;              // Default: 1500
  maxConcurrent?: number;           // Default: Infinity
}
```

### MessageContext (new, passed to handlers)

```typescript
interface MessageContext {
  skipped: Message[];               // Intermediate messages, chronological
  totalSinceLastHandler: number;    // skipped.length + 1
}
```

All handler types (`MentionHandler`, `MessageHandler`, `SubscribedMessageHandler`,
`DirectMessageHandler`) now accept an optional `MessageContext` as their last
parameter. Existing handlers that don't use it are unaffected.

### StateAdapter (new methods)

```typescript
interface StateAdapter {
  enqueue(threadId: string, entry: QueueEntry, maxSize: number): Promise<number>;
  dequeue(threadId: string): Promise<QueueEntry | null>;
  queueDepth(threadId: string): Promise<number>;
}
```

Implemented across all four state adapters:
- **MemoryStateAdapter**: in-process array
- **RedisStateAdapter**: Lua script (RPUSH + LTRIM + PEXPIRE)
- **IoRedisStateAdapter**: same Lua approach
- **PostgresStateAdapter**: new `chat_state_queues` table with atomic dequeue

## Architecture

`handleIncomingMessage` was refactored into composable pieces:

- `dispatchToHandlers()` — shared handler dispatch logic (mention detection,
  subscription routing, pattern matching). Extracted from the old monolithic
  method so all strategies reuse it.
- `handleDrop()` — original lock-or-fail path (preserves `onLockConflict` compat)
- `handleQueueOrDebounce()` — enqueue if busy, drain or debounce after
- `handleConcurrent()` — skip locking entirely
- `drainQueue()` — collect all pending, dispatch latest with skipped context
- `debounceLoop()` — sleep/check/repeat until no new messages arrive

## Queue Entry TTL

Queued messages have a configurable TTL (`queueEntryTtlMs`, default 90s). Stale
entries are discarded on dequeue with a `message-expired` log event. This
prevents unbounded accumulation and ensures handlers don't process messages
that are no longer relevant.

## Observability

All strategies emit structured log events at `info` level:

| Event                 | Strategy         | Data                                  |
|-----------------------|------------------|---------------------------------------|
| `message-queued`      | queue            | threadId, messageId, queueDepth       |
| `message-dequeued`    | queue, debounce  | threadId, messageId, skippedCount     |
| `message-dropped`     | drop, queue      | threadId, messageId, reason           |
| `message-expired`     | queue, debounce  | threadId, messageId                   |
| `message-superseded`  | debounce         | threadId, droppedId                   |
| `message-debouncing`  | debounce         | threadId, messageId, debounceMs       |
| `message-debounce-reset` | debounce      | threadId, messageId                   |

## Backward Compatibility

- Default remains `'drop'` — zero breaking changes for existing users
- `onLockConflict` continues to work but is marked `@deprecated`
- Handler signatures are backward-compatible (new `context` param is optional)
- Deduplication always runs regardless of strategy

## Files Changed

- `packages/chat/src/types.ts` — new types, updated handler signatures
- `packages/chat/src/chat.ts` — strategy routing, drain/debounce loops
- `packages/chat/src/index.ts` — export new types
- `packages/chat/src/mock-adapter.ts` — queue methods for test mock
- `packages/state-memory/src/index.ts` — in-memory queue
- `packages/state-redis/src/index.ts` — Redis queue (Lua)
- `packages/state-ioredis/src/index.ts` — ioredis queue (Lua)
- `packages/state-pg/src/index.ts` — Postgres queue table
- `packages/chat/src/chat.test.ts` — tests for queue, debounce, concurrent

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: comprehensive test coverage for concurrency strategies and queue methods

Add tests across all state adapters and the Chat class:

**MemoryStateAdapter** (8 new tests):
- enqueue/dequeue single entry
- dequeue from empty queue returns null
- dequeue from nonexistent thread returns null
- queueDepth returns 0 for empty queue
- FIFO ordering across multiple entries
- maxSize trimming (keeps newest)
- maxSize=1 debounce behavior (last-write-wins)
- queue isolation by thread
- queue cleared on disconnect

**PostgresStateAdapter** (8 new tests):
- INSERT query for enqueue
- overflow trimming query
- depth return value
- parsed entry from dequeue
- null from empty dequeue
- atomic DELETE-RETURNING for dequeue
- queueDepth return value
- zero depth for empty queue

**RedisStateAdapter / IoRedisStateAdapter** (3+3 existence checks):
- enqueue, dequeue, queueDepth method existence

**Chat concurrency** (5 new tests):
- drop-newest policy when queue is full
- drop-oldest policy evicts oldest entries
- expired entries skipped during drain
- onNewMessage pattern handlers receive context
- onSubscribedMessage handlers receive skipped context

Total new tests: 27 (780 chat + 33 memory + 59 pg)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Address feedback

* Support a channel locking strategy, make it default for WhatsApp and Telegram

* docs: fix typo "Committment" → "Commitment" (#274)

Co-authored-by: Arif Kobel <arif.kobel@phorax.com>

* Add webhook verification to GChat (#287)

- Issues a warning if required env vars are not present (also for telegram)
- Makes telegram use a time-safe verifier

* Make adapters depend on `chat` as a real dep (#289)

Without this, changeset will make any dep change a major change

* Version Packages (#290)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Fix serialization

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Arif Kobel <102538661+ArifKobel@users.noreply.github.com>
Co-authored-by: Arif Kobel <arif.kobel@phorax.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-03-24 13:46:38 -07:00
github-actions[bot] 80a8a34a73 Version Packages (#290)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@chat-adapter/discord@4.21.0 @chat-adapter/whatsapp@4.21.0 @chat-adapter/telegram@4.21.0 @chat-adapter/teams@4.21.0 @chat-adapter/state-redis@4.21.0 @chat-adapter/state-pg@4.21.0 @chat-adapter/state-memory@4.21.0 @chat-adapter/state-ioredis@4.21.0 @chat-adapter/slack@4.21.0 @chat-adapter/shared@4.21.0 @chat-adapter/linear@4.21.0 @chat-adapter/github@4.21.0 @chat-adapter/gchat@4.21.0 chat@4.21.0
2026-03-23 08:04:36 -07:00
Malte Ubl d778f722af Make adapters depend on chat as a real dep (#289)
Without this, changeset will make any dep change a major change
2026-03-23 07:57:42 -07:00
Malte Ubl 000792b8f8 Add webhook verification to GChat (#287)
- Issues a warning if required env vars are not present (also for telegram)
- Makes telegram use a time-safe verifier
2026-03-23 07:21:43 -07:00
Arif Kobel 9c498f4fd7 docs: fix typo "Committment" → "Commitment" (#274)
Co-authored-by: Arif Kobel <arif.kobel@phorax.com>
2026-03-21 16:14:00 -07:00
Malte Ubl 8337173704 Programmatically ensure that SKILL.md is always up-to-date and compiles (#272)
* Update the SKILL.md and add a test that checks its consistency

* Refactor docs test

* Typed and knip
2026-03-19 16:45:38 -07:00
Malte Ubl af8c9580ac Revert "Revert "Test TS compilation of all docs (#264)" (#265)" (#266)
This reverts commit d90f50c1a3.
2026-03-17 15:17:36 -07:00
Malte Ubl d90f50c1a3 Revert "Test TS compilation of all docs (#264)" (#265)
This reverts commit 449850f8b8.
2026-03-17 15:16:33 -07:00
Malte Ubl 449850f8b8 Test TS compilation of all docs (#264)
* Test TS compilation of all docs

* Lint
2026-03-17 15:11:52 -07:00
Malte Ubl d114ebfabf toAIMessages docs improvements (#263)
* toAIMessages docs improvements

* toAIMessages docs improvements
2026-03-17 14:38:23 -07:00
Hayden Bleasel 6646702128 Security fixes (#262)
* Bump Next

* Fix code scanning issues
2026-03-17 11:23:47 -07:00
Hayden Bleasel d2eac74d9a Change chat to peer dependency in all adapters (#258)
* refactor: move chat to peerDependencies for adapters

Update dependency to peerDependencies to avoid version conflicts.

Slack-Thread: https://vercel.slack.com/archives/C08077A6JDB/p1773694216309039?thread_ts=1773694216.309039&cid=C08077A6JDB
Co-authored-by: Hayden Bleasel <4142719+haydenbleasel@users.noreply.github.com>

* fix: use workspace:* for chat peerDependency in all adapters

Ensures adapters always require a compatible version of chat at publish time,
rather than the overly broad >=4.0.0 range.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: v0 <v0[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 11:07:35 -07:00
Hayden Bleasel 28c1f03828 Update docs (#255) 2026-03-16 12:20:08 -07:00
XLor e45a67f491 feat: add adpater disconnect hook (#219)
* feat: add adpater disconnect hook

* fix: use Promise.allSettled for resilient shutdown, add docs and changeset

- Use Promise.allSettled so one failing adapter doesn't prevent others from disconnecting
- Add test for error resilience during shutdown
- Remove unnecessary type guards in tests
- Fix lint: sort interface members, format test file
- Add changeset (minor bump for chat)
- Document disconnect hook in API docs and adapter building guide

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 12:17:37 -07:00
David Zhang 85a1d7f317 feat(telegram): convert entities to markdown in parsed messages (#232)
* feat(telegram): convert entities to markdown in parsed messages

Telegram delivers formatting (bold, italic, links, code, etc.) as separate
entity objects alongside plain text. Previously, parseTelegramMessage only
used the raw text, losing all entity information — most critically, text_link
entity URLs were dropped entirely.

This adds applyTelegramEntities() which reconstructs markdown syntax from
entities before storing the message text. Supported entity types: text_link,
bold, italic, code, pre, and strikethrough. Other entity types (url, mention,
bot_command) are already present in the text and left unchanged.

Also adds the missing `url` and `language` fields to TelegramMessageEntity.

* chore: add changeset for telegram entity markdown conversion

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 12:06:07 -07:00
Ben Sabic 1d36004d95 fix(telegram): set parse_mode for markdown messages (#245)
* fix(telegram): set parse_mode for markdown messages

postMessage and editMessage only set parse_mode when a card was present,
causing markdown messages to render as plain text with visible formatting
characters. Now also sets parse_mode when the message has a markdown field.

Closes #226

* refactor: extract resolveParseMode helper to deduplicate postMessage/editMessage

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 11:54:23 -07:00
Ray Arayilakath 95fd8ce055 fix(chat): correctly type thread and channel interface (#242)
* fix(chat): correctly type thread and channel interface

* fix(chat): nit make comment match `Message` comment for consistency

* chore: add changeset for thread/channel type fix

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 11:51:38 -07:00
Ben Sabic 13ba1c73b8 fix: rename step-finish to finish-step for AI SDK v5+ compatibility (#244)
AI SDK v5 renamed the `step-finish` event to `finish-step`, causing
`fromFullStream()` to silently ignore step boundaries and concatenate
multi-step agent output without paragraph separators.

Closes #238

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-03-16 11:41:12 -07:00
Ray Arayilakath b095fe2e21 docs: fix incorrect reference to c.executionCtx.waitUntil (#249) 2026-03-16 11:39:12 -07:00
github-actions[bot] dcd7af3656 Version Packages (#251)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@chat-adapter/discord@4.20.2 @chat-adapter/whatsapp@4.20.2 @chat-adapter/telegram@4.20.2 @chat-adapter/teams@4.20.2 @chat-adapter/state-redis@4.20.2 @chat-adapter/state-pg@4.20.2 @chat-adapter/state-memory@4.20.2 @chat-adapter/state-ioredis@4.20.2 @chat-adapter/slack@4.20.2 @chat-adapter/shared@4.20.2 @chat-adapter/linear@4.20.2 @chat-adapter/github@4.20.2 @chat-adapter/gchat@4.20.2 chat@4.20.2
2026-03-16 03:57:36 -04:00
Matan Kushner 01bd059058 fix(github): correctly trigger removeReaction in multi-tenant mode (#250) 2026-03-16 03:49:32 -04:00
Malte Ubl f612b44faf feat(slack): resolve @displayname mentions to <@USER_ID> in outgoing messages (#230)
* feat(slack): resolve @displayname mentions to <@USER_ID> in outgoing messages

Build a reverse index from display name → user IDs during lookupUser(),
track thread participants on incoming messages, and resolve @name mentions
to Slack's <@USER_ID> format before sending. Disambiguates using thread
participants when multiple users share a display name. Also increases
user/channel cache TTLs to 8 days.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(slack): use replace offset for duplicate mentions, invalidate cache on user_change

* Revert "fix(slack): use replace offset for duplicate mentions, invalidate cache on user_change"

This reverts commit 8cb0d908d2.

* fix(slack): use replace offset for duplicate mentions, invalidate cache on user_change (#236)

* fix(slack): use replace offset for duplicate mentions, invalidate cache on user_change

* style: align handleUserChange to async/await, expand changeset

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Matan Kushner <hello@matchai.dev>
2026-03-13 05:50:43 -07:00
github-actions[bot] f7cc3fa00f Version Packages (#228)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@chat-adapter/state-ioredis@4.20.1 @chat-adapter/gchat@4.20.1 @chat-adapter/telegram@4.20.1 @chat-adapter/teams@4.20.1 @chat-adapter/state-redis@4.20.1 @chat-adapter/state-pg@4.20.1 @chat-adapter/state-memory@4.20.1 chat@4.20.1 @chat-adapter/slack@4.20.1 @chat-adapter/shared@4.20.1 @chat-adapter/linear@4.20.1 @chat-adapter/discord@4.20.1 @chat-adapter/github@4.20.1 @chat-adapter/whatsapp@4.20.1
2026-03-12 10:04:30 -07:00
Matan Kushner 8d88b8c0e2 fix(github): accumulate stream before posting, log fallback edit errors (#227)
* fix(github): accumulate stream before posting, log fallback edit errors

The GitHub adapter relied on chat-sdk's default fallbackStream which
posts a placeholder then edits it every 500ms. GitHub returns 422 on
these edits because body is empty during TTFT, and rapid edits risk
secondary rate limits.

Add stream() to GitHubAdapter that accumulates the full text before
posting once. Also log fallbackStream edit errors instead of silently
swallowing them.

* test: add tests for GitHub adapter stream() and fallbackStream logging

* style: format with biome

* refactor: use Logger instead of console.warn in fallbackStream

Plumb the Chat logger into ThreadImpl so fallbackStream uses the
structured logger instead of raw console.warn.

* test: simplify fallbackStream logging test

Reuse mockLogger from mock-adapter.ts and rely on createMockAdapter's
default editMessage mock instead of re-specifying the resolved value.

* test: use vi.mocked, drop redundant stream=undefined

* chore: add changeset

* refactor: extract accumulateStream utility, deduplicate GitHub and WhatsApp adapters

* Revert "refactor: extract accumulateStream utility, deduplicate GitHub and WhatsApp adapters"

This reverts commit 0a3ca3cc9a.
2026-03-12 08:31:42 -07:00
Malte Ubl 97be8a9ac9 feat(slack): resolve bare channel mentions to display names (#229)
* feat(slack): resolve bare channel mentions to display names

Mirror the existing lookupUser() pattern to resolve bare <#C123> channel
mentions into <#C123|channelName> via conversations.info API with caching.
Channels with existing labels (<#C123|general>) are left unchanged.

Also fix 22 pre-existing test timeouts in sandboxed environments by
adding botUserId to skip real auth.test calls, and mocking users.info
where parseSlackMessage/handleSlashCommand calls lookupUser.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Replay test

* changeset

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-12 08:29:29 -07:00