@diegoaltoworks/chatter 0.60.0 → 0.62.0

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 (125) hide show
  1. package/README.md +56 -17
  2. package/dist/auth/secretStrength.d.ts +1 -1
  3. package/dist/auth/secretStrength.d.ts.map +1 -1
  4. package/dist/bin/create-apikey.js +45 -28
  5. package/dist/bin/wa-pair.js +113 -46
  6. package/dist/channels/backoff.d.ts +10 -0
  7. package/dist/channels/backoff.d.ts.map +1 -0
  8. package/dist/channels/gates.d.ts +15 -8
  9. package/dist/channels/gates.d.ts.map +1 -1
  10. package/dist/channels/index.d.ts +1 -1
  11. package/dist/channels/index.d.ts.map +1 -1
  12. package/dist/channels/index.js +38 -5
  13. package/dist/channels/index.mjs +37 -5
  14. package/dist/channels/matrix/api.d.ts +152 -0
  15. package/dist/channels/matrix/api.d.ts.map +1 -0
  16. package/dist/channels/matrix/channel.d.ts +46 -0
  17. package/dist/channels/matrix/channel.d.ts.map +1 -0
  18. package/dist/channels/matrix/handler.d.ts +58 -0
  19. package/dist/channels/matrix/handler.d.ts.map +1 -0
  20. package/dist/channels/matrix/index.d.ts +37 -0
  21. package/dist/channels/matrix/index.d.ts.map +1 -0
  22. package/dist/channels/matrix/index.js +1128 -0
  23. package/dist/channels/matrix/index.mjs +1079 -0
  24. package/dist/channels/matrix/sync.d.ts +53 -0
  25. package/dist/channels/matrix/sync.d.ts.map +1 -0
  26. package/dist/channels/matrix/updates.d.ts +104 -0
  27. package/dist/channels/matrix/updates.d.ts.map +1 -0
  28. package/dist/channels/pipeline.d.ts +14 -14
  29. package/dist/channels/pipeline.d.ts.map +1 -1
  30. package/dist/channels/polling.d.ts +67 -0
  31. package/dist/channels/polling.d.ts.map +1 -0
  32. package/dist/channels/telegram/channel.d.ts +2 -56
  33. package/dist/channels/telegram/channel.d.ts.map +1 -1
  34. package/dist/channels/telegram/index.js +53 -23
  35. package/dist/channels/telegram/index.mjs +53 -23
  36. package/dist/channels/telegram/poll.d.ts +5 -1
  37. package/dist/channels/telegram/poll.d.ts.map +1 -1
  38. package/dist/channels/telegram/webhook.d.ts +2 -10
  39. package/dist/channels/telegram/webhook.d.ts.map +1 -1
  40. package/dist/channels/whatsapp/authState.d.ts +40 -9
  41. package/dist/channels/whatsapp/authState.d.ts.map +1 -1
  42. package/dist/channels/whatsapp/channel.d.ts +25 -4
  43. package/dist/channels/whatsapp/channel.d.ts.map +1 -1
  44. package/dist/channels/whatsapp/inbound.d.ts +23 -25
  45. package/dist/channels/whatsapp/inbound.d.ts.map +1 -1
  46. package/dist/channels/whatsapp/index.d.ts +3 -3
  47. package/dist/channels/whatsapp/index.d.ts.map +1 -1
  48. package/dist/channels/whatsapp/index.js +123 -41
  49. package/dist/channels/whatsapp/index.mjs +120 -41
  50. package/dist/channels/whatsapp/pairing.d.ts.map +1 -1
  51. package/dist/client/src/Chat.d.ts.map +1 -1
  52. package/dist/client/src/ChatButton.d.ts.map +1 -1
  53. package/dist/client/src/browser.d.ts.map +1 -1
  54. package/dist/core/answer.d.ts +8 -0
  55. package/dist/core/answer.d.ts.map +1 -1
  56. package/dist/core/buildLock.d.ts +52 -0
  57. package/dist/core/buildLock.d.ts.map +1 -0
  58. package/dist/core/pipeline.d.ts +42 -5
  59. package/dist/core/pipeline.d.ts.map +1 -1
  60. package/dist/core/prompts.d.ts +2 -0
  61. package/dist/core/prompts.d.ts.map +1 -1
  62. package/dist/core/retrieval.d.ts +100 -25
  63. package/dist/core/retrieval.d.ts.map +1 -1
  64. package/dist/core/session.d.ts +8 -0
  65. package/dist/core/session.d.ts.map +1 -1
  66. package/dist/flows/index.d.ts +1 -1
  67. package/dist/flows/index.d.ts.map +1 -1
  68. package/dist/flows/index.js +8 -6
  69. package/dist/flows/index.mjs +7 -6
  70. package/dist/flows/intent.d.ts +1 -1
  71. package/dist/flows/intent.d.ts.map +1 -1
  72. package/dist/flows/loader.d.ts +1 -2
  73. package/dist/flows/loader.d.ts.map +1 -1
  74. package/dist/flows/params.d.ts +1 -1
  75. package/dist/flows/params.d.ts.map +1 -1
  76. package/dist/flows/session.d.ts +4 -2
  77. package/dist/flows/session.d.ts.map +1 -1
  78. package/dist/flows/types.d.ts +2 -3
  79. package/dist/flows/types.d.ts.map +1 -1
  80. package/dist/history/index.d.ts +1 -1
  81. package/dist/history/index.d.ts.map +1 -1
  82. package/dist/history/index.js +6 -3
  83. package/dist/history/index.mjs +5 -3
  84. package/dist/history/tursoStore.d.ts +6 -4
  85. package/dist/history/tursoStore.d.ts.map +1 -1
  86. package/dist/images/index.js +2 -2
  87. package/dist/images/index.mjs +2 -2
  88. package/dist/index.d.ts +7 -5
  89. package/dist/index.d.ts.map +1 -1
  90. package/dist/index.js +596 -295
  91. package/dist/index.mjs +595 -295
  92. package/dist/mcp-server/index.d.ts +5 -5
  93. package/dist/mcp-server/index.d.ts.map +1 -1
  94. package/dist/mcp-server/logger.d.ts +12 -2
  95. package/dist/mcp-server/logger.d.ts.map +1 -1
  96. package/dist/mcp-server/rate-limiter.d.ts +10 -3
  97. package/dist/mcp-server/rate-limiter.d.ts.map +1 -1
  98. package/dist/mcp-server/types.d.ts +20 -5
  99. package/dist/mcp-server/types.d.ts.map +1 -1
  100. package/dist/mcp-server.js +479 -244
  101. package/dist/mcp-server.mjs +485 -244
  102. package/dist/middleware/apiKey.d.ts +10 -0
  103. package/dist/middleware/apiKey.d.ts.map +1 -0
  104. package/dist/middleware/auth.d.ts.map +1 -1
  105. package/dist/middleware/cors.d.ts.map +1 -1
  106. package/dist/middleware/ratelimit.d.ts.map +1 -1
  107. package/dist/middleware/referrer.d.ts.map +1 -1
  108. package/dist/middleware/session.d.ts +1 -1
  109. package/dist/middleware/session.d.ts.map +1 -1
  110. package/dist/routes/demo.d.ts.map +1 -1
  111. package/dist/routes/openai.d.ts.map +1 -1
  112. package/dist/routes/private.d.ts.map +1 -1
  113. package/dist/routes/public.d.ts.map +1 -1
  114. package/dist/scheduler/index.js +1 -1
  115. package/dist/scheduler/index.mjs +1 -1
  116. package/dist/scheduler/scheduler.d.ts +9 -1
  117. package/dist/scheduler/scheduler.d.ts.map +1 -1
  118. package/dist/server.d.ts.map +1 -1
  119. package/dist/server.js +491 -268
  120. package/dist/server.mjs +497 -268
  121. package/dist/shortener.d.ts +5 -4
  122. package/dist/shortener.d.ts.map +1 -1
  123. package/dist/types.d.ts +129 -75
  124. package/dist/types.d.ts.map +1 -1
  125. package/package.json +11 -2
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  [![CI](https://github.com/diegoaltoworks/chatter/actions/workflows/ci.yml/badge.svg)](https://github.com/diegoaltoworks/chatter/actions/workflows/ci.yml)
10
10
  [![Demo](https://img.shields.io/badge/demo-chatter--demo-blue)](https://github.com/diegoaltoworks/chatter-demo)
11
11
 
12
- [Features](#features) • [Quick Start](#quick-start) • [Documentation](#documentation) • [Demo](#demo)
12
+ [Features](#features) - [Quick Start](#quick-start) - [Documentation](#documentation) - [Demo](#demo)
13
13
 
14
14
  </div>
15
15
 
@@ -18,15 +18,15 @@
18
18
  - 🤖 **RAG-Powered**: Retrieval-Augmented Generation using OpenAI embeddings and Turso vector database
19
19
  - 🎨 **Customizable Widgets**: Pre-built chat components (ChatBot, Chat, ChatButton) with full styling control
20
20
  - 📝 **Markdown Rendering**: Assistant replies render Markdown (headings, lists, code blocks, links) safely with HTML escaping
21
- - 🔌 **OpenAI-Compatible API**: `POST /v1/chat/completions` (streaming + non-streaming) so any chat UI or SDK — [Deep Chat](https://deepchat.dev), [assistant-ui](https://www.assistant-ui.com), the OpenAI SDKs — can be the front end ([guide](./docs/integrations.md))
22
- - 🎛️ **Headless Mode**: Run the server as a pure API with `features: { headless: true }` — no built-in widget or demo pages
21
+ - 🔌 **OpenAI-Compatible API**: `POST /v1/chat/completions` (streaming + non-streaming) so any chat UI or SDK - [Deep Chat](https://deepchat.dev), [assistant-ui](https://www.assistant-ui.com), the OpenAI SDKs - can be the front end ([guide](./docs/integrations.md))
22
+ - 🎛️ **Headless Mode**: Run the server as a pure API with `features: { headless: true }` - no built-in widget or demo pages
23
23
  - 🔐 **Built-in API Key Management**: JWT-based API keys with CLI tool for easy creation
24
24
  - 🔑 **Authentication Ready**: Built-in support for JWT, Clerk, and custom auth providers
25
25
  - 📱 **Mobile Optimized**: Responsive design with iOS/Android-specific fixes
26
26
  - ⚡ **High Performance**: Built on Hono with streaming support
27
27
  - 🛡️ **Security First**: Rate limiting, CORS, referrer checking, and input guardrails
28
28
  - 💸 **Usage Metering**: Per-caller and global daily caps for paid features, multi-instance safe, via `@diegoaltoworks/chatter/usage` ([guide](./docs/usage.md))
29
- - 💬 **Channels**: Built-in WhatsApp and Telegram transports plus a Channel SPI for plugging in any other one — allowlist/mute gates, reply rate-limiting, and a shared inbound pipeline every channel reuses ([WhatsApp](./docs/channels.md), [Telegram](./docs/telegram.md), [build your own](./docs/build-a-channel.md))
29
+ - 💬 **Channels**: Built-in WhatsApp, Telegram and Matrix transports plus a Channel SPI for plugging in any other one - allowlist/mute gates, reply rate-limiting, and a shared inbound pipeline every channel reuses ([WhatsApp](./docs/channels.md), [Telegram](./docs/telegram.md), [Matrix](./docs/matrix.md), [build your own](./docs/build-a-channel.md))
30
30
  - 🧩 **Flows**: Multi-turn, schema-driven slot-filling for structured conversations, with hybrid keyword + LLM intent matching ([guide](./docs/flows.md))
31
31
  - 🖼️ **Images**: On-demand generation and editing with cache-before-spend ordering and optional Cloudinary upload ([guide](./docs/images.md))
32
32
  - 🎭 **Personas**: Windowed, per-contact prompt layers and named greetings from a JSON registry ([guide](./docs/personas.md))
@@ -56,26 +56,26 @@ const app = await createServer({
56
56
  Bun.serve({ port: 8181, fetch: app.fetch });
57
57
 
58
58
  # Create API key for clients
59
- npx chatter create-apikey --name "my-app" --expires-in 365d
59
+ npx chatter --name "my-app" --expires-in 365d
60
60
  ```
61
61
 
62
62
  **Requirements:** OpenAI API key, Turso database, and Bun >= 1.2 or Node >= 24. See [Requirements Guide](./docs/requirements.md) for setup instructions.
63
63
 
64
64
  ### Runtime
65
65
 
66
- The package runs on **Bun or Node** (>= 24 — it is built and published for both,
66
+ The package runs on **Bun or Node** (>= 24 - it is built and published for both,
67
67
  and CI loads the built bundles under each). Two things differ:
68
68
 
69
- - **Serving the app** — `Bun.serve({ port: 8181, fetch: app.fetch })` on Bun;
69
+ - **Serving the app** - `Bun.serve({ port: 8181, fetch: app.fetch })` on Bun;
70
70
  `serve({ port: 8181, fetch: app.fetch })` from [`@hono/node-server`](https://github.com/honojs/node-server) on Node.
71
- - **Static assets** — serving the widget files (`/chatter.js`, `/chatter.css`)
71
+ - **Static assets** - serving the widget files (`/chatter.js`, `/chatter.css`)
72
72
  and a `publicDir` needs a runtime adapter, picked automatically. On Node that
73
73
  adapter is `@hono/node-server`, an optional peer dependency: without it,
74
74
  `/chatter.js` and `/chatter.css` 404 and the server logs an actionable
75
75
  error naming the missing package. Install it, or run with
76
76
  `features: { headless: true }` if the server should serve no files.
77
77
 
78
- Everything else — the chat pipeline, RAG, channels, the OpenAI-compatible API —
78
+ Everything else - the chat pipeline, RAG, channels, the OpenAI-compatible API -
79
79
  is runtime-neutral. Development and the quality gates run on Bun.
80
80
 
81
81
  ## Examples
@@ -90,7 +90,7 @@ is runtime-neutral. Development and the quality gates run on Bun.
90
90
 
91
91
  ## Documentation
92
92
 
93
- Complete guides for setup, deployment, and integration — see
93
+ Complete guides for setup, deployment, and integration - see
94
94
  [docs/index.md](./docs/index.md) for the full, ordered walkthrough:
95
95
 
96
96
  - **[Architecture](./docs/ARCHITECTURE.md)** - Load-bearing invariants, each linked to the test that enforces it
@@ -103,6 +103,7 @@ Complete guides for setup, deployment, and integration — see
103
103
  - **[WhatsApp Channel](./docs/channels.md)** - Link a WhatsApp number as a transport
104
104
  - **[Building a Channel](./docs/build-a-channel.md)** - Plug in a new transport
105
105
  - **[Telegram Channel](./docs/telegram.md)** - Run a bot on the official Bot API, no extra dependency
106
+ - **[Matrix Channel](./docs/matrix.md)** - Run a bot on the client-server API, no extra dependency (unencrypted rooms only)
106
107
  - **[Flows](./docs/flows.md)** - Multi-turn, schema-driven slot-filling flows
107
108
  - **[Images](./docs/images.md)** - Generate and cache images on demand
108
109
  - **[Conversation History](./docs/history.md)** - Structural, host-replaceable multi-turn context
@@ -110,8 +111,23 @@ Complete guides for setup, deployment, and integration — see
110
111
  - **[Deployment](./docs/deployment.md)** - Google Cloud Run, Fly.io, Railway, VPS
111
112
  - **[Packaging](./docs/packaging.md)** - The subpath contract and the release chain
112
113
  - **[Testing](./docs/testing.md)** - Comprehensive testing guide
113
- - **[Sprint Review](./docs/sprint-review.md)** - Cross-module & security-invariant audit
114
114
  - **[FAQs](./docs/faqs.md)** - Troubleshooting and common questions
115
+ - **Architecture Decisions** - The ADRs behind the shape of the codebase:
116
+ [0001, brain and sockets split](./docs/decisions/0001-brain-and-sockets-split.md),
117
+ [0002, no LangChain in core](./docs/decisions/0002-no-langchain-in-core.md),
118
+ [0003, slot-filling is brain territory](./docs/decisions/0003-slot-filling-is-brain-territory.md),
119
+ [0004, main-protection keeps only non-fast-forward and deletion protections](./docs/decisions/0004-main-protection-stays-non-fast-forward-only.md)
120
+ - **Patterns** - Worked "how do I add X" guides:
121
+ [adding a capability](./docs/patterns/adding-a-capability.md),
122
+ [adding a store](./docs/patterns/adding-a-store.md),
123
+ [adding a retriever](./docs/patterns/adding-a-retriever.md),
124
+ [exemplars](./docs/patterns/exemplars.md)
125
+
126
+ ### Archive
127
+
128
+ - **[Cross-Module Review](./docs/sprint-review.md)** - A dated, point-in-time
129
+ audit (v0.40) of cross-module paths and security invariants, not a
130
+ standing guarantee about the current codebase.
115
131
 
116
132
  ## Demo
117
133
 
@@ -120,7 +136,7 @@ Complete guides for setup, deployment, and integration — see
120
136
  The [chatter-demo](https://github.com/diegoaltoworks/chatter-demo) repository contains a complete, production-ready implementation showing:
121
137
 
122
138
  - Full server setup with custom configuration
123
- - 8 live demo implementations (Widget/React × Button/Inline × Public/Private)
139
+ - 8 live demo implementations (Widget/React x Button/Inline x Public/Private)
124
140
  - Clerk authentication integration
125
141
  - Deployment configuration for Google Cloud Run
126
142
  - Knowledge base and prompt customization
@@ -156,7 +172,7 @@ See [Client Setup Guide](./docs/client.md) for detailed integration examples, th
156
172
 
157
173
  ## MCP Server Integration
158
174
 
159
- Use Chatter as a Model Context Protocol (MCP) server to expose your chatbot to Claude Desktop, VS Code extensions, and other MCP-compatible tools. `@modelcontextprotocol/sdk` and `zod` are optional peer dependencies scoped to this subpath — install them to use it: `bun add @modelcontextprotocol/sdk zod`.
175
+ Use Chatter as a Model Context Protocol (MCP) server to expose your chatbot to Claude Desktop, VS Code extensions, and other MCP-compatible tools. `@modelcontextprotocol/sdk` and `zod` are optional peer dependencies scoped to this subpath - install them to use it: `bun add @modelcontextprotocol/sdk zod`.
160
176
 
161
177
  **Basic Setup:**
162
178
  ```typescript
@@ -199,7 +215,8 @@ const server = await createMCPServer({
199
215
  // ... other config ...
200
216
  toolRateLimit: 30, // Max 30 requests per minute per tool (optional)
201
217
  logging: {
202
- console: true, // JSON logs to stdout (default: true)
218
+ console: true, // JSON `mcp_chat` events via the logger, stderr (default: true)
219
+ content: false, // also log the conversation itself (default: false)
203
220
  onChat: async (event) => {
204
221
  // Custom logging - includes conversation tracking and cost data
205
222
  console.log('Chat event:', {
@@ -230,6 +247,14 @@ const server = await createMCPServer({
230
247
  });
231
248
  ```
232
249
 
250
+ The default `mcp_chat` console event carries metadata only: tool name,
251
+ conversation id, message and context counts, duration and token cost. Set
252
+ `logging.content: true` plus `logLevel: "debug"` to also get an
253
+ `mcp_chat_content` event with the user message, history, retrieved context and
254
+ answer. `onChat` always receives the full event, since the host decides where
255
+ it lands. Content is scrubbed of credentials wherever it is emitted. See
256
+ [docs/server.md](docs/server.md#logging).
257
+
233
258
  **Claude Desktop Configuration:**
234
259
 
235
260
  Add to your `claude_desktop_config.json`:
@@ -264,13 +289,13 @@ Both tools:
264
289
 
265
290
  **Features:**
266
291
  - **Conversation ID Tracking**: Pass `conversationId` parameter to maintain session continuity across tool calls
267
- - **Cost Tracking**: Every response includes token usage (prompt/completion/total). Add `openai: { pricing: { promptPer1M, completionPer1M } }` to also get an estimated USD cost — without it, `estimatedCost` is `null` rather than guessing at a price for the wrong model
292
+ - **Cost Tracking**: Every response includes token usage (prompt/completion/total). Add `openai: { pricing: { promptPer1M, completionPer1M } }` to also get an estimated USD cost - without it, `estimatedCost` is `null` rather than guessing at a price for the wrong model
268
293
  - **Rate Limiting**: Optional per-tool rate limiting (requests per minute) to control API usage
269
294
  - **Observability**: Comprehensive logging with conversation tracking and cost data
270
295
 
271
296
  ## Plugins
272
297
 
273
- ### Talker — Voice Calls & SMS
298
+ ### Talker - Voice Calls, SMS & WhatsApp
274
299
 
275
300
  Add phone call and SMS support to your Chatter bot with [Talker](https://github.com/diegoaltoworks/talker):
276
301
 
@@ -293,7 +318,21 @@ const app = await createServer({
293
318
  });
294
319
  ```
295
320
 
296
- One server, one port — web chat, phone calls, and SMS. See the [Talker README](https://github.com/diegoaltoworks/talker) for full documentation.
321
+ One server, one port - web chat, phone calls, and SMS. See the [Talker README](https://github.com/diegoaltoworks/talker) for full documentation.
322
+
323
+ ### Two ways to do WhatsApp
324
+
325
+ Chatter's own [WhatsApp channel](./docs/channels.md) and Talker's Twilio
326
+ integration both reach WhatsApp, through very different paths - pick based on
327
+ what you're building:
328
+
329
+ | | This repo's [WhatsApp channel](./docs/channels.md) | Talker's Twilio WhatsApp |
330
+ | --- | --- | --- |
331
+ | WhatsApp access | Unofficial, via [Baileys](https://github.com/WhiskeySockets/Baileys) - a linked-device client, not the WhatsApp Business API | Sanctioned path, via Twilio's WhatsApp Business API integration |
332
+ | Cost | Free besides hosting | Twilio usage-based pricing |
333
+ | Risk | Real risk of the linked number being banned - use a number you can afford to lose | Runs through WhatsApp's own approved channel |
334
+ | Setup | Link a number by QR code or pairing code, no third party | Twilio account and WhatsApp Business API approval |
335
+ | Best for | Prototyping, personal or low-stakes bots | Production, customer-facing deployments |
297
336
 
298
337
  ## License
299
338
 
@@ -4,5 +4,5 @@
4
4
  * forgery or WhatsApp account takeover — so a short or empty value fails
5
5
  * fast with an actionable error instead of quietly producing a weak key.
6
6
  */
7
- export declare function assertStrongSecret(secret: string | undefined, label: string): void;
7
+ export declare function assertStrongSecret(secret: string | undefined, label: string): asserts secret is string;
8
8
  //# sourceMappingURL=secretStrength.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"secretStrength.d.ts","sourceRoot":"","sources":["../../src/auth/secretStrength.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAUlF"}
1
+ {"version":3,"file":"secretStrength.d.ts","sourceRoot":"","sources":["../../src/auth/secretStrength.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,IAAI,MAAM,CAU1B"}
@@ -125,58 +125,75 @@ var ApiKeyManager = class {
125
125
  }
126
126
  };
127
127
 
128
- // bin/create-apikey.ts
129
- async function main() {
130
- const args = process.argv.slice(2);
128
+ // bin/create-apikey-args.ts
129
+ function parseArgs(args) {
131
130
  const options = {};
132
131
  for (let i = 0; i < args.length; i++) {
133
- if (args[i] === "--name" && args[i + 1]) {
134
- options.name = args[i + 1];
135
- i++;
136
- } else if (args[i] === "--expires-in" && args[i + 1]) {
137
- options.expiresIn = args[i + 1];
132
+ const arg = args[i];
133
+ if (arg === "--help" || arg === "-h") {
134
+ return { ok: true, help: true };
135
+ }
136
+ if (arg === "--name" || arg === "--expires-in") {
137
+ const value = args[i + 1];
138
+ if (value === void 0 || value.startsWith("-")) {
139
+ return { ok: false, error: `${arg} requires a value` };
140
+ }
141
+ if (arg === "--name") options.name = value;
142
+ else options.expiresIn = value;
138
143
  i++;
139
- } else if (args[i] === "--help" || args[i] === "-h") {
140
- console.log(`
144
+ continue;
145
+ }
146
+ return { ok: false, error: `Unknown argument: ${arg}` };
147
+ }
148
+ return { ok: true, help: false, ...options };
149
+ }
150
+
151
+ // bin/create-apikey.ts
152
+ var HELP_TEXT = `
141
153
  Chatter API Key Generator
142
154
 
143
155
  Usage:
144
- npx chatter create-apikey [options]
156
+ npx chatter [options]
145
157
 
146
158
  Options:
147
159
  --name <name> Name/label for the API key (default: "api-key")
148
160
  --expires-in <time> Expiration time (default: "365d")
149
161
  Formats: s=seconds, m=minutes, h=hours, d=days, M=months, y=years
150
162
  Examples: 30d, 12h, 1y, 6M
163
+ --help, -h Show this help.
151
164
 
152
165
  Environment:
153
166
  CHATTER_SECRET Required. Secret key for signing JWT tokens
154
167
 
155
168
  Examples:
156
- npx chatter create-apikey --name "mobile-app" --expires-in 365d
157
- npx chatter create-apikey --name "test-key" --expires-in 1h
158
- `);
159
- process.exit(0);
160
- }
161
- }
162
- const secret = process.env.CHATTER_SECRET;
163
- if (!secret) {
164
- console.error("\u274C Error: CHATTER_SECRET environment variable is not set");
165
- console.error("\n Please set it before running this command:");
166
- console.error(" export CHATTER_SECRET=your-secret-key-here");
167
- console.error("\n Or prefix the command:");
168
- console.error(" CHATTER_SECRET=your-secret npx chatter create-apikey\n");
169
+ npx chatter --name "mobile-app" --expires-in 365d
170
+ npx chatter --name "test-key" --expires-in 1h
171
+ `;
172
+ async function main() {
173
+ const parsed = parseArgs(process.argv.slice(2));
174
+ if (!parsed.ok) {
175
+ console.error(`\u274C ${parsed.error}`);
176
+ console.log(HELP_TEXT);
169
177
  process.exit(1);
170
178
  }
179
+ if (parsed.help) {
180
+ console.log(HELP_TEXT);
181
+ process.exit(0);
182
+ }
183
+ const secret = process.env.CHATTER_SECRET;
171
184
  try {
172
185
  assertStrongSecret(secret, "CHATTER_SECRET");
173
186
  } catch (error) {
174
187
  console.error(`\u274C ${error.message}`);
188
+ console.error("\n Set it before running this command:");
189
+ console.error(" export CHATTER_SECRET=your-secret-key-here");
190
+ console.error("\n Or prefix the command:");
191
+ console.error(" CHATTER_SECRET=your-secret npx chatter\n");
175
192
  process.exit(1);
176
193
  }
177
194
  const manager = new ApiKeyManager(secret);
178
- const keyName = options.name || "api-key";
179
- const expiresIn = options.expiresIn || "365d";
195
+ const keyName = parsed.name || "api-key";
196
+ const expiresIn = parsed.expiresIn || "365d";
180
197
  try {
181
198
  const apiKey = await manager.create({
182
199
  name: keyName,
@@ -199,12 +216,12 @@ Examples:
199
216
  `);
200
217
  console.log(" Usage:");
201
218
  console.log(" curl -X POST https://your-bot.example.com/api/public/chat \\");
202
- console.log(` -H "x-api-key: ${apiKey.slice(0, 50)}..." \\`);
219
+ console.log(` -H "x-api-key: ${apiKey}" \\`);
203
220
  console.log(' -H "Content-Type: application/json" \\');
204
221
  console.log(` -d '{"message": "Hello"}'`);
205
222
  console.log("");
206
223
  } catch (error) {
207
- console.error("\u274C Error creating API key:", error);
224
+ console.error(`\u274C Error creating API key: ${error.message}`);
208
225
  process.exit(1);
209
226
  }
210
227
  }
@@ -108,27 +108,55 @@ function ensureAuthSchema(client) {
108
108
  }
109
109
  return ready;
110
110
  }
111
- async function useTursoAuthState(db2, secret, sessionId2, runtime) {
112
- await ensureAuthSchema(db2);
111
+ function createTursoWaAuthKV(db2) {
112
+ return {
113
+ async read(id) {
114
+ await ensureAuthSchema(db2);
115
+ const result = await db2.execute({
116
+ sql: "SELECT value FROM wa_auth WHERE id = ?",
117
+ args: [id]
118
+ });
119
+ const row = result.rows[0];
120
+ return row ? row.value : null;
121
+ },
122
+ async write(id, value) {
123
+ await ensureAuthSchema(db2);
124
+ await db2.execute({
125
+ sql: "INSERT OR REPLACE INTO wa_auth (id, value) VALUES (?, ?)",
126
+ args: [id, value]
127
+ });
128
+ },
129
+ async remove(id) {
130
+ await ensureAuthSchema(db2);
131
+ await db2.execute({ sql: "DELETE FROM wa_auth WHERE id = ?", args: [id] });
132
+ },
133
+ // Mirrors isAuthRowForSession - keep the two in sync.
134
+ async clear(sessionId2) {
135
+ await ensureAuthSchema(db2);
136
+ if (sessionId2 === "default") {
137
+ await db2.execute("DELETE FROM wa_auth WHERE id NOT LIKE 's:%'");
138
+ } else {
139
+ await db2.execute({
140
+ sql: "DELETE FROM wa_auth WHERE id LIKE ?",
141
+ args: [`s:${sessionId2}/%`]
142
+ });
143
+ }
144
+ }
145
+ };
146
+ }
147
+ async function useAuthState(store, secret, sessionId2, runtime) {
113
148
  const rowId = (id) => sessionId2 === "default" ? id : `s:${sessionId2}/${id}`;
114
149
  async function read(id) {
115
- const result = await db2.execute({
116
- sql: "SELECT value FROM wa_auth WHERE id = ?",
117
- args: [rowId(id)]
118
- });
119
- const row = result.rows[0];
120
- if (!row) return null;
121
- return JSON.parse(await decrypt(row.value, secret), runtime.bufferJSON.reviver);
150
+ const value = await store.read(rowId(id));
151
+ if (value === null) return null;
152
+ return JSON.parse(await decrypt(value, secret), runtime.bufferJSON.reviver);
122
153
  }
123
154
  async function write(id, value) {
124
155
  const payload = await encrypt(JSON.stringify(value, runtime.bufferJSON.replacer), secret);
125
- await db2.execute({
126
- sql: "INSERT OR REPLACE INTO wa_auth (id, value) VALUES (?, ?)",
127
- args: [rowId(id), payload]
128
- });
156
+ await store.write(rowId(id), payload);
129
157
  }
130
158
  async function remove(id) {
131
- await db2.execute({ sql: "DELETE FROM wa_auth WHERE id = ?", args: [rowId(id)] });
159
+ await store.remove(rowId(id));
132
160
  }
133
161
  const creds = await read("creds") ?? runtime.initAuthCreds();
134
162
  return {
@@ -165,18 +193,13 @@ async function useTursoAuthState(db2, secret, sessionId2, runtime) {
165
193
  }
166
194
  },
167
195
  saveCreds: () => write("creds", creds),
168
- clear: async () => {
169
- if (sessionId2 === "default") {
170
- await db2.execute("DELETE FROM wa_auth WHERE id NOT LIKE 's:%'");
171
- } else {
172
- await db2.execute({
173
- sql: "DELETE FROM wa_auth WHERE id LIKE ?",
174
- args: [`s:${sessionId2}/%`]
175
- });
176
- }
177
- }
196
+ // Scoped to THIS session - never nuke a sibling number's pairing.
197
+ clear: () => store.clear(sessionId2)
178
198
  };
179
199
  }
200
+ async function useTursoAuthState(db2, secret, sessionId2, runtime) {
201
+ return useAuthState(createTursoWaAuthKV(db2), secret, sessionId2, runtime);
202
+ }
180
203
 
181
204
  // src/channels/whatsapp/baileys.ts
182
205
  function wrapMissingBaileysError(cause) {
@@ -196,6 +219,11 @@ function loadBaileys() {
196
219
  return cached;
197
220
  }
198
221
 
222
+ // src/channels/backoff.ts
223
+ function exponentialBackoffMs(baseMs, maxMs, n) {
224
+ return Math.min(baseMs * 2 ** Math.max(0, Math.min(n, 20)), maxMs);
225
+ }
226
+
199
227
  // src/channels/whatsapp/pairing.ts
200
228
  var RESTART_REQUIRED_STATUS = 515;
201
229
  var MAX_PAIRING_RECONNECTS = 12;
@@ -206,9 +234,10 @@ var REGISTERED_CONFIRM_TIMEOUT_MS = 15e3;
206
234
  var REGISTERED_CONFIRM_POLL_MS = 500;
207
235
  function pairingReconnectDelayMs(statusCode, attempt) {
208
236
  if (statusCode === RESTART_REQUIRED_STATUS) return RESTART_REQUIRED_DELAY_MS;
209
- return Math.min(
210
- PAIRING_RECONNECT_DELAY_MS * 2 ** Math.max(0, attempt - 1),
211
- PAIRING_RECONNECT_MAX_DELAY_MS
237
+ return exponentialBackoffMs(
238
+ PAIRING_RECONNECT_DELAY_MS,
239
+ PAIRING_RECONNECT_MAX_DELAY_MS,
240
+ attempt - 1
212
241
  );
213
242
  }
214
243
  function decidePairingClose(input) {
@@ -395,14 +424,46 @@ function resolveQrGenerate(mod) {
395
424
  return void 0;
396
425
  }
397
426
 
398
- // bin/wa-pair.ts
399
- var args = process.argv.slice(2);
400
- function flagValue(name) {
401
- const index = args.indexOf(name);
402
- return index >= 0 ? args[index + 1] : void 0;
427
+ // bin/wa-pair-args.ts
428
+ function parseArgs(args) {
429
+ let sessionId2;
430
+ let codeValue;
431
+ let reset2 = false;
432
+ for (let i = 0; i < args.length; i++) {
433
+ const arg = args[i];
434
+ if (arg === "--help" || arg === "-h") {
435
+ return { ok: true, help: true };
436
+ }
437
+ if (arg === "--reset") {
438
+ reset2 = true;
439
+ continue;
440
+ }
441
+ if (arg === "--code") {
442
+ const value = args[i + 1];
443
+ if (value === void 0 || value.startsWith("-")) {
444
+ return { ok: false, error: "--code requires a phone number, e.g. --code 447700900123" };
445
+ }
446
+ codeValue = value;
447
+ i++;
448
+ continue;
449
+ }
450
+ if (arg.startsWith("-")) {
451
+ return { ok: false, error: `Unknown argument: ${arg}` };
452
+ }
453
+ if (sessionId2 !== void 0) {
454
+ return { ok: false, error: `Unexpected extra argument: ${arg}` };
455
+ }
456
+ sessionId2 = arg;
457
+ }
458
+ const phoneNumber2 = codeValue === void 0 ? void 0 : codeValue.replace(/[^0-9]/g, "");
459
+ if (codeValue !== void 0 && !phoneNumber2) {
460
+ return { ok: false, error: "--code requires a phone number, e.g. --code 447700900123" };
461
+ }
462
+ return { ok: true, help: false, sessionId: sessionId2 ?? "default", phoneNumber: phoneNumber2, reset: reset2 };
403
463
  }
404
- if (args.includes("--help") || args.includes("-h")) {
405
- console.log(`
464
+
465
+ // bin/wa-pair.ts
466
+ var HELP_TEXT = `
406
467
  WhatsApp pairing CLI
407
468
 
408
469
  Usage:
@@ -416,21 +477,23 @@ Options:
416
477
  --code <phoneNumber> Pair via pairing code instead of QR (digits only,
417
478
  e.g. 447700900123). For headless setups.
418
479
  --reset Wipe the stored session first, then pair fresh.
419
- --help, -h Show this help.
480
+ --help, -h Show this help.
420
481
 
421
482
  Environment:
422
- TURSO_URL, TURSO_AUTH_TOKEN Required. The database auth state is stored in.
483
+ TURSO_URL, TURSO_AUTH_TOKEN Required. The database the auth state is stored in.
423
484
  WA_SESSION_SECRET Required. Encrypts the stored session at rest.
424
- `);
425
- process.exit(0);
426
- }
427
- var sessionId = args.find((a, i) => !a.startsWith("--") && args[i - 1] !== "--code") ?? "default";
428
- if (args.includes("--code") && !flagValue("--code")) {
429
- console.error("\u274C --code requires a phone number, e.g. --code 447700900123");
485
+ `;
486
+ var parsed = parseArgs(process.argv.slice(2));
487
+ if (!parsed.ok) {
488
+ console.error(`\u274C ${parsed.error}`);
489
+ console.log(HELP_TEXT);
430
490
  process.exit(1);
431
491
  }
432
- var phoneNumber = flagValue("--code")?.replace(/[^0-9]/g, "");
433
- var reset = args.includes("--reset");
492
+ if (parsed.help) {
493
+ console.log(HELP_TEXT);
494
+ process.exit(0);
495
+ }
496
+ var { sessionId, phoneNumber, reset } = parsed;
434
497
  var sessionSecret = process.env.WA_SESSION_SECRET;
435
498
  try {
436
499
  assertStrongSecret(sessionSecret, "WA_SESSION_SECRET");
@@ -442,9 +505,13 @@ if (!process.env.TURSO_URL) {
442
505
  console.error("\u274C TURSO_URL is required.");
443
506
  process.exit(1);
444
507
  }
508
+ if (!process.env.TURSO_AUTH_TOKEN) {
509
+ console.error("\u274C TURSO_AUTH_TOKEN is required.");
510
+ process.exit(1);
511
+ }
445
512
  var db = (0, import_client.createClient)({
446
513
  url: process.env.TURSO_URL,
447
- authToken: process.env.TURSO_AUTH_TOKEN || ""
514
+ authToken: process.env.TURSO_AUTH_TOKEN
448
515
  });
449
516
  var printedQrFallbackHelp = false;
450
517
  function printQrFallback(qr) {
@@ -561,6 +628,6 @@ async function start() {
561
628
  process.exit(0);
562
629
  }
563
630
  start().catch((error) => {
564
- console.error("\u274C Unexpected error:", error);
631
+ console.error(`\u274C Unexpected error: ${error.message}`);
565
632
  process.exit(1);
566
633
  });
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Exponential backoff with a cap: `base * 2^n`, `n` clamped to `[0, 20]` so
3
+ * the exponent never overflows long before `max` would bind anyway. Every
4
+ * poll/sync/reconnect loop in `src/channels/` computes its retry delay this
5
+ * way (`./telegram/poll.ts`, `./matrix/sync.ts`, `./whatsapp/channel.ts`,
6
+ * `./whatsapp/pairing.ts`); only `base`, `max`, and what `n` counts
7
+ * (failures seen vs. attempts made) differ between them.
8
+ */
9
+ export declare function exponentialBackoffMs(baseMs: number, maxMs: number, n: number): number;
10
+ //# sourceMappingURL=backoff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backoff.d.ts","sourceRoot":"","sources":["../../src/channels/backoff.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAErF"}
@@ -95,14 +95,21 @@ export declare function underReplyRateLimit(msg: Pick<ChannelMessage, "chatId" |
95
95
  group: (chatId: string) => boolean;
96
96
  }): boolean;
97
97
  /**
98
- * Every session's own identities ever seen, keyed by session id. A
99
- * multi-session deployment (e.g. two linked numbers in one process) gives
100
- * each session only its own identity from its transport — a message that
101
- * actually came from a *different* session's identity looks like a
102
- * stranger, not "us", and the two sessions can ping-pong replies at each
103
- * other indefinitely. Callers populate this directly (`registry.set(sessionId, ids)`)
104
- * as each session's identities become known, typically on connect and kept
105
- * in sync as they change.
98
+ * Every session's own identities ever seen, keyed by session id.
99
+ *
100
+ * The failure this exists to prevent: link two numbers to one process (a
101
+ * common WhatsApp setup — a "support" and a "sales" line sharing a server),
102
+ * and each session's transport only tells that session its own identity. A
103
+ * message from the OTHER linked number's session then looks like a
104
+ * stranger, not "us" — the bot answers it, the other session sees THAT
105
+ * reply as a new stranger message and answers it back, and the two numbers
106
+ * ping-pong replies at each other indefinitely, burning the rate limit and
107
+ * spamming both chats until someone notices and kills the process. Sharing
108
+ * one registry across every session (rather than each session checking only
109
+ * its own identity) is what lets session A recognise session B's identity
110
+ * as "us" too, and stop the loop before it starts. Callers populate this
111
+ * directly (`registry.set(sessionId, ids)`) as each session's identities
112
+ * become known, typically on connect and kept in sync as they change.
106
113
  *
107
114
  * Entries are NOT expected to be removed when a session disconnects: a
108
115
  * session's own identity is still "us" while it reconnects, and losing that
@@ -1 +1 @@
1
- {"version":3,"file":"gates.d.ts","sourceRoot":"","sources":["../../src/channels/gates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,iFAAiF;AACjF,MAAM,WAAW,cAAc;IAC7B,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,eAAe,EAAE,OAAO,CAAC;IACzB,mEAAmE;IACnE,WAAW,EAAE,OAAO,CAAC;IACrB,wFAAwF;IACxF,YAAY,EAAE,OAAO,CAAC;IACtB,sHAAsH;IACtH,OAAO,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEnE,MAAM,WAAW,iBAAiB;IAChC,gGAAgG;IAChG,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,kIAAkI;IAClI,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACxB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAOD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,IAAI,CAAC,iBAAiB,EAAE,cAAc,CAAC,GAC9C,OAAO,CAQT;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,iBAAiB,GAAG,aAAa,CAUjG;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,8BAA8B,CAC5C,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EAChB,GAAG,GAAE,MAAM,MAAiB;UAcR,MAAM,GAAG,OAAO;;EAiBrC;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,IAAI,CAAC,cAAc,EAAE,QAAQ,GAAG,iBAAiB,CAAC,EACvD,QAAQ,EAAE;IAAE,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IAAC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAA;CAAE,GAChF,OAAO,CAET;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,uBAAuB,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;AAE5D,qHAAqH;AACrH,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,MAAM,GAAG,SAAS,EACtB,QAAQ,EAAE,uBAAuB,GAChC,OAAO,CAMT;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,UAAU,CAAC,EACjD,QAAQ,EAAE,uBAAuB,GAChC,OAAO,CAET"}
1
+ {"version":3,"file":"gates.d.ts","sourceRoot":"","sources":["../../src/channels/gates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,iFAAiF;AACjF,MAAM,WAAW,cAAc;IAC7B,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,eAAe,EAAE,OAAO,CAAC;IACzB,mEAAmE;IACnE,WAAW,EAAE,OAAO,CAAC;IACrB,wFAAwF;IACxF,YAAY,EAAE,OAAO,CAAC;IACtB,sHAAsH;IACtH,OAAO,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEnE,MAAM,WAAW,iBAAiB;IAChC,gGAAgG;IAChG,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,kIAAkI;IAClI,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACxB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAOD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,IAAI,CAAC,iBAAiB,EAAE,cAAc,CAAC,GAC9C,OAAO,CAQT;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,iBAAiB,GAAG,aAAa,CAUjG;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,8BAA8B,CAC5C,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EAChB,GAAG,GAAE,MAAM,MAAiB;UAcR,MAAM,GAAG,OAAO;;EAiBrC;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,IAAI,CAAC,cAAc,EAAE,QAAQ,GAAG,iBAAiB,CAAC,EACvD,QAAQ,EAAE;IAAE,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IAAC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAA;CAAE,GAChF,OAAO,CAET;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,MAAM,uBAAuB,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;AAE5D,qHAAqH;AACrH,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,MAAM,GAAG,SAAS,EACtB,QAAQ,EAAE,uBAAuB,GAChC,OAAO,CAMT;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,UAAU,CAAC,EACjD,QAAQ,EAAE,uBAAuB,GAChC,OAAO,CAET"}
@@ -42,7 +42,7 @@ export interface Channel {
42
42
  stop?(): void | Promise<void>;
43
43
  }
44
44
  export * from "./gates";
45
- export { createInboundPipeline, type InboundPipeline, type InboundPipelineConfig, type InboundPipelineDeps, type InboundPipelineOutcome, type InboundReplySender, type InboundTurn, } from "./pipeline";
45
+ export { createInboundPipeline, type InboundPipeline, type InboundPipelineConfig, type InboundPipelineDeps, type InboundPipelineOutcome, type InboundReplySender, type InboundTurn, resolveBrainHooks, } from "./pipeline";
46
46
  export type { ChannelSender, ChannelSenderRegistry } from "./senders";
47
47
  export { createSenderRegistry } from "./senders";
48
48
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/channels/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAEnD,MAAM,WAAW,OAAO;IACtB,2FAA2F;IAC3F,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,IAAI,EAAE,kBAAkB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD;;;;;;OAMG;IACH,IAAI,CAAC,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B;AAED,cAAc,SAAS,CAAC;AACxB,OAAO,EACL,qBAAqB,EACrB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,KAAK,WAAW,GACjB,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/channels/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAEnD,MAAM,WAAW,OAAO;IACtB,2FAA2F;IAC3F,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,IAAI,EAAE,kBAAkB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD;;;;;;OAMG;IACH,IAAI,CAAC,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B;AAED,cAAc,SAAS,CAAC;AACxB,OAAO,EACL,qBAAqB,EACrB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC"}