Commit Graph

868 Commits

Author SHA1 Message Date
Salman Shaikh 2ffed48bea feat: make adapter internals protected to enable subclassing (#475)
* chore: changed class function access to protected

* chore: format

* chore: added changeset

* chore(changeset): drop unchanged shared package, expand description

* refactor(adapters): keep internal state private, only protected for extension surface

Narrow scope of #475: caches, polling/runtime state, and one-shot warning
flags stay private. Methods and shared helpers (logger, formatConverter,
chat, config) remain protected as the documented extension surface.

Also fixes a typecheck failure where gchat's oauth2Client (now private)
no longer requires a portable type for the emitted .d.ts.

* test(adapters): add subclass extensibility tests

Each adapter now has a compile-time test that subclasses can access the
documented protected surface. If any of these members revert to private,
the test file fails to type-check.

* style: apply ultracite formatting to subclass tests

* style(slack): mark static cache TTL constants as readonly

These three protected static cache TTLs are configuration constants, not
mutable state. Marking them readonly prevents subclasses (the new extension
surface from this PR) from mutating values shared across every instance
in-process.

* test(adapters): document intent of subclass extensibility tests

Mirrors the inline comment from the Telegram subclass test across the other
nine adapter tests so future readers immediately understand these blocks are
type-only sentinels — they fail at typecheck (not vitest) if a member reverts
to private.

* docs(adapters): document subclassing for adapter customization

Adds a "Customizing an adapter via subclassing" section to the Adapters page
that walks through extending an official adapter to override a protected hook
(using the issue #433 Telegram processUpdate scenario as the canonical
example) and clarifies that private members are intentionally off-limits.

* refactor(linear): expose accessTokenExpiry to subclasses

The surrounding refreshClientCredentialsToken and ensureValidToken methods
are now protected, but accessTokenExpiry was kept private — meaning a
subclass overriding either method couldn't read or update the expiry without
calling super. Flipping it to protected lets subclasses fully reimplement
the token-refresh flow.

* chore(changeset): bump adapters from patch to minor

This PR adds a new, additive capability — subclassing official adapters to
override protected hooks. Per CONTRIBUTING.md, additive backward-compatible
features warrant a minor bump rather than a patch.

* docs(adapters): clarify subclassing surface stability

Correct the parenthetical describing what stays `private` (credentials are
now `protected`) and add a callout warning that the `protected` extension
surface is intentionally broader than the public API but not yet fully
stable, so subclass authors know to pin versions and prefer overriding
the smallest hook.

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-05-10 20:44:41 +10:00
Ben Sabic d2ec28c447 chore: Add Messenger logo to homepage (#473)
* chore: Add Messenger logo to homepage

* chore: Use Messenger brand colour for logo

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-05-09 19:41:53 +10:00
Vishal Yathish 2279f1db70 Revert "feat(slack): expose direct WebClient access via adapter.client" (#472)
* Revert "feat(slack): expose direct WebClient access via adapter.client (#471)"

This reverts commit 8366b8b0fb.

* Fix: The `createSlackAdapter()` helper function silently drops the `apiUrl` config field, so custom Slack API URLs (e.g., for GovSlack) are ignored when using the helper.

This commit fixes the issue reported at packages/adapter-slack/src/index.ts:5055

**Bug explanation:**

The `SlackAdapterConfig` interface defines an `apiUrl` field (line 166) that allows users to override the Slack Web API base URL — useful for GovSlack or self-hosted gateways. The `SlackAdapter` constructor reads this field at line 622:

```typescript
const slackApiUrl = config.apiUrl ?? process.env.SLACK_API_URL;
```

However, the `createSlackAdapter()` helper function (around line 5055) constructs a `resolved` config object that includes many fields from the user's config but omits `apiUrl`. This means when a user writes:

```typescript
createSlackAdapter({ apiUrl: "https://slack-gov.com/api/" })
```

The `apiUrl` is silently dropped and the `WebClient` is created without the custom URL. The `SLACK_API_URL` environment variable fallback still works (since it's checked in the constructor), but explicit config via the helper is lost.

This is clearly a bug — all other config fields are forwarded through the `resolved` object, and `apiUrl` was simply forgotten.

**Fix explanation:**

Added `apiUrl: config?.apiUrl,` to the `resolved` config object in `createSlackAdapter()`. This ensures the `apiUrl` value from user config is properly forwarded to the `SlackAdapter` constructor, matching the pattern used for all other optional config fields.

Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: visyat <vishal.yathish@gmail.com>

---------

Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
2026-05-09 02:21:33 -07:00
Ben Sabic 8366b8b0fb feat(slack): expose direct WebClient access via adapter.client (#471)
* feat(slack): expose direct WebClient access via adapter.client

Mirror the Linear and GitHub adapter pattern by exposing the underlying
@slack/web-api WebClient as `adapter.client` for any Web API call not
covered by the SDK's high-level methods.

Resolution order:
1. Token from the current request context (multi-workspace webhooks,
   `withBotToken()`).
2. The default `botToken` when configured as a static string or a
   synchronous resolver function.

Throws AuthenticationError outside of any context in multi-workspace
mode, or when `botToken` is configured as an async resolver. For both,
bind the token explicitly with `adapter.withBotToken(token, () => ...)`.

Internally, the existing private `client` field is renamed to `_client`
so the public getter can return per-token cached `WebClient` instances.
All internal API calls continue to route through `_client.foo(await
this.withToken(...))` unchanged. Also fixes `createSlackAdapter()`
silently dropping the `apiUrl` config field, surfaced by the new
apiUrl-propagation test.

* docs(slack): document direct WebClient access

Add Slack to the "Direct client access" section of the chat-sdk.dev
docs (api/chat.mdx, usage.mdx) alongside Linear and GitHub. Update the
multi-tenant Callout to spell out both Slack constraints — request
context required in multi-workspace mode, and `withBotToken()` required
when `botToken` is an async resolver.

Add a parallel "Direct WebClient access" section to the Slack adapter
README with a usage example, the token resolution order, and the
async-resolver workaround.

* feat(example): add Channel Info button using slack.client

Demonstrate the new direct WebClient access pattern in the nextjs-chat
demo with a "Channel Info (Slack)" button. The handler resolves the
Slack adapter from the action event, reaches into
`adapter.client.conversations.info` (channels:read scope, already in
the example manifest), and renders the result as a Card with channel
name, member count, topic, purpose, and the standard flags. Falls back
to a friendly message on non-Slack platforms.

* feat(example): add Pin Message button using slack.client.pins.add

Pin the welcome card itself via `adapter.client.pins.add({ channel,
timestamp: event.messageId })` to demonstrate calling a Slack Web API
endpoint not wrapped by the SDK. Adds the required `pins:write` scope
to the example Slack manifest.

* chore(example): render channel info as a table and include num_members

Replace the Fields/Section layout in the Channel Info card with a
two-column Table for a tidier presentation, and pass
`include_num_members: true` so the Members row is actually populated
(Slack's `conversations.info` omits it by default).

* test(slack): expand coverage for adapter.client

Adds three tests:

- Cache differentiation: distinct tokens produce distinct WebClient
  instances so per-workspace credentials never bleed across calls.
- apiUrl env var resolution: SLACK_API_URL is honored by the WebClient
  the new getter returns (covers GovSlack-style deployments).
- End-to-end multi-workspace token routing: a real block_actions
  webhook drives `processAction`, and the handler-side
  `event.adapter.client.token` matches the installation's bot token —
  proving the request-context-bound client works inside webhook
  dispatch.

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-05-09 18:59:39 +10:00
Ben Sabic 0adf3adef6 feat(tests): add @chat-adapter/tests test kit (#470)
* feat(tests): add @chat-adapter/tests test kit

New package providing Vitest factories, custom matchers, and a setup file for
people building Chat SDK adapters and bots.

Factories: createMockAdapter, createMockChatInstance, createMockState (with
working in-memory subscriptions/locks/KV/queues), createTestMessage,
mockLogger / createMockLogger.

Matchers: toHavePosted(threadId, textPattern?), toHaveDispatched(handler),
toBeSubscribedTo(threadId). Auto-register via the
'@chat-adapter/tests/setup' subpath in vitest setupFiles.

chat and vitest are peer dependencies. Adapter-specific helpers (e.g. signed
Slack webhook builders) belong in each adapter's own /testing subpath, not
in this kit.

* test(integration-tests): allow @chat-adapter/tests imports in README check

* docs: add Testing page covering @chat-adapter/tests

New content/docs/testing.mdx walks bot authors and custom-adapter authors
through the kit's factories, custom matchers, and setup file. Added under
the Usage section in the sidebar, after error-handling.

Cross-link from contributing/testing.mdx clarifying that the hand-rolled
patterns there are for repo contributors building first-party adapters,
while consumers of Chat SDK should use @chat-adapter/tests.

* test(integration-tests): allow @chat-adapter/tests imports in docs check

* fix(tests): match real Adapter.postMessage signature in toHavePosted

Adapter.postMessage is (threadId: string, message: AdapterPostableMessage)
— previously the matcher read args[0] as { id: string } and args[1] as
{ text: string }, neither of which match the actual SDK shape. The matcher's
own tests fed the same wrong shape into the mock so they passed locally
while the matcher silently failed against any real bot or adapter.

Now compares args[0] as a string threadId, and extracts a comparable string
from AdapterPostableMessage's union — strings directly, PostableMarkdown
.markdown, PostableRaw.raw, and PostableCard.fallbackText. PostableAst and
fallback-less cards aren't text-matchable; documented in the JSDoc.

Tests updated to call postMessage with the real signature and to cover
each comparable AdapterPostableMessage shape.

* test(tests): add smoke tests driving matchers against a real Chat

Construct a real `Chat` with a `createMockAdapter` + `createMockState` and
exercise `Chat.thread().post()` and `.subscribe()` end-to-end. The matchers
(toHavePosted, toBeSubscribedTo) then assert against the actual call shape
the SDK uses, so a future signature drift breaks here instead of silently
agreeing with whatever wrong shape lives in the unit tests.

This is the regression guard for the postMessage-shape bug fixed in the
prior commit: each new matcher in subsequent PRs should be paired with a
smoke case here.

* feat(tests): round out adapter mutation matchers

Adds toHaveEdited, toHaveDeleted, toHaveReactedWith, toHaveStartedTyping,
and toHavePostedToChannel — covering the common Adapter mutation surface
that bot authors assert on. Each matcher's signature was checked against
packages/chat/src/types.ts, and each is paired with a smoke case that
drives a real Chat through the corresponding Thread/Channel API so
signature drift breaks the smoke test instead of silently agreeing with
the unit tests.

Emoji matching accepts both plain strings and EmojiValue ({ name }).
Text matching reuses the same extraction rules as toHavePosted —
strings, PostableMarkdown.markdown, PostableRaw.raw, and
PostableCard.fallbackText. Documented in matcher JSDoc, README, and
the Testing docs page.

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-05-09 14:17:36 +10:00
Vishal Yathish c46fdb6b34 [slack] add support for external installation providers for bot token management (#467)
* [slack] add support for external installation providers for bot token management

* changeset

* fix(slack): correct stale 'Connex' comment to 'Vercel Connect'

* docs(slack): expand changeset with provider details and Enterprise Grid notes

* docs(slack): document installationProvider and Enterprise Grid lookup keys

* test(slack): cover installationProvider for interactive payloads on both install types

* fix(slack): route rehydrateAttachment through installationProvider

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-05-09 11:11:06 +10:00
Dima Voytenko 0f0c203165 fix(slack): prefer webhookVerifier over signingSecret (#468)
* fix(slack): prefer webhookVerifier over signingSecret and SLACK_SIGNING_SECRET

When `webhookVerifier` is configured, ignore both `config.signingSecret`
and the `SLACK_SIGNING_SECRET` env var. Previously a configured
`signingSecret` (or env var) would shadow the verifier the caller wired
up, which is the opposite of the intended override behavior.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(slack): update README to match new webhookVerifier precedence

Mirror the JSDoc/changeset wording: webhookVerifier wins over both
signingSecret and SLACK_SIGNING_SECRET. The previous note still claimed
signingSecret won, contradicting the behavior fixed in e77359b.

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-05-09 10:34:24 +10:00
github-actions[bot] 5edcbbf7ef chore(release): version packages (#464)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@chat-adapter/web@4.28.1 @chat-adapter/messenger@4.28.1
2026-05-08 04:09:48 -07:00
github-actions[bot] b3fc64d34e chore(release): version packages (#442)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-05-08 04:01:03 -07:00
josh 0cc3d06fd4 docs: fix stale API examples, adapter matrix, and broken links (#463)
* 1

* 2

* cs

* 3
2026-05-08 03:58:08 -07:00
Ben Sabic 2de905cb0c fix: regenerate pnpm-lock.yaml after merge conflict (#462)
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-05-08 17:40:23 +10:00
josh c1cd9b5da1 feat(chat): add callbackUrl to buttons and modals (#454)
* 1

* wfw

* 4224

* dfe

* wip

* f

* 22

* tsts

* more

* ch

* dc

* t

* tm

* docs

* ex

* k

* cs

* lock

* test(chat): expand callbackUrl coverage

* docs: document callbackUrl handling for adapter authors

* docs: expand changeset for callbackUrl feature

* docs(skill): mention callbackUrl on Button and Modal

* feat(example): add modal callbackUrl workflow demo

* test(integration): add replay tests for callbackUrl flows

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-05-08 17:26:18 +10:00
Vishal Yathish 68025ca965 [messenger] add messenger (meta) platform adapter to chat sdk (#461)
* [messenger] add messenger (meta) platform adapter to chat sdk

- Webhook handling with HMAC-SHA256 signature verification
- Generic and Button template support for cards
- Postback, reaction, delivery/read confirmation handling
- Message caching for fetchMessages (Messenger has no history API)
- Replay tests and ~98% code coverage

Co-authored-by: Dimitar K. Nikolov <mitkodkn@users.noreply.github.com>
Co-authored-by: Ben Sabic <27636870+bensabic@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix: The `@chat-adapter/messenger` package version is `4.15.0` while all other packages in the Changesets fixed version group are at `4.27.0`, breaking the fixed versioning contract.

This commit fixes the issue reported at packages/adapter-messenger/package.json:3

**Bug explanation:**

The repository uses Changesets with a `"fixed"` configuration: `[["chat", "@chat-adapter/*"]]`. This means all packages matching these patterns must always share the same version number. Every package in the group (`chat`, `@chat-adapter/discord`, `@chat-adapter/gchat`, `@chat-adapter/github`, `@chat-adapter/linear`, `@chat-adapter/shared`, `@chat-adapter/slack`, `@chat-adapter/teams`, `@chat-adapter/telegram`, `@chat-adapter/web`, `@chat-adapter/whatsapp`, and the state packages) is at version `4.27.0`, except `@chat-adapter/messenger` which is at `4.15.0`.

This is likely because the messenger adapter was newly added to the monorepo (copied from a template or created fresh) and its version was never aligned with the rest of the fixed group. This mismatch will cause problems with the Changesets release workflow — when Changesets tries to bump versions for the fixed group, it may produce inconsistent or errored releases because one package is 12 minor versions behind the others.

**Fix explanation:**

Changed `"version": "4.15.0"` to `"version": "4.27.0"` in `packages/adapter-messenger/package.json` to align it with all other packages in the fixed version group.

Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: visyat <vishal.yathish@gmail.com>

* Fix: Messenger adapter env var guard only checks `FACEBOOK_APP_SECRET` but `createMessengerAdapter` requires all three env vars, causing a `ValidationError` crash at Next.js build time when only `FACEBOOK_APP_SECRET` is set.

This commit fixes the issue reported at examples/nextjs-chat/src/lib/adapters.ts:154

**Bug Analysis:**

The build failure is confirmed in the Vercel build log with:
```
Error [ValidationError]: pageAccessToken is required. Set FACEBOOK_PAGE_ACCESS_TOKEN or provide it in config.
```

The root cause is in `examples/nextjs-chat/src/lib/adapters.ts` at line ~154. The messenger adapter guard only checks for `FACEBOOK_APP_SECRET`:
```typescript
if (process.env.FACEBOOK_APP_SECRET) {
```

However, `createMessengerAdapter` (in `packages/adapter-messenger/src/index.ts`) validates and throws `ValidationError` for each of three required env vars: `FACEBOOK_APP_SECRET`, `FACEBOOK_PAGE_ACCESS_TOKEN`, and `FACEBOOK_VERIFY_TOKEN`. When only `FACEBOOK_APP_SECRET` is set in the Vercel project environment, the guard passes, `createMessengerAdapter` is called, and it throws a `ValidationError` for the missing `FACEBOOK_PAGE_ACCESS_TOKEN`. Since this code runs at module evaluation time during the Next.js build's "Collecting page data" phase, the uncaught error crashes the entire build.

This is inconsistent with other adapters in the same file. For example, the WhatsApp adapter checks both `WHATSAPP_ACCESS_TOKEN` and `WHATSAPP_PHONE_NUMBER_ID`, and the gchat/github/linear/whatsapp adapters all wrap creation in try-catch blocks.

**Fix:**

1. Updated the env var guard to check all three required environment variables (`FACEBOOK_APP_SECRET`, `FACEBOOK_PAGE_ACCESS_TOKEN`, and `FACEBOOK_VERIFY_TOKEN`) before attempting to create the adapter.
2. Wrapped the `createMessengerAdapter` call in a try-catch block (matching the pattern used by gchat, github, linear, and whatsapp adapters) so that any unexpected validation errors are caught and logged as warnings instead of crashing the build.

Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: visyat <vishal.yathish@gmail.com>

---------

Co-authored-by: Dimitar K. Nikolov <mitkodkn@users.noreply.github.com>
Co-authored-by: Ben Sabic <27636870+bensabic@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
2026-05-08 17:25:54 +10:00
Ben Sabic e48d8cec8f ci: pin GitHub Actions to commit SHAs (#460)
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-05-07 18:50:16 -07:00
josh eb5f94a8ee feat(chat): add message.subject and adapter client access (#459)
* 1

* w

* 3f

* x

* ln

* gh

* sl

* t1

* u

* t2

* t3

* t4

* t5

* d

* d2

* cs

* fx

* cl

* docs: clean up subject + .client docs and restructure nav

- subject.mdx: simplify prose, drop redundant platform lists, link to MessageSubject API and getAdapter
- api/message.mdx: add MessageSubject TypeTable
- api/chat.mdx: expand getAdapter with Direct client access content
- adapters.mdx: add Parent subject and Native client rows to feature matrix
- usage.mdx: mention .client under Accessing adapters
- adapter-github/-linear READMEs: add Direct API client section
- meta.json: split Features into Messaging + Interactivity, move error-handling to Usage
- title case across messaging-cluster page titles

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-05-08 10:51:53 +10:00
Max 36864dae41 fix(docs): scroll code blocks in adapter READMEs (#453)
* fix(docs): scroll code blocks in adapter READMEs

* fix(docs): preserve right padding on scroll-end
2026-05-06 09:51:12 -07:00
Max 3cfb77fa50 feat(docs): render GFM alerts as Callout (#452) 2026-05-06 09:49:38 -07:00
dependabot[bot] 3e4764db4d build(deps-dev): bump postcss from 8.5.10 to 8.5.11 (#451)
Bumps [postcss](https://github.com/postcss/postcss) from 8.5.10 to 8.5.11.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.5.10...8.5.11)

---
updated-dependencies:
- dependency-name: postcss
  dependency-version: 8.5.11
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-05 17:33:20 -07:00
Ben Sabic 3490a8c84c feat: add @chat-adapter/web — browser chat UI for chat-sdk bots (#444)
* feat(chat): expose awaitable Promise from processMessage

Return the inner task as Promise<void> instead of void so streaming
adapters can await full handler completion and surface user-handler
rejections at the wire level. waitUntil semantics for existing webhook
adapters are unchanged — the SDK still tracks the work with errors
swallowed (and logged) so platforms don't retry on handler bugs.

Required by @chat-adapter/web, whose response body is the user
handler's stream.

* feat(adapter-web): add @chat-adapter/web package

A new platform adapter that lets a chat-sdk bot serve a browser chat
UI alongside Slack/Teams/Discord/etc. without writing any client-side
glue. Speaks the AI SDK UI message stream protocol, so @ai-sdk/react's
useChat and the ai-elements component library work out of the box.

- `@chat-adapter/web` — server: createWebAdapter({ userName, getUser })
- `@chat-adapter/web/react` — client: useChat() preconfigured with
  DefaultChatTransport against /api/chat (override via `api`)

Defaults that matter for v1:
- `isDM: true` — every web message routes through onDirectMessage
- `persistMessageHistory: true` — chat-sdk caches each turn in the
  configured state adapter so handlers can read prior context via
  thread.messages / channel.messages (no platform history API exists)
- channelId === threadId — web has no separate channel concept; this
  prevents cross-conversation bleed when a single user has multiple
  useChat sessions
- Native `adapter.stream` implementation pumps text-deltas straight
  onto the SSE response — no post+edit fallback

Out of scope for v1: cards/JSX rendering, reactions, modals, file
uploads, edit/delete, multi-tab proactive push.

* feat(example-nextjs-chat): wire up web adapter and add /chat page

- Register the web adapter in lib/adapters.ts with a demo getUser
  (single shared identity — replace with NextAuth/Clerk/cookie auth
  in production)
- Expose POST /api/chat backed by bot.webhooks.web (using next/after
  for waitUntil)
- Add a minimal /chat page using @chat-adapter/web/react's useChat —
  same bot.onDirectMessage handler that powers Slack now powers the
  browser too

Bumps `ai` to ^6.0.174 to align with @ai-sdk/react@^3 (avoids dual
provider-utils versions in the workspace).

* docs: list @chat-adapter/web in registry

- Add an entry to adapters.json so the package shows up on /adapters
- Add a globe SVG to lib/logos.tsx and wire it into the icon map
- Mention the new adapter in docs/adapters.mdx

* feat(adapter-web): tighten request handling and message construction

- Reject user ids containing ':' with HTTP 400 — the character would
  corrupt the thread-id round-trip through decodeThreadId
- Skip emitting text-start/text-end in postMessage when the resolved
  text is empty so useChat doesn't render blank assistant bubbles
- Derive the parseMessage author from raw.role so rehydrated assistant
  messages report the bot identity instead of "unknown"
- Drop the duplicate handler-error log; chat.processMessage already
  logs at ERROR level
- Document the actual persistMessageHistory default (true) and the
  state-cache rationale; promote the fetchMessages no-op rationale
  into its JSDoc

* test(adapter-web): add direct coverage for stream()

- Aborting request.signal mid-stream short-circuits the iterator and
  still writes text-end via the finally block
- Non-text StreamChunks (task_update, plan_update) are dropped without
  emitting any delta
- The SentMessage returned from thread.post matches the id used in
  text-start / text-end events

* docs(adapter-web): expand README into the full adapter docs page

The docs site renders each adapter's README, so flesh out
@chat-adapter/web to match the depth of @chat-adapter/slack:
authentication boundary, threading semantics, streaming,
persistence, React hook reference, configuration table,
feature matrix, and troubleshooting.

* docs(adapter-web): drop unsupported provider import from streaming example

* fix(adapter-web): validate conversationId for reserved colon character

* fix(example): show error state in web chat demo

* fix(example): add thinking indicator to web chat demo

* feat(example): redesign web chat demo with tailwind

* chore: remove redundant changeset

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: dancer <josh@afterima.ge>
2026-05-05 15:55:40 -07:00
Ben Sabic d7999aba26 chore: add Vercel Code Approvers files mirroring CODEOWNERS (#450)
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-05-05 15:47:48 -07:00
Ben Sabic 46d183bdab feat(chat): add Transcripts API + rename per-thread cache to threadHistory (#448)
* feat(chat): add Transcripts API and rename per-thread cache to threadHistory

Introduce `bot.transcripts` for cross-platform per-user message persistence.
When `ChatConfig.transcripts` and `ChatConfig.identity` are configured, every
inbound message has its `userKey` resolved during dispatch and the API exposes
`append` / `list` / `count` / `delete` keyed by that user. Backed by the
existing `StateAdapter.appendToList` primitive — every built-in state adapter
supports it with no contract changes.

Rename the existing per-thread history cache from `messageHistory` to
`threadHistory` (with backwards compat for `ChatConfig.messageHistory` and
`Adapter.persistMessageHistory`) so the two persistence layers don't share a
"messages" name. The state-adapter storage key prefix is unchanged so existing
data isn't orphaned.

`delete()` writes a tombstone via `appendToList(key, _, { maxLength: 1 })`
rather than `state.delete(key)`, because `state.delete` only addresses the
k/v namespace on every non-memory state adapter — `list()` and `count()`
filter the tombstone out so the API contract is preserved.

* docs(chat): cover Transcripts API and Conversation history

Add a Features-style "Conversation history" guide (`/docs/conversation-history`)
walking through identity resolution, the LLM-context append/list pattern,
filtering, and per-user deletion for DSR flows.

Add an API reference page at `/docs/api/transcripts` with `<TypeTable>` blocks
for `ChatConfig.transcripts`, `ChatConfig.identity`, every method on
`bot.transcripts`, and the `TranscriptEntry` shape.

Wire both into the corresponding `meta.json` files.

* example(nextjs-chat): wire Transcripts API into the AI mode handler

Replace the brittle `threadState.history` shim with `bot.transcripts.list({ ..., threadId, limit })` as the fallback context source for platforms without
`fetchMessages` (Telegram, WhatsApp). Drop the `history` field from
`ThreadState` accordingly.

Add a hardcoded `TEST_USER_KEY = "test-user"` so the API can be exercised
without juggling real user identities, plus "Show Transcripts" and
"Clear Transcripts" buttons in the welcome card so the store can be
inspected and reset from chat.

* fix(chat): tighten Transcripts API public surface and wiring

Polish on top of the Transcripts API + threadHistory rename, addressing
review concerns before merge.

Public surface (`types.ts`, `index.ts`):
- Expose `transcripts` on the `ChatInstance` interface so callers typed
  against the public interface can reach `bot.transcripts`.
- Promote the `count` argument to a named `CountQuery` interface,
  matching `DeleteTarget` / `ListQuery`. Exported from `index.ts`.
- Document on `TranscriptsApi.list()` that pagination is intentionally
  out-of-scope — the store keeps at most `maxPerUser` entries per user.
- Reconcile the `TranscriptEntry.id` JSDoc with the implementation:
  UUID assigned at append time, returned in append order, not
  lexicographically sortable; use `timestamp` for cross-store ordering.

Wiring (`chat.ts`):
- Include `threadId` in the identity-resolver failure log context so
  operators can correlate failures with the source thread.

Stale-reference sweep:
- Replace lingering "Messages API" / `chat.messages` /
  `messages.storeFormatted` strings in shipped JSDoc with the new
  `transcripts` names (these ride into `.d.ts` and are user-visible).
- Fix the dead `[Messages API](./messages.ts)` link in the existing
  thread-history-rename changeset.

* test(chat): cover dual-read precedence, resolver edges, concurrent ops

Fill gaps in the Transcripts API + threadHistory rename test suite:

`chat.test.ts` (persistThreadHistory block):
- top-level `config.messageHistory` (deprecated alias) flows through
  to the per-thread cache when `threadHistory` is unset
- `threadHistory` takes precedence over `messageHistory` when both are
  set — pinned by asserting `appendToList` receives the new config's
  `maxLength` / `ttlMs`
- both `persistThreadHistory` and `persistMessageHistory` set on the
  adapter still triggers persistence

`transcripts-wiring.test.ts`:
- sync resolver returning a plain string populates `message.userKey`
- resolver returning `""` is treated as no userKey (truthy check at
  the dispatch hook would silently flip if a future change moved to
  `!== undefined`)

`transcripts.test.ts`:
- concurrent append/delete/append interleave preserves invariants:
  `count()` and `list()` agree (no tombstone leak), no pre-delete
  entry survives, and the post-delete result is bounded by the two
  concurrent appends

* docs(chat): use named types in Transcripts API reference

- `formatted` rows in the AppendInput / TranscriptEntry TypeTables
  now render `FormattedContent | undefined` (the alias actually
  exported from `chat`), instead of `Root | undefined` which would
  force readers to pull the type from `mdast` directly.
- `count` signature uses the new `CountQuery` named type, with a
  one-liner describing its single field.

* example(nextjs-chat): fix stale comment on transcripts demo handler

The action handler was relabelled to `transcripts` when it was wired
to `bot.transcripts.list`, but the leading comment still read
"Demonstrate fetchMessages and allMessages" from the previous
iteration. Update it to describe the transcripts demo.

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-05-05 15:25:37 -07:00
Raimond Lume 3546b3fddb feat(slack): use native markdown_text for outgoing messages (#440)
* feat(slack): use native markdown_text field for outgoing messages

Slack now natively renders markdown via the `markdown_text` parameter on
chat.postMessage / postEphemeral / update / scheduleMessage and via
response_url payloads. The adapter passes markdown through directly instead
of converting to mrkdwn.

- Tables, headings, code fences, blockquotes, and nested lists render
  natively in Slack instead of falling back to ASCII / mrkdwn.
- `string` and `{ raw }` messages still go to `text` (preserves literal `*`).
- `{ markdown }` and `{ ast }` messages go to `markdown_text` (12k char limit).
- `renderWithTableBlocks`, `toBlocksWithTable`, `mdastTableToSlackBlock`,
  and the AST→mrkdwn renderer (`fromAst` / `nodeToMrkdwn`) are removed.
- `SlackMarkdownConverter` alias is removed; use `SlackFormatConverter`.
- `renderFormatted(ast)` now returns standard markdown (was mrkdwn).
- Incoming `message` events still arrive as mrkdwn and are parsed unchanged.

Net -473 lines across markdown.ts and the five sender call sites.

* fix(slack): use mrkdwn fallback for response_url edits
2026-05-05 14:12:22 -07:00
josh f46a6fb0bc fix(telegram): apply MarkdownV2 entity safety trim to streaming chunks (#446) 2026-05-04 14:00:28 -07:00
Ben Sabic aa1b08a246 docs: cover gchat verification, linear token encryption, shared crypto helpers (#445)
* docs: cover gchat verification, linear token encryption, shared crypto helpers

The adapter hardening pass in #441 made gchat JWT verification required,
added optional at-rest encryption for Linear OAuth tokens, and promoted
the AES-256-GCM helpers into @chat-adapter/shared. Update the affected
package READMEs and the adapter-authoring guide to match.

* docs(adapter-shared): correct EncryptedTokenData field names

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-05-04 13:56:10 -07:00
Malte Ubl 9824d3335e Adapter hardening pass (#441)
* Adapter hardening pass

* chore: remove redundant changeset

---------

Co-authored-by: dancer <josh@afterima.ge>
2026-05-02 08:37:45 -07:00
josh d9c2fcaf93 docs: update documentation for 4.27.0 release (#439)
* docs: update documentation for 4.27.0 release

* docs: fix broken apiUrl link in adapters feature matrix

* docs: fix error code thrown-by columns and add missing UNKNOWN_USER_ID_FORMAT
2026-05-01 07:12:42 -07:00
dependabot[bot] 7b4480af61 build(deps): bump pnpm/action-setup from 5 to 6 (#437)
Bumps [pnpm/action-setup](https://github.com/pnpm/action-setup) from 5 to 6.
- [Release notes](https://github.com/pnpm/action-setup/releases)
- [Commits](https://github.com/pnpm/action-setup/compare/v5...v6)

---
updated-dependencies:
- dependency-name: pnpm/action-setup
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-01 06:37:50 -07:00
Ben Sabic 2797b49a2c docs: update opengraph image (#438)
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-05-01 06:36:46 -07:00
josh a5cf3ceba1 ci: remove vercel deploy step from release workflow (#436) 2026-04-30 14:44:20 -07:00
github-actions[bot] f55378a3d8 chore(release): version packages (#378)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@chat-adapter/shared@4.27.0
2026-04-30 13:41:37 -07:00
josh ded6f78088 fix(slack): enrich link previews with unfurl metadata from attachments (#395)
* fix(slack): enrich link previews with unfurl metadata from attachments

* fix(slack): add trailing slash normalization for unfurl URL matching

* fix(slack): store unfurl metadata from message_changed and enrich subsequent messages

* fix(slack): poll for unfurls so link metadata survives the message_changed race
2026-04-30 13:19:22 -07:00
josh 39a3863d4d fix(docs): exclude opengraph-image.png from proxy matcher (#435) 2026-04-30 12:56:25 -07:00
David Harvey 733f3feeea docs: add Blooio iMessage adapter (#434)
Made-with: Cursor
2026-04-30 19:21:38 +10:00
mdnanocom 8a0c7b308d [chat] fix Slack streaming team ID for interactive payloads (#330)
* [chat] fix Slack streaming team ID for interactive payloads

* chore: downgrade changeset to patch

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-04-29 08:17:05 -07:00
Aamir Jawaid ed46bae52e feat(adapter-teams): native streaming for DMs via emit (#416)
* feat(adapter-teams): use native Teams SDK streaming for DMs

Use ctx.stream.emit() from the Teams SDK for DM streaming instead of
manual post+edit. This sends proper typing activities with streamType
channelData, giving the native streaming UI in Teams.

- Capture IStreamer from activity context in handleMessageActivity
- Block handler with deferred promise so stream stays alive during processing
- streamViaEmit() for DMs: uses stream.emit() with incremental text deltas
- Group chats: accumulate full response and post as single message (no flicker)
- Handle StreamCancelledError and stream.canceled for graceful cancellation

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(tests): update Teams streaming assertions for accumulate-and-post

Group chats now accumulate streamed chunks and post as a single message
instead of post+edit, so assertions should check sentActivities not
updatedActivities.

* style: format replay-streaming test

* chore: add changeset for teams native streaming

---------
2026-04-29 08:08:29 -07:00
Aamir Jawaid 7a67ff5997 docs(adapter-teams): simplify bot setup using Teams CLI (#402)
* docs(adapter-teams): simplify bot setup using Teams CLI

Replace manual 6-step Azure portal walkthrough with Teams CLI commands.
`teams app create` handles AAD registration, secret generation, bot
registration, and channel setup in a single command. Also updates RSC
permission and troubleshooting sections to reference CLI equivalents.

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

* docs(adapter-teams): remove bot migration section

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

* docs(adapter-teams): clarify message history permissions by context

RSC permissions cover channels and group chats (no admin consent).
Azure AD Chat.Read.All is only needed for DM history. Add permission
table and az CLI commands for DM setup.

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

* docs(adapter-teams): simplify bot setup using Teams CLI

Replace manual 6-step Azure portal walkthrough with Teams CLI commands.
Correct message history permissions: RSC for channels/group chats,
Azure AD only for DM history. Add local dev tunnel tip.

* docs(adapter-teams): add teams status step after login

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 07:59:08 -07:00
Ben Sabic a520797922 feat: add chat.getUser() for cross-platform user lookups (#391)
* feat: add chat.getUser() for cross-platform user lookups

Add UserInfo type and optional getUser() method to the Adapter interface.
Implement on Slack (extends existing lookupUser with email/avatar),
Discord, Google Chat, GitHub, Linear, and Telegram adapters.

Add "Who Am I" button to the example app demonstrating the feature.
Update docs with getUser API reference and usage examples.

* fix(chat): improve getUser across slack and gchat adapters

- slack: return null from lookupUser on failure instead of fallback
  object, removing the isBot === undefined sentinel in getUser
- slack: use image_192 instead of image_72 for better avatar quality
- gchat: cache avatarUrl from webhook sender payload
- gchat: return avatarUrl in getUser response
- gchat: fix tests to use current cache format with isBot field
- docs: document null return, fix example to use message.author

* chore: fix lint

* docs(chat): include Microsoft Teams in getUser supported adapters list

* feat(adapter-teams): add getUser() support (#404)

* feat(adapter-teams): add getUser() via Microsoft Graph API

- Cache aadObjectId from activity.from during webhook handling
- Implement getUser() using Graph GET /users/{user-id} endpoint
- Requires User.Read.All application permission
- Returns null gracefully when user hasn't interacted or Graph call fails

* docs: add getUser() section to Teams adapter README

* chore: apply ultracite formatting to adapter-teams getUser

* fix(chat): cover all 7 adapters in getUser inference and document per-platform constraints

---------

Co-authored-by: dancer <josh@afterima.ge>
2026-04-29 07:58:33 -07:00
josh 70281dc58f feat(chat): add initialOption and option_groups to ExternalSelect (#410)
* feat(chat): add initialOption and option_groups to ExternalSelect

* docs(modals): document ExternalSelect initialOption and option_groups, truncate group label to 75 chars
2026-04-28 09:32:15 +10:00
dependabot[bot] de55c1e217 build(deps-dev): bump postcss from 8.5.6 to 8.5.10 (#424)
Bumps [postcss](https://github.com/postcss/postcss) from 8.5.6 to 8.5.10.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.5.6...8.5.10)

---
updated-dependencies:
- dependency-name: postcss
  dependency-version: 8.5.10
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-27 12:25:41 -07:00
Ben Sabic e4908d8eb2 docs(state-pg): disclose chat_state_lists and chat_state_queues tables (#430)
* docs(state-pg): disclose chat_state_lists and chat_state_queues tables

The Data model section in the @chat-adapter/state-pg README listed only
three tables, but ensureSchema() also creates chat_state_lists and
chat_state_queues. Add them so the docs accurately describe what the
adapter writes to the database.

Closes #428

* docs(state-pg): document expired row cleanup for lists and queues

Extend the "Expired row cleanup" section to cover chat_state_lists and
chat_state_queues — both have expires_at columns. Note that queue
entries are purged opportunistically per-thread on enqueue/dequeue, and
list entries are filtered on read but never deleted, then add both
tables to the suggested periodic cleanup SQL.

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-04-27 12:23:53 -07:00
Ben Sabic 90cd8f181a chore: refresh release workflow, community health files, and agent docs (#431)
* ci(release): pin changesets/action and enable npm provenance

Pin changesets/action to the v1.7.0 commit SHA, switch to GitHub-API
commit mode (signed commits via the API), set the version PR commit
and title to a conventional "chore(release): version packages", and
enable npm provenance attestations on publish via NPM_CONFIG_PROVENANCE.

* chore: add CODEOWNERS

Default ownership goes to @vercel/chat-sdk; release-plumbing paths
(release workflow, changeset config, CODEOWNERS itself) stay locked
to @cramforce since the publish workflow is bound to npm Trusted
Publisher by filename.

* docs: add SUPPORT.md and tidy issue contact links

Add a SUPPORT.md pointing users to docs, the issue chooser, and the
security advisory flow. Also update .github/config.yml: point the
Documentation contact link at chat-sdk.dev/docs (was a github.com
README anchor) and remove the GitHub Discussions entry, which 404s
because Discussions isn't enabled on the repo.

* chore: add docs issue and adapter request templates

Two new issue templates so reports come in pre-shaped:
- Documentation Issue — page/section, type (typo, outdated, missing,
  broken link, etc.), description, suggested fix.
- Adapter Request — platform name, adapter type (platform/state), API
  docs link, use case, existing community work, willingness to help
  maintain.

* docs(contributing): point contributors at issue templates and SUPPORT.md

Add a "Reporting issues" section at the top of CONTRIBUTING.md that
links to the issue chooser (now covering bugs, features, docs issues,
and adapter requests) and to SUPPORT.md for general questions, with
the SECURITY.md private-disclosure path called out separately.

* chore: add pre-merge checklist to PR template

Adds four self-attestation boxes contributors can tick before
requesting review, surfacing requirements that already live in
CONTRIBUTING.md so they're not forgotten:

- Signed and verified commits (CONTRIBUTING explicitly bounces PRs
  with unsigned commits).
- `pnpm validate` passes (lint, typecheck, tests, build in one go).
- Changeset added when a package's behavior changes.
- Docs updated for user-facing changes.

The "or N/A" wording on the last two avoids forcing a yes for
internal-only or docs-only PRs.

* chore: add Telegram and WhatsApp to bug report platform dropdown

The bug report platform dropdown was missing Telegram and WhatsApp,
which both have official adapters (@chat-adapter/telegram and
@chat-adapter/whatsapp). Reporters had to fall back to "Other" for
bugs in those adapters, losing the platform signal.

* docs(readme): fix CONTRIBUTING link path and add Support section

The Contributing section linked to ./CONTRIBUTING.md, but the file
actually lives at .github/CONTRIBUTING.md, so the link 404'd on
github.com. Repoint it.

Also add a Support section linking SUPPORT.md (general help) and
SECURITY.md (private vulnerability reporting) so those community
health files are reachable from the repo entry point instead of
only via GitHub's auto-surfacing.

* docs(contributing): add adapter authoring, commit conventions, and docs sections

Three additions to CONTRIBUTING.md to round out the file alongside
the recently added issue templates and PR checklist:

- "Building your own adapter" — points contributors who hit the
  Adapter Request template at the existing community-adapter guide
  on chat-sdk.dev rather than leaving them to discover it.
- "Commit messages" — codifies the Conventional Commits style the
  repo already uses; the release workflow now relies on the
  "chore(release): version packages" convention for its auto-PR,
  so consistency in new commits keeps changelogs predictable.
- "Updating documentation" — names the apps/docs/content/docs/
  source path, links the live site, and shows the local preview
  command so the PR-template "Documentation updated" checkbox is
  actionable.

* docs: trim agent docs, rename CLAUDE.md to AGENTS.md, add CLAUDE.md pointer

Move the agent guidance to AGENTS.md (the cross-tool convention) and
leave CLAUDE.md as a one-line "@AGENTS.md" pointer so Claude Code
keeps auto-loading the same content.

While renaming, trim and update the file:

- "packages/chat-sdk" was wrong — directory is "packages/chat", npm
  name is "chat".
- Updated the package list to include adapter-{discord,telegram,
  github,linear,zoom,shared}, state-{ioredis,pg}, integration-tests,
  and the apps/docs and examples/nextjs-chat trees.
- Replaced the verbose recording-and-replay jq walkthrough with a
  one-line pointer to the integration-tests README.
- Dropped the duplicated Changesets walkthrough — full guidance now
  lives in CONTRIBUTING.md.
- Condensed ~120 lines of generic Ultracite/Biome rules to a short
  list of non-obvious gotchas (Biome enforces the rest automatically).
- Added Conventional Commits (load-bearing for the release workflow's
  auto-PR), the apps/docs/content/docs/ docs path with the "pnpm
  --filter docs dev" preview command, a pointer to the community
  health files, and POSTGRES_URL/DATABASE_URL for the new state-pg
  adapter.

Net: 344 → ~125 lines.

* docs(nav): rename "Source" link to "GitHub"

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-04-27 05:22:35 -07:00
Ben Sabic b0ab804f18 - Bundle guide markdown and a templates manifest with the chat package at resources/guides/*.md and resources/templates.json so AI agents can discover Chat SDK resources offline (#423)
- Add scripts/sync-resources.ts (run via pnpm sync-resources) that reads apps/docs/resources-edge-config.json, fetches each guide's .md version over https with a timeout and size cap, writes templates.json, and regenerates the Available resources block in skills/chat/SKILL.md
- Migrate the Slack Next.js, Discord Nuxt, and Hono code-review guides from on-site MDX to Vercel KB and register them in the resources edge-config JSON alongside the existing external guides
- Remove /docs/guides MDX content, sidebar entries, top-level Guides nav entry, getting-started cards, and the dead /guides/ branch in the sitemap route now that all guides live externally and are surfaced on /resources
- Replace the homepage Guides/Templates section and the standalone Adapters pill section with a single two-column Resources + Adapters section (icons, headings, descriptions, outline buttons, divider), and drop the URL footer from ResourceCard on the Resources page
- Update skills/chat/SKILL.md to point at resources/guides and resources/templates.json and list the available guides and templates between marker comments that sync-resources rewrites
- Add tsx to knip's ignoreBinaries so npx tsx in the new script does not fail lint

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: dancer <josh@afterima.ge>
2026-04-27 10:20:44 +10:00
josh b0db3cab19 fix(docs): stack get-started cta and snippet on narrow viewports (#429) 2026-04-27 09:13:30 +10:00
Dima Voytenko 2531e9cff5 feat(slack): dynamic botToken resolver and custom webhookVerifier (#421)
* feat(slack): dynamic botToken resolver and custom webhookVerifier

Allow `botToken` to be a function returning `string | Promise<string>`
so apps can rotate or lazily fetch tokens; the resolver is invoked per
API call. Add `webhookVerifier: (request) => string | Promise<string>`
as an alternative to `signingSecret` for custom request verification —
returns the verified body text or throws to produce a 401.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* change verifier signature to make it compatible with  function

* make scheduleMessage cancel() rotation-safe and honor verifier body

- scheduleMessage cancel(): re-resolve token in single-workspace mode so
  rotation works. Slack rotated tokens have a 12h TTL and scheduled messages
  can outlive their schedule-time token, leaving cancel() with stale auth.
  Multi-workspace still snapshots ctx.token since cancel() runs outside
  the AsyncLocalStorage frame.
- webhookVerifier: when it returns a string, use it as the verified body
  for downstream parsing. JSDoc previously implied this contract; the code
  only checked truthiness.
- webhookVerifier JSDoc: explicit SECURITY note that timestamp/replay
  protection is the implementer's responsibility when bypassing signingSecret.
- Tests: cover Attachment.fetchData snapshot semantics — multi-workspace
  uses the ctx token captured at attachment creation; single-workspace
  re-resolves the default provider per fetch (rotation-safe).

* docs(slack): document botToken resolver and webhookVerifier in README

* opt out of SLACK_SIGNING_SECRET env fallback when webhookVerifier is set

A webhookVerifier passed in config was being silently shadowed by
SLACK_SIGNING_SECRET in the env (read by both createSlackAdapter and the
SlackAdapter constructor). An explicit verifier now opts out of that
fallback in both code paths. Added a regression test that stubs the env
var via vi.stubEnv.

* register handleReactionEvent's outer promise via waitUntil

handleReactionEvent does async work (conversations.replies, users.info)
before delegating to chat.processReaction, which is the only point that
registers a waitUntil task. The outer prep work was untracked, so callers
that drained waitUntil tasks could complete before the reaction handler
finished — flaky in CI under tight microtask scheduling. Track the outer
promise too so the full handler is awaited.

* fix(integration-tests): drain waitUntil cascade in test tracker

---------

Co-authored-by: dancer <josh@afterima.ge>
2026-04-26 16:00:37 -07:00
Ben Sabic 93b658f6e6 docs: redesign homepage to mirror AI SDK structure (#427)
Restructures the docs homepage around the AI SDK section flow: hero
with command prompt + interactive demo, OSS stats grid, supported
platforms with feature pillars, Chat SDK Core code showcase, "Scale
with confidence" Vercel ecosystem integrations, and a closing
get-started section with three guides.

- New: oss-stats-section, supported-platforms, code-section,
  integrations-section, get-started-section.
- Removed unused layout helpers: adapters-section, centered-section,
  cta, one-two-section, text-grid-section, templates (folded into
  get-started-section).
- Hero loses the badge and embeds the existing Demo as the
  hero-interactive piece.
- Three guide cards link to the Slack agent guide, Knowledge Agent
  template, and GitHub code review bot guide.

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-04-26 15:36:22 -07:00
Ben Sabic 99c0cd767e docs: require signed commits, trim contributor guide (#426)
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-04-26 22:31:03 +10:00
Divyansh Golyan 8e291c07bc Add MySQL community state adapter (#425) 2026-04-26 21:52:00 +10:00
Ben Sabic 0b610643d7 - Add changeset job to CI that runs on pull requests (#422)
- Detect when files under packages/** have changed and skip the check otherwise
- Exclude markdown-only changes from triggering the check
- Run pnpm changeset status against the PR base branch to fail when a changeset is missing

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-04-25 17:06:57 +10:00
Ben Sabic d630e6c8a7 fix(chat): honor concurrency.maxConcurrent in the concurrent strategy (#419)
Closes #417.

- handleConcurrent now acquires a per-thread semaphore slot when
  maxConcurrent is finite; fast path preserved for the default Infinity.
- Constructor throws on maxConcurrent < 1 (would deadlock) and warns
  when maxConcurrent is paired with a non-concurrent strategy (was
  previously ignored silently).

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-04-24 11:59:42 -07:00
Ben Sabic d440b0f920 chore: add changeset for #415 (Teams SDK 2.0.8) (#418)
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-04-24 11:51:11 -07:00