mirror of
https://github.com/vercel/chat.git
synced 2026-09-14 18:32:29 +08:00
chore/integrate-commitlint
97 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
79227ae991 |
docs: refresh adapter pages with hand-authored MDX (#474)
## Summary
Refreshes the adapter docs end-to-end so every adapter — official,
vendor-official, and community — now ships hand-authored MDX, lives
under a clean URL structure, and renders on a polished
sidebar/right-rail layout dedicated to `/adapters` (the shared `/docs`
chrome is untouched).
```mermaid
flowchart LR
subgraph Before
direction TB
OB[official] --> CB[community<br/>incl. 5 vendor pages]
end
subgraph After
direction TB
OA[official] --> VA[vendor-official<br/>5 pages] --> CA[community]
end
Before -.-> After
```
### Content & routing
- **New `/adapters/vendor-official/<slug>` route** for vendor-maintained
adapters (Beeper Matrix, Photon iMessage, Liveblocks, Resend, Zernio).
Sidebar gets a third labelled group ("Vendor-Official Adapters") between
Official and Community, with a top divider matching the existing
Community treatment.
- **All 13 vendor-official + community adapters migrated** from runtime
README fetching to hand-authored MDX with rich `features:` matrices and
full body content (install, quick start, configuration, auth,
gateway/streaming, troubleshooting). README fetch stays as a fallback
for any future community adapter that hasn't been migrated yet, gated by
a new `mdxBody: true` frontmatter flag.
- **Messenger filter pages removed** (`/adapters/for/<messenger>` + the
"Browse by messenger" chip row on `/adapters`). Existing URLs
308-redirect to `/adapters`.
- **Permanent redirects** from
`/adapters/community/{matrix,imessage,resend,zernio,liveblocks}` to
their new `/adapters/vendor-official/...` paths.
- **Fixed** `/docs/adapters` and `/docs/state` so the bare pages are
accessible again — the previous catch-all redirect (`:slug*`) was
swallowing them. Switched to `:slug+` so subpath URLs still 308 while
the bare pages render.
### Visual polish
- **Adapter-only sidebar variant** (`AdaptersDocsLayout` +
`AdaptersSidebar`) with uppercase eyebrow separators, tighter rows, and
a thin themed scrollbar utility class. The shared `/docs` sidebar is
untouched.
- **Restyled `AdapterHero`**: drops the badges row + packageName, sits
the title inline with the logo, larger 17 px tagline, horizontal divider
beneath the block.
- **Restyled `PackageInstall`** as a tabbed dark single-line snippet
with a `$` prompt prefix and a copy button — replaces the previous
multi-line `CodeBlock` layout.
- **New "Deploy your chat app on Vercel" upsell card** (`<Upsell />`)
replaces the old `EditSource / ScrollTop / Feedback / CopyPage` footer
cluster on every adapter detail page.
- **Listing & messenger pages**: align the H1 to a tighter `text-4xl
sm:text-[44px]`, and the section headers to `text-base font-medium
tracking-tight` with a one-line muted lede.
### Tooling & tests
- Added `mdxBody: true` opt-in to the adapter frontmatter schema
(`source.config.ts`), and updated both detail-page handlers
(`community/[slug]` and the new `vendor-official/[slug]`) to render the
MDX body when present, falling back to README fetch otherwise.
- Refactored both detail-page handlers to flatten the body-render
branches into a `renderBody()` helper, removing the nested ternaries
that were tripping `lint/style/noNestedTernary`.
- New test file
[`packages/integration-tests/src/docs-adapters.test.ts`](https://github.com/vercel/chat/blob/docs/refresh-adapters/packages/integration-tests/src/docs-adapters.test.ts)
— **220 new assertions** covering:
- Adapter MDX frontmatter completeness, slug ↔ filename consistency, and
`type ∈ {platform, state}`.
- Vendor-official invariants: exactly the expected slugs,
`vendorOfficial: true`, `community: true`, `author`, `mdxBody: true`,
`<FeatureSupport />` rendered.
- Community invariants: `community: true` (never vendor-official),
`mdxBody: true`, `<FeatureSupport />`.
- Official invariants: never flagged, `packageName` always under
`@chat-adapter/*`.
- `adapters.json` ↔ MDX sync on `packageName` / `type` / `community` /
`vendorOfficial`.
- Extended `VALID_DOC_PACKAGES` so `docs-content.test.ts` accepts the
new vendor-official + community packages, plus `@chat-adapter/web`,
`@chat-adapter/web/react`, and `@chat-adapter/messenger`.
### Per-package AGENTS.md
- Added `AGENTS.md` to every official adapter and state adapter (14
packages), each tailored to that adapter's surface — overview, directory
layout, build/test commands, public exports, thread ID format, webhook
flow, authentication, format conversion, cards/streaming, platform
quirks, testing approach, coding conventions, and release rules.
- Added a one-line `CLAUDE.md` (`@AGENTS.md`) beside each so Claude Code
picks up the same instructions through its built-in resolver — same
convention as the root.
### Web adapter copy
- Cleaned up the Web adapter tagline (removed inline backticks) and
dropped the now-redundant "v1 scope" section from the body.
---------
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
|
||
|
|
67c1794a54 |
docs(chat): clarify direct message routing precedence (#491)
## summary clarifies that registered `onDirectMessage` handlers take precedence for incoming DM messages before subscribed-message, mention, and pattern routing updates the direct messages, event handling, thread subscription, and API docs so they match the current runtime behavior adds `onDirectMessage` to the Chat API docs fixes #432 |
||
|
|
add27309fb | feat(telegram): support typed attachment uploads (#485) | ||
|
|
fdebde7988 |
Reapply "feat(slack): expose direct WebClient access via adapter.client" (#472) (#476)
This reverts commit
|
||
|
|
2ffed48bea |
feat: make adapter internals protected to enable subclassing (#475)
* chore: changed class function access to protected * chore: format * chore: added changeset * chore(changeset): drop unchanged shared package, expand description * refactor(adapters): keep internal state private, only protected for extension surface Narrow scope of #475: caches, polling/runtime state, and one-shot warning flags stay private. Methods and shared helpers (logger, formatConverter, chat, config) remain protected as the documented extension surface. Also fixes a typecheck failure where gchat's oauth2Client (now private) no longer requires a portable type for the emitted .d.ts. * test(adapters): add subclass extensibility tests Each adapter now has a compile-time test that subclasses can access the documented protected surface. If any of these members revert to private, the test file fails to type-check. * style: apply ultracite formatting to subclass tests * style(slack): mark static cache TTL constants as readonly These three protected static cache TTLs are configuration constants, not mutable state. Marking them readonly prevents subclasses (the new extension surface from this PR) from mutating values shared across every instance in-process. * test(adapters): document intent of subclass extensibility tests Mirrors the inline comment from the Telegram subclass test across the other nine adapter tests so future readers immediately understand these blocks are type-only sentinels — they fail at typecheck (not vitest) if a member reverts to private. * docs(adapters): document subclassing for adapter customization Adds a "Customizing an adapter via subclassing" section to the Adapters page that walks through extending an official adapter to override a protected hook (using the issue #433 Telegram processUpdate scenario as the canonical example) and clarifies that private members are intentionally off-limits. * refactor(linear): expose accessTokenExpiry to subclasses The surrounding refreshClientCredentialsToken and ensureValidToken methods are now protected, but accessTokenExpiry was kept private — meaning a subclass overriding either method couldn't read or update the expiry without calling super. Flipping it to protected lets subclasses fully reimplement the token-refresh flow. * chore(changeset): bump adapters from patch to minor This PR adds a new, additive capability — subclassing official adapters to override protected hooks. Per CONTRIBUTING.md, additive backward-compatible features warrant a minor bump rather than a patch. * docs(adapters): clarify subclassing surface stability Correct the parenthetical describing what stays `private` (credentials are now `protected`) and add a callout warning that the `protected` extension surface is intentionally broader than the public API but not yet fully stable, so subclass authors know to pin versions and prefer overriding the smallest hook. --------- Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
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 |
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>
|
||
|
|
0adf3adef6 |
feat(tests): add @chat-adapter/tests test kit (#470)
* feat(tests): add @chat-adapter/tests test kit
New package providing Vitest factories, custom matchers, and a setup file for
people building Chat SDK adapters and bots.
Factories: createMockAdapter, createMockChatInstance, createMockState (with
working in-memory subscriptions/locks/KV/queues), createTestMessage,
mockLogger / createMockLogger.
Matchers: toHavePosted(threadId, textPattern?), toHaveDispatched(handler),
toBeSubscribedTo(threadId). Auto-register via the
'@chat-adapter/tests/setup' subpath in vitest setupFiles.
chat and vitest are peer dependencies. Adapter-specific helpers (e.g. signed
Slack webhook builders) belong in each adapter's own /testing subpath, not
in this kit.
* test(integration-tests): allow @chat-adapter/tests imports in README check
* docs: add Testing page covering @chat-adapter/tests
New content/docs/testing.mdx walks bot authors and custom-adapter authors
through the kit's factories, custom matchers, and setup file. Added under
the Usage section in the sidebar, after error-handling.
Cross-link from contributing/testing.mdx clarifying that the hand-rolled
patterns there are for repo contributors building first-party adapters,
while consumers of Chat SDK should use @chat-adapter/tests.
* test(integration-tests): allow @chat-adapter/tests imports in docs check
* fix(tests): match real Adapter.postMessage signature in toHavePosted
Adapter.postMessage is (threadId: string, message: AdapterPostableMessage)
— previously the matcher read args[0] as { id: string } and args[1] as
{ text: string }, neither of which match the actual SDK shape. The matcher's
own tests fed the same wrong shape into the mock so they passed locally
while the matcher silently failed against any real bot or adapter.
Now compares args[0] as a string threadId, and extracts a comparable string
from AdapterPostableMessage's union — strings directly, PostableMarkdown
.markdown, PostableRaw.raw, and PostableCard.fallbackText. PostableAst and
fallback-less cards aren't text-matchable; documented in the JSDoc.
Tests updated to call postMessage with the real signature and to cover
each comparable AdapterPostableMessage shape.
* test(tests): add smoke tests driving matchers against a real Chat
Construct a real `Chat` with a `createMockAdapter` + `createMockState` and
exercise `Chat.thread().post()` and `.subscribe()` end-to-end. The matchers
(toHavePosted, toBeSubscribedTo) then assert against the actual call shape
the SDK uses, so a future signature drift breaks here instead of silently
agreeing with whatever wrong shape lives in the unit tests.
This is the regression guard for the postMessage-shape bug fixed in the
prior commit: each new matcher in subsequent PRs should be paired with a
smoke case here.
* feat(tests): round out adapter mutation matchers
Adds toHaveEdited, toHaveDeleted, toHaveReactedWith, toHaveStartedTyping,
and toHavePostedToChannel — covering the common Adapter mutation surface
that bot authors assert on. Each matcher's signature was checked against
packages/chat/src/types.ts, and each is paired with a smoke case that
drives a real Chat through the corresponding Thread/Channel API so
signature drift breaks the smoke test instead of silently agreeing with
the unit tests.
Emoji matching accepts both plain strings and EmojiValue ({ name }).
Text matching reuses the same extraction rules as toHavePosted —
strings, PostableMarkdown.markdown, PostableRaw.raw, and
PostableCard.fallbackText. Documented in matcher JSDoc, README, and
the Testing docs page.
---------
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
|
||
|
|
0cc3d06fd4 |
docs: fix stale API examples, adapter matrix, and broken links (#463)
* 1 * 2 * cs * 3 |
||
|
|
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> |
||
|
|
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> |
||
|
|
eb5f94a8ee |
feat(chat): add message.subject and adapter client access (#459)
* 1 * w * 3f * x * ln * gh * sl * t1 * u * t2 * t3 * t4 * t5 * d * d2 * cs * fx * cl * docs: clean up subject + .client docs and restructure nav - subject.mdx: simplify prose, drop redundant platform lists, link to MessageSubject API and getAdapter - api/message.mdx: add MessageSubject TypeTable - api/chat.mdx: expand getAdapter with Direct client access content - adapters.mdx: add Parent subject and Native client rows to feature matrix - usage.mdx: mention .client under Accessing adapters - adapter-github/-linear READMEs: add Direct API client section - meta.json: split Features into Messaging + Interactivity, move error-handling to Usage - title case across messaging-cluster page titles --------- Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
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>
|
||
|
|
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>
|
||
|
|
3546b3fddb |
feat(slack): use native markdown_text for outgoing messages (#440)
* feat(slack): use native markdown_text field for outgoing messages
Slack now natively renders markdown via the `markdown_text` parameter on
chat.postMessage / postEphemeral / update / scheduleMessage and via
response_url payloads. The adapter passes markdown through directly instead
of converting to mrkdwn.
- Tables, headings, code fences, blockquotes, and nested lists render
natively in Slack instead of falling back to ASCII / mrkdwn.
- `string` and `{ raw }` messages still go to `text` (preserves literal `*`).
- `{ markdown }` and `{ ast }` messages go to `markdown_text` (12k char limit).
- `renderWithTableBlocks`, `toBlocksWithTable`, `mdastTableToSlackBlock`,
and the AST→mrkdwn renderer (`fromAst` / `nodeToMrkdwn`) are removed.
- `SlackMarkdownConverter` alias is removed; use `SlackFormatConverter`.
- `renderFormatted(ast)` now returns standard markdown (was mrkdwn).
- Incoming `message` events still arrive as mrkdwn and are parsed unchanged.
Net -473 lines across markdown.ts and the five sender call sites.
* fix(slack): use mrkdwn fallback for response_url edits
|
||
|
|
aa1b08a246 |
docs: cover gchat verification, linear token encryption, shared crypto helpers (#445)
* docs: cover gchat verification, linear token encryption, shared crypto helpers The adapter hardening pass in #441 made gchat JWT verification required, added optional at-rest encryption for Linear OAuth tokens, and promoted the AES-256-GCM helpers into @chat-adapter/shared. Update the affected package READMEs and the adapter-authoring guide to match. * docs(adapter-shared): correct EncryptedTokenData field names --------- Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
d9c2fcaf93 |
docs: update documentation for 4.27.0 release (#439)
* docs: update documentation for 4.27.0 release * docs: fix broken apiUrl link in adapters feature matrix * docs: fix error code thrown-by columns and add missing UNKNOWN_USER_ID_FORMAT |
||
|
|
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> |
||
|
|
70281dc58f |
feat(chat): add initialOption and option_groups to ExternalSelect (#410)
* feat(chat): add initialOption and option_groups to ExternalSelect * docs(modals): document ExternalSelect initialOption and option_groups, truncate group label to 75 chars |
||
|
|
b0ab804f18 |
- Bundle guide markdown and a templates manifest with the chat package at resources/guides/*.md and resources/templates.json so AI agents can discover Chat SDK resources offline (#423)
- Add scripts/sync-resources.ts (run via pnpm sync-resources) that reads apps/docs/resources-edge-config.json, fetches each guide's .md version over https with a timeout and size cap, writes templates.json, and regenerates the Available resources block in skills/chat/SKILL.md - Migrate the Slack Next.js, Discord Nuxt, and Hono code-review guides from on-site MDX to Vercel KB and register them in the resources edge-config JSON alongside the existing external guides - Remove /docs/guides MDX content, sidebar entries, top-level Guides nav entry, getting-started cards, and the dead /guides/ branch in the sitemap route now that all guides live externally and are surfaced on /resources - Replace the homepage Guides/Templates section and the standalone Adapters pill section with a single two-column Resources + Adapters section (icons, headings, descriptions, outline buttons, divider), and drop the URL footer from ResourceCard on the Resources page - Update skills/chat/SKILL.md to point at resources/guides and resources/templates.json and list the available guides and templates between marker comments that sync-resources rewrites - Add tsx to knip's ignoreBinaries so npx tsx in the new script does not fail lint Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> Co-authored-by: dancer <josh@afterima.ge> |
||
|
|
e8c4b1a6c4 |
docs: update adapter docs (#412)
* docs: update adapter docs * docs: update adapter docs * docs: update adapter docs --------- Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
a179b29141 |
Implement external_select block kit for Slack (#397)
* feat(slack): external_select's block kit implementation - block_suggestion handler for slack webgook - new <ExternalSelect/> to chat modal jsx - new .onOptionsLoad() * feat: add tests for new external_selec implementation * feat(docs): Slack externa_select docs * chore: changeset * chore: remove docs from changeset --------- Co-authored-by: dancer <josh@afterima.ge> |
||
|
|
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>
|
||
|
|
46fc5bbe9d |
Sync docs with @vercel/geistdocs 1.2.0–1.2.3 (#399)
* docs: sync geistdocs template 1.2.0–1.2.3 + polish
- Ran @vercel/geistdocs@1.2.2 update --sync against origin/main
(already contains merged 1.2.3) for:
- components/geistdocs/*
- components/geistcn-fallbacks/**/* (new)
- components/ui/command-prompt.tsx, navigation-menu.tsx
- app/styles/geistdocs.css
- Manual overlays (paths skipped by sync due to chat customizations):
- app/[lang]/layout.tsx: drop scroll-smooth (1.2.2)
- app/[lang]/docs/[[...slug]]/page.tsx: MobileDocsBar + disable
default TOC popover (1.2.0)
- app/[lang]/docs/layout.tsx: wrap in bg-background-200 (1.2.3)
- components/ui/badge.tsx: secondary variant → bg-gray-300/text-gray-1000 (1.2.3)
- Home hero: replace Get Started + Installer with CommandPrompt
humans/agents switcher ("npm install chat" / "npx skills add vercel/chat")
- (home) layout: swap bg-sidebar dark:bg-background for bg-background-200
so /, /adapters, /resources share the navbar surface
- DesktopMenu: active-state detection with longest-prefix match
(so /docs/api highlights "API", not also "Docs")
- navbar-logo dropdown: drop Chat SDK self-entry
- New geist-fill icons (check-circle-fill, cross-circle-fill,
warning-fill) ported from @vercel/geistcn-assets; new
components/custom/status-icons.tsx registers Check/Cross/Warn MDX
components
- content/docs/adapters.mdx: replace ✅/❌/⚠️ emojis with the new
icons (emoji.mdx intentionally left alone)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: use LogoChatSdk from geistcn-fallbacks as the app Logo
Replace the inline Chat SDK wordmark SVG in geistdocs.tsx with
<LogoChatSdk /> so the navbar and other Logo consumers share the
same source of truth.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: default hero CommandPrompt to humans tab
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
37dbb4ac8b |
feat(chat): add thread.getParticipants() (#386)
* feat(chat): add thread.getParticipants() method Returns unique human participants in a thread by scanning message history. Excludes the bot itself. Useful for subscribing only to 1:1 conversations and unsubscribing when others join. * fix: filter all bots in getParticipants(), not just self Third-party bots (e.g. Jira, GitHub) were included as participants because only isMe was checked. Now filters on isBot as well. * docs: add getParticipants() to Thread API reference and guides --------- Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
47a9c04fca | fix(docs): replace thread.stream() with adapter.stream() in streaming docs (#387) | ||
|
|
bca47924b7 |
feat: enhance task update structure with optional details field (#385)
- Added a `details` field to the `task_update` type for providing additional context in task updates. - Updated relevant documentation and test cases to reflect the new field, improving clarity on task progress reporting. |
||
|
|
608d5f00b0 |
feat(chat): add chat.thread() for creating thread handles (#380)
* feat(chat): add chat.thread() method for creating thread handles Allows constructing a Thread handle from a thread ID outside of webhook contexts, enabling proactive messaging to existing threads. Closes #148 * docs: add chat.thread() to API reference --------- Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
1e7c55150e | feat: restore attachment fetchData after queue/debounce serialization (#338) | ||
|
|
ccc042a608 |
docs: misc updates and fixes (#370)
* docs: misc updates and fixes * docs: update feature matrix to show Teams modal support --------- Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
2235c160ca |
fix(chat): export standalone reviver for workflow-safe deserialization (#257)
* fix(chat): export standalone reviver for workflow-safe deserialization Importing the Chat instance into Vercel Workflow files pulls in adapter packages that depend on Node.js modules, which aren't available in the workflow sandbox. This adds a standalone `reviver` function exported from `chat` that deserializes Thread/Channel/Message objects without needing a Chat instance or its adapter dependencies. Also updates the durable chat sessions guide to use the standalone reviver and dynamic bot imports inside step functions. Closes #243 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(chat): preserve adapterName on deserialized toJSON and add standalone reviver docs * fix: sort imports in index.ts --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: dancer <josh@afterima.ge> Co-authored-by: Ben Sabic <27636870+bensabic@users.noreply.github.com> Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
1856198f6d |
fix(slack): OAuth redirect handling (#307)
* Fix Slack OAuth redirect handling * fix slack check --------- Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
1fcab01302 |
Move messenger pages to /adapters/for/ and add browse section (#332)
* feat: refine adapter categorization and display logic in AdaptersGrid component Slack-Thread: https://vercel.slack.com/archives/C08077A6JDB/p1775414281367229?thread_ts=1775414281.367229&cid=C08077A6JDB Co-authored-by: Malte Ubl <89679+cramforce@users.noreply.github.com> * feat: rename messenger to for in adapter paths and add messenger links Slack-Thread: https://vercel.slack.com/archives/C08077A6JDB/p1775414281367229?thread_ts=1775414281.367229&cid=C08077A6JDB Co-authored-by: Malte Ubl <89679+cramforce@users.noreply.github.com> --------- Co-authored-by: v0 <v0[bot]@users.noreply.github.com> Co-authored-by: Malte Ubl <89679+cramforce@users.noreply.github.com> |
||
|
|
acc4a336a0 | Minor docs fix (#296) | ||
|
|
f2d8957797 | Docs for the new concurrency strategies (#294) | ||
|
|
9c498f4fd7 |
docs: fix typo "Committment" → "Commitment" (#274)
Co-authored-by: Arif Kobel <arif.kobel@phorax.com> |
||
|
|
d114ebfabf |
toAIMessages docs improvements (#263)
* toAIMessages docs improvements * toAIMessages docs improvements |
||
|
|
28c1f03828 | Update docs (#255) | ||
|
|
e45a67f491 |
feat: add adpater disconnect hook (#219)
* feat: add adpater disconnect hook * fix: use Promise.allSettled for resilient shutdown, add docs and changeset - Use Promise.allSettled so one failing adapter doesn't prevent others from disconnecting - Add test for error resilience during shutdown - Remove unnecessary type guards in tests - Fix lint: sort interface members, format test file - Add changeset (minor bump for chat) - Document disconnect hook in API docs and adapter building guide Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
b095fe2e21 | docs: fix incorrect reference to c.executionCtx.waitUntil (#249) | ||
|
|
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>
|
||
|
|
4c7a2b2bec |
Document scheduled message support across all docs (#207)
Add thread.schedule() and channel.schedule() documentation to the API reference, usage guides, feature matrix, error handling guide, adapter building guide, and SKILL.md. Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
7c4719b3f3 |
Adds two Workflow-focused guides (#211)
* Add workflow guides for durable chat sessions and scheduled posts * Update workflow guides * fix: use correct Workflow APIs and fix TypeScript issues - Replace `defineHook`/`.create()`/`.resume()` with `createHook` + `resumeHook` from documented Workflow APIs - Add TypeScript 5.2+ note for `using` keyword (explicit resource management) - Fix `result.rowCount` null safety in cancelScheduledPost Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
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
|
||
|
|
30698ed6d8 |
Adapters (#204)
* Initial marketplace draft * Update language * Add logos to cards * Redesign cards * Update adapter-card.tsx * Update adapter-card.tsx * Migrate marketplace to adapters * Update meta.json * Split adapters into new three groups * Add iMessage * Move adapter docs to READMEs * Cleanup docs * Add more logos, implement shadcn ui components * Update adapters.json * Add Streamdown * Fetch vercel readmes from workspace * Update readme-content.tsx * Upgrade Streamdown * Update global.css * Update adapters.json * Update adapters.json * Add links to docs * List upcoming official adapters * Update adapters.json * Fix adapters links * Fix typo * Misc fixes * Update adapters.json * Update adapters.json * Migrate new info * Update pnpm-lock.yaml * Update adapter-card.tsx * Add postgres to adapters page * Update adapter-card.tsx * Migrate postgres docs * Add pg to valid README imports Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Move feature matrices from docs to package READMEs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Remove packages tables from adapter/state docs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Genericize adapter/state doc descriptions Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: improve adapters page accessibility and empty state handling * Add custom adapter building section to SKILL.md * Use currentColor for GitHub, Linear, and Memory icons * Use GitHub API for README fetch, add heading to fallback state Use the GitHub REST API instead of raw.githubusercontent.com to automatically resolve the repo's default branch, so community adapters using master or other branch names work correctly. * Update adapters-grid.tsx --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
dac88f8ad1 |
docs: mention PostgreSQL state adapter across remaining docs (#201)
Several docs pages only referenced Redis as the production state adapter. Add PostgreSQL mentions to usage page, guide "Next steps" sections, adapters overview, SKILL.md, and package README. Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> |
||
|
|
eb49b2a9d1 |
feat: add forceReleaseLock + onLockConflict for interrupt/steerability (#193)
* feat: add forceReleaseLock and onLockConflict for steerability Amp-Thread-ID: https://ampcode.com/threads/T-019cc675-20e8-73db-b852-5690bafe0008 Co-authored-by: Amp <amp@ampcode.com> * fix: add forceReleaseLock to mock state adapter Implements the new StateAdapter.forceReleaseLock method in the mock adapter so tests using createMockState() don't break. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: support async onLockConflict callbacks Allow the onLockConflict callback to return a Promise, enabling users to check external state (e.g. DB queries) before deciding whether to force-release or drop. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: add tests for forceReleaseLock and onLockConflict Covers: default drop behavior, force mode, sync/async callbacks returning force/drop, and forceReleaseLock on memory adapter. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: add changeset for forceReleaseLock and onLockConflict Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: alphabetize interface members, add forceReleaseLock to ioredis adapter Biome enforces alphabetical ordering on interface members. Also adds the missing forceReleaseLock implementation to the ioredis state adapter. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add missing @chat-adapter/state-ioredis to changeset Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add comment noting race window after force-release Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: add forceReleaseLock tests for ioredis adapter Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: assert lock re-acquisition after force-release Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: add forceReleaseLock tests for state-redis adapter Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: replace fragile acquireLock call count with last-call assertion Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: document concurrent handler execution after force-release Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use .at(-1) and single-line format for biome compliance Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add forceReleaseLock to state-pg adapter Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: document onLockConflict and forceReleaseLock Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Amp <amp@ampcode.com> Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
dd60e67f5f | fix: discord docs (#199) | ||
|
|
8fe175f7fe |
Implement state adapter based on Postgres (#154)
* Initial work on postgres state store * Run fix * Replace pre-written CHANGELOG with proper changeset The CHANGELOG was manually written with a version entry. This repo uses Changesets to manage versioning, so add a proper changeset file instead. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add missing @vitest/coverage-v8 dev dependency The vitest config specifies coverage provider "v8" but the package was missing from devDependencies. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Drop drizzle-orm, use raw postgres queries The adapter only needs 3 simple tables with basic CRUD. drizzle-orm is a full ORM that adds significant dependency weight for no real benefit here. The ensureSchema() method was already using raw queries. This halves the dependency surface to just the postgres driver. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add comprehensive unit tests for postgres state adapter Test factory function edge cases (missing URL, env var fallbacks, custom keyPrefix) and ensureConnected guard for all state operations. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Document expired row cleanup limitation for postgres adapter Unlike Redis, Postgres doesn't auto-delete expired rows. Document the opportunistic cleanup behavior and provide SQL for periodic cleanup. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Remove stale drizzle keyword from package.json drizzle-orm was removed in a prior commit but the keyword remained. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix expiresAt parsing in acquireLock The postgres library returns timestamptz columns as JavaScript Date objects, not strings. Wrapping in new Date() was unnecessary overhead. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Document lock atomicity difference vs Redis adapters The Postgres ON CONFLICT approach relies on row-level locking rather than a single atomic SET NX PX like Redis. Note this for users who need high-contention distributed locking. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Use crypto.randomUUID() for lock token generation Math.random() is not cryptographically secure and has a higher collision risk in distributed environments. crypto.randomUUID() is available in Node 16+ and provides better uniqueness guarantees. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Delete CHANGELOG.md * Add comprehensive unit tests for postgres state adapter Mock the postgres module and SQL client to achieve 100% coverage across statements, branches, functions, and lines. Tests cover factory function, connection lifecycle, subscriptions, locking, cache operations, and the owned-client disconnect path. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add setIfNotExists method and merge with main Implement the setIfNotExists method added to the StateAdapter interface since this branch diverged. Uses INSERT ... ON CONFLICT DO NOTHING with RETURNING to atomically check-and-set. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add postgres to state docs navigation and memory adapter callout Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Migrate postgres state adapter from postgres to pg (node-postgres) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Rename state-postgres to state-pg and align version to 4.17.0 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Hayden Bleasel <hello@haydenbleasel.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
c744bdc2ef | Update teams.mdx |