mirror of
https://github.com/vercel/chat.git
synced 2026-09-14 18:32:29 +08:00
79227ae991
## 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>
252 lines
9.8 KiB
Markdown
252 lines
9.8 KiB
Markdown
# AGENTS.md — `@chat-adapter/github`
|
|
|
|
Guidance for coding agents working inside the GitHub adapter package.
|
|
The top-level repository [AGENTS.md](../../AGENTS.md) covers
|
|
monorepo-wide build, lint, and release rules — read it first. This
|
|
file documents the adapter-specific surface, conventions, and pitfalls.
|
|
|
|
## Overview
|
|
|
|
`@chat-adapter/github` connects a Chat SDK bot to issue and
|
|
pull-request comment threads on GitHub. It covers:
|
|
|
|
- HTTP webhook endpoint at `/api/webhooks/github` for `issues`,
|
|
`issue_comment`, `pull_request`, `pull_request_review_comment`,
|
|
and `pull_request_review` events, validated with HMAC-SHA256
|
|
against the App secret.
|
|
- Comment posting, editing, and deletion via the REST API (Issues,
|
|
Pull Requests, and Reviews share comment endpoints).
|
|
- Reactions on comments + issues + pull requests.
|
|
- GitHub App authentication with installation-scoped tokens that
|
|
auto-refresh.
|
|
- DM-style routing — there's no native GitHub DM, so DMs map to a
|
|
user-mentions-only thread on a designated repo (configurable).
|
|
|
|
## Directory layout
|
|
|
|
```
|
|
packages/adapter-github/
|
|
├── src/
|
|
│ ├── index.ts # GitHubAdapter + createGitHubAdapter
|
|
│ ├── index.test.ts
|
|
│ ├── cards.ts # PostableMessage / Card → markdown comment body
|
|
│ ├── cards.test.ts
|
|
│ ├── markdown.ts # GitHubFormatConverter (mdast ↔ GFM)
|
|
│ ├── markdown.test.ts
|
|
│ └── types.ts # GitHub webhook event typings
|
|
├── sample-messages.md # captured GitHub webhook deliveries
|
|
├── package.json
|
|
├── tsconfig.json
|
|
├── tsup.config.ts
|
|
├── vitest.config.ts
|
|
└── README.md
|
|
```
|
|
|
|
`sample-messages.md` holds full webhook deliveries for the supported
|
|
event types — extend it when adding handler coverage so the parser
|
|
tests stay grounded in real payloads.
|
|
|
|
## Build, test, typecheck
|
|
|
|
```bash
|
|
pnpm build
|
|
pnpm dev
|
|
pnpm test
|
|
pnpm test:watch
|
|
pnpm typecheck
|
|
pnpm clean
|
|
|
|
# from repo root
|
|
pnpm --filter @chat-adapter/github build
|
|
pnpm --filter @chat-adapter/github test
|
|
```
|
|
|
|
Replay tests live in
|
|
`packages/integration-tests/src/replay-github-*.test.ts`.
|
|
|
|
## Public surface
|
|
|
|
Main exports from `src/index.ts`:
|
|
|
|
- `createGitHubAdapter(config?)` — primary factory. Auto-detects
|
|
`GITHUB_APP_ID`, `GITHUB_PRIVATE_KEY` (PEM, may be base64-encoded),
|
|
`GITHUB_WEBHOOK_SECRET`, and the optional `GITHUB_INSTALLATION_ID`
|
|
for single-installation deployments.
|
|
- `GitHubAdapter` class — implements `Adapter<GitHubThreadId,
|
|
unknown>`. Public methods: `handleWebhook`, `postMessage`,
|
|
`editMessage`, `deleteMessage`, `addReaction`, `removeReaction`,
|
|
`fetchThread`, `listThreads`, `fetchMessages`, `fetchSingleMessage`,
|
|
`fetchChannelInfo`, `postChannelMessage`, `openDM`,
|
|
`getInstallation`, `setInstallation`.
|
|
- Configuration: `GitHubAdapterConfig`, `GitHubThreadId`,
|
|
`GitHubInstallation`.
|
|
- Helpers: `cardToMarkdown`, `cardToFallbackText`,
|
|
`GitHubFormatConverter`, `decodeThreadId`, `encodeThreadId`,
|
|
`isDM`.
|
|
|
|
## Thread ID format
|
|
|
|
GitHub URLs use `{owner}/{repo}/issues/{number}` and
|
|
`{owner}/{repo}/pull/{number}`. The adapter encodes the same shape:
|
|
|
|
```
|
|
github:{owner}:{repo}:{type}:{number}
|
|
```
|
|
|
|
`type` is `issue`, `pr`, or `review` (review threads on a PR).
|
|
`encodeThreadId` / `decodeThreadId` are the only sanctioned
|
|
constructors.
|
|
|
|
`isDM(threadId)` returns `true` when the underlying issue is in the
|
|
configured DM repo (per `dmRepo` option).
|
|
|
|
## Webhook flow
|
|
|
|
`GitHubAdapter.handleWebhook(request, options)` is the entry point.
|
|
|
|
1. **Signature verification** — every delivery includes
|
|
`X-Hub-Signature-256` (HMAC-SHA256 over the raw body). The adapter
|
|
verifies it against `webhookSecret` with a timing-safe compare.
|
|
2. **Event routing**
|
|
- `issues` (`opened`, `edited`, `closed`, `reopened`) → optional
|
|
hooks; for `opened`, also dispatches `chat.handleIncomingMessage`
|
|
when the issue body @mentions the bot.
|
|
- `issue_comment` → `chat.handleIncomingMessage`. Mentions resolve
|
|
by matching `@app-name` against the bot's app login.
|
|
- `pull_request` (`opened`, `synchronize`, etc.) → optional hooks.
|
|
- `pull_request_review_comment` → `chat.handleIncomingMessage`
|
|
scoped to the review thread.
|
|
- `pull_request_review` (`submitted`) → review-level events.
|
|
- `reaction` → `chat.handleReaction` (added / removed).
|
|
- `installation` / `installation_repositories` → installation
|
|
lifecycle hooks; the adapter calls
|
|
`setInstallation` / `deleteInstallation` automatically when
|
|
`state` is configured.
|
|
3. **Token resolution** — every outbound API call goes through
|
|
`withInstallationToken(installationId, …)`, which reads the
|
|
installation token from cache or mints a fresh one via the App
|
|
private key.
|
|
|
|
## Authentication (GitHub App)
|
|
|
|
Three pieces are required: the App ID, the App private key, and a
|
|
webhook secret. For multi-installation apps, the adapter:
|
|
|
|
- Reads the installation ID from the webhook payload
|
|
(`installation.id`).
|
|
- Mints an installation access token via `POST
|
|
/app/installations/{id}/access_tokens`, signing the JWT with the
|
|
App private key (RS256).
|
|
- Caches the token in the configured state adapter under
|
|
`{installationKeyPrefix}:{installationId}` until ~5 min before
|
|
expiry.
|
|
|
|
For single-installation deployments, set `GITHUB_INSTALLATION_ID`
|
|
explicitly and skip the lookup. Out-of-webhook code uses
|
|
`withInstallationToken(installationId, async () => { … })`.
|
|
|
|
## Format conversion
|
|
|
|
`GitHubFormatConverter` (in `markdown.ts`) maps:
|
|
|
|
- mdast → GitHub Flavored Markdown — straightforward; GFM is a
|
|
superset of mdast. Tables, fenced code with language hints,
|
|
task lists, footnotes, and strikethrough all round-trip cleanly.
|
|
- GFM → mdast — same. The tricky bits are GFM tables, autolinks
|
|
(`https://…` becomes a link automatically), and
|
|
`@mentions` / `#issues` / `commit-sha` references which the
|
|
converter preserves as text by default.
|
|
- Mentions of `@app-name[bot]` are detected via a leading-token check
|
|
against the bot login.
|
|
|
|
`renderPostable` returns the body of an issue/PR/comment. Cards are
|
|
rendered as a markdown body — GitHub has no card primitive.
|
|
|
|
## Cards (markdown comments)
|
|
|
|
`cardToMarkdown` walks a Chat SDK `Card` JSX tree and emits a markdown
|
|
comment body:
|
|
|
|
- Header / Section → markdown headings + body paragraphs.
|
|
- Field → bullet list with `**label:** value` entries.
|
|
- Image → markdown image (``); for binary uploads, the
|
|
adapter posts to the user-content CDN first via the
|
|
`attachments.upload` flow.
|
|
- LinkButton / Button → markdown links. GitHub doesn't support
|
|
interactive buttons inside comments, so callbacks fall back to a
|
|
link to a deep-link URL the bot can host.
|
|
- Divider → `---`.
|
|
- Table → GFM table.
|
|
|
|
`cardToFallbackText` is identical to `cardToMarkdown` for this adapter
|
|
because GitHub renders markdown directly.
|
|
|
|
## File uploads
|
|
|
|
GitHub comments can include image attachments that resolve to user-
|
|
content URLs. `postMessage` handles binary uploads transparently:
|
|
|
|
- Binary files get pre-uploaded to the user-content CDN via the
|
|
authenticated `attachments.upload` endpoint.
|
|
- The returned URL is inlined into the markdown body as
|
|
``.
|
|
- Larger-than-25 MB files are rejected with `ValidationError`.
|
|
|
|
## GitHub quirks worth remembering
|
|
|
|
- **Bot accounts append `[bot]`** to their login in mention syntax —
|
|
always strip / re-add it consistently or mention detection breaks.
|
|
- **Reaction set is fixed** (`+1`, `-1`, `laugh`, `hooray`, `confused`,
|
|
`heart`, `rocket`, `eyes`). Custom reactions throw 422.
|
|
- **Review comments vs issue comments** are different endpoints. The
|
|
adapter chooses based on the `type` in the encoded thread id; never
|
|
mix them.
|
|
- **Edit history.** GitHub keeps an edit log on every comment;
|
|
rapid-fire edits can hit the abuse-detection limit (~30 per minute
|
|
per author). Streaming aborts gracefully when this triggers.
|
|
- **Webhook redeliveries** — GitHub retries on non-2xx and may deliver
|
|
the same event twice. Handlers must be idempotent.
|
|
- **`X-GitHub-Delivery`** is the dedupe key. Persist it for replay
|
|
protection if the bot performs side effects.
|
|
|
|
## Testing approach
|
|
|
|
- **Unit tests** colocated with each module (`*.test.ts`). The card →
|
|
markdown round-trip suite is the most comprehensive because GitHub
|
|
is forgiving and easy to fool with malformed markdown.
|
|
- **Replay tests** in
|
|
`packages/integration-tests/src/replay-github-*.test.ts` consume
|
|
full webhook deliveries.
|
|
- **JWT signing** uses the same private key in tests and production;
|
|
test fixtures use a throwaway PEM checked into
|
|
`sample-messages.md`.
|
|
|
|
When you add support for a new event type, capture a fresh delivery
|
|
in `sample-messages.md`.
|
|
|
|
## Coding conventions
|
|
|
|
- Use named exports throughout. No default exports.
|
|
- Webhook event types live in `types.ts` — extend rather than
|
|
importing from `@octokit/webhooks-types` to keep the bundle small.
|
|
- Errors map to `@chat-adapter/shared` (`AuthenticationError`,
|
|
`AdapterRateLimitError`, `NetworkError`, `ValidationError`).
|
|
- Top-level regex literals (`MENTION_REGEX`, `ISSUE_NUMBER_REGEX`,
|
|
etc.).
|
|
- The App private key is sensitive — never log it, never round-trip
|
|
it through structured logging or error messages.
|
|
|
|
## Releases
|
|
|
|
Behavioural changes need a changeset (`pnpm changeset`, choose
|
|
`@chat-adapter/github` plus `chat` if a public type changed). Sample
|
|
fixtures and AGENTS.md edits don't.
|
|
|
|
## Where to look next
|
|
|
|
- User-facing docs: [`apps/docs/content/adapters/official/github.mdx`](../../apps/docs/content/adapters/official/github.mdx)
|
|
- README: [`packages/adapter-github/README.md`](README.md)
|
|
- Shared error/utility helpers: [`packages/adapter-shared/src/index.ts`](../adapter-shared/src/index.ts)
|
|
- Core Adapter contract: [`packages/chat/src/adapter.ts`](../chat/src/adapter.ts)
|
|
- Sample webhook payloads: [`packages/adapter-github/sample-messages.md`](sample-messages.md)
|