@xylex-group/chat-adapter-athena
v0.1.2
Published
Athena platform adapter for the Vercel Chat SDK
Maintainers
Readme
Athena Chat SDK Adapter
@xylex-group/chat-adapter-athena maps Athena rooms and messages to the
Vercel Chat SDK Adapter contract.
Status
This is an internal preview for server runtimes only (Node webhooks or long-running processes). Do not import this package in browser bundles — construction throws in document environments.
Core message operations, reactions, room mapping, direct messages,
sequence-backed history, signed HTTP webhooks, minimal rich-body v1 mentions,
and opt-in server-side realtime delivery are implemented through an injected
AthenaAdapterClient or createAthenaAdapterFromUrl. The URL factory delegates
credential and request-header construction to Athena JS and accepts session,
bearer, or cookie Chat credentials; it does not send an API key to Chat.
Package metadata is publish-ready (publishConfig, MIT LICENSE, pack gates).
Publish remains a deliberate release step; prefer the monorepo path until the
first npm release is cut.
Install
npm install @xylex-group/chat-adapter-athena chat @xylex-group/athenaPeer dependency: @xylex-group/athena >=4.1.0 <6. Runtime dependency:
chat (Vercel Chat SDK).
Usage
import { Chat } from "chat";
import {
createAthenaAdapter,
createAthenaAdapterFromUrl,
type AthenaAdapterClient,
} from
"@xylex-group/chat-adapter-athena";
declare const client: AthenaAdapterClient;
const bot = new Chat({
userName: "athena-bot",
adapters: {
athena: createAthenaAdapter({
botUserId: "athena-bot-user-id",
client,
}),
},
});For Athena JS-managed session credentials, construct the adapter directly:
const adapter = createAthenaAdapterFromUrl({
botUserId: "athena-bot-user-id",
chat: { sessionToken: process.env.ATHENA_SESSION_TOKEN! },
url: "https://athena.example.com",
});Do not recreate Athena transport or authentication.
Realtime lifecycle (server-only)
Realtime delivery is deliberately opt-in. Start it only in a long-running server
process after the adapter has been initialized by Chat SDK. It accepts
chat.message.created and chat.message.updated, maps each to a Chat SDK
message, and calls ChatInstance.processMessage.
const cursorStore = /* durable implementation */;
const dedupeStore = /* durable implementation */;
const adapter = createAthenaAdapter({
botUserId: "athena-bot-user-id",
client,
lifecycle: {
cursorStore,
dedupeStore,
reconnect: { maxAttempts: 5 },
onError: (error) => logger.error(error),
},
});
const bot = new Chat({ userName: "athena-bot", adapters: { athena: adapter } });
await adapter.subscribe("room-123");
await adapter.start();
// On shutdown:
await adapter.stop();Signed HTTP webhooks (serverless)
When Athena core has chat_http_delivery enabled (see
docs/chat-signed-http-delivery.md), point the destination at your Chat SDK
route and configure the adapter with the shared signing secret:
const adapter = createAthenaAdapter({
botUserId: "athena-bot-user-id",
client,
webhook: {
signingSecret: process.env.ATHENA_CHAT_HTTP_SIGNING_SECRET!,
signingKeyId: process.env.ATHENA_CHAT_HTTP_SIGNING_KEY_ID,
// Prefer a durable dedupe store in multi-instance deployments:
// dedupeStore: durableDedupe,
},
});
const bot = new Chat({ userName: "athena-bot", adapters: { athena: adapter } });
// Chat SDK will call adapter.handleWebhook(request) after initialize.createAthenaAdapterFromUrl forwards webhook to the adapter as well, so the
URL factory supports signed HTTP delivery without hand-building a client:
const adapter = createAthenaAdapterFromUrl({
botUserId: "athena-bot-user-id",
chat: { sessionToken: process.env.ATHENA_SESSION_TOKEN! },
url: "https://athena.example.com",
webhook: {
signingSecret: process.env.ATHENA_CHAT_HTTP_SIGNING_SECRET!,
},
});Verification uses HMAC-SHA256 over the raw body bytes
(v1.<timestamp>.<event_id>. + body), a timestamp window (default 300s), and
constant-time compare. Duplicate event_id values are acked without reprocessing.
Cards, modals, and slash commands remain unsupported (deferred).
The cursor store persists subscriptions and must atomically retain the maximum
processed room_seq; the dedupe store keys stable message revisions. On a new
connection, stored subscriptions are resumed from their saved cursors. Cursor
advancement happens only after processMessage succeeds. subscribe(roomId,
fromSeq) and unsubscribe(roomId) change the persisted lifecycle set.
The adapter defaults to in-memory cursor and dedupe stores only for single-process previews. Provide durable injected stores in every production or multi-instance deployment. The adapter uses Athena JS's realtime connection; it does not create a WebSocket, duplicate authentication, or implement a transport.
Capabilities
| Feature | Status | Notes |
| --- | --- | --- |
| Post, edit, delete | Supported | Uses Athena durable messages. |
| History | Supported | Maps before_seq / after_seq pagination. |
| Reactions | Supported | Uses the existing message reaction endpoints. |
| Attachments | Supported | Exposes only authorized storage paths. |
| Rich content | Minimal v1 | Markdown text + body_json { v: 1, format, mentions? } metadata. |
| Direct messages | Supported | Uses Athena's deterministic direct-room resolver. |
| Webhooks | Supported | Signed HTTP outbox verify + processMessage (requires webhook.signingSecret). |
| WebSocket delivery | Supported, server-only | Opt-in lifecycle handles created/updated events with durable cursor and dedupe ports. |
| Mentions | Supported (metadata) | Mapped from rich body v1 mentions[]. |
| Cards, modals, commands | Unsupported | Deferred (CAT-006). |
Security
Keep the adapter server-only. Never expose the injected Athena client, session credentials, storage keys, or private attachment URLs to browser code.
Compatibility
| Adapter | Chat SDK | Athena JS | Athena backend |
| --- | --- | --- | --- |
| 0.1.1 | 4.36.0 | 4.1.0 | Current /chat/* contract |
The package name uses the Xylex scope until @chat-adapter scope access is
verified by a publish-authorized maintainer.
