Commit Graph

159 Commits

Author SHA1 Message Date
Ben Sabic 2a2b2c5500 feat(instagram): add native DM adapter (#770)
Adds a first-party Instagram Direct Messages adapter backed by Meta's
Instagram API with Instagram Login.

- Verifies webhook challenges and HMAC signatures, then normalizes DMs,
story replies, media, quick replies, postbacks, and reactions.
- Sends plain text, cards, quick replies, typing indicators, URL
attachments, and uploaded media through `graph.instagram.com`.
- Maps authentication, rate-limit, and 24-hour messaging-window failures
to typed adapter errors.
- Registers Instagram in the adapter catalog, CLI scaffold, official
docs, replay suite, and Next.js example.

## Usage

```ts
import { createInstagramAdapter } from "@chat-adapter/instagram";
import { Chat } from "chat";

const bot = new Chat({
  userName: "mystore",
  adapters: { instagram: createInstagramAdapter() },
});
```

## Webhook

```ts
export async function POST(request: Request) {
  return bot.webhooks.instagram(request);
}
```

## Verification

- `pnpm --filter @chat-adapter/instagram test`
- `pnpm --filter @chat-adapter/instagram typecheck`
- `pnpm --filter example-nextjs-chat typecheck`
- `pnpm --filter example-nextjs-chat build`
- `pnpm check`
- `pnpm konsistent`

## Live Testing

<table>
  <tr>
<td><img width="1440" height="2109" alt="1000000502"
src="https://github.com/user-attachments/assets/9fdb8c3b-4e41-4c81-9426-08756a5e4201"
/></td>
<td><img width="1440" height="1995" alt="1000000503"
src="https://github.com/user-attachments/assets/8a572493-c57a-4412-9049-5737aaa9dfd0"
/></td>
  </tr>
</table>

Closes #729 / Co-Authored by @ivandujaut

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-08-07 17:38:15 +01:00
Ben Sabic 0ec6a7361b feat(notion): add Notion comments adapter (#689)
Adds `@chat-adapter/notion`, an official adapter that lets a Chat SDK
bot take part in **Notion comment discussions** (page-level and
block/discussion threads) with the same handler code used for Slack,
Linear, GitHub, etc. Inbound events arrive via Notion webhooks
(`comment.created`) with HMAC signature verification; outbound actions
use the Comments REST API. Because Notion lets a connection edit its own
comments, the adapter supports **Post+Edit streaming**.

### Highlights

- **Webhooks** — `comment.created` verified with `X-Notion-Signature`
HMAC over the raw body (timing-safe), plus the one-time
`verification_token` handshake. Returns a fast 200 with idempotent,
state-backed dedupe.
- **Post+Edit streaming** — posts the first chunk, then `PATCH`es the
comment as tokens arrive, throttled to Notion's ~3 req/s limit (global
token bucket, `Retry-After` aware). Long bodies are split into
sequential comments to stay under the 2000-char rich-text cap.
- **Mentions** — three modes: `mention` (default; plain-text `@userName`
/ `@botUserId`), `all-comments`, and `keyword`.
- **`message.subject`** — resolves the parent page via the Pages API
(title, url, archived status, author).
- **File uploads** — up to 3 native attachments via the File Uploads API
(binary `single_part`; public URLs via `external_url` with bounded
polling); overflow and failures fall back to markdown links.
- **History** — `fetchMessages` over list-comments (open comments only),
direction-aware.
- Cards render as markdown fallback; reactions / typing / DMs are typed
no-ops or errors. Registered in the `chat/adapters` catalog and the
`create-chat-sdk` scaffold; pinned to `Notion-Version: 2026-03-11`.

### Usage

```ts
// lib/bot.ts
import { Chat } from "chat";
import { createNotionAdapter } from "@chat-adapter/notion";
import { createRedisState } from "@chat-adapter/state-redis";

export const bot = new Chat({
  userName: "notion-bot",
  adapters: { notion: createNotionAdapter() }, // reads NOTION_TOKEN + NOTION_VERIFICATION_TOKEN
  state: createRedisState(),
});

bot.onNewMention(async (thread, message) => {
  const subject = await message.subject; // parent page metadata (title, url, …)
  await thread.post(`Thanks for the mention on **${subject?.title ?? "this page"}**!`);
});
```

```ts
// app/api/webhooks/notion/route.ts
import { bot } from "@/lib/bot";

export const POST = (request: Request): Promise<Response> => bot.webhooks.notion(request);
```

### Configuration

Auto-detects `NOTION_TOKEN` and `NOTION_VERIFICATION_TOKEN`, plus
optional `NOTION_BOT_USERNAME`, `NOTION_MENTION_MODE`,
`NOTION_KEYWORDS`, and `NOTION_VERSION`; everything is overridable via
`createNotionAdapter({ … })`. The docs page covers the full connection +
webhook setup (capabilities, content access, and the webhook-URL-lock
warning).

Changeset bumps `@chat-adapter/notion`, `chat`, and `create-chat-sdk`
(minor). Layered as four commits: `feat` (adapter +
catalog/scaffold/emoji), `docs`, `test`, `chore(example)`.

---------

Signed-off-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: dancer <josh@afterima.ge>
2026-08-05 14:15:44 +01:00
dependabot[bot] 7cda0e008e build(deps-dev): bump postcss from 8.5.16 to 8.5.18 (#744)
Bumps [postcss](https://github.com/postcss/postcss) from 8.5.16 to
8.5.18.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/postcss/postcss/releases">postcss's
releases</a>.</em></p>
<blockquote>
<h2>8.5.18</h2>
<ul>
<li>Restricted loading previous source maps file to the
<code>opts.from</code> folder for security reasons (use <code>unsafeMap:
true</code> to disable the check).</li>
</ul>
<h2>8.5.17</h2>
<ul>
<li>Fixed <code>Maximum call stack size exceeded</code> error.</li>
<li>Fixed Prototype hijacking for <code>postcss.fromJSON()</code>.</li>
<li>Fixed <code>Input#origin()</code> for unmapped end position (by <a
href="https://github.com/chatman-media"><code>@​chatman-media</code></a>).</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/postcss/postcss/blob/main/CHANGELOG.md">postcss's
changelog</a>.</em></p>
<blockquote>
<h2>8.5.18</h2>
<ul>
<li>Restricted loading previous source maps file to the
<code>opts.from</code> folder for security reasons (use <code>unsafeMap:
true</code> to disable the check).</li>
</ul>
<h2>8.5.17</h2>
<ul>
<li>Fixed <code>Maximum call stack size exceeded</code> error.</li>
<li>Fixed Prototype hijacking for <code>postcss.fromJSON()</code>.</li>
<li>Fixed <code>Input#origin()</code> for unmapped end position (by <a
href="https://github.com/chatman-media"><code>@​chatman-media</code></a>).</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/postcss/postcss/commit/4c0d194c136fd374495d0993c890d794cab65b81"><code>4c0d194</code></a>
Release 8.5.18 version</li>
<li><a
href="https://github.com/postcss/postcss/commit/92b4e7891ec7b811821d01acc8aa0f010caf41e2"><code>92b4e78</code></a>
Update dependencies</li>
<li><a
href="https://github.com/postcss/postcss/commit/95663d3eb7ba26f4854dd19d3b4f4425760cf56c"><code>95663d3</code></a>
Limit where source map can be loaded for security reasons</li>
<li><a
href="https://github.com/postcss/postcss/commit/74e25ae9f4efaa56a41a449064a655d7da78072c"><code>74e25ae</code></a>
Release 8.5.17 version</li>
<li><a
href="https://github.com/postcss/postcss/commit/d1518afd5a88f42728b30b87f8917210f363f9f1"><code>d1518af</code></a>
Fix Maximum call stack size exceeded error</li>
<li><a
href="https://github.com/postcss/postcss/commit/2421312ffea96ba77b35ce24a1b2d9c2e22b5e83"><code>2421312</code></a>
Fix linter</li>
<li><a
href="https://github.com/postcss/postcss/commit/a50352c583df991710f92ccac25b36304695161a"><code>a50352c</code></a>
Fix CI</li>
<li><a
href="https://github.com/postcss/postcss/commit/33948f0969bb858acdd52c9692e3a785a3ed0a73"><code>33948f0</code></a>
Prevent prototype hijacking in fromJSON</li>
<li><a
href="https://github.com/postcss/postcss/commit/2131909351161cd2c5fc2be58b14919a873ea824"><code>2131909</code></a>
Update dependencies</li>
<li><a
href="https://github.com/postcss/postcss/commit/93440abcca92793b31c5d1fdf5f2da7b58b27599"><code>93440ab</code></a>
Fix non-closed <code>\&lt;div align=&quot;center&quot;&gt;</code> in
README (<a
href="https://redirect.github.com/postcss/postcss/issues/2110">#2110</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/postcss/postcss/compare/8.5.16...8.5.18">compare
view</a></li>
</ul>
</details>
<br />

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-01 00:43:07 +01:00
dependabot[bot] 379842f2b2 build(deps): bump next from 16.2.6 to 16.2.11 (#740)
Bumps [next](https://github.com/vercel/next.js) from 16.2.6 to 16.2.11.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/vercel/next.js/releases">next's
releases</a>.</em></p>
<blockquote>
<h2>v16.2.11</h2>
<p>This release contains security fixes for the following
advisories:</p>
<p>High:</p>
<ul>
<li><a
href="https://github.com/vercel/next.js/security/advisories/GHSA-m99w-x7hq-7vfj">Denial
of Service in App Router using Server Actions</a></li>
<li><a
href="https://github.com/vercel/next.js/security/advisories/GHSA-6gpp-xcg3-4w24">Middleware
/ Proxy bypass in App Router applications using Turbopack and single
locale</a></li>
<li><a
href="https://github.com/vercel/next.js/security/advisories/GHSA-p9j2-gv94-2wf4">Server-Side
Request Forgery in rewrites via attacker-controlled destination
hostname</a></li>
<li><a
href="https://github.com/vercel/next.js/security/advisories/GHSA-89xv-2m56-2m9x">Server-Side
Request Forgery in Server Actions on custom servers</a></li>
</ul>
<p>Moderate:</p>
<ul>
<li><a
href="https://github.com/vercel/next.js/security/advisories/GHSA-68g3-v927-f742">Cache
confusion of response bodies for requests with bodies</a></li>
<li><a
href="https://github.com/vercel/next.js/security/advisories/GHSA-4633-3j49-mh5q">Cache
confusion of response bodies for requests with bodies containing invalid
UTF-8 byte sequences</a></li>
<li><a
href="https://github.com/vercel/next.js/security/advisories/GHSA-q8wf-6r8g-63ch">Denial
of Service in the Image Optimization API using SVGs</a></li>
<li><a
href="https://github.com/vercel/next.js/security/advisories/GHSA-955p-x3mx-jcvp">Unauthenticated
disclosure of internal Server Function endpoints</a></li>
<li><a
href="https://github.com/vercel/next.js/security/advisories/GHSA-4c39-4ccg-62r3">Unbounded
Server Action payload in Edge runtime</a></li>
</ul>
<h2>v16.2.10</h2>
<p>Contains no changes except publishing <code>@next/swc-wasm-web</code>
which was accidentally not published since 16.2.4.</p>
<h2>v16.2.9</h2>
<p>Empty release to ensure <code>next@latest</code> points at a stable
release. Next.js only allows publishing with Trusted Publishing enabled.
In order to fix NPM dist-tags, we have to release a new version.
Updating dist-tags is not possible with Trusted Publishing.</p>
<h2>v16.2.8</h2>
<p>Release with no changes in an attempt to fix <code>next@latest</code>
pointing at a prerelease version.</p>
<h2>v16.2.7</h2>
<blockquote>
<p>[!NOTE]
This release is backporting bug fixes. It does <strong>not</strong>
include all pending features/changes on canary.</p>
</blockquote>
<h3>Core Changes</h3>
<ul>
<li>Backport documentation fixes for v16.2 (<a
href="https://redirect.github.com/vercel/next.js/issues/93804">#93804</a>)</li>
<li>[backport] Patch <code>playwright-core</code> to resolve
<code>_finishedPromise</code> on <code>requestFailed</code> (<a
href="https://redirect.github.com/vercel/next.js/issues/93920">#93920</a>)</li>
<li>[backport] Fix dev mode hydration failure when page is served from
HTTP cache (<a
href="https://redirect.github.com/vercel/next.js/issues/93492">#93492</a>)</li>
<li>[backport] Fix catch-all <code>router.query</code> corruption with
<code>basePath</code> + <code>rewrites</code> (<a
href="https://redirect.github.com/vercel/next.js/issues/93917">#93917</a>)</li>
<li>[backport] Encode non-ASCII characters in cache tags at construction
(<a
href="https://redirect.github.com/vercel/next.js/issues/93918">#93918</a>)</li>
<li>[backport] Fix server action forwarding loop with middleware
rewrites (<a
href="https://redirect.github.com/vercel/next.js/issues/93919">#93919</a>)</li>
<li>[backport] Turbopack: switch from base40 to base38 hash encoding (<a
href="https://redirect.github.com/vercel/next.js/issues/93932">#93932</a>)</li>
<li>[ci] Disable hanging node 24 typescript tests on 16.2 backport
branch (<a
href="https://redirect.github.com/vercel/next.js/issues/94164">#94164</a>)</li>
<li>[backport] Fix &quot;type: module&quot; in project dir when using
standalone or adapters (<a
href="https://redirect.github.com/vercel/next.js/issues/94050">#94050</a>)</li>
<li>[backport] Propagate adapter preferred regions (<a
href="https://redirect.github.com/vercel/next.js/issues/94200">#94200</a>)</li>
<li>[16.2.x] Don't drop <code>FormData</code> entries (<a
href="https://redirect.github.com/vercel/next.js/issues/94240">#94240</a>)</li>
<li>[backport] feat(turbopack): add LocalPathOrProjectPath PostCSS
config resolution (<a
href="https://redirect.github.com/vercel/next.js/issues/94284">#94284</a>)</li>
</ul>
<h3>Credits</h3>
<p>Huge thanks to <a
href="https://github.com/eps1lon"><code>@​eps1lon</code></a>, <a
href="https://github.com/icyJoseph"><code>@​icyJoseph</code></a>, <a
href="https://github.com/unstubbable"><code>@​unstubbable</code></a>, <a
href="https://github.com/mischnic"><code>@​mischnic</code></a>, <a
href="https://github.com/bgw"><code>@​bgw</code></a>, <a
href="https://github.com/timneutkens"><code>@​timneutkens</code></a>,
and <a
href="https://github.com/lukesandberg"><code>@​lukesandberg</code></a>
for helping!</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/vercel/next.js/commit/9beca0821cf4606ae33466ed6f4fc75f2887a4da"><code>9beca08</code></a>
v16.2.11</li>
<li><a
href="https://github.com/vercel/next.js/commit/3c48c7af78f2c01691065cb303da1b107a2c8617"><code>3c48c7a</code></a>
[16.x] Fix Turbopack middleware matcher with i18n single locale</li>
<li><a
href="https://github.com/vercel/next.js/commit/ac1eff3f7a7285176396ecc69c3b160a3d6ad1a2"><code>ac1eff3</code></a>
[16.x] Improve performance of checking valid MPA form submissions</li>
<li><a
href="https://github.com/vercel/next.js/commit/9a4651e754f70b12e397694ffc41f44c3ba8cc17"><code>9a4651e</code></a>
[16.x] Enforce <code>serverActions.bodySizeLimit</code> for Server
Actions in Edge runtime</li>
<li><a
href="https://github.com/vercel/next.js/commit/b51206321854193208c0805ba42acc49287f942b"><code>b512063</code></a>
[16.x] Set correct origin for internal redirects in custom server</li>
<li><a
href="https://github.com/vercel/next.js/commit/d3033266c6dff23f7be71e19341fe3a8c6e2c599"><code>d303326</code></a>
[16.x] Ensure exotic rewrite param values are properly encoded</li>
<li><a
href="https://github.com/vercel/next.js/commit/73b94872bc343d09494b50394d8c08eb9fc8e56a"><code>73b9487</code></a>
[16.x] fix(fetch-cache): key fetch(Request, init) by the effective
request</li>
<li><a
href="https://github.com/vercel/next.js/commit/bf9d17fb30501829f6fd7c0ee8e44e2794565742"><code>bf9d17f</code></a>
[16.x] fix(incremental-cache): byte-exact fetch cache key for binary
bodies</li>
<li><a
href="https://github.com/vercel/next.js/commit/fe28768f533582ea8f6ee7d7a7498715927d45f5"><code>fe28768</code></a>
[16.x] fix(next/image): improve performance of detectContentType()</li>
<li><a
href="https://github.com/vercel/next.js/commit/d8afb8d550ac4ac5c106ea1410c3af43eaf1d469"><code>d8afb8d</code></a>
[16.x] Performance improvements when decoding React Server function
payloads</li>
<li>Additional commits viewable in <a
href="https://github.com/vercel/next.js/compare/v16.2.6...v16.2.11">compare
view</a></li>
</ul>
</details>
<br />

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-01 00:42:41 +01:00
C. T. Lin 6714efc3a1 feat: support AI SDK v7 (ai@7) as a peer dependency (#691)
Closes #690

## What

Widens the AI SDK peer dependency ranges so the Chat SDK installs
cleanly next to `ai@7`:

- `chat`: `ai@^6.0.182 || ^7.0.0`
- `@chat-adapter/web`: `ai@^6 || ^7`, `@ai-sdk/react@^3 || ^4`,
`@ai-sdk/svelte@^4 || ^5`, `@ai-sdk/vue@^3 || ^4`

This also unbreaks `create-chat-sdk` scaffolds, which install
`ai@latest` (now v7) next to `chat` and currently hit a peer conflict
out of the box.

## The one real v6 → v7 break

In v7, `tool()` with an `execute` function returns
`ExecutableTool<Tool<...>>` — an internal type from
`@ai-sdk/provider-utils` that `ai` does not re-export. The `chat/ai`
tool factories relied on inference, so declaration emit failed with
TS2742 (17 errors). The factories now declare explicit `Tool<Input,
Output>` return types, which is exactly the shape the previously
published `.d.ts` already had — the public type surface is unchanged,
and the emitted declarations only reference types from `ai` (portable
for consumers on either major).

Everything else checked out compatible:

- v7 stream parts keep `text-delta` / `finish-step` shapes, so
`fromFullStream` duck-typing works unchanged; `fullStream` remains as a
deprecated alias
- tool-level `needsApproval` is deprecated in v7 but still typed and
honored
- `createUIMessageStream`, `createUIMessageStreamResponse`,
`isTextUIPart`, `UIMessage`, `UIMessageStreamWriter`, `ChatInit`,
`DefaultChatTransport` all still exported — `@chat-adapter/web` needed
zero source changes

## Other changes

- devDependencies move to v7 so the workspace develops/tests against the
latest major
- `examples/nextjs-chat` and `examples/nuxt-chat` move to `ai@^7`
(required — mixing majors across the workspace fails typecheck, since
`chat`'s d.ts resolves `ai` types from its own devDependency)
- Test-only: the `ToolExecutionOptions` stub type is now derived from
`Tool["execute"]` because v7 made the generic parameter required
- Changeset included (minor for `chat` and `@chat-adapter/web`)

## Verification

The same source was verified against **both majors** (`ai@6.0.182` and
`ai@7.0.17`): `tsc --noEmit` and the full test suites (`chat`: 1035
tests, `@chat-adapter/web`: 21 tests) pass on each. `pnpm validate`
(knip + check + typecheck + test + build, including both examples) is
green on v7.

Note for adopters: `ai@7` itself requires Node.js ≥ 22 and is ESM-only;
`chat` keeps `engines.node >= 20` since `ai` is an optional peer.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Signed-off-by: chentsulin <chentsulin@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: dancer <josh@afterima.ge>
2026-07-13 21:50:27 +10:00
Ben Sabic 0f743c9b33 feat(slack): support native Slack agents (#698)
Builds on the Agent messaging experience support from #684 with a
declarative config layer for building Slack agents, plus hardening for
native streaming.

Everything is configured on `createSlackAdapter()` — no per-event wiring
required.

## Slack adapter (`@chat-adapter/slack`)

### `suggestedPrompts`

Static payload or per-thread resolver, applied automatically when an
assistant/agent thread opens:

- `assistant_thread_started` (legacy `assistant_view`), with the
thread's `thread_ts`
- Messages-tab `app_home_opened` (with `agentView` enabled), without
`thread_ts` so prompts pin atop the agent conversation

The resolver receives the thread context (`channelId`, `userId`, legacy
`threadTs`/`teamId`/`enterpriseId`, and normalized active-view
`entities` under `agentView`); returning `null`/`undefined` skips the
thread. Prompts beyond Slack's 4-prompt limit are dropped with a
warning. Resolver/API failures are logged, never a webhook 500. Applied
via `waitUntil` inside the request scope so multi-workspace token
context propagates.

### `loadingMessages`

Default rotating status strings for the assistant thinking indicator,
used by `startTyping` and `setAssistantStatus` when no explicit
status/messages are passed.

### `nativeStreaming` + automatic post-and-edit fallback

- New `nativeStreaming` config (default `true`). Set `false` on Slack
flavours without the `chat.startStream` family (e.g., GovSlack) to
always stream via post-and-edit.
- If the workspace rejects the **first** native streaming call,
`stream()` falls back to throttled post-and-edit mid-stream instead of
failing the reply; already-consumed text is preserved (it lives in the
renderer). Permanent platform errors (`unknown_method`,
`method_deprecated`, `feature_not_enabled`) latch native streaming off
for subsequent streams on the adapter instance; transient errors don't
latch.
- Structured chunks (`task_update`/`plan_update`) are skipped in
fallback mode; failures after native content has rendered still
propagate (mixing surfaces would duplicate output).
- Also updates the stale streaming description in the package AGENTS.md
(the adapter now streams via
`chat.startStream`/`appendStream`/`stopStream`, not `chat.update`).

### `feedbackButtons`

Appends Slack's native thumbs up/down (a `context_actions` block with a
`feedback_buttons` element) to every streamed reply on
`chat.stopStream`, after any `StreamingPlan` `endWith` blocks. Pass
`true` for defaults or an options object (`actionId`, labels, values).
Clicks dispatch through the regular `block_actions` flow to
`bot.onAction` with a positive/negative value — no new plumbing. Exports
`buildFeedbackButtonsBlock(options?)` for attaching the same block to
non-streamed messages.

New exported types: `SlackFeedbackButtonsOptions`,
`SlackSuggestedPrompt`, `SlackSuggestedPrompts`,
`SlackSuggestedPromptsContext`, `SlackSuggestedPromptsOptions`.

## Docs

- Configuration table rows for `agentView`, `suggestedPrompts`,
`loadingMessages`, `nativeStreaming`, `feedbackButtons`.
- New "Native streaming" and "Feedback buttons" sections plus
declarative suggested-prompts examples.
- All agent content grouped under a new **Advanced → Agents** subsection
(Agent messaging experience → Assistants API → Native streaming →
Feedback buttons). Heading titles unchanged, so existing anchors keep
resolving.
- TypeTable descriptions rewritten as plain text (they don't render
markdown).

## Example app (`examples/nextjs-chat`)

- `SLACK_AGENT_OPTIONS` shared across both Slack adapter branches:
active-view-aware `suggestedPrompts` resolver, `loadingMessages`,
`feedbackButtons` with an `ai_feedback` acknowledgment handler.
- Env toggles: `SLACK_AGENT_VIEW` (agent_view mode) and
`SLACK_NATIVE_STREAMING` (compare native vs post-and-edit).
- Commented `agent_view` blocks in `slack-manifest.yml` (feature block,
`assistant:write` scope, agent events) with a note that the switch is
irreversible.
- AI flows call `startTyping()` without an explicit status so configured
loading messages rotate.

## Test plan

- `pnpm validate` and `pnpm konsistent` pass.
- 26 new unit tests: suggested prompts
(static/resolver/agent_view/truncation/error paths), loading message
defaults, native streaming fallback (opt-out, mid-stream fallback,
permanent-error latching, transient non-latching, propagation after
native render, structured-chunk skipping), feedback buttons (block
shape, custom options, ordering after `endWith`, webhook round-trip of a
click).
- Each adapter commit was built and verified independently (typecheck +
full suite green at every step) for bisectability.
- Verified manually against a live `agent_view` workspace: prompts
pinned on thread open, loading messages rotating in the thinking
indicator, native token-by-token streaming in DMs and channel threads,
post-and-edit fallback via the opt-out flag, and feedback clicks
dispatching to `onAction`.

## Notes

- One changeset covers the three adapter features (`minor` for
`@chat-adapter/slack`).
- Known follow-up (not in this PR): under `agentView`, a subscribed
conversation-scoped DM thread (the #684 openDM bridge) routes DM
messages to a thread without `thread_ts`, which silently pins DMs to
post-and-edit. Worth deciding whether the bridge should keep per-message
threading for replies or log loudly when it redirects.

## Screenshots

| Suggested Prompts | Feedback Buttons |
| --- | --- |
| <img width="647" height="347" alt="CleanShot 2026-07-13 at 13 26 27"
src="https://github.com/user-attachments/assets/4c89932b-ed19-4b4a-83ae-d3d022d0c120"
/> | <img width="825" height="276" alt="CleanShot 2026-07-13 at 13 28
23"
src="https://github.com/user-attachments/assets/6bdf6979-5209-4bed-b0bc-dfbee7165455"
/> |

---------

Signed-off-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-07-13 12:27:25 +01:00
josh 8021c3eab3 fix(examples): forward X CRC challenge in the catch-all webhook route (#702)
## summary

the shared `[platform]` webhook route only forwarded a GET to the
adapter when the query carried Meta's `hub.mode`/`hub.verify_token`
challenge params, so X's Account Activity CRC challenge (a GET with
`crc_token`) fell through to the plain health-check response and never
reached the adapter. `@chat-adapter/x` already answers CRC inside
`handleWebhook`, but the catch-all route never handed it the request, so
X webhook registration would fail when wired through this shared
multi-platform route. added `crc_token` to the forward condition

the dedicated `app/api/webhooks/x/route.ts` shown in the X adapter docs
forwards GET unconditionally and was never affected; this only fixes the
shared catch-all example

<details>
<summary>before / after</summary>

```ts
// before
if (
  url.searchParams.has("hub.mode") ||
  url.searchParams.has("hub.verify_token")
) {
  return webhookHandler(request)
}

// after
if (
  url.searchParams.has("hub.mode") ||
  url.searchParams.has("hub.verify_token") ||
  url.searchParams.has("crc_token")
) {
  return webhookHandler(request)
}
```

</details>

## test plan

- the X adapter's `handleWebhook` already answers the `crc_token` GET
(covered by its `CRC challenge` unit tests); this change routes that GET
to it through the shared example
- confirmed the WhatsApp/Messenger `hub.*` challenge forwarding is
unchanged
- POST event delivery path untouched

Signed-off-by: dancer <josh@afterima.ge>
2026-07-13 18:56:24 +10:00
Ben Sabic 4717a38407 feat(slack): support data table and data visualization blocks (#696)
Adds support for Slack's [data
table](https://docs.slack.dev/reference/block-kit/blocks/data-table-block)
and [data
visualization](https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block)
Block Kit blocks.

- **`chat`**: new cross-platform `ChartElement` + `Chart()` builder (JSX
supported) mirroring Slack's model — pie `segments`, or bar/area/line
`series` against shared `categories`. `Table()` gains optional `caption`
and `pageSize`. Charts degrade to a text table on other platforms via
the shared card fallback (`chartElementToFallbackText`).
- **`@chat-adapter/slack`**: card tables now render as paginated,
sortable `data_table` blocks by default (header-only tables keep the
plain `table` block; oversized tables still fall back to ASCII). Charts
render as `data_visualization` blocks; charts violating Slack
constraints — including the undocumented **max 2 charts per message** —
fall back to a text rendering instead of an API rejection. Same
treatment in the `@chat-adapter/slack/blocks` subpath.
- **`postMessage`** now surfaces Slack's per-block validation messages
on `invalid_blocks` errors (this is how the 2-chart limit was found).
- Example app gets a **Show Charts** button and table pagination on
**Show Table**; docs, feature matrices, and changeset updated.

Verified live against Slack: data table pagination/sorting and both
chart types render natively.

<table>
  <tr>
    <th>Data Table</th>
    <th>Data Charts</th>
  </tr>
  <tr>
<td><img width="979" height="896" alt="CleanShot 2026-07-12 at 23 28 32"
src="https://github.com/user-attachments/assets/3307bd90-9322-452f-86fb-07d46446822d"
/></td>
<td><img width="955" height="879" alt="CleanShot 2026-07-12 at 23 29 02"
src="https://github.com/user-attachments/assets/ddb31a1b-e3fd-457c-a2e6-bde4934afebe"
/></td>
  </tr>
</table>

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-07-13 07:57:40 +01:00
damianborowy-nexos 1721fa01e7 feat(slack): add Slack Agent messaging experience (agent_view) support (#684)
## Summary

Add support for Slack's Agent messaging experience (`agent_view`), the
2026 replacement for `assistant_view`.

## Core (`chat`)

- New `onAppContextChanged` event carrying the active-view context as a
normalized `AppContextEntity[]` (`channel` / `canvas` / `list` /
`message` / `unknown`) describing what the user is currently viewing.
- `AppHomeOpenedEvent` now carries:
  - the same folded active-view context as optional `entities`
- the opened `tab` (`"home"` / `"messages"`), so handlers can
distinguish a Home-tab open from the DM-open signal under `agent_view`

## Slack adapter (`@chat-adapter/slack`)

- **`agentView` config flag.** Under `agent_view`:
- `app_home_opened` is the DM-open signal and fires regardless of tab
(branch on `event.tab` if you also publish a Home view)
- DM messages are threaded per Slack's new model — each user message is
a thread root (`thread_ts ?? ts`)
- conversation-scoped threads returned by `openDM()` keep working: when
that thread is subscribed, incoming top-level DM messages route to it,
so `onSubscribedMessage` and per-thread state behave the same as in
legacy mode
- **`app_context_changed` routing** with normalized entities. Malformed
payloads degrade gracefully: a missing `context` yields `entities: []`,
and entities with a null/malformed `value` normalize to `kind:
"unknown"` — never a webhook 500.
- **`getAppContext(message)`** helper to read the folded active-view
context off a DM message.
- **`setSuggestedPrompts`** accepts an optional thread reference
(`agent_view` lets prompts sit at the top of the agent conversation).
- **Env auth fallback now keys off auth fields**: `SLACK_BOT_TOKEN` /
`SLACK_CLIENT_ID` / `SLACK_CLIENT_SECRET` fallback is disabled only when
an auth-related field (`botToken`, `clientId`, `clientSecret`,
`installationProvider`) is passed explicitly, rather than by the
presence of any config object. This lets non-auth options compose with
env auth — e.g. `createSlackAdapter({ agentView: true })` picks up env
credentials — and matches the semantics documented in the adapter's
AGENTS.md. *(Behavior change for callers passing non-auth-only configs
while relying on env vars being ignored.)*
- Bumped `@slack/web-api` to `^7.18.0` (adds the optional `thread_ts`
typing for `setSuggestedPrompts`).

## Docs

- New "Agent messaging experience" section on the Slack adapter page
(config, manifest snippet, threading model, openDM bridge).
- "Handling active-view context" section in handling-events, plus
`tab`/`entities` rows on the app-home event table.
- Callout: under `agent_view`, bot replies are threaded per user
message, so `conversations.history` only returns the user's side of a DM
— build AI conversation history from transcripts instead of channel
history.

## Example app (`examples/nextjs-chat`)

- Plain `SLACK_BOT_TOKEN` adapter branch (previously Slack was only
wired via Vercel Connect).
- DM AI history built from transcripts instead of channel history (see
docs callout above); assistant turns persisted.
- The `dm me` trigger regex now matches mention text, which carries the
`@bot` prefix on Slack.

## Test plan

- `pnpm validate` and `pnpm konsistent` pass.
- Unit tests cover the new events, entity normalization (including
malformed payloads), `agent_view` DM threading, the openDM subscription
bridge, `tab` passthrough, `setSuggestedPrompts` thread handling, and
env-fallback behavior; an integration replay test exercises the full
webhook flow.
- Verified manually against a live `agent_view` workspace:
`onAppContextChanged` entities, folded context on `app_home_opened` and
DM messages, `tab` values for both tabs, per-message DM threading, the
openDM subscription bridge, and signed malformed-payload replays (all
return 200).
- Legacy regression pass with `agentView` off: conversation-scoped DM
threading, Home-tab-only `app_home_opened`, mention flow unchanged.

### Slack references

- Agent messaging experience:
https://docs.slack.dev/changelog/2026/06/30/agent-messages-tab/
- Active-view context:
https://docs.slack.dev/changelog/2026/07/02/app-context/

## Checklist

- [x] All commits are signed and verified
- [x] All commits are signed off for the DCO (`git commit -s`)
- [x] `pnpm validate` passes
- [x] Changeset added
- [x] Documentation updated

---------

Signed-off-by: Damian Borowy <301205838+damianborowy-nexos@users.noreply.github.com>
Signed-off-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-07-12 19:35:28 +10:00
Ben Sabic 2548043136 chore(examples): switch demo AI model to anthropic/claude-sonnet-5 (#688)
## Summary
- Update Next.js and Nuxt example bots from `xai/grok-4.5` to
`anthropic/claude-sonnet-5` (6 model string sites total).

## Test plan
- [x] Confirm example bots still start
- [x] Trigger AI mode in an example and verify requests go to Claude
Sonnet 5

Signed-off-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-07-09 19:35:20 +10:00
josh b3123815fa docs: use xai/grok-4.5 across docs, guides, and examples (#687)
## summary

- swap chat model strings to `xai/grok-4.5` across the docs site, the
shipped guides, and the example bots, so the docs lead with the latest
model
- apps/docs: ai overview, ai-sdk-tools, and streaming pages, the two
landing-page code samples, and the live chat demo route
(`app/api/chat/route.ts`, previously `openai/gpt-4.1-mini`)
- packages/chat/resources/guides: the seven guides that use a chat model
(slack connect, slack + ai sdk, liveblocks, vercel blob, github code
review, daily digest, ai gateway)
- examples: nextjs-chat and nuxt-chat bots
- deliberately left non-chat model strings as they were, since grok-4.5
cannot fill those roles: the `openai/text-embedding-3-small` embedding
model and the `openai/gpt-4o-mini` reranker
- in the ai gateway guide, grok-4.5 is now the primary model but the
fallback list stays cross-provider (`anthropic/claude-opus-4.8`,
`google/gemini-3.1-pro-preview`) so the failover example still
demonstrates real cross-provider fallback
- also fixed a pre-existing prose/code mismatch in that guide, the
fallback prose said `claude-opus-4.7` while the code listed `4.8`, now
aligned to `4.8`

---------

Signed-off-by: dancer <josh@afterima.ge>
2026-07-09 13:35:36 +10:00
dependabot[bot] 5267669563 build(deps): bump nuxt from 4.3.1 to 4.4.7 (#680)
Bumps [nuxt](https://github.com/nuxt/nuxt/tree/HEAD/packages/nuxt) from
4.3.1 to 4.4.7.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/nuxt/nuxt/releases">nuxt's
releases</a>.</em></p>
<blockquote>
<h2>v4.4.7</h2>
<blockquote>
<p>4.4.7 is a security hotfix release.</p>
</blockquote>
<p>👉 make sure to check <a
href="https://github.com/nuxt/nuxt/security/advisories">https://github.com/nuxt/nuxt/security/advisories</a>
to view open advisories resolved by this release.</p>
<h2>👉 Changelog</h2>
<p><a
href="https://github.com/nuxt/nuxt/compare/v4.4.6...v4.4.7">compare
changes</a></p>
<h3>🩹 Fixes</h3>
<ul>
<li><strong>nitro:</strong> Assign <code>noSSR</code> before deciding
payload extraction (<a
href="https://redirect.github.com/nuxt/nuxt/pull/35108">#35108</a>)</li>
<li><strong>vite:</strong> Avoid filtering out dirs with shared prefix
from <code>allowDirs</code> (<a
href="https://redirect.github.com/nuxt/nuxt/pull/35112">#35112</a>)</li>
<li><strong>nuxt:</strong> Use resolve from <code>pathe</code> for
buildCache path boundary check (<a
href="https://redirect.github.com/nuxt/nuxt/pull/35111">#35111</a>)</li>
<li><strong>nuxt:</strong> Prevent sibling-directory traversal in test
component wrapper (<a
href="https://redirect.github.com/nuxt/nuxt/pull/35110">#35110</a>)</li>
<li><strong>nitro:</strong> Pass event data to <code>isValid</code> in
dev clipboard-copy listener (<a
href="https://redirect.github.com/nuxt/nuxt/pull/35109">#35109</a>)</li>
<li><strong>nuxt:</strong> Validate protocols in
<code>reloadNuxtApp</code> path before reload (<a
href="https://redirect.github.com/nuxt/nuxt/pull/35115">#35115</a>)</li>
<li><strong>vite:</strong> Prefix public asset virtuals with null byte
(<a
href="https://github.com/nuxt/nuxt/commit/9e303b438">9e303b438</a>)</li>
<li><strong>nuxt:</strong> Re-run <code>getCachedData</code> after
initial fetch (<a
href="https://redirect.github.com/nuxt/nuxt/pull/35122">#35122</a>)</li>
<li><strong>nuxt:</strong> Propagate
<code>useFetch</code>/<code>useAsyncData</code> factory types (<a
href="https://redirect.github.com/nuxt/nuxt/pull/35133">#35133</a>)</li>
<li><strong>vite:</strong> Close vite dev server on nuxt close (<a
href="https://github.com/nuxt/nuxt/commit/a10a68abc">a10a68abc</a>)</li>
<li><strong>kit,nuxt:</strong> Handle cancelling prompts to install
packages (<a
href="https://github.com/nuxt/nuxt/commit/e84813229">e84813229</a>)</li>
<li><strong>kit:</strong> Avoid excluding node-context files in legacy
tsconfig (<a
href="https://redirect.github.com/nuxt/nuxt/pull/35152">#35152</a>)</li>
<li><strong>nuxt:</strong> Handle missing payload in chunkError listener
(<a
href="https://redirect.github.com/nuxt/nuxt/pull/35155">#35155</a>)</li>
<li><strong>nuxt:</strong> Await in-lifght template generation when
closing nuxt (<a
href="https://redirect.github.com/nuxt/nuxt/pull/35181">#35181</a>)</li>
<li><strong>nuxt:</strong> Clarify page and layout usage warnings (<a
href="https://redirect.github.com/nuxt/nuxt/pull/35184">#35184</a>)</li>
<li><strong>webpack:</strong> Surface compilation errors when
stats.toString is empty (<a
href="https://github.com/nuxt/nuxt/commit/073b07851">073b07851</a>)</li>
<li><strong>nuxt:</strong> Reject prototype-chain keys in the island
registry (<a
href="https://redirect.github.com/nuxt/nuxt/pull/35205">#35205</a>)</li>
<li><strong>nuxt:</strong> Apply <code>isScriptProtocol</code> guard to
<code>navigateTo</code> open option (<a
href="https://redirect.github.com/nuxt/nuxt/pull/35206">#35206</a>)</li>
<li><strong>nuxt:</strong> Prevent server-only page island from
recursing via <code>&lt;NuxtPage&gt;</code> (<a
href="https://redirect.github.com/nuxt/nuxt/pull/35198">#35198</a>)</li>
<li><strong>rspack,webpack:</strong> Require loopback host when missing
same-origin signals (<a
href="https://redirect.github.com/nuxt/nuxt/pull/35200">#35200</a>)</li>
<li><strong>nitro:</strong> Gate chrome devtools workspace endpoint to
local requests (<a
href="https://redirect.github.com/nuxt/nuxt/pull/35201">#35201</a>)</li>
<li><strong>nuxt:</strong> Escape props in
<code>&lt;NuxtClientFallback&gt;</code> ssr output (<a
href="https://redirect.github.com/nuxt/nuxt/pull/35199">#35199</a>)</li>
<li><strong>kit:</strong> Improve TS extension stripping/substitutions
(<a
href="https://redirect.github.com/nuxt/nuxt/pull/35233">#35233</a>)</li>
<li><strong>nuxt:</strong> Preserve
<code>.d.mts</code>/<code>.d.cts</code> in <code>resolveTypePaths</code>
(<a
href="https://redirect.github.com/nuxt/nuxt/pull/35235">#35235</a>)</li>
<li><strong>nuxt:</strong> Escape <code>&lt;NoScript&gt;</code> slot
content (<a
href="https://github.com/nuxt/nuxt/commit/4b054e9d9">4b054e9d9</a>)</li>
<li><strong>nuxt:</strong> Match route rules case-insensitively to
mirror <code>vue-router</code> (<a
href="https://github.com/nuxt/nuxt/commit/07e39cd6f">07e39cd6f</a>)</li>
<li><strong>nuxt:</strong> Reject script-capable protocols in
<code>&lt;NuxtLink&gt;</code> href (<a
href="https://github.com/nuxt/nuxt/commit/0103ce06f">0103ce06f</a>)</li>
<li><strong>nuxt:</strong> Block path-normalization open redirect in
<code>navigateTo</code> (<a
href="https://github.com/nuxt/nuxt/commit/2cce6fb02">2cce6fb02</a>)</li>
<li><strong>nuxt:</strong> Reject cross-origin paths in
<code>reloadNuxtApp</code> (<a
href="https://github.com/nuxt/nuxt/commit/e447a793c">e447a793c</a>)</li>
<li><strong>vite:</strong> Bind vite-node IPC to a permissioned
filesystem socket (<a
href="https://github.com/nuxt/nuxt/commit/1f9f4767a">1f9f4767a</a>)</li>
</ul>
<h3>💅 Refactors</h3>
<ul>
<li><strong>kit,nuxt,vite:</strong> Use <code>es2023</code> array
methods (<a
href="https://redirect.github.com/nuxt/nuxt/pull/34980">#34980</a>)</li>
<li><strong>nuxt:</strong> Replace <code>runInNewContext</code> with AST
walker (<a
href="https://github.com/nuxt/nuxt/commit/d72a89ef4">d72a89ef4</a>)</li>
</ul>
<h3>📖 Documentation</h3>
<ul>
<li>Document vite client and server options (<a
href="https://redirect.github.com/nuxt/nuxt/pull/35090">#35090</a>)</li>
<li>Add dedicated module dependencies page (<a
href="https://redirect.github.com/nuxt/nuxt/pull/35171">#35171</a>)</li>
<li>Add nodeTsConfig and sharedTsConfig options (<a
href="https://redirect.github.com/nuxt/nuxt/pull/35231">#35231</a>)</li>
<li>Edit for clarity and grammar (<a
href="https://redirect.github.com/nuxt/nuxt/pull/35214">#35214</a>)</li>
</ul>
<h3>🏡 Chore</h3>
<ul>
<li>Use <code>execFileSync</code> for safety in release scripts (<a
href="https://github.com/nuxt/nuxt/commit/1d7baaf01">1d7baaf01</a>)</li>
<li>Assert there is always a tag (<a
href="https://github.com/nuxt/nuxt/commit/e98c47c3c">e98c47c3c</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/nuxt/nuxt/commit/b7d57903b947e788fadfcfdf88be7951943731bf"><code>b7d5790</code></a>
v4.4.7</li>
<li><a
href="https://github.com/nuxt/nuxt/commit/dbc58965ca7ffa21d7cc4207a4c40a62e0762f4a"><code>dbc5896</code></a>
chore: lint</li>
<li><a
href="https://github.com/nuxt/nuxt/commit/e447a793c47766834f7497f8412a76cd56fd8ee1"><code>e447a79</code></a>
fix(nuxt): reject cross-origin paths in <code>reloadNuxtApp</code></li>
<li><a
href="https://github.com/nuxt/nuxt/commit/d72a89ef451965a8a1abb58d3bd6eab4865631a0"><code>d72a89e</code></a>
refactor(nuxt): replace <code>runInNewContext</code> with AST
walker</li>
<li><a
href="https://github.com/nuxt/nuxt/commit/2cce6fb02e621196d56df92e05594e07469b5a6d"><code>2cce6fb</code></a>
fix(nuxt): block path-normalization open redirect in
<code>navigateTo</code></li>
<li><a
href="https://github.com/nuxt/nuxt/commit/0103ce06fbbbdfa079a7f020ef8ce00121eac4a3"><code>0103ce0</code></a>
fix(nuxt): reject script-capable protocols in
<code>\&lt;NuxtLink&gt;</code> href</li>
<li><a
href="https://github.com/nuxt/nuxt/commit/07e39cd6f26e407b4192b7865bd17bc44536b9bb"><code>07e39cd</code></a>
fix(nuxt): match route rules case-insensitively to mirror
<code>vue-router</code></li>
<li><a
href="https://github.com/nuxt/nuxt/commit/4b054e9d95f8daf366cb144b52782047c511a66e"><code>4b054e9</code></a>
fix(nuxt): escape <code>\&lt;NoScript&gt;</code> slot content</li>
<li><a
href="https://github.com/nuxt/nuxt/commit/03d83bfab531b27fbc1dac7cdea9227b3707476e"><code>03d83bf</code></a>
fix(nuxt): preserve <code>.d.mts</code>/<code>.d.cts</code> in
<code>resolveTypePaths</code> (<a
href="https://github.com/nuxt/nuxt/tree/HEAD/packages/nuxt/issues/35235">#35235</a>)</li>
<li><a
href="https://github.com/nuxt/nuxt/commit/46960b2b18e79d64f212b2b77d879b3f88ab72cb"><code>46960b2</code></a>
fix(nuxt): escape props in <code>\&lt;NuxtClientFallback&gt;</code> ssr
output (<a
href="https://github.com/nuxt/nuxt/tree/HEAD/packages/nuxt/issues/35199">#35199</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/nuxt/nuxt/commits/v4.4.7/packages/nuxt">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=nuxt&package-manager=npm_and_yarn&previous-version=4.3.1&new-version=4.4.7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts page](https://github.com/vercel/chat/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-07 23:27:39 +10:00
Ben Sabic 03d274283f feat(examples): add nuxt-chat example app (#609)
Adds `examples/nuxt-chat`, a Nuxt 4 reference app for Chat SDK scoped to
the Slack and web adapters.

The Nitro server exposes `/api/webhooks/{platform}` for Slack events and
`/api/chat` for the browser UI, with H3-to-Fetch conversion that
preserves the raw request body for signature verification. Bot handlers
are ported from `nextjs-chat` — interactive cards, modals, slash
commands, transcripts, reactions, and AI streaming — without the
workflow demos.

The `/chat` page is a client-only Vue UI using `@chat-adapter/web/vue`
and the AI SDK. A Slack app manifest ships with the scopes and events
needed for pins, reactions, channel joins, and interactivity.

Monorepo plumbing covers changeset ignore, CI build exclusion,
`AGENTS.md`, knip entry paths for the Nuxt 4 `app/` directory, and biome
globals for Nitro auto-imports.

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-07-07 23:04:48 +10:00
Ben Sabic 6750d59e72 feat(github): add Vercel Connect support (#650)
Adds Vercel Connect support to the GitHub adapter:

- A new `installationToken` config option (string or resolver) supplies
installation access tokens directly, skipping the GitHub App private-key
JWT exchange.
- A new optional `webhookVerifier` verifies inbound webhooks (Connect
trigger-forwarded requests via a Vercel OIDC token) in place of the
GitHub webhook secret.

Pair with `connectGitHubAdapter()` from `@vercel/connect/chat`. Includes
a changeset (`@chat-adapter/github` minor).

Stacked on #647 (base `vercel-connect/base`).

## Companion

`@vercel/connect/chat` subpath: vercel/vercel#16826.

<img width="933" height="755" alt="CleanShot 2026-06-30 at 12 02 18"
src="https://github.com/user-attachments/assets/cc834560-0486-4f09-b8d5-8264be360544"
/>

---------

Signed-off-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-07-03 01:38:51 +10:00
Ben Sabic 4115c9431e feat(linear): add Vercel Connect support (#649)
Adds Vercel Connect support to the Linear adapter:

- `accessToken` now accepts a `() => string | Promise<string>` resolver
in addition to a string, so tokens can be sourced from Vercel Connect at
runtime.
- A new optional `webhookVerifier` verifies inbound webhooks (Connect
trigger-forwarded requests via a Vercel OIDC token) in place of the
Linear webhook secret.
- Connect-mode outbound calls outside webhook handling are supported via
`withInstallation(organizationId, fn)`.

Pair with `connectLinearAdapter()` from `@vercel/connect/chat`. Includes
a changeset (`@chat-adapter/linear` minor).

Stacked on #647 (base `vercel-connect/base`).

## Companion

`@vercel/connect/chat` subpath: vercel/vercel#16826.

<img width="929" height="664" alt="CleanShot 2026-06-30 at 12 35 30"
src="https://github.com/user-attachments/assets/c5861cb9-d66b-42c6-b838-5b4983f48646"
/>

---------

Signed-off-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-07-03 01:23:21 +10:00
Ben Sabic ba687cb13c docs(slack): document Vercel Connect support (#648)
Documents authenticating the Slack adapter with Vercel Connect via
`connectSlackAdapter()` from `@vercel/connect/chat`. The Slack adapter
already supports a `botToken` resolver and a `webhookVerifier`, so this
is a documentation-only change (no changeset).

Stacked on #647 (base `vercel-connect/base`).

## Companion

`@vercel/connect/chat` subpath: vercel/vercel#16826.

<img width="824" height="527" alt="CleanShot 2026-06-30 at 12 03 26"
src="https://github.com/user-attachments/assets/cbced069-8913-4848-9cf1-df0e5f614353"
/>

---------

Signed-off-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-07-03 01:01:51 +10:00
dependabot[bot] df825b3a56 build(deps-dev): bump postcss from 8.5.15 to 8.5.16 (#658)
Bumps [postcss](https://github.com/postcss/postcss) from 8.5.15 to
8.5.16.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/postcss/postcss/releases">postcss's
releases</a>.</em></p>
<blockquote>
<h2>8.5.16</h2>
<ul>
<li>Fixed <code>Input#origin()</code> position (by <a
href="https://github.com/mizdra"><code>@​mizdra</code></a>).</li>
<li>Fixed <code>raws</code> after rehydrating a JSON AST (by <a
href="https://github.com/sarathfrancis90"><code>@​sarathfrancis90</code></a>).</li>
<li>Fixed putting parent-less node in <code>nodes</code> of new node (by
<a
href="https://github.com/MahinAnowar"><code>@​MahinAnowar</code></a>).</li>
<li>Fixed computing <code>offset</code> in <code>positionBy()</code> (by
<a
href="https://github.com/greymoth-jp"><code>@​greymoth-jp</code></a>).</li>
<li>Fixed <code>rangeBy()</code> on <code>index: 0</code> (by <a
href="https://github.com/sarathfrancis90"><code>@​sarathfrancis90</code></a>).</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/postcss/postcss/blob/main/CHANGELOG.md">postcss's
changelog</a>.</em></p>
<blockquote>
<h2>8.5.16</h2>
<ul>
<li>Fixed <code>Input#origin()</code> position (by <a
href="https://github.com/mizdra"><code>@​mizdra</code></a>).</li>
<li>Fixed <code>raws</code> after rehydrating a JSON AST (by <a
href="https://github.com/sarathfrancis90"><code>@​sarathfrancis90</code></a>).</li>
<li>Fixed putting parent-less node in <code>nodes</code> of new node (by
<a
href="https://github.com/MahinAnowar"><code>@​MahinAnowar</code></a>).</li>
<li>Fixed computing <code>offset</code> in <code>positionBy()</code> (by
<a
href="https://github.com/greymoth-jp"><code>@​greymoth-jp</code></a>).</li>
<li>Fixed <code>rangeBy()</code> on <code>index: 0</code> (by <a
href="https://github.com/sarathfrancis90"><code>@​sarathfrancis90</code></a>).</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/postcss/postcss/commit/92ccc93ff15bd193491d67fad9763e62d489dfad"><code>92ccc93</code></a>
Release 8.5.16 version</li>
<li><a
href="https://github.com/postcss/postcss/commit/818bdd6043359af773ccc3ca8663053d61a707c8"><code>818bdd6</code></a>
Update formatting</li>
<li><a
href="https://github.com/postcss/postcss/commit/46e451068ee6160b837865b715cf6972f28fabd5"><code>46e4510</code></a>
Fix <code>Input#origin()</code> returning incorrect position (<a
href="https://redirect.github.com/postcss/postcss/issues/2036">#2036</a>)</li>
<li><a
href="https://github.com/postcss/postcss/commit/34942ce76c0b0c9ee65b1421017ac71855e722c4"><code>34942ce</code></a>
Fix tests</li>
<li><a
href="https://github.com/postcss/postcss/commit/d4feed645314ee421edf80ee9ebe453cc75c997f"><code>d4feed6</code></a>
Don't clone root-less child nodes in container constructor (<a
href="https://redirect.github.com/postcss/postcss/issues/2097">#2097</a>)</li>
<li><a
href="https://github.com/postcss/postcss/commit/da323fc8d327a38199a21987dcbf7e27e3bc34f3"><code>da323fc</code></a>
Revert version update to fix old Node.js on CI</li>
<li><a
href="https://github.com/postcss/postcss/commit/886336919497516df8f140d0fb327bd125e35053"><code>8863369</code></a>
Update dependencies</li>
<li><a
href="https://github.com/postcss/postcss/commit/3828982213fec6bc13d0791b1adf40393be0935e"><code>3828982</code></a>
Preserve node raws when rehydrating a JSON AST (<a
href="https://redirect.github.com/postcss/postcss/issues/2100">#2100</a>)</li>
<li><a
href="https://github.com/postcss/postcss/commit/d1e80b830386b08dcd5b962fd466d1c51f28e82d"><code>d1e80b8</code></a>
Fix Node#rangeBy() ignoring index 0 (<a
href="https://redirect.github.com/postcss/postcss/issues/2091">#2091</a>)</li>
<li><a
href="https://github.com/postcss/postcss/commit/b91e4a63907325d98b75d11fda546bdd91acc608"><code>b91e4a6</code></a>
Fix Node.js 26 tests</li>
<li>Additional commits viewable in <a
href="https://github.com/postcss/postcss/compare/8.5.15...8.5.16">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a
href="https://www.npmjs.com/~GitHub%20Actions">GitHub Actions</a>, a new
releaser for postcss since your current version.</p>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=postcss&package-manager=npm_and_yarn&previous-version=8.5.15&new-version=8.5.16)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts page](https://github.com/vercel/chat/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-01 13:22:22 +01:00
Ben Sabic e0a155e718 fix(examples): add @vercel/oidc to fix nextjs-chat Vercel build (#645)
- The `nextjs-chat` example's generated workflow step route
(`/.well-known/workflow/v1/step`) bundles `@workflow/world-vercel →
@vercel/queue`, and `@vercel/queue` has an unconditional `import
"@vercel/oidc"`.
- `@vercel/oidc` is only a deep transitive dependency, so it isn't
hoisted into the example app. Vercel's isolated build can't resolve it
and fails with `Module not found: Can't resolve '@vercel/oidc'`. (It
resolves locally only because pnpm symlinks it, which is why the GitHub
Actions build — which excludes the example — stays green.)
- Declaring `@vercel/oidc` as a direct dependency of the example fixes
resolution for the bundler.

No changeset needed — `example-*` packages are private and excluded from
versioning.

---------

Signed-off-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-06-27 08:42:13 +10:00
Shujan Islam f3de12823c Added Express.js chat-sdk example project (#551)
## Summary

Added a new example project demonstrating how to use `chat-sdk` with
Express.js.

Previously, there was no example showing how to integrate `chat-sdk` in
a simple Express.js setup. This PR adds a minimal Express.js project
with a Discord bot integration to demonstrate how `chat-sdk` can be used
in a practical server-side workflow.

Closes #519

## Test plan

- Ran the example project locally
- Verified the Express.js server starts successfully
- Verified the Discord bot connects and responds as expected
- Confirmed the example demonstrates basic `chat-sdk` usage with
Express.js

## Checklist

- [x] All commits are signed and verified
- [x] `pnpm validate` passes
- [ ] Changeset added (or N/A — see
[CONTRIBUTING.md](./CONTRIBUTING.md))
- [x] Documentation updated (or N/A)

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-06-20 13:15:14 +10:00
dependabot[bot] db61173386 build(deps-dev): bump postcss from 8.5.14 to 8.5.15 (#541)
Bumps [postcss](https://github.com/postcss/postcss) from 8.5.14 to
8.5.15.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/postcss/postcss/releases">postcss's
releases</a>.</em></p>
<blockquote>
<h2>8.5.15</h2>
<ul>
<li>Fixed declaration parsing performance (by <a
href="https://github.com/homanp"><code>@​homanp</code></a>).</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/postcss/postcss/blob/main/CHANGELOG.md">postcss's
changelog</a>.</em></p>
<blockquote>
<h2>8.5.15</h2>
<ul>
<li>Fixed declaration parsing performance (by <a
href="https://github.com/homanp"><code>@​homanp</code></a>).</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/postcss/postcss/commit/eae46db765d752cf8f40c4fa2b0b85030079c43d"><code>eae46db</code></a>
Release 8.5.15 version</li>
<li><a
href="https://github.com/postcss/postcss/commit/79508ffa59e42c02056aca61b88bc393c8b516c4"><code>79508ff</code></a>
Update CI actions</li>
<li><a
href="https://github.com/postcss/postcss/commit/b128e2131288a411c6e28071d0929542c49e74eb"><code>b128e21</code></a>
Speed up declaration parsing by avoiding creating new array on each
token</li>
<li><a
href="https://github.com/postcss/postcss/commit/9825dca02c33cf610e2a842be767468b67fbecf9"><code>9825dca</code></a>
Fix code format</li>
<li><a
href="https://github.com/postcss/postcss/commit/55789c865281e2be194fa5b4e41dd046be3a2307"><code>55789c8</code></a>
Update dependencies</li>
<li><a
href="https://github.com/postcss/postcss/commit/84fbbe9009cb3cc3bbb4cc3a9b65d468f4844d95"><code>84fbbe9</code></a>
Install older pnpm action for old Node.js</li>
<li><a
href="https://github.com/postcss/postcss/commit/9f860bd78ec1dbc4f0ae72d693f03f956baa38cb"><code>9f860bd</code></a>
Revert pnpm action for old Node.js</li>
<li><a
href="https://github.com/postcss/postcss/commit/08771986d47359545f502e009763e223b66bfcf6"><code>0877198</code></a>
Update CI actions</li>
<li><a
href="https://github.com/postcss/postcss/commit/b2d1a335cea818f8b27e5cfb90147648afe3e582"><code>b2d1a33</code></a>
Fix linter warnings</li>
<li><a
href="https://github.com/postcss/postcss/commit/0700dac92283bc259977dff2743ca74a00f58267"><code>0700dac</code></a>
Merge pull request <a
href="https://redirect.github.com/postcss/postcss/issues/2088">#2088</a>
from rootvector2/add-oss-fuzz-harness</li>
<li>Additional commits viewable in <a
href="https://github.com/postcss/postcss/compare/8.5.14...8.5.15">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=postcss&package-manager=npm_and_yarn&previous-version=8.5.14&new-version=8.5.15)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts page](https://github.com/vercel/chat/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-20 06:01:52 -07:00
dependabot[bot] c9559c89ee build(deps): bump next from 16.2.3 to 16.2.6 (#488)
Bumps [next](https://github.com/vercel/next.js) from 16.2.3 to 16.2.6.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/vercel/next.js/releases">next's
releases</a>.</em></p>
<blockquote>
<h2>v16.2.6</h2>
<blockquote>
<p>[!NOTE]
This release contains security fixes and backported bug fixes. It does
<strong>not</strong> include all pending features/changes on canary.</p>
</blockquote>
<h3>Security Fixes</h3>
<p>The following advisories have been addressed:</p>
<p><strong>High:</strong></p>
<ul>
<li><a
href="https://github.com/vercel/next.js/security/advisories/GHSA-8h8q-6873-q5fj">GHSA-8h8q-6873-q5fj:
Denial of Service with Server Components</a></li>
<li><a
href="https://github.com/vercel/next.js/security/advisories/GHSA-267c-6grr-h53f">GHSA-267c-6grr-h53f:
Middleware / Proxy bypass in App Router applications via
segment-prefetch routes</a></li>
<li><a
href="https://github.com/vercel/next.js/security/advisories/GHSA-26hh-7cqf-hhc6">GHSA-26hh-7cqf-hhc6:
Middleware / Proxy bypass in App Router applications via
segment-prefetch routes - <strong>Incomplete Fix
Follow-Up</strong></a></li>
<li><a
href="https://github.com/vercel/next.js/security/advisories/GHSA-mg66-mrh9-m8jx">GHSA-mg66-mrh9-m8jx:
Denial of Service via connection exhaustion in applications using Cache
Components</a></li>
<li><a
href="https://github.com/vercel/next.js/security/advisories/GHSA-492v-c6pp-mqqv">GHSA-492v-c6pp-mqqv:
Middleware / Proxy bypass through dynamic route parameter
injection</a></li>
<li><a
href="https://github.com/vercel/next.js/security/advisories/GHSA-c4j6-fc7j-m34r">GHSA-c4j6-fc7j-m34r:
Server-side request forgery in applications using WebSocket
upgrades</a></li>
<li><a
href="https://github.com/vercel/next.js/security/advisories/GHSA-36qx-fr4f-26g5">GHSA-36qx-fr4f-26g5:
Middleware / Proxy bypass in Pages Router applications using
i18n</a></li>
</ul>
<p><strong>Moderate:</strong></p>
<ul>
<li><a
href="https://github.com/vercel/next.js/security/advisories/GHSA-ffhc-5mcf-pf4q">GHSA-ffhc-5mcf-pf4q:
Cross-site scripting in App Router applications using CSP
nonces</a></li>
<li><a
href="https://github.com/vercel/next.js/security/advisories/GHSA-gx5p-jg67-6x7h">GHSA-gx5p-jg67-6x7h:
Cross-site scripting in beforeInteractive scripts with untrusted
input</a></li>
<li><a
href="https://github.com/vercel/next.js/security/advisories/GHSA-h64f-5h5j-jqjh">GHSA-h64f-5h5j-jqjh:
Denial of Service in the Image Optimization API</a></li>
<li><a
href="https://github.com/vercel/next.js/security/advisories/GHSA-wfc6-r584-vfw7">GHSA-wfc6-r584-vfw7:
Cache poisoning in React Server Component responses</a></li>
</ul>
<p><strong>Low:</strong></p>
<ul>
<li><a
href="https://github.com/vercel/next.js/security/advisories/GHSA-vfv6-92ff-j949">GHSA-vfv6-92ff-j949:
Cache poisoning via collisions in React Server Component
cache-busting</a></li>
<li><a
href="https://github.com/vercel/next.js/security/advisories/GHSA-3g8h-86w9-wvmq">GHSA-3g8h-86w9-wvmq:
Middleware / Proxy redirects can be cache-poisoned</a></li>
</ul>
<h3>Core Changes</h3>
<ul>
<li>fix: preserve HTTP access fallbacks during prerender recovery (<a
href="https://redirect.github.com/vercel/next.js/issues/92231">#92231</a>)</li>
<li>Fix fallback route params case in app-page handler (<a
href="https://redirect.github.com/vercel/next.js/issues/91737">#91737</a>)</li>
<li>Fix invalid HTML response for route-level RSC requests in deployment
adapter (<a
href="https://redirect.github.com/vercel/next.js/issues/91541">#91541</a>)</li>
<li>Patch setHeader for direct route handlers (<a
href="https://redirect.github.com/vercel/next.js/issues/93101">#93101</a>)</li>
<li>Include deployment id in <code>cacheHandlers</code> keys (<a
href="https://redirect.github.com/vercel/next.js/issues/93453">#93453</a>)</li>
<li>Fix double-encoding of URL pathname parts in client param parsing
(<a
href="https://redirect.github.com/vercel/next.js/issues/93491">#93491</a>)</li>
</ul>
<h2>v16.2.5</h2>
<blockquote>
<p>[!NOTE]
This release contains security fixes and backported bug fixes. It does
<strong>not</strong> include all pending features/changes on canary.</p>
</blockquote>
<h3>Security Fixes</h3>
<p>The following advisories have been addressed:</p>
<p><strong>High:</strong></p>
<ul>
<li><a
href="https://github.com/vercel/next.js/security/advisories/GHSA-8h8q-6873-q5fj">GHSA-8h8q-6873-q5fj:
Denial of Service with Server Components</a></li>
<li><a
href="https://github.com/vercel/next.js/security/advisories/GHSA-267c-6grr-h53f">GHSA-267c-6grr-h53f:
Middleware / Proxy bypass in App Router applications via
segment-prefetch routes</a></li>
<li><a
href="https://github.com/vercel/next.js/security/advisories/GHSA-mg66-mrh9-m8jx">GHSA-mg66-mrh9-m8jx:
Denial of Service via connection exhaustion in applications using Cache
Components</a></li>
<li><a
href="https://github.com/vercel/next.js/security/advisories/GHSA-492v-c6pp-mqqv">GHSA-492v-c6pp-mqqv:
Middleware / Proxy bypass through dynamic route parameter
injection</a></li>
<li><a
href="https://github.com/vercel/next.js/security/advisories/GHSA-c4j6-fc7j-m34r">GHSA-c4j6-fc7j-m34r:
Server-side request forgery in applications using WebSocket
upgrades</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/vercel/next.js/commit/ee6e79b1792a4d401ddf2480f40a83549fe8e722"><code>ee6e79b</code></a>
v16.2.6</li>
<li><a
href="https://github.com/vercel/next.js/commit/afa053d9eb9c2a68c7eba43e84fe6bed8babcd45"><code>afa053d</code></a>
Turbopack: Match proxy matchers with webpack implementation (<a
href="https://redirect.github.com/vercel/next.js/issues/93594">#93594</a>)</li>
<li><a
href="https://github.com/vercel/next.js/commit/97a154e5bbee0cb1ac3fb8aa4db66ac36e796e3d"><code>97a154e</code></a>
Turbopack: Fix middleware matcher suffix (<a
href="https://redirect.github.com/vercel/next.js/issues/93590">#93590</a>)</li>
<li><a
href="https://github.com/vercel/next.js/commit/83899bc89103d4df1479e065c7c1e09d4698a7b6"><code>83899bc</code></a>
[backport] Disable build caches for production/staging/force-preview
deploys ...</li>
<li><a
href="https://github.com/vercel/next.js/commit/7b222b90954d607fc28a34e9b360a9b1636bc206"><code>7b222b9</code></a>
[backport][test] Pin package manager to patch versions (<a
href="https://redirect.github.com/vercel/next.js/issues/93595">#93595</a>)</li>
<li><a
href="https://github.com/vercel/next.js/commit/a8dc24f1fe23d4a22d24fac734837f7c824138f7"><code>a8dc24f</code></a>
[backport] Turbopack: more strict vergen setup (<a
href="https://redirect.github.com/vercel/next.js/issues/93587">#93587</a>)</li>
<li><a
href="https://github.com/vercel/next.js/commit/766148f9cd48c0e218acafcd0f15defc14871bf4"><code>766148f</code></a>
v16.2.5</li>
<li><a
href="https://github.com/vercel/next.js/commit/0dd94836a8b43209fcfefa448c141683c22c1a27"><code>0dd9483</code></a>
fix: add explicit checks for RSC header (<a
href="https://redirect.github.com/vercel/next.js/issues/83">#83</a>) (<a
href="https://redirect.github.com/vercel/next.js/issues/98">#98</a>)</li>
<li><a
href="https://github.com/vercel/next.js/commit/d166096c399c4fc4e09cd2d1bf26dca6579a855d"><code>d166096</code></a>
fix proxy matching for segment prefetch URLs (<a
href="https://redirect.github.com/vercel/next.js/issues/89">#89</a>) (<a
href="https://redirect.github.com/vercel/next.js/issues/96">#96</a>)</li>
<li><a
href="https://github.com/vercel/next.js/commit/9d50c0b7190f59c470308578e12882788819f14c"><code>9d50c0b</code></a>
Strip next-resume header from incoming requests (<a
href="https://redirect.github.com/vercel/next.js/issues/92">#92</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/vercel/next.js/compare/v16.2.3...v16.2.6">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a
href="https://www.npmjs.com/~GitHub%20Actions">GitHub Actions</a>, a new
releaser for next since your current version.</p>
</details>
<br />

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-14 20:22:55 -07:00
Ben Sabic ac8a20779c feat(chat): add chat/ai subpath for AI SDK utilities (#492)
## Summary

Introduces a dedicated `chat/ai` subpath as the home for every Vercel AI
SDK helper that ships with Chat SDK. Importing from this subpath keeps
the optional `ai` and `zod` peer dependencies out of bundles that don't
use them.

### What's new

- **`createChatTools`** — exposes Chat SDK operations as ready-to-use AI
SDK tools so an agent can read, post, react, edit, delete, and manage
thread subscriptions across every adapter the supplied `Chat` instance
has registered.
- Write operations require user approval by default (`requireApproval:
true`); toggle globally or per-tool.
- Three presets — `reader`, `messenger`, `moderator` — scope the
toolset.
- Individual tools can also be cherry-picked (`import { postMessage,
addReaction } from "chat/ai"`).
- **`toAiMessages`** (and the `Ai*` / `ToAiMessagesOptions` types) now
live alongside the tools at `chat/ai`. The previous `chat` re-exports
continue to work, but are flagged `@deprecated` with an editor hint
pointing to the new home — migration is a one-line import change.
- **Docs** — new `/docs/ai` section between Usage and Adapters in the
sidebar:
  - `/docs/ai` — Overview
  - `/docs/ai/ai-sdk-tools` — `createChatTools` guide
  - `/docs/ai/to-ai-messages` — `toAiMessages` reference
  - `/docs/ai/types` — Reference for every type exported from `chat/ai`
- **Example app** — `examples/nextjs-chat` now demos the new surface via
a "Run Agent Demo" button on the welcome card and a free-form `/agent
<prompt>` slash command (streaming, with a placeholder so users get
immediate feedback in channel contexts where Slack's typing-status API
is a no-op).

### Future plans

`createChatTools` currently exposes the cross-adapter Chat SDK surface
only. A natural follow-up is to also support **platform-specific tools**
— e.g. expose Slack-only `pin`/`unpin`, Discord-only thread archiving,
GitHub-only issue commenting, etc., so users can further extend what
their agent can do without dropping back to raw adapter calls. The shape
would likely be additional opt-in factories under `chat/ai` (or
per-adapter subpaths like `@chat-adapter/slack/ai`) that return tools
layered on top of the platform-specific adapter clients, while keeping
the cross-platform `createChatTools` API as the lowest common
denominator.

### Coverage

- `createChatTools` orchestrator: 100% statements / 94.7% branches.
- Every tool factory's `execute()` is exercised end-to-end (29 tests in
`index.test.ts`).
- `toAiMessages` keeps its existing 35-test suite covering role mapping,
attachment handling, links, transforms, and unsupported-attachment
fallbacks.
- Tools folder overall: 99.0% statements / 86.1% branches / 97.4%
functions / 98.9% lines.

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: dancer <josh@afterima.ge>
2026-05-14 20:13:42 -07:00
Ben Sabic fdebde7988 Reapply "feat(slack): expose direct WebClient access via adapter.client" (#472) (#476)
This reverts commit 2279f1db70.

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
2026-05-12 08:32:54 +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
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
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 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
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
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
Sergei Patrikeev b9a1961aa5 fix(telegram): MarkdownV2 rendering + telegram-chat reference example (#407)
* fix(telegram): switch parse_mode from legacy Markdown to MarkdownV2

The Telegram adapter hardcoded `parse_mode: "Markdown"` (legacy) but
rendered messages via the SDK's generic `stringifyMarkdown()`, which
emits standard markdown. Two incompatible dialects glued together:

- Standard markdown uses `**bold**`, Telegram legacy uses `*bold*`
- Legacy Markdown has no escape rules — any message with `.`, `!`,
  `(`, `)`, `-`, `_` in unexpected positions was rejected with
  `can't parse entities`, which is virtually every LLM-generated
  response
- Legacy Markdown is deprecated by Telegram and lacks support for
  underline, strikethrough, spoiler, and blockquote

This commit:

- Switches TELEGRAM_MARKDOWN_PARSE_MODE to "MarkdownV2"
- Replaces fromAst() with a proper AST → MarkdownV2 renderer:
  - Single `*bold*`, `_italic_`, `~strike~` markers
  - Context-aware escaping: 20-char matrix for normal text, only
    `` ` `` and `\` inside code blocks, only `)` and `\` inside link
    URLs
  - Headings rendered as bold (MarkdownV2 has no heading syntax)
  - Ordered/unordered lists with escaped dashes and periods
  - Blockquotes with per-line `>` prefix
  - Tables pre-empted and rendered as ASCII code blocks
  - Explicit handlers for reference-style links, images, HTML, and
    definitions so nothing is silently dropped
- Routes card fallback text through `fromMarkdown` (not raw escape)
  with `boldFormat: "**"` — @chat-adapter/shared's cardToFallbackText
  defaults `boldFormat` to "*" (Slack mrkdwn), which would render as
  italic on Telegram. Explicit "**" keeps the card title rendered as
  real MarkdownV2 bold.
- Fixes resolveParseMode so every message routed through the format
  converter (`{markdown}`, `{ast}`, cards, JSX) gets
  `parse_mode: "MarkdownV2"`. Previously only `{markdown}` and cards
  were covered, so `{ast}` messages shipped without parse_mode and
  rendered asterisks literally.
- Documents inbound vs outbound dialects on applyTelegramEntities /
  escapeMarkdownInEntity (inbound entities → standard markdown)
  versus the new outbound MarkdownV2 renderer, so future
  contributors don't confuse the two.

Tests: full 20-char MarkdownV2 escape matrix, context-escape tests
for code blocks and link URLs, nested-formatting tests, edge cases
(empty, whitespace-only, raw HTML), and an end-to-end LLM-output
corpus test that asserts MarkdownV2 validity (no unescaped special
chars outside entities or code blocks). Regression guards added in
index.test.ts for the AST / plain-string / raw parse_mode paths and
for card-title MarkdownV2 bold rendering.

Fixes #226

* feat(examples): add telegram-chat reference bot

Polling-mode Telegram bot that exercises the adapter end-to-end:
MarkdownV2 rendering, interactive cards with inline keyboards,
reactions, file uploads, and streaming edits. Runs with a single
`pnpm --filter example-telegram-chat start`; no webhook, no public
URL, no external API keys.

Menu structure — three categorized sub-menus reached from any DM text:

- Text & Markdown: plain, inline emphasis, code block, links, list+table,
  20-char torture string, LLM-style corpus, streaming editMessage loop
- Cards & Actions: interactive approval card (edits in-place on press),
  callback_data size probe demonstrating the 64-byte limit, LinkButton
- Media & Reactions: on-demand reaction one-shot (briefly subscribes),
  generated 1×1 PNG upload, generated minimal PDF upload

Zero new runtime deps. PNG/PDF are hand-rolled in memory
(lib/png.ts, lib/pdf.ts) rather than pulled from a binary-processing
library. Failure handling is consistent: every demo runner is
try/catch-wrapped and posts an inline ❌ line with the error message.

Excluded from npm release via .changeset/config.json.

* fix(telegram): produce valid MarkdownV2 when truncating long messages

The MarkdownV2 migration widened a latent truncation bug into a reliable
400. The previous truncator sliced at 4096/1024 chars and appended
literal "..." — but in MarkdownV2 `.` is a reserved character, the slice
can leave an orphan trailing `\`, and it can cut through a paired
entity (`*bold*`, `` `code` ``) leaving it unclosed.

Unify the two truncate methods into one `truncateForTelegram(text,
limit, parseMode)` that appends `\.\.\.` for MarkdownV2 and walks back
past unbalanced entity delimiters or orphan backslashes. Plain text
keeps literal `...`. Adds 8 length-limit tests.

Related cleanup:
- Move MarkdownV2 string utilities and Bot API limits to markdown.ts.
- Type renderMarkdownV2 exhaustively on mdast's `Nodes` union with a
  `never` assertion so new node kinds fail the build. Replaces the
  hand-rolled `AstNode` interface. Adds explicit cases for table /
  tableRow / tableCell (throw — preprocessed by fromAst),
  footnoteDefinition, footnoteReference, yaml.
- Introduce `TelegramParseMode = "MarkdownV2" | "plain"` replacing
  `string | undefined`. `toBotApiParseMode` handles the wire mapping.
- Re-export `Nodes` from the chat package; re-export
  `TelegramReactionType` from the adapter entry.

* feat(examples): add length-limit demos to telegram-chat reference bot

Three new menu entries exercise the MarkdownV2 truncation path that the
prior commit fixed:

- Long (5000 plain) — basic truncation, verifies escaped `\.\.\.` ellipsis
- Long (bold crosses 4096) — entity-balancing heuristic for unclosed `*`
- Long (code crosses 4096) — entity-balancing heuristic for unclosed `` ` ``

Each button posts a message whose rendered length exceeds Telegram's
4096-char limit and would have produced `can't parse entities` 400s
against the previous truncator. Serves as an interactive smoke test
alongside the unit tests in packages/adapter-telegram.

* test(telegram): add unit tests for truncation helpers and MarkdownV2 boundary trimming

* docs(telegram): update README to reflect MarkdownV2 parse mode

* chore: unexport trimToMarkdownV2SafeBoundary to fix knip

---------

Co-authored-by: dancer <josh@afterima.ge>
2026-04-21 05:27:47 -07:00
Hayden Bleasel 7e90d9c8fa Add Slack Socket Mode support (#162)
* Add slack/socket mode dependency

* Update config types and SlackAdapter class

* Add socket mode methods, extract interactive dispatch

* Update createSlackAdapter factory function

* Write tests for socket mode

* Create slack-socket-mode.md

* Run fix

* Fix polynomial regex issues

* Fix: Floating promises in `routeSocketEvent` for slash commands and interactive payloads can cause unhandled promise rejections that crash the Node.js process.


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

**Bug Analysis:**

In `routeSocketEvent` (line 1150), which is a synchronous `void` method, two async operations produce floating promises:

1.  `this.handleSlashCommand(params)` (line 1165) - `handleSlashCommand` is `async` and always returns a `Promise<Response>`. It calls `await this.lookupUser(userId)` which internally calls `await this.chat.getState().get()` (before the try/catch around the API call), and `this.chat.processSlashCommand()`. Any of these could throw.
    
2.  `this.dispatchInteractivePayload(payload)` (line 1172) - Returns `Response | Promise<Response>`. When the payload type is `view_submission`, it delegates to `async handleViewSubmission()`, which calls `await this.chat.processModalSubmit()` and accesses `payload.view.state.values` (which could throw on malformed payloads).
    

Since `routeSocketEvent` is synchronous (`void` return type) and called from a sync context within the socket mode event handler (after `await ack()` has already completed), these returned promises are fire-and-forget. If any reject, it triggers an unhandled promise rejection, which in Node.js 15+ terminates the process by default.

In contrast, in the webhook code path (`handleWebhook`), these same methods are always `return`-ed from async functions, so their promises are properly chained to the caller.

**Fix:**

Added `.catch()` handlers to both floating promises:

1.  For `handleSlashCommand`: Added `.catch()` that logs the error via `this.logger.error`.
2.  For `dispatchInteractivePayload`: Since it returns `Response | Promise<Response>` (only a Promise for `view_submission`), used `instanceof Promise` to conditionally attach a `.catch()` handler only when the result is a Promise.

This approach was chosen over making `routeSocketEvent` async because: (a) it doesn't change the method signature, (b) the caller doesn't need to await it (the ack has already been sent), and (c) errors are logged rather than silently swallowed.


Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: haydenbleasel <hello@haydenbleasel.com>

* Add socket mode forwarding support to Slack adapter

- Export SlackForwardedSocketEvent type
- Add x-slack-socket-token check at top of handleWebhook() for forwarded events
- Update routeSocketEvent() to accept WebhookOptions and use waitUntil
- Add startSocketModeListener(), runSocketModeListener(), forwardSocketEvent()

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

* Add tests for socket mode forwarding

- Forwarded event accepted/rejected based on appToken
- Bypasses signature verification for forwarded events
- Options passthrough to handlers
- startSocketModeListener returns 200/500 appropriately

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

* Add socket mode cron route and vercel config

- New /api/slack/socket-mode route using createPersistentListener
- Mirrors Discord gateway pattern (CRON_SECRET auth, Redis coordination)
- Cron runs every 9 min, listener duration 10 min

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

* Fix signingSecret defaulting to empty string in socket mode

Make signingSecret optional (string | undefined) instead of falling
back to "". verifySignature now returns false when no secret is
configured, preventing HMAC with an empty key from silently passing.

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

* Wrap event_callback in try-catch in routeSocketEvent

Sync errors from processEventPayload were silently dropped in
socket mode. Wrap with try-catch for parity with slash_commands
and interactive cases.

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

* Use dedicated socketForwardingSecret for forwarding auth

Stop using the Slack app-level token (xapp-...) as the bearer token
for HTTP forwarding. Adds socketForwardingSecret config option
(auto-detected from SLACK_SOCKET_FORWARDING_SECRET) with fallback
to appToken for backwards compatibility.

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

* Replace double cast with type guard for socket event body

Validate body.event exists and construct a properly typed
SlackWebhookPayload instead of using `as unknown as`.

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

* Internalize SlackForwardedSocketEvent type

Remove export — only used internally by the forwarding mechanism.

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

* Fix formatting in socketForwardingSecret check

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

* Add socket mode documentation to Slack adapter README

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

* fix(slack): use SDK envelope type for socket mode event routing

* fix(slack): pass interactive response through ack in socket mode

* feat(chat): add clear modal response action to close entire view stack

* chore: update changeset for clear modal action

---------

Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: dancer <josh@afterima.ge>
2026-04-20 08:09:26 -07:00
dependabot[bot] bf9a2a43ce build(deps): bump next from 16.1.7 to 16.2.3 (#362)
Bumps [next](https://github.com/vercel/next.js) from 16.1.7 to 16.2.3.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v16.1.7...v16.2.3)

---
updated-dependencies:
- dependency-name: next
  dependency-version: 16.2.3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-14 15:18:07 +10:00
Samy Pessé bc94f0afc4 Multi-tenant support in Linear adapter (#344)
* Support passing scopes as a config to the linear adapter

* Changeset

* Add multi-tenant support for Linear

* Lint

* Cleanup

* Fix tests

* Make organizationId mandatory

* Fix

* Fix fetch of comments

* Start agent sessions support

* Continue cleanup

* Cleanup more

* Simplify more

* Fix

* Fix fetchMessages for agent sessions

* Improve streaming to linear

* Fix agent raw messages

* Fix tests

* Lint

* Simplify

* Work

* Remove debug logs and adapt tests

* Fix organizationId

* Cleanup types

* Cleanup and comment

* Fix webhook handling

* Expose prompt context

* First review pass

* Rework the changeset

* Improve getInstallation and expose refreshInstallation

* Fix error on agent sessions coming from issue delegation

---------

Co-authored-by:  <dancer@users.noreply.github.com>
2026-04-13 20:42:16 -07:00
Aamir Jawaid ce7cd2f8f0 feat(adapter-teams): add Select and RadioSelect support (#341)
* feat(adapter-teams): add Select and RadioSelect support in card Actions

Map Select and RadioSelect card elements to Teams Adaptive Card
ChoiceSetInput (compact and expanded styles). Auto-inject a submit
button when inputs exist without explicit buttons, and fan out the
auto-submit payload into individual onAction calls per input.
2026-04-09 11:08:25 -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
Hayden Bleasel 6646702128 Security fixes (#262)
* Bump Next

* Fix code scanning issues
2026-03-17 11:23:47 -07:00
Malte Ubl e20637158d feat: add LinkPreview to Message for URL and embedded message support (#217)
* feat: add LinkPreview to Message for URL and embedded message support

Add `links: LinkPreview[]` to `Message` so handlers can access URLs
shared in messages. Each LinkPreview contains the URL and optional
unfurl metadata (title, description, siteName, imageUrl).

On Slack, links are extracted from rich_text block elements (falling
back to <url> patterns in text). Links pointing to other Slack messages
(*.slack.com/archives/{channel}/p{ts}) include a `fetchMessage()`
callback that retrieves and parses the linked message.

`toAiMessages()` now appends link metadata to message content
automatically, labeling embedded message links distinctly so AI models
understand the context.

- Add LinkPreview interface to core types
- Add links field to Message, MessageData, SerializedMessage
- Extract links in Slack adapter (blocks + text fallback)
- Provide fetchMessage for Slack message URLs
- Set links: [] in all other adapters
- Include link metadata in toAiMessages() output
- Document LinkPreview in message API docs
- Document toAiMessages() in streaming and handling-events docs
- Add toAiMessages to API overview

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

* fix: remove redundant links: [] from adapters for backwards compatibility

The Message constructor already defaults links to [] when not provided,
so adapters that don't support link extraction don't need to pass it
explicitly. This makes the change backwards-compatible for third-party
adapters — they get an empty links array without any code changes.

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

* fix: avoid polynomial regex in Slack link extraction

Replace `[^>|]+` pattern (which backtracks on `|`) with `[^>]+`
and a programmatic indexOf split. This prevents ReDoS on untrusted
message text.

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

* feat: add attachment support and mention tests to toAiMessages

toAiMessages now includes image and text-file attachments as multipart
content compatible with AI SDK's UserContent type:

- Images → ImagePart (via fetchData base64 or URL fallback)
- Text files (text/*, application/json, etc.) → FilePart
- Video/audio → warns via onUnsupportedAttachment callback
- Other file types → silently skipped

The function is now async to support fetchData() calls for inlining
attachment data as base64 data URIs. When fetchData fails, falls back
to the attachment URL.

Also adds mention rendering tests verifying that @mentions appear as
@name (not Slack's <@U123> syntax) in toAiMessages output, both in
plain messages and with links/includeNames enabled.

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

* fix: make AiMessage types structurally compatible with AI SDK

Use a discriminated union (AiUserMessage | AiAssistantMessage) so
AiMessage[] is directly assignable to ModelMessage[] without casts.
Match DataContent type (string | Uint8Array | ArrayBuffer | Buffer)
for image/file parts to ensure structural compatibility.

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

* fix(example): pass image attachments to AI via toAiMessages

The onNewMention handler was passing message.text directly to the AI
agent, dropping any image attachments. Now uses toAiMessages([message])
which includes images via fetchData as base64 inline data, enabling
the AI to actually see uploaded images.

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

* fix: pass image data as Buffer, not data URI

The AI SDK expects DataContent (Buffer/Uint8Array/base64 string) for
image and file parts, not data URIs. Passing `data:image/png;base64,...`
caused "Could not process image" errors from the API. Now passes the
raw Buffer from fetchData() directly, with mediaType set separately.

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

* fix: require fetchData for attachments, no URL fallback

Slack's url_private requires Bearer token auth that AI providers can't
provide. Remove URL fallback — attachments are only included when
fetchData() succeeds (which handles auth internally). Log errors
instead of silently falling through.

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

* debug: add temporary logging to toAiMessages image handling

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

* fix: use data URIs for image/file attachments in toAiMessages

The AI SDK's convertToLanguageModelV2DataContent parses data: URIs
to extract both the base64 content and media type. Raw base64 strings
lose the media type (returns mediaType: void 0), and raw Buffers may
not serialize correctly across network boundaries. Data URIs are the
most reliable format.

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

* debug: log fetched image size to diagnose API rejection

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

* debug: log prompt structure to diagnose image rejection

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

* fix: use FilePart instead of ImagePart for image attachments

The AI SDK's ImagePart with data URI strings doesn't work correctly
through the AI Gateway. Use FilePart (type: "file") with data URI
in the data field instead — this matches the working pattern used by
other projects and handles image data correctly across all providers.

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

* debug: comprehensive logging at every decision point in toAiMessages

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

* debug: log data prefix to verify content format

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

* debug: log token capture and detect HTML responses from Slack file fetch

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

* fix: improve Slack file fetch error message and remove debug logging

When Slack returns an HTML login page instead of file data (typically
due to missing "files:read" OAuth scope), the error message now
explicitly tells the user what scope to add. Also removes all
temporary debug logging from toAiMessages and createAttachment.

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

* Add transform support

* changeset

* address-feedback

* lint

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-12 07:23:09 -07:00
Matthew Lewis d565c61e9d add github username (#216) 2026-03-11 10:41:24 -04:00
Achraf Ghellach 60f5d8e19f feat: add WhatsApp Business Cloud API adapter (#102)
* feat: add WhatsApp Business Cloud API adapter

Add @chat-adapter/whatsapp with support for sending/receiving messages,
reactions, interactive reply buttons, typing indicators, and webhook
verification via the Meta Graph API. Includes full test suite,
documentation updates, and workspace/turbo configuration.

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

* feat: add media download, attachments, and location support to WhatsApp adapter

- Add downloadMedia() public method for fetching images, documents,
  audio, video, and stickers via the Graph API (two-step: URL then binary)
- Populate message attachments with lazy fetchData() for all media types
- Add location support with Google Maps URL and structured text
- Add audio, video, sticker, and location fields to WhatsAppInboundMessage
- Set isMention: true on all messages (WhatsApp DMs are always direct)
- Update parseMessage to include attachments and isMention
- Add 10 new tests covering all media types, locations, and isMention
- Update docs feature matrix to reflect media receive support

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

* fix: address PR review feedback for WhatsApp adapter

- Validate Graph API response before accessing messages[0].id in
  sendTextMessage and sendInteractiveMessage
- Escape backticks and backslashes in escapeWhatsApp()
- Apply escapeWhatsApp() to renderText() content in all style branches
- Use webhook phoneNumberId in buildMessage() instead of this.phoneNumberId
- Encode proper threadId in parseMessage() instead of empty string
- Strict decodeThreadId() validation (exactly 2 segments after prefix)
- Add tests for extra segments in decodeThreadId and threadId in parseMessage

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

* Migrate improvements from #179

Bring over several enhancements from chitru's WhatsApp adapter PR (#179):

- Voice message support (separate from audio)
- Legacy button response handling (template quick replies)
- Callback data encoding/decoding for interactive reply round-trips
- Message truncation at WhatsApp's 4096 char limit
- Example app integration (adapters, webhook route, package.json)
- GET webhook forwarding for WhatsApp verification challenges
- Package README and changeset
- Tests for all new functionality (68 total)

Co-Authored-By: Chitru Shrestha <chitra.shrestha@akuru.com.au>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(whatsapp): add error handling for inbound message processing

Wrap handleInboundMessage calls in try/catch to log errors if
synchronous processing fails (e.g., thread ID encoding). The async
processing already has its own error handling in Chat.processMessage.

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

* fix(whatsapp): prevent markdown regex from matching across newlines

Use [^\n*] and [^\n~] in fromWhatsAppFormat regex to prevent bold/strike
spans from merging across line boundaries. Adds a regression test.

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

* fix(whatsapp): use WhatsAppInteractiveMessage type instead of object

Replace the untyped `object` parameter in sendInteractiveMessage with
the proper WhatsAppInteractiveMessage type for full type safety.

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

* fix(whatsapp): hoist emoji mapping to module-level constant

Move the emoji name-to-unicode mapping out of resolveEmoji() so it is
not re-allocated on every call.

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

* fix(whatsapp): remove duplicate JSDoc comment in types

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

* fix(example): add startTyping to WhatsApp recording methods

The adapter supports typing indicators but the method was missing from
the recording proxy list.

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

* fix(whatsapp): fix formatting and add package to readme test allowlist

Fix line-length formatting in markdown.ts regex and add
@chat-adapter/whatsapp to the valid packages list in readme tests.

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

* fix(whatsapp): use defaultEmojiResolver instead of custom emoji map

Replace the hand-rolled EMOJI_MAP with the shared defaultEmojiResolver
from the chat SDK. WhatsApp uses unicode emoji like GChat, so toGChat()
provides the correct mapping with broader coverage.

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

* fix(whatsapp): make Graph API version configurable

Add apiVersion option to WhatsAppAdapterConfig (defaults to v21.0)
so users can upgrade without waiting for a package release.

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

* fix(whatsapp): validate lat/lng before constructing Google Maps URL

Coerce and validate latitude/longitude with Number.isFinite() to
prevent unexpected URL construction from malformed webhook payloads.

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

* fix(whatsapp): throw on editMessage instead of silently sending new message

Callers expecting an edit would get duplicate messages with the silent
fallback. Throwing makes the unsupported operation explicit.

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

* fix(whatsapp): document regex asymmetry between toWhatsApp and fromWhatsApp

Explain why toWhatsAppFormat doesn't need newline guards like
fromWhatsAppFormat does — the standard markdown parser output
never produces spans crossing line boundaries.

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

* fix(whatsapp): document callback data passthrough behavior

Add comments explaining that non-prefixed and malformed callback data
is intentionally passed through for legacy/external button IDs.

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

* fix(whatsapp): add editMessage and deleteMessage to recording methods

Include all adapter methods in the recording list for complete
debugging traces, even for unsupported operations.

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

* fix(whatsapp): preserve escaped formatting chars in toWhatsAppFormat

Escaped asterisks and tildes in standard markdown (e.g. \* and \~) are
now preserved through the conversion pipeline so WhatsApp renders them
as literal characters instead of misinterpreting them as formatting.

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

* fix(whatsapp): split long messages instead of truncating

Replace silent truncation at 4096 chars with message splitting that
breaks on paragraph (\n\n) then line (\n) boundaries, sending multiple
messages so no content is lost. Adds 8 tests for the splitting logic.

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

* fix(whatsapp): align editMessage/deleteMessage behavior and docs

- Fix README: editMessage/deleteMessage both throw, not fallback/no-op
- Fix editMessage JSDoc to reflect it throws
- Make deleteMessage throw instead of silently warning (consistent with editMessage)
- Bump @types/node to ^25.3.2 to match monorepo
- Add sample-messages.md with webhook payload examples

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

* docs(whatsapp): add adapter documentation page

Add whatsapp.mdx covering installation, usage, Meta app setup,
webhook config, interactive messages, media attachments, 24-hour
messaging window, configuration, features, and troubleshooting.
Also add WhatsApp to the adapters navigation in meta.json.

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

* fix: add whatsapp adapter debug logging and try/catch

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

* fix(whatsapp): convert emoji placeholders in outgoing messages

WhatsApp adapter was sending raw {{emoji:wave}} placeholders instead of
Unicode emoji. Apply convertEmojiPlaceholders on all outgoing paths:
text messages, card fallback text, and interactive message fields.

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

* Fix button rendering and streaming (needs to buffer)

* fix(example): handle editMessage failure on WhatsApp

WhatsApp Cloud API doesn't support message editing. Catch the error
in the demo "processing" animation and send a follow-up instead.

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

* feat(chat): add onDirectMessage handler, stop treating DMs as mentions

DMs now route to dedicated onDirectMessage handlers instead of being
forced through onNewMention. If no DM handlers registered, DMs fall
through to onNewMention for backward compat. Adapters no longer set
isMention=true for DMs — the Chat SDK handles routing via adapter.isDM().

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

* feat(chat): always route DMs to onDirectMessage regardless of subscription

Previously, onDirectMessage only fired for unsubscribed DM threads.
Subscribed DMs were routed to onSubscribedMessage, which was confusing
on non-threaded platforms (WhatsApp, Telegram) where all DMs share one
threadId — after the first message, onDirectMessage never fired again.

Now, DMs always route to onDirectMessage first, and onSubscribedMessage
only handles non-DM subscribed threads. Backward compat is preserved:
if no onDirectMessage handlers are registered, DMs fall through as
mentions.

The example bot is simplified accordingly — onDirectMessage now fetches
conversation history via fetchMessages each time instead of relying on
subscribe() and stored state.

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

* feat(chat): pass channel as third argument to DirectMessageHandler

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

* fix(example): reply to channel instead of thread in DM handler

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

* fix(example): use thread instead of channel for DM operations

Channel ID is only two parts (whatsapp:{phoneNumberId}) which isn't a
valid conversation target on WhatsApp. The thread ID includes the user
phone and is required for startTyping/post/fetchMessages.

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

* Revert "fix(example): use thread instead of channel for DM operations"

This reverts commit f4801d7015.

* fix(adapters): return valid thread IDs from channelIdFromThreadId

WhatsApp's channelIdFromThreadId was stripping the user WA ID, producing
an invalid ID that caused ValidationError on channel operations like
startTyping(). Since every WhatsApp conversation is a 1:1 DM, channel
and thread are identical.

Telegram's channelIdFromThreadId was returning a raw chatId without the
telegram: prefix, which is not a valid thread ID for adapter operations.

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

* fix(chat): normalize fullStream in Channel.post() to extract text deltas

Channel.post() was coercing AI SDK fullStream objects to strings via +=,
producing "[object Object]" output. Now uses fromFullStream() to extract
text-delta events, matching how Thread.post() already handles streams.

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

* fix(example): use thread.allMessages for DM history instead of adapter directly

The DM handler was calling channel.adapter.fetchMessages() which always
returns empty on WhatsApp (no native history API). Now uses
thread.allMessages which falls back to the persisted message history
cache, giving the AI conversation context.

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

* feat(chat): add message history support to Channel for DM platforms

Channel now falls back to the persisted message history cache when the
adapter lacks native message fetch (e.g. WhatsApp, Telegram). Incoming
messages are persisted under both thread and channel IDs. Outgoing
messages from channel.post() are also persisted.

The example DM handler now uses channel.messages instead of calling the
adapter directly.

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

* fix(whatsapp): improve markdown rendering and remove broken typing indicator

- Convert headings to bold text, thematic breaks to text separators,
  and tables to code blocks (WhatsApp doesn't support these)
- Convert standard italic (*text*) to WhatsApp italic (_text_) since
  WhatsApp uses *text* for bold
- Make startTyping a no-op (Cloud API doesn't support typing indicators)
- Update channelIdFromThreadId test for channel===thread change

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

* fix(example): reverse channel.messages to chronological order for AI

channel.messages yields newest first but AI expects chronological order.

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

* fix(chat): auto-sort messages chronologically in toAiMessages

toAiMessages now sorts by dateSent (oldest first) so callers don't need
to worry about iteration order from channel.messages or thread.messages.

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

* fix(chat): pass accumulated stream text as markdown in Channel.post()

Stream text was posted as a plain string, bypassing the adapter's format
converter. Now wraps it as { markdown: accumulated } so headings, bold,
italic etc. are properly converted for each platform.

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

* fix(whatsapp): use stringifier options for emphasis and bullets

Use emphasis: '_' and bullet: '-' options in stringifyMarkdown so the
only * in output is **strong**, avoiding conflicts between list bullets
and italic markers. Simplifies toWhatsAppFormat to only convert
**bold** -> *bold* and ~~strike~~ -> ~strike~.

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

* fix(whatsapp): flatten bold inside headings to avoid triple asterisks

When AI outputs headings with bold text like `## **Choose React if:**`,
the heading-to-bold conversion created nested strong nodes producing
`***text***`. Now flattens strong children in headings so they merge
into a single bold span.

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

* test(whatsapp): add full toBe assertion for complex markdown conversion

Also use ━━━ for thematic breaks instead of --- to avoid remark escaping.

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

* test: add WhatsApp replay tests from production recordings

Adds WhatsApp DM replay test infrastructure:
- Fixture from real webhook recordings (dm/whatsapp.json)
- WhatsApp test utilities with HMAC-signed request factory and
  Graph API fetch mock (whatsapp-utils.ts)
- 6 replay tests covering DM handling, thread/channel IDs, message
  sending, status update filtering, sequential messages, and
  message history persistence

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

* fix(whatsapp): fix type narrowing in replay test after merge

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

* Update WhatsApp logo

* Update adapters.json

* Update logos.tsx

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
Co-authored-by: Chitru Shrestha <chitra.shrestha@akuru.com.au>
Co-authored-by: Malte Ubl <malte.ubl@gmail.com>
2026-03-10 15:16:10 -07:00
Malte Ubl 350717aa6b Switch back to sonnet. Otherwise streaming is too fast. Remove the immediate response on AI responses. 2026-03-07 12:42:26 -08:00
Malte Ubl a3cfc1aee3 Support native tables in slack (#194)
* Support native tables in slack

Brand new per https://docs.slack.dev/reference/block-kit/blocks/table-block/
Only one per message.

* Record streams

* Error handling

* Debugging

* fix-delta

* fix-delta2

* fix-delta3

* fix-delta4

* fix-delta5

* show-table

* show-table2

* replay-test

* changeset

* lint-types
2026-03-07 12:31:18 -08:00
Hayden Bleasel ebbe108b76 Fix code scanning issues 2026-03-06 17:15:03 -08:00
Hayden Bleasel 5cfd9be5fa Fix JSX issues (#161)
* Fix JSX issues

* fix: add null guards for event.thread in onAction handlers

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-03-05 16:13:33 -08:00
Malte Ubl ff954f938b Correctly handle AI SDK stream concatenation (#170)
* Correctly handle AI SDK stream concatenation

* changset

* docs

---------

Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
2026-03-04 11:02:02 -08:00
Nicolás Montone f0c7050349 add member_joined_channel event support to Slack adapter (#167)
* Add member_joined_channel event support to Slack adapter

* changset

* Apply suggestion from @NicolasMontone

* fix
2026-03-03 16:24:40 -08:00
Malte Ubl 02e7ef605c Implement markdown and AST rendering for tables across adapters (#160)
* Implement markdown and AST rendering for tables across adapters

* Render the default instead

* Fix bot app

* lint

* streaming-rendering

* fix-streaming

* fix-streaming-agent

* fix-streaming-again2

* fix-streaming-again3

* prefix tests

* no final edit

* changeset

* feedback

* lint

* Copilot feedback
2026-03-03 09:43:18 -08:00
Peter Clement f01b92fec4 Ensure handling of Discord slash commands (#152)
* Correctly handle discord slash commands

* chore: remove unintended bot.tsx formatting churn

* tidy up

* Expand subcommand path into event.command

* Fix race condition

* Run fix

* Fix duplicate slash-context check

* Fix integration test mock

* Run fix

* Update docs

---------

Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com>
2026-03-02 13:21:25 -08:00