Files
vercel__chat/PROJECT.md
Malte Ubl d2ffcc2cbe The great rename
We've acquired `chat` on npm. This plan covers renaming all packages and setting up automated publishing.

| Current Name | New Name | Version |
|--------------|----------|---------|
| `chat-sdk` | `chat` | 4.0.0 |
| `@chat-sdk/slack` | `@chat-adapter/slack` | 4.0.0 |
| `@chat-sdk/gchat` | `@chat-adapter/gchat` | 4.0.0 |
| `@chat-sdk/teams` | `@chat-adapter/teams` | 4.0.0 |
| `@chat-sdk/state-memory` | `@chat-adapter/state-memory` | 4.0.0 |
| `@chat-sdk/state-redis` | `@chat-adapter/state-redis` | 4.0.0 |
| `@chat-sdk/state-ioredis` | `@chat-adapter/state-ioredis` | 4.0.0 |

1. **Main package** (`packages/chat-sdk/package.json`):
   - Change `"name": "chat-sdk"` → `"name": "chat"`
   - Change `"version": "0.1.0"` → `"version": "4.0.0"`

2. **Adapter packages**:
   - `packages/adapter-slack/package.json`: `@chat-sdk/slack` → `@chat-adapter/slack`
   - `packages/adapter-gchat/package.json`: `@chat-sdk/gchat` → `@chat-adapter/gchat`
   - `packages/adapter-teams/package.json`: `@chat-sdk/teams` → `@chat-adapter/teams`
   - Update all versions to `4.0.0`
   - Update dependency on `chat-sdk` → `chat`

3. **State packages**:
   - `packages/state-memory/package.json`: `@chat-sdk/state-memory` → `@chat-adapter/state-memory`
   - `packages/state-redis/package.json`: `@chat-sdk/state-redis` → `@chat-adapter/state-redis`
   - `packages/state-ioredis/package.json`: `@chat-sdk/state-ioredis` → `@chat-adapter/state-ioredis`
   - Update all versions to `4.0.0`
   - Update dependency on `chat-sdk` → `chat`

Update `peerDependencies` and `dependencies` in each package:

```json
// Before
"peerDependencies": {
  "chat-sdk": "workspace:*"
}

// After
"peerDependencies": {
  "chat": "workspace:*"
}
```

Search and replace across the codebase:

| Find | Replace |
|------|---------|
| `from "chat-sdk"` | `from "chat"` |
| `from 'chat-sdk'` | `from 'chat'` |
| `import("chat-sdk")` | `import("chat")` |
| `from "@chat-sdk/slack"` | `from "@chat-adapter/slack"` |
| `from "@chat-sdk/gchat"` | `from "@chat-adapter/gchat"` |
| `from "@chat-sdk/teams"` | `from "@chat-adapter/teams"` |
| `from "@chat-sdk/state-memory"` | `from "@chat-adapter/state-memory"` |
| `from "@chat-sdk/state-redis"` | `from "@chat-adapter/state-redis"` |
| `from "@chat-sdk/state-ioredis"` | `from "@chat-adapter/state-ioredis"` |

Update any references to old package names in:
- `pnpm-workspace.yaml`
- `turbo.json` (task filters)
- Root `package.json` scripts

1. **README.md files** in each package
2. **Examples** (`examples/nextjs-chat/`)
3. **AGENTS.md** and other docs
4. **JSDoc comments** referencing package names

1. **Install changesets**:
   ```bash
   pnpm add -D @changesets/cli
   pnpm changeset init
   ```

2. **Configure `.changeset/config.json`**:
   ```json
   {
     "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
     "changelog": "@changesets/cli/changelog",
     "commit": false,
     "fixed": [],
     "linked": [
       ["chat", "@chat-adapter/*"]
     ],
     "access": "public",
     "baseBranch": "main",
     "updateInternalDependencies": "patch",
     "ignore": ["example-nextjs-chat"]
   }
   ```

3. **Add GitHub Action** (`.github/workflows/release.yml`):
   ```yaml
   name: Release

   on:
     push:
       branches:
         - main

   concurrency: ${{ github.workflow }}-${{ github.ref }}

   jobs:
     release:
       name: Release
       runs-on: ubuntu-latest
       steps:
         - uses: actions/checkout@v4
         - uses: pnpm/action-setup@v2
           with:
             version: 9
         - uses: actions/setup-node@v4
           with:
             node-version: 20
             cache: 'pnpm'

         - run: pnpm install
         - run: pnpm build
         - run: pnpm test

         - name: Create Release Pull Request or Publish
           uses: changesets/action@v1
           with:
             publish: pnpm changeset publish
           env:
             GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
             NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
   ```

4. **Add publish config to each package.json**:
   ```json
   "publishConfig": {
     "access": "public"
   }
   ```

1. Run full build: `pnpm build`
2. Run typecheck: `pnpm typecheck`
3. Run lint: `pnpm lint`
4. Run tests: `pnpm test`
5. Test local linking in example app

| File | Changes |
|------|---------|
| `packages/chat-sdk/package.json` | Rename to `chat`, version 4.0.0 |
| `packages/adapter-slack/package.json` | Rename to `@chat-adapter/slack`, update deps |
| `packages/adapter-gchat/package.json` | Rename to `@chat-adapter/gchat`, update deps |
| `packages/adapter-teams/package.json` | Rename to `@chat-adapter/teams`, update deps |
| `packages/state-memory/package.json` | Rename to `@chat-adapter/state-memory`, update deps |
| `packages/state-redis/package.json` | Rename to `@chat-adapter/state-redis`, update deps |
| `packages/state-ioredis/package.json` | Rename to `@chat-adapter/state-ioredis`, update deps |
| `examples/nextjs-chat/package.json` | Update all dependencies |
| `packages/integration-tests/package.json` | Update all dependencies |
| `turbo.json` | Update package references |
| All `*.ts` files | Update import statements |
| All `README.md` files | Update package names in docs |
| `.changeset/config.json` | Create new |
| `.github/workflows/release.yml` | Create new |

1. Update all `package.json` files (names, versions, dependencies)
2. Run `pnpm install` to update lockfile
3. Update all import statements in source files
4. Update all import statements in test files
5. Update documentation
6. Set up changesets
7. Run full validation
8. Create initial changeset for 4.0.0 release
9. Commit and push

If issues arise:
1. Git revert the rename commit
2. Run `pnpm install` to restore lockfile
3. Previous npm versions remain available
2026-01-02 13:56:55 -08:00

4.2 KiB

Overview

We are building an abstraction library, similar to an ORM for databases, that allows me to program applicatons that interact with chat systems like Slack, Microsoft Teams, Google Chat, and Discord.

The primary use case are AI agents that help users of the chat app.

Goals

  • Programming language: TypeScript
  • For very common tasks I should be able to write a program that doesn't care about whether the user is using Slack, Teams, etc.
  • First-class support for listening to new @-mentions (first time mention in a thread) and then following subsequent messages in the thread
  • For very advanced formatting, an escape hatch is fine, but it should be rarely used.
  • Serverless compatibility. Should work with @vercel/slack-bolt for Slack and have a higher level API compatible with it.
  • Compatibility with modern web frameworks for the webhooks (Next.js, Hono, etc.)
  • Built-in ability to "lock a thread" which ensures that only one instance of the bot replies at a time even if multiple webhooks fire in short order (using e.g. the Redis state handler)

Functionality

  • Listening to all new threads or messages in a channel. Optionally, subscribing to all future messages in a thread
  • Listening to new @-mentions in any message in a subscribed channel
  • Ability to post to a thread
  • Ability to make new threads
  • Ability to unsubscribe from threads

Implementation / Architecture

  • I assume that all these chat apps have an API that is basically
    • A way to send messages, new thread or into a threa
    • Receive a webhook with new messages
    • But I have not researched this. Some may just give you all messages, some may have matchers.
  • There should be a common interface that the end user of our library program and an adapter directory with one entry for Slack, Teams, etc. that implements it.

Pseudo code

This isn't the final API, just me thinking of what would be nice.

Setting up the bot

[lib/bot.ts]

import {Chat} from "chat";
import {Bold} from "chat/jsx-runtime";
import {SlackAdapter} from "@chat-adapter/slack";
import {TeamsAdapter} from "@chat-adapter/teams";
import {GoogleChatAdapter} from "@chat-adapter/gchat";
import {createRedisState} from "@chat-adapter/state-redis";

export const bot = new Chat({
  userName: "mybot" // @mybot in Slack, Teams, etc.
  adapters: {
    slack: new SlackAdapter({
      secret: …
    }),
    teams: new TeamsAdapter({
      secret: …
    }),
    google: new GoogleChatAdapter({
      userName: "awesomeBot", // Called @awesomeBot on Google
      secret: …
    }),
  },
  conversationState: createRedisState()
});

// Threads are default-locked to this instance while callbacks run
bot.onNewMention(async (thread, message) => {
  await thread.subscribe();
  await thread.post("Thanks for adding me");
});

bot.onNewMessage(/topic/, (thread, message) => {
  await thread.subscribe();
  await thread.post("Thanks for adding me");
});

bot.onSubscribedMessage(async (thread, newMessage) => {
  const reply = myAgent.generate({
    recentMessages: thread.recentMessages,
    newMessage
  });
  thread.post(`**Agent** response: ${reply}`);
})

Interfaces

interface Thread {
  recentMessages: Message[];
  allMessages: AsyncIteractor<Message>;
  subscribe: (thread: Thread, latestMessage: Message) => Promise<void>;
  unsubscribe: () => Promise<unknown>;
  post: (message: string | FormattedMessage) => Promise<void>;
  // Make recentMessages reflect the current state
  refresh: () => Promise<void>;
}

interface Message {
  text: string;
  formatted: FormattedMessage;
  author: {
    userName: string; // handle for @-mention
    fullName: string;
    userId: string; // Unique ID
    isBot: false | true | "unknown";
    isMe: boolean; // Whether the message was sent by this bot
  };
  metadata: {
    dateSent: Date; // and other metadata
  };
}

One WebHook route per adapter

[app / api / webhooks / slack / route.ts];

import { bot } from "@/lib/bot";

export const POST = bot.webhooks.slack;
[app / api / webhooks / teams / route.ts];

import { bot } from "@/lib/bot";

export const POST = bot.webhooks.teams;
[app / api / webhooks / google / route.ts];

import { bot } from "@/lib/bot";

export const POST = bot.webhooks.google;