@relaymessenger/chat-sdk-adapter 0.2.1 → 0.3.0-staging.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/LICENSE +3 -3
  2. package/README.md +218 -98
  3. package/dist/adapter.d.ts +156 -0
  4. package/dist/adapter.d.ts.map +1 -0
  5. package/dist/adapter.js +723 -0
  6. package/dist/adapter.js.map +1 -0
  7. package/dist/client.d.ts +84 -0
  8. package/dist/client.d.ts.map +1 -0
  9. package/dist/client.js +266 -0
  10. package/dist/client.js.map +1 -0
  11. package/dist/content.d.ts +11 -0
  12. package/dist/content.d.ts.map +1 -0
  13. package/dist/content.js +172 -0
  14. package/dist/content.js.map +1 -0
  15. package/dist/credentials.d.ts +13 -0
  16. package/dist/credentials.d.ts.map +1 -0
  17. package/dist/credentials.js +28 -0
  18. package/dist/credentials.js.map +1 -0
  19. package/dist/index.d.ts +17 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +10 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/reactions.d.ts +15 -0
  24. package/dist/reactions.d.ts.map +1 -0
  25. package/dist/reactions.js +58 -0
  26. package/dist/reactions.js.map +1 -0
  27. package/dist/signature.d.ts +22 -0
  28. package/dist/signature.d.ts.map +1 -0
  29. package/dist/signature.js +98 -0
  30. package/dist/signature.js.map +1 -0
  31. package/dist/thread-id.d.ts +8 -0
  32. package/dist/thread-id.d.ts.map +1 -0
  33. package/dist/thread-id.js +27 -0
  34. package/dist/thread-id.js.map +1 -0
  35. package/dist/turn.d.ts +16 -0
  36. package/dist/turn.d.ts.map +1 -0
  37. package/dist/turn.js +22 -0
  38. package/dist/turn.js.map +1 -0
  39. package/dist/types.d.ts +193 -0
  40. package/dist/types.d.ts.map +1 -0
  41. package/dist/types.js +27 -0
  42. package/dist/types.js.map +1 -0
  43. package/dist/webhook.d.ts +9 -0
  44. package/dist/webhook.d.ts.map +1 -0
  45. package/dist/webhook.js +115 -0
  46. package/dist/webhook.js.map +1 -0
  47. package/package.json +52 -32
  48. package/dist/src/adapter.d.ts +0 -125
  49. package/dist/src/adapter.js +0 -724
  50. package/dist/src/chunk.d.ts +0 -25
  51. package/dist/src/chunk.js +0 -108
  52. package/dist/src/client.d.ts +0 -112
  53. package/dist/src/client.js +0 -180
  54. package/dist/src/format.d.ts +0 -43
  55. package/dist/src/format.js +0 -266
  56. package/dist/src/idempotency.d.ts +0 -50
  57. package/dist/src/idempotency.js +0 -75
  58. package/dist/src/index.d.ts +0 -14
  59. package/dist/src/index.js +0 -8
  60. package/dist/src/reactions.d.ts +0 -12
  61. package/dist/src/reactions.js +0 -98
  62. package/dist/src/signature.d.ts +0 -46
  63. package/dist/src/signature.js +0 -95
  64. package/dist/src/threadId.d.ts +0 -12
  65. package/dist/src/threadId.js +0 -28
  66. package/dist/src/turn.d.ts +0 -38
  67. package/dist/src/turn.js +0 -15
  68. package/dist/src/types.d.ts +0 -177
  69. package/dist/src/types.js +0 -7
package/LICENSE CHANGED
@@ -5,9 +5,9 @@ Copyright (c) 2026 Companion Inc.
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
7
7
  in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9
+ of the Software, and to permit persons to whom the Software is furnished to do
10
+ so, subject to the following conditions:
11
11
 
12
12
  The above copyright notice and this permission notice shall be included in all
13
13
  copies or substantial portions of the Software.
package/README.md CHANGED
@@ -1,10 +1,23 @@
1
1
  # @relaymessenger/chat-sdk-adapter
2
2
 
3
- Relay adapter for the [Vercel Chat SDK](https://chat-sdk.dev). Relay is a
4
- consumer messenger where people talk to agents the way they talk to contacts;
5
- this package makes a Relay conversation a Chat SDK thread, so anything built on
6
- the Chat SDK can reach Relay users. Raw HTTPS remains the canonical contract;
7
- this is a thin, dependency-free binding of it.
3
+ Vendor-official Relay adapter for
4
+ [Vercel Chat SDK](https://chat-sdk.dev), targeting `chat@4.39.0`.
5
+
6
+ Source is maintained in
7
+ [`RelayMessenger/Relay-SDK`](https://github.com/RelayMessenger/Relay-SDK/tree/main/packages/chat-sdk-adapter)
8
+ under `packages/chat-sdk-adapter`.
9
+
10
+ Relay Chats map one-to-one to Chat SDK threads. Provider thread IDs are stable
11
+ `relay:<chat UUID>` values; provider message IDs are bare Relay Message UUIDs.
12
+
13
+ ## Install
14
+
15
+ ```sh
16
+ npm install chat@4.39.0 @chat-adapter/state-memory@4.39.0 \
17
+ @relaymessenger/chat-sdk-adapter
18
+ ```
19
+
20
+ ## Minimal use
8
21
 
9
22
  ```ts
10
23
  import { createMemoryState } from "@chat-adapter/state-memory";
@@ -12,11 +25,11 @@ import { createRelayAdapter } from "@relaymessenger/chat-sdk-adapter";
12
25
  import { Chat } from "chat";
13
26
 
14
27
  const chat = new Chat({
15
- userName: "My Agent",
28
+ userName: "My Relay Agent",
16
29
  adapters: {
17
30
  relay: createRelayAdapter({
18
- token: process.env.RELAY_AGENT_TOKEN!,
19
- webhookSecret: process.env.RELAY_WEBHOOK_SECRET!,
31
+ token: process.env.RELAY_AGENT_TOKEN,
32
+ webhookSecret: process.env.RELAY_WEBHOOK_SECRET,
20
33
  }),
21
34
  },
22
35
  state: createMemoryState(),
@@ -24,98 +37,205 @@ const chat = new Chat({
24
37
 
25
38
  chat.onNewMention(async (thread, message) => {
26
39
  await thread.subscribe();
27
- await thread.post({ markdown: `You said: ${message.text}` });
40
+ await thread.post(`You said: ${message.text}`);
28
41
  });
29
42
 
30
- // Mount as a POST route.
43
+ // Mount this on the Relay webhook URL.
31
44
  export const POST = (request: Request) => chat.webhooks.relay(request);
32
45
  ```
33
46
 
34
- `chat` is a peer dependency: install it alongside this package.
35
-
36
- ## With eve
37
-
38
- eve's Chat SDK channel bridges any adapter to an agent. See
39
- [`examples/eve`](../../examples/eve) in this repository for the channel file and
40
- the environment it needs.
41
-
42
- ## What the adapter enforces
43
-
44
- - Standard Webhooks signature verification over the exact raw body, then
45
- `event_id` deduplication, because Relay delivers at least once. The event id
46
- is claimed before the handler runs, so two redeliveries racing each other
47
- cannot both dispatch, and released again if the handler throws, so Relay's
48
- retry of a failed turn is still handled. **This window is a bounded set in
49
- memory, in one process.** A restart, or a second instance behind the same
50
- webhook URL, has no claim to lose and will dispatch the event again. The
51
- idempotency key below is what makes that second dispatch harmless.
52
- - A deterministic `Idempotency-Key` on every `POST /v1/messages`, derived from
53
- the inbound event id and the send's position in the turn, and from nothing
54
- else. Relay hashes the request body server side and stores it beside the key,
55
- so a retry carrying the same body replays the first response and a retry
56
- carrying a different body is refused with 409 `idempotency_conflict`. Keeping
57
- the content out of the key is what leaves both of those reachable: a key that
58
- moved with the body would make every retry a new send, and a handler backed
59
- by a model rarely writes the same words twice.
60
- - Group `invocation_id` threading. Relay delivers a group message to an agent
61
- only when that agent was invoked, and the reply is scoped to that single-use
62
- invocation, so the first send of a turn carries it and a second raises
63
- `RelayInvocationSpentError` rather than going out bare and taking a 403.
64
- - Chunking rather than truncation. Relay caps a text part at 8 KB and a message
65
- at 32 parts; a longer reply becomes more parts, and then more messages. Each
66
- text part draws as its own balloon in the app, so a long reply arrives as a
67
- stack of bubbles rather than one tall one. A split consumes the whitespace it
68
- lands on, and nothing else.
69
-
70
- ## Formatting
71
-
72
- Relay does not render Markdown. A text part carries canonical plain text plus
73
- `styles` runs with UTF-16 offsets, and clients draw the runs. So `{ markdown }`
74
- and `{ ast }` are flattened to the text a person reads, with emphasis carried
75
- across as style ranges: `strong` becomes `bold`, `emphasis` becomes `italic`,
76
- `delete` becomes `strikethrough`, and inline or fenced code becomes `monospace`.
77
-
78
- Constructs Relay has no style for keep their information in the text rather than
79
- losing it. A link whose label differs from its target renders as `label (url)`,
80
- a blockquote keeps its `> ` line prefix, a list keeps its markers, and a table is
81
- drawn as a monospace ASCII grid. A plain string is sent verbatim with an empty
82
- `styles` array, which is Relay's marker for structured plain text rather than a
83
- legacy Markdown body.
84
-
85
- ## Capabilities
86
-
87
- | Chat SDK operation | Relay |
88
- | --------------------------------- | ---------------------------------------------------------------- |
89
- | `postMessage`, `editMessage`, `deleteMessage` | `POST /v1/messages`, `PATCH` and `DELETE /v1/messages/{id}` |
90
- | `addReaction`, `removeReaction` | `POST /v1/messages/{id}/reactions` |
91
- | `startTyping` | `POST /v1/conversations/{id}/typing`, ephemeral, 80-char label |
92
- | `markAsRead` | `POST /v1/conversations/{id}/read` |
93
- | `fetchMessages`, `fetchThread` | `GET /v1/conversations/{id}/messages` and `/v1/conversations/{id}` |
94
- | `getUser` | `GET /v1/users/{id}`, scoped to a shared conversation |
95
- | `stream` | Buffered, then committed as one message |
96
-
97
- Things Relay does not do, stated rather than faked:
98
-
99
- - **No streaming bubbles.** A turn commits exactly one canonical message, so
100
- `stream` buffers the whole reply and posts it once. Nothing partial reaches a
101
- recipient. With eve's Chat SDK channel, set `streaming: false`.
102
- - **No forward history cursor.** `GET /v1/conversations/{id}/messages` pages
103
- backwards with `before_sequence`, so `fetchMessages({ direction: "forward" })`
104
- throws `NotImplementedError` instead of walking the whole conversation.
105
- - **No agent-initiated DM.** `POST /v1/conversations/direct` accepts a user
106
- session, not an Agent Token, so `openDM` is not implemented. A conversation
107
- starts when a person adds the agent.
108
- - **No single-message read.** The API has no `GET /v1/messages/{id}`, so
109
- `fetchMessage` is not implemented and the Chat SDK returns `null`.
110
- - **No cards.** Relay removed interactive components. A card is delivered as its
111
- fallback text, so the words arrive but buttons do not render and a
112
- human-in-the-loop prompt cannot be answered in the app.
113
- - **No edits carrying media.** Relay requires an edit to stay text-bearing and
114
- rejects attachment parts.
115
-
116
- Attachments work in both directions. Inbound media and voice memo parts arrive as
117
- Chat SDK attachments; outbound attachments with a public HTTPS URL are sent as
118
- media parts, and outbound bytes are uploaded through `POST /v1/attachments`
119
- first.
120
-
121
- Docs: <https://docs.relayapp.im>.
47
+ An executable Node HTTP example is in [`examples/server.mjs`](examples/server.mjs).
48
+
49
+ ## Factory API
50
+
51
+ ```ts
52
+ type RelayCredential =
53
+ | string
54
+ | (() => string | Promise<string>);
55
+
56
+ interface RelayAdapterOptions {
57
+ token?: RelayCredential; // RELAY_AGENT_TOKEN fallback
58
+ webhookSecret?: RelayCredential; // RELAY_WEBHOOK_SECRET fallback
59
+ typing?: boolean; // default true
60
+ userName?: string; // default "Relay Agent"
61
+ agentId?: string; // this agent's Relay Contact UUID
62
+ baseUrl?: string; // default https://api.relayapp.im
63
+ fetch?: typeof fetch;
64
+ client?: RelayClient;
65
+ signatureToleranceSeconds?: number; // default 300
66
+ idempotencyKeyResolver?: (context: {
67
+ chatId: string;
68
+ threadId: string;
69
+ parts: readonly RelayOutgoingPart[];
70
+ replyToMessageId?: string;
71
+ }) => string | Promise<string>;
72
+ }
73
+
74
+ createRelayAdapter(options?: RelayAdapterOptions): RelayAdapter;
75
+ ```
76
+
77
+ Credential functions satisfy Vercel's vendor-official non-static credential
78
+ requirement. The token resolver is called for every Relay API request. The
79
+ webhook-secret resolver is called for every webhook delivery. Values are not
80
+ cached.
81
+
82
+ Every send caused by an inbound webhook carries
83
+ `Idempotency-Key: relay-chat-sdk:<event_id>:<send ordinal>`. A redelivery starts
84
+ at ordinal zero again, so the same event/body replays while changed recovery
85
+ content reaches Relay under the same key and receives the contract's 409
86
+ `idempotency_conflict`. This context uses `AsyncLocalStorage` only for the
87
+ active turn and is never persisted.
88
+
89
+ Posts made outside an inbound webhook have no Relay `event_id`. The adapter
90
+ therefore requires `idempotencyKeyResolver` for those non-empty posts rather
91
+ than manufacturing a random key that changes on recovery. Think integrations
92
+ should return their stable Action/delivery identity from this resolver.
93
+
94
+ ### Think runtime typing
95
+
96
+ For a runtime that owns typing timing, disable Chat SDK surface typing:
97
+
98
+ ```ts
99
+ const relay = createRelayAdapter({
100
+ token,
101
+ webhookSecret,
102
+ typing: false,
103
+ });
104
+ ```
105
+
106
+ With `typing: false`, both `startTyping()` and `endTyping()` validate the thread
107
+ ID but make no Relay request. This prevents Think's pre-inference
108
+ `ChatThread.startTyping()` from surfacing. Other adapter instances default to
109
+ normal `POST`/`DELETE /v1/chats/{chatId}/typing` support.
110
+
111
+ ## Locked contract
112
+
113
+ This package was rewritten against:
114
+
115
+ - Relay Server `9b4d5bb32cc749c6fd271969948c385300d404d6`
116
+ - OpenAPI SHA-256
117
+ `f62f431fc0daa48500926bf87753f81c3fdda25ab463b130ca97f2896367e0a5`
118
+ - Relay API `v1`
119
+ - Relay webhook payload version `2026-08-30`
120
+ - `chat@4.39.0`
121
+
122
+ The exact OpenAPI snapshot is retained under `contracts/` for reproducible
123
+ contract tests and is excluded from the npm package.
124
+
125
+ ## Supported surface
126
+
127
+ | Chat SDK operation | Locked Relay v1 operation |
128
+ | --- | --- |
129
+ | `postMessage`, `postChannelMessage` | `POST /v1/chats/{chatId}/messages` |
130
+ | `reply` | Same route with `message.reply_to` |
131
+ | `stream` | Buffered, then one canonical Message; never partial bubbles |
132
+ | outbound public-URL media | Message `media` part |
133
+ | outbound bytes/files | Explicitly allocate/upload with `@relaymessenger/sdk`, then post a stable public HTTPS URL |
134
+ | inbound media | Chat SDK `Attachment` with `fetchData()` |
135
+ | `addReaction`, `removeReaction` | `POST /v1/messages/{messageId}/reactions` |
136
+ | `startTyping`, `endTyping` | `POST`/`DELETE /v1/chats/{chatId}/typing` |
137
+ | `markAsRead` | `POST /v1/chats/{chatId}/read` |
138
+ | `fetchMessages({ direction: "forward" })` | `GET /v1/chats/{chatId}/messages` |
139
+ | `fetchMessage` | `GET /v1/messages/{messageId}` |
140
+ | `fetchThread`, `fetchChannelInfo` | `GET /v1/chats/{chatId}` |
141
+
142
+ ### Inbound attachments
143
+
144
+ An inbound Relay media part becomes a Chat SDK `Attachment` carrying
145
+ `url`, `mimeType`, `name`, `size`, `type`, `width`, `height`, and a
146
+ `fetchData()` that resolves to the bytes as an `ArrayBuffer`. Nothing is
147
+ downloaded until you call it.
148
+
149
+ ```ts
150
+ for (const attachment of message.attachments) {
151
+ const bytes = await attachment.fetchData?.();
152
+ }
153
+ ```
154
+
155
+ **A Relay download URL expires 60 minutes after Relay minted it.** The URL is a
156
+ sealed, unauthenticated download capability, so `fetchData()` sends no Agent
157
+ Token; after that window the download fails with `RelayApiError` HTTP 404.
158
+
159
+ That expiry is not a limit on queued work. `Message.toJSON()` drops
160
+ `fetchData`, so queue and debounce strategies call `rehydrateAttachment()` to
161
+ rebuild it — and the rebuilt closure calls `GET /v1/attachments/{attachmentId}`
162
+ first, which mints a new 60-minute download link on every request. A Message may
163
+ sit in a queue for as long as you like and still read its bytes. The serialized
164
+ URL is kept in `fetchMetadata` only as the fallback for an attachment whose
165
+ metadata predates this behavior.
166
+
167
+ `GET /v1/attachments/{attachmentId}` authorizes any Chat participant who could
168
+ read the Message, so an agent reads the attachments of messages sent to it
169
+ without owning them.
170
+
171
+ Relay text parts are plain text and are limited to 10,000 UTF-16 code units.
172
+ Long Chat SDK text is split without breaking surrogate pairs. A Relay Message
173
+ is limited to 100 parts. A public HTTPS attachment URL is sent directly.
174
+ Automatic byte/file uploads are rejected because a fresh Attachment allocation
175
+ would change the Message body after an ambiguous send. Allocate and upload with
176
+ `@relaymessenger/sdk`, retain that prepared identity durably, then give this
177
+ adapter the stable HTTPS URL.
178
+
179
+ Think's `thread.post(callback.stream())` path is safe when the stream is empty:
180
+ the adapter returns a local no-op result so Chat SDK does not enter its
181
+ post-then-edit fallback. Non-empty streams are fully buffered and committed in
182
+ one request. Empty string posts are the same no-op. No partial or placeholder
183
+ Message reaches Relay.
184
+
185
+ ## Explicitly unsupported
186
+
187
+ The adapter throws Chat SDK `NotImplementedError` rather than calling an
188
+ undocumented route for:
189
+
190
+ - message editing and deletion;
191
+ - editable drafts or partial streaming bubbles;
192
+ - open-only direct messages;
193
+ - public Contact lookup;
194
+ - backward history pagination.
195
+
196
+ Relay's locked chat-history cursor advances oldest-to-newest. It cannot satisfy
197
+ Chat SDK's backward-cursor semantics, so callers must request
198
+ `direction: "forward"` explicitly.
199
+
200
+ Cards have no Relay interaction surface. Their fallback text is sent; a card
201
+ without text is rejected.
202
+
203
+ ## Webhooks
204
+
205
+ The adapter internally verifies current signed Standard Webhooks over the exact
206
+ raw body:
207
+
208
+ ```text
209
+ HMAC-SHA256(secret, "${webhook-id}.${webhook-timestamp}.${rawBody}")
210
+ ```
211
+
212
+ Every valid current event type is acknowledged. `message.received` is
213
+ dispatched to Chat SDK message handlers, and non-self `reaction.added` /
214
+ `reaction.removed` events are dispatched to reaction handlers. Receipt,
215
+ participant, Chat metadata, typing, and Contact events have no matching Chat
216
+ SDK inbound hook and are acknowledged without fabricated behavior.
217
+
218
+ Direct Chats route through Chat SDK's direct-message path and do not need an
219
+ `isMention` flag. In a group, `isMention` is true only when a canonical text
220
+ part's `mention` equals the receiving Chat `owner_handle`; when `agentId` is
221
+ configured, the owner UUID must match it.
222
+
223
+ This package adds no persistence and no adapter-owned delivery-idempotency
224
+ store. Think Actions remain the delivery-idempotency owner outside webhook
225
+ turns. `RelayClient.sendMessage()` requires an explicit idempotency key.
226
+
227
+ ## Development
228
+
229
+ ```sh
230
+ npm ci
231
+ npm run check
232
+ npm run build
233
+ npm run test:unit
234
+ npm run test:workerd
235
+ npm run test:installed
236
+ ```
237
+
238
+ `@chat-adapter/shared@4.39.0` provides shared adapter utilities and errors. That
239
+ published package has no `/tests` export; Vercel's published contract runner is
240
+ `@chat-adapter/tests@4.39.0`, which this package uses alongside
241
+ `@chat-adapter/shared`.
@@ -0,0 +1,156 @@
1
+ import { Message, type Adapter, type AdapterPostableMessage, type Attachment, type ChatInstance, type EmojiValue, type FetchOptions, type FetchResult, type FormattedContent, type RawMessage, type StreamChunk, type ThreadInfo, type UserInfo, type WebhookOptions } from "chat";
2
+ import { RelayClient } from "./client.js";
3
+ import type { RelayClientOptions } from "./client.js";
4
+ import { type RelayCredential } from "./credentials.js";
5
+ import type { RelayRawMessage, RelayThreadId } from "./types.js";
6
+ export declare const RELAY_ADAPTER_NAME = "relay";
7
+ export interface RelayIdempotencyKeyContext {
8
+ chatId: string;
9
+ parts: ReadonlyArray<import("./types.js").RelayOutgoingPart>;
10
+ replyToMessageId?: string;
11
+ threadId: string;
12
+ }
13
+ export type RelayIdempotencyKeyResolver = (context: RelayIdempotencyKeyContext) => string | Promise<string>;
14
+ export interface RelayAdapterOptions extends Omit<RelayClientOptions, "token"> {
15
+ /**
16
+ * Relay Agent Token or per-call resolver.
17
+ * Defaults to `RELAY_AGENT_TOKEN`.
18
+ */
19
+ token?: RelayCredential;
20
+ /**
21
+ * Standard Webhooks signing secret or per-delivery resolver.
22
+ * Defaults to `RELAY_WEBHOOK_SECRET`.
23
+ */
24
+ webhookSecret?: RelayCredential;
25
+ /**
26
+ * Disable Chat SDK surface typing for runtimes that own typing UX.
27
+ * Defaults to true.
28
+ */
29
+ typing?: boolean;
30
+ /** Display name shown to Chat SDK handlers. */
31
+ userName?: string;
32
+ /** This Relay agent Contact UUID, when known. */
33
+ agentId?: string;
34
+ /** Standard Webhooks timestamp tolerance in seconds. */
35
+ signatureToleranceSeconds?: number;
36
+ /**
37
+ * Stable key source for posts made outside an inbound webhook turn.
38
+ * Inbound turns always derive keys from event_id plus send ordinal.
39
+ */
40
+ idempotencyKeyResolver?: RelayIdempotencyKeyResolver;
41
+ /** Controlled client injection for tests and custom transports. */
42
+ client?: RelayClient;
43
+ }
44
+ /**
45
+ * Relay adapter for `chat@4.39.0`, limited to the locked public v1 contract.
46
+ */
47
+ export declare class RelayAdapter implements Adapter<RelayThreadId, RelayRawMessage> {
48
+ readonly name = "relay";
49
+ readonly userName: string;
50
+ readonly botUserId?: string;
51
+ readonly lockScope: "thread";
52
+ readonly persistThreadHistory = false;
53
+ readonly typing: boolean;
54
+ readonly client: RelayClient;
55
+ private readonly signatureToleranceSeconds?;
56
+ private readonly webhookSecret;
57
+ private readonly idempotencyKeyResolver;
58
+ private readonly turns;
59
+ /**
60
+ * Request-scoped Chat kind hints. Entries exist only while an inbound
61
+ * dispatch is active; this is not persistence or a delivery-idempotency
62
+ * store.
63
+ */
64
+ private readonly activeChatKinds;
65
+ private chat?;
66
+ constructor(options?: RelayAdapterOptions);
67
+ initialize(chat: ChatInstance): Promise<void>;
68
+ encodeThreadId(data: RelayThreadId): string;
69
+ decodeThreadId(threadId: string): RelayThreadId;
70
+ channelIdFromThreadId(threadId: string): string;
71
+ renderFormatted(content: FormattedContent): string;
72
+ /**
73
+ * Build the Chat SDK `fetchData` closure for one Relay media part.
74
+ *
75
+ * The download runs through this adapter's own Relay client, so a consumer
76
+ * supplied `fetch` (Workers, a proxy, a test double) serves attachment bytes
77
+ * exactly as it serves every other Relay call.
78
+ */
79
+ private attachmentData;
80
+ /**
81
+ * Mint a replacement download URL for an attachment id.
82
+ *
83
+ * Returns `undefined` when the call cannot produce one and the caller still
84
+ * holds a fallback URL; rethrows when there is nothing to fall back to, so a
85
+ * genuine authorization or not-found failure is never swallowed.
86
+ */
87
+ private freshDownloadUrl;
88
+ /**
89
+ * Build the `fetchData` closure for an attachment that survived a queue.
90
+ *
91
+ * The stored URL may already have expired, so the id is tried first: Relay
92
+ * mints a new download link on every `GET /v1/attachments/{attachmentId}`.
93
+ * The serialized URL is the fallback for the one case the id cannot cover —
94
+ * an attachment whose `fetchMetadata` predates this adapter version.
95
+ */
96
+ private rehydratedAttachmentData;
97
+ /**
98
+ * Rebuild `fetchData` on an attachment that survived serialization.
99
+ *
100
+ * `Message.toJSON()` drops `data` and `fetchData`, so Chat SDK calls this
101
+ * when a queue or debounce strategy rehydrates a Message. A Relay download
102
+ * URL expires 60 minutes after Relay minted it, and a queue can hold a
103
+ * Message for longer, so this re-mints from `fetchMetadata.attachmentId` and
104
+ * falls back to the stored URL only when the id call cannot serve one.
105
+ */
106
+ rehydrateAttachment(attachment: Attachment): Attachment;
107
+ parseMessage(raw: RelayRawMessage): Message<RelayRawMessage>;
108
+ private unsupported;
109
+ private send;
110
+ private sendInTurn;
111
+ private resolveExternalIdempotencyKey;
112
+ private noopResult;
113
+ private isMentioned;
114
+ private enterChatKind;
115
+ /**
116
+ * Chat SDK asks synchronously while dispatching. Relay's stable thread ID
117
+ * intentionally contains only the Chat UUID, so the direct/group hint is
118
+ * scoped to the active webhook dispatch and never persisted.
119
+ */
120
+ isDM(threadId: string): boolean;
121
+ postMessage(threadId: string, message: AdapterPostableMessage): Promise<RawMessage<RelayRawMessage>>;
122
+ postChannelMessage(channelId: string, message: AdapterPostableMessage): Promise<RawMessage<RelayRawMessage>>;
123
+ reply(threadId: string, messageId: string, message: AdapterPostableMessage): Promise<RawMessage<RelayRawMessage>>;
124
+ editMessage(threadId: string, messageId: string, _message: AdapterPostableMessage): Promise<RawMessage<RelayRawMessage>>;
125
+ deleteMessage(threadId: string, messageId: string): Promise<void>;
126
+ addReaction(threadId: string, messageId: string, emoji: EmojiValue | string): Promise<void>;
127
+ removeReaction(threadId: string, messageId: string, emoji: EmojiValue | string): Promise<void>;
128
+ startTyping(threadId: string): Promise<void>;
129
+ endTyping(threadId: string): Promise<void>;
130
+ markAsRead(threadId: string, messageId: string): Promise<void>;
131
+ /**
132
+ * Relay's public chat cursor advances oldest-to-newest. Chat SDK's backward
133
+ * cursor contract cannot be represented, so only explicit forward reads are
134
+ * supported rather than faking backward pagination.
135
+ */
136
+ fetchMessages(threadId: string, options?: FetchOptions): Promise<FetchResult<RelayRawMessage>>;
137
+ fetchChannelMessages(channelId: string, options?: FetchOptions): Promise<FetchResult<RelayRawMessage>>;
138
+ fetchMessage(threadId: string, messageId: string): Promise<Message<RelayRawMessage> | null>;
139
+ fetchThread(threadId: string): Promise<ThreadInfo>;
140
+ fetchChannelInfo(channelId: string): Promise<{
141
+ channelVisibility: import("chat").ChannelVisibility | undefined;
142
+ id: string;
143
+ isDM: boolean | undefined;
144
+ metadata: Record<string, unknown>;
145
+ name?: string | undefined;
146
+ }>;
147
+ getChannelVisibility(): "private";
148
+ getUser(_userId: string): Promise<UserInfo | null>;
149
+ openDM(_userId: string): Promise<string>;
150
+ stream(threadId: string, stream: AsyncIterable<string | StreamChunk>): Promise<RawMessage<RelayRawMessage> | null>;
151
+ handleWebhook(request: Request, options?: WebhookOptions): Promise<Response>;
152
+ private initializedChat;
153
+ private dispatch;
154
+ }
155
+ export declare function createRelayAdapter(options?: RelayAdapterOptions): RelayAdapter;
156
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}