Commit Graph

14 Commits

Author SHA1 Message Date
Bryan Hunter bdeb2bf1b1 fix(workflow): isolate chat serializers from node runtime (#806)
## Failure

Workflow SDK `5.0.0-beta.40` produces an invalid workflow bundle when a
Chat SDK serializable class such as `Message`, `ThreadImpl`, or
`ChannelImpl` crosses a workflow step boundary.

The Workflow compiler imports the emitted module containing each class
to register its `@workflow/serde` methods. In Chat SDK `4.37.0`, tsup
emits those classes in `dist/index.js`. The root entry also imports the
conversation-scoping implementation added in #751, which uses
`AsyncLocalStorage` from `node:async_hooks`. Serializer registration
therefore pulls Node-only code into the sandboxed workflow bundle before
any workflow or step executes.

Build warning:

```text
Serde warning for classes "ChannelImpl", "Message", "ThreadImpl":
Workflow bundle contains Node.js built-in imports: async_hooks.
These will fail at runtime in the workflow sandbox.
```

Deployed workflows then fail during module initialization:

```text
var import_async_hooks = require("async_hooks");
                         ^

ReferenceError: require is not defined
```

## Minimal reproduction

```json
{
  "dependencies": {
    "chat": "4.37.0",
    "workflow": "5.0.0-beta.40"
  }
}
```

```ts
import { Message } from "chat";

async function createMessageStep(value: string): Promise<Message> {
  "use step";

  return new Message({
    id: "message",
    threadId: "slack:C123:123.456",
    text: value,
    formatted: {
      type: "root",
      children: [
        {
          type: "paragraph",
          children: [{ type: "text", value }],
        },
      ],
    },
    raw: {},
    author: {
      userId: "U123",
      userName: "user",
      fullName: "User",
      isBot: false,
      isMe: false,
    },
    metadata: { dateSent: new Date(), edited: false },
    attachments: [],
  });
}

export async function testWorkflow(value: string): Promise<string> {
  "use workflow";

  const message = await createMessageStep(value);
  return message.text;
}
```

Running `workflow build` on `4.37.0` emits the warning; deploying the
output produces the runtime failure above.

## Fix

- Add a dedicated `chat/serialization` package entry for `Message`,
`ThreadImpl`, `ChannelImpl`, `reviver`, and their serialized DTO types.
- Make serializer code a second tsup entry and explicitly enable
splitting. The serializer-bearing classes are now emitted into a shared
chunk with no dependency on `Chat` or its Node-only conversation
context.
- Preserve the existing root exports and automatic `@workflow/serde`
behavior. Existing `import { Message } from "chat"` workflow code
remains valid.
- Add a post-build module-graph assertion that fails if any emitted
serializer registration can transitively import a Node.js builtin.
- Test against Workflow SDK `5.0.0-beta.40`, the compiler version that
exposed the invalid bundle.
- Add a minor changeset for the fixed-version Chat SDK packages,
producing the `4.38.0` release line.

After the change, the emitted serializer classes live in a sandbox-safe
shared chunk while `AsyncLocalStorage` remains in a separate Node
runtime chunk. The exact reproduction compiles successfully with `5
steps, 1 workflow` and no Serde warning.

## Control cases

The failure requires a serializable Chat class to cross a durable
boundary. These cases were already safe and remain unchanged:

- `AsyncLocalStorage` used entirely inside a `"use step"` function.
- A Chat `Message` created and consumed within one step while returning
plain data.
- Request handlers that convert Chat objects to plain workflow DTOs
before starting a workflow.
- `@vercel/sandbox` used entirely inside a step.

## Validation

- Committed beta.40 reproduction fixture: type-correct and compiled
during every Chat package build with no Node builtin / Serde warning.
- Emitted serializer module graph: no transitive Node.js builtins.
- Chat package: 1,113 tests pass.
- Chat package typecheck passes.
- Repository formatting and lint checks pass.
- Package build passes.

Full repository validation reaches the pre-existing `knip` baseline and
reports unrelated unused dependencies and unlisted binaries in examples
and adapter packages.

---------

Signed-off-by: bryan-hunter <bryan.hunter@vercel.com>
2026-08-10 09:03:49 -05: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 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
Ben Sabic 8f3af76565 feat: add create-chat-sdk CLI (#603)
Adds `create-chat-sdk`, a CLI that scaffolds a Next.js Chat SDK bot
project:

```bash
npm create chat-sdk@latest my-bot

# non-interactive
npm create chat-sdk@latest -- my-bot --adapter slack redis -y
```

The user picks platform and state adapters interactively or via
`--adapter`, and the CLI generates a webhook-only project with
`src/lib/bot.ts`, `.env.example`, `next.config.ts`, `package.json`, and
a README, then optionally runs `git init` and installs dependencies.
There are no pages or client UI in the template.

Adapter choices come straight from the `chat/adapters` catalog, so the
CLI has no adapter registry of its own. When a coding agent such as
Cursor or Claude Code runs the CLI, it uses non-interactive defaults and
requires an explicit platform adapter. `--interactive` forces prompts.

## also in this pr

- `google-chat` is renamed to `gchat` everywhere, including docs pages,
the OG image, and adapter catalog. Old URLs redirect permanently,
including language-prefixed and `/og` paths
- a new docs page is available at `chat-sdk.dev/docs/create-chat-sdk`,
and the CLI is promoted on the homepage, package READMEs, and agent
skill
- `create-chat-sdk` releases independently with a minor changeset for
its initial `0.1.0` release

---------

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: dancer <josh@afterima.ge>
2026-06-16 08:48:38 +01:00
Ben Sabic 9921dcd1c4 docs(seo): improve npm metadata, README discoverability, and structured data (#587)
Improves Chat SDK discoverability across npm, READMEs, and the docs site
for search engines and AI coding agents.

- **npm metadata**: point every published package `homepage` at
chat-sdk.dev deep links; expand `chat` keywords/description; fix
`repository.directory` (`packages/chat-sdk` → `packages/chat`); align
state adapter keywords
- **READMEs**: add npm callouts, Documentation/Guides links, and AI
Coding Agents sections (skill install, optional Vercel Plugin,
`llms.txt` / `llms-full.txt`) across all published packages and the repo
root
- **docs JSON-LD**: `HowTo` / `TechArticle` on getting-started,
streaming, and cards; `CollectionPage` + official-only `ItemList` on
`/adapters` (with split human vs JSON-LD descriptions)
- **UTMs**: add `chat-sdk_site` / `chat-sdk_repo` tracking params to
Resources links in selected MDX pages and adapter READMEs (discord,
github, slack, liveblocks, getting-started, ai index)
- **contract tests**: integration-tests guardrails for npm metadata and
README discoverability so future package additions don't drift

---------

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

Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
Co-authored-by: dancer <josh@afterima.ge>
2026-04-27 10:20:44 +10:00
Hayden Bleasel a563031ea0 Update knip.json 2026-03-01 10:45:02 -08:00
Hayden Bleasel 8a9a6d2374 Upgrade vitest 2026-02-27 09:58:28 -08:00
Hayden Bleasel e970a6939b Upgrade Biome configuration to use Ultracite preset (#81)
* Upgrade Biome to Ultracite

* Remove package commands

* Update biome.jsonc

* Update biome.jsonc

* Initial fixes

* Update biome.jsonc

* Remaining fixes

* Update pnpm-lock.yaml

* Fix commands

* Update knip.json

* Merge Claude files

* Fix skipped test

* Misc fixes
2026-02-20 22:01:10 -08:00
Hayden Bleasel d39c0820b7 Update knip.json 2026-02-16 17:33:08 -08:00
Hayden Bleasel 63afae427b Misc fixes 2026-02-16 17:30:07 -08:00
Malte Ubl da0b16fa9a JSX 2026-01-01 19:01:37 -08:00