flow_chat 0.9.0 → 0.10.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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/pages.yml +43 -0
  3. data/CHANGELOG.md +26 -0
  4. data/README.md +166 -295
  5. data/Rakefile +12 -1
  6. data/SECURITY.md +1 -1
  7. data/docs/architecture.md +52 -479
  8. data/docs/async-background-processing.md +31 -265
  9. data/docs/configuration.md +106 -613
  10. data/docs/factory-pattern.md +27 -324
  11. data/docs/gateway-context-variables.md +140 -143
  12. data/docs/gateway-development.md +86 -650
  13. data/docs/getting-started.md +40 -379
  14. data/docs/instrumentation.md +88 -279
  15. data/docs/platforms/instagram.md +278 -0
  16. data/docs/platforms/messenger.md +205 -0
  17. data/docs/platforms/telegram.md +47 -951
  18. data/docs/platforms/ussd.md +38 -653
  19. data/docs/platforms/whatsapp.md +73 -1321
  20. data/docs/superpowers/plans/2026-07-09-inbound-media-support.md +732 -0
  21. data/docs/superpowers/plans/2026-07-09-inbound-media-support.md.tasks.json +58 -0
  22. data/docs/superpowers/plans/2026-08-10-messenger-instagram.md +4064 -0
  23. data/docs/superpowers/plans/2026-08-10-messenger-instagram.md.tasks.json +226 -0
  24. data/docs/superpowers/plans/2026-08-16-unified-choice-resolution.md +972 -0
  25. data/docs/superpowers/plans/2026-08-16-unified-choice-resolution.md.tasks.json +88 -0
  26. data/docs/superpowers/specs/2026-07-09-inbound-media-support-design.md +195 -0
  27. data/docs/superpowers/specs/2026-08-10-messenger-instagram-design.md +391 -0
  28. data/docs/testing.md +26 -297
  29. data/examples/http_controller.rb +12 -10
  30. data/examples/intercom_configuration_example.rb +19 -24
  31. data/examples/intercom_controller.rb +8 -20
  32. data/examples/multi_tenant_whatsapp_controller.rb +61 -166
  33. data/examples/simulator_controller.rb +0 -1
  34. data/examples/ussd_controller.rb +86 -158
  35. data/examples/whatsapp_controller.rb +16 -15
  36. data/examples/whatsapp_media_examples.rb +27 -79
  37. data/lib/flow_chat/app.rb +129 -11
  38. data/lib/flow_chat/choice_titles.rb +95 -0
  39. data/lib/flow_chat/config.rb +124 -1
  40. data/lib/flow_chat/delivery_error.rb +9 -0
  41. data/lib/flow_chat/http/configuration_error.rb +9 -0
  42. data/lib/flow_chat/http/gateway/simple.rb +24 -4
  43. data/lib/flow_chat/http/middleware/choice_mapper.rb +94 -0
  44. data/lib/flow_chat/input.rb +86 -0
  45. data/lib/flow_chat/instagram/client.rb +32 -0
  46. data/lib/flow_chat/instagram/configuration.rb +147 -0
  47. data/lib/flow_chat/instagram/configuration_error.rb +7 -0
  48. data/lib/flow_chat/instagram/gateway/send_api.rb +63 -0
  49. data/lib/flow_chat/instagram/middleware/choice_mapper.rb +22 -0
  50. data/lib/flow_chat/instagram/renderer.rb +23 -0
  51. data/lib/flow_chat/instrumentation/metrics_collector.rb +6 -1
  52. data/lib/flow_chat/instrumentation.rb +160 -1
  53. data/lib/flow_chat/intercom/client.rb +34 -28
  54. data/lib/flow_chat/intercom/configuration.rb +2 -49
  55. data/lib/flow_chat/intercom/configuration_error.rb +9 -0
  56. data/lib/flow_chat/intercom/gateway/intercom_api.rb +81 -57
  57. data/lib/flow_chat/intercom/middleware/choice_mapper.rb +101 -0
  58. data/lib/flow_chat/intercom/renderer.rb +57 -5
  59. data/lib/flow_chat/media.rb +121 -0
  60. data/lib/flow_chat/messenger/client.rb +264 -0
  61. data/lib/flow_chat/messenger/configuration.rb +103 -0
  62. data/lib/flow_chat/messenger/configuration_error.rb +9 -0
  63. data/lib/flow_chat/messenger/gateway/send_api.rb +42 -0
  64. data/lib/flow_chat/messenger/middleware/choice_mapper.rb +185 -0
  65. data/lib/flow_chat/messenger/renderer.rb +150 -0
  66. data/lib/flow_chat/meta/challenge.rb +24 -0
  67. data/lib/flow_chat/meta/choice_ladder.rb +37 -0
  68. data/lib/flow_chat/meta/configuration_error.rb +7 -0
  69. data/lib/flow_chat/meta/gateway_identity.rb +38 -0
  70. data/lib/flow_chat/meta/messaging_gateway.rb +468 -0
  71. data/lib/flow_chat/meta/signature.rb +30 -0
  72. data/lib/flow_chat/meta/signature_validation.rb +66 -0
  73. data/lib/flow_chat/meta/webhook_verification.rb +43 -0
  74. data/lib/flow_chat/named_configuration.rb +65 -0
  75. data/lib/flow_chat/prompt.rb +13 -16
  76. data/lib/flow_chat/renderers/markdown_support.rb +109 -0
  77. data/lib/flow_chat/security.rb +76 -0
  78. data/lib/flow_chat/session/middleware.rb +11 -2
  79. data/lib/flow_chat/simulator/controller.rb +31 -15
  80. data/lib/flow_chat/simulator/views/simulator.html.erb +184 -20
  81. data/lib/flow_chat/telegram/client.rb +47 -4
  82. data/lib/flow_chat/telegram/configuration.rb +2 -42
  83. data/lib/flow_chat/telegram/configuration_error.rb +9 -0
  84. data/lib/flow_chat/telegram/gateway/bot_api.rb +46 -28
  85. data/lib/flow_chat/telegram/middleware/choice_mapper.rb +77 -16
  86. data/lib/flow_chat/telegram/renderer.rb +10 -2
  87. data/lib/flow_chat/text_truncator.rb +75 -0
  88. data/lib/flow_chat/ussd/middleware/choice_mapper.rb +10 -0
  89. data/lib/flow_chat/version.rb +1 -1
  90. data/lib/flow_chat/whatsapp/client.rb +14 -7
  91. data/lib/flow_chat/whatsapp/configuration.rb +12 -51
  92. data/lib/flow_chat/whatsapp/configuration_error.rb +9 -0
  93. data/lib/flow_chat/whatsapp/gateway/cloud_api.rb +254 -203
  94. data/lib/flow_chat/whatsapp/middleware/choice_mapper.rb +144 -57
  95. data/lib/flow_chat/whatsapp/renderer.rb +121 -60
  96. data/lib/flow_chat.rb +0 -10
  97. data/site/.nojekyll +0 -0
  98. data/site/.og-card.html +89 -0
  99. data/site/favicon.svg +6 -0
  100. data/site/index.html +209 -0
  101. data/site/og.png +0 -0
  102. metadata +51 -3
  103. data/lib/flow_chat/whatsapp/id_generator.rb +0 -124
@@ -1,429 +1,90 @@
1
- # Getting Started with FlowChat
1
+ # Getting Started
2
2
 
3
- This guide will walk you through setting up FlowChat and building your first conversational application that works across multiple platforms.
3
+ This guide takes you from an empty Rails app to a running FlowChat flow. It reuses the same flow and processor shown in the [README](../README.md).
4
4
 
5
5
  ## Prerequisites
6
6
 
7
- - Ruby 2.3+ (Ruby 3.0+ recommended)
8
- - Rails 6.0+ (Rails 7.0+ recommended)
9
- - Basic understanding of Rails controllers and routing
7
+ - A Rails application.
8
+ - Ruby 3.0 or newer.
9
+ - A cache the session store can use, such as `Rails.cache`.
10
10
 
11
- ## Installation
11
+ ## Install
12
12
 
13
- Add FlowChat to your Rails application:
13
+ Add the gem to your Gemfile:
14
14
 
15
15
  ```ruby
16
- # Gemfile
17
- gem 'flow_chat'
16
+ gem "flow_chat"
18
17
  ```
19
18
 
20
- ```bash
21
- bundle install
22
- ```
23
-
24
- ## Quick Start: Your First Multi-Platform App
25
-
26
- Let's build a simple survey application that works across USSD, WhatsApp, Telegram, and HTTP APIs.
19
+ Then run `bundle install`. There are no migrations and no generators to run.
27
20
 
28
- ### 1. Create the Flow
21
+ ## Configure the cache
29
22
 
30
- Create a new file `app/flow_chat/survey_flow.rb`:
23
+ The cache-backed session store needs a cache. Set it once during boot, for example in an initializer:
31
24
 
32
25
  ```ruby
33
- class SurveyFlow < FlowChat::Flow
34
- def start
35
- # Welcome message that works on all platforms
36
- name = app.screen(:name) do |prompt|
37
- prompt.ask "Welcome to our survey! What's your name?",
38
- validate: ->(input) {
39
- return "Name must be at least 2 characters" if input.length < 2
40
- nil
41
- },
42
- transform: ->(input) { input.strip.titleize }
43
- end
44
-
45
- # Rating question with platform-appropriate choices
46
- rating = app.screen(:rating) do |prompt|
47
- prompt.select "Hi #{name}! Rate our service:", {
48
- "5" => "⭐⭐⭐⭐⭐ Excellent",
49
- "4" => "⭐⭐⭐⭐ Good",
50
- "3" => "⭐⭐⭐ Average",
51
- "2" => "⭐⭐ Poor",
52
- "1" => "⭐ Very Poor"
53
- }
54
- end
55
-
56
- # Feedback collection
57
- feedback = app.screen(:feedback) do |prompt|
58
- prompt.ask "Any additional feedback?",
59
- validate: ->(input) {
60
- return "Feedback too short" if input.length < 5
61
- nil
62
- }
63
- end
64
-
65
- # Save the survey (your business logic)
66
- save_survey(name, rating, feedback, app.msisdn)
67
-
68
- # Thank you message
69
- app.say "Thank you #{name}! Your feedback (#{rating}⭐) has been recorded."
70
- end
71
-
72
- private
73
-
74
- def save_survey(name, rating, feedback, phone)
75
- # Your database logic here
76
- Rails.logger.info "Survey: #{name} (#{phone}) rated #{rating}/5: #{feedback}"
77
-
78
- # Example: Save to database
79
- # Survey.create!(
80
- # name: name,
81
- # rating: rating.to_i,
82
- # feedback: feedback,
83
- # phone: phone,
84
- # platform: app.platform
85
- # )
86
- end
87
- end
26
+ # config/initializers/flow_chat.rb
27
+ FlowChat::Config.cache = Rails.cache
88
28
  ```
89
29
 
90
- ### 2. Create Controllers for Each Platform
30
+ Without a cache configured, `FlowChat::Session::CacheSessionStore` raises when it tries to read or write a session.
31
+
32
+ ## Write your first flow
91
33
 
92
- #### USSD Controller
34
+ A flow is a class that inherits `FlowChat::Flow` and reads and writes the conversation through `app`. Each `app.screen` is one step:
93
35
 
94
36
  ```ruby
95
- # app/controllers/ussd_controller.rb
96
- class UssdController < ApplicationController
97
- skip_forgery_protection
98
-
99
- def nalo_webhook
100
- processor = FlowChat::Processor.new(self) do |config|
101
- config.use_gateway FlowChat::Ussd::Gateway::Nalo
102
- config.use_session_store FlowChat::Session::CacheSessionStore
103
-
104
- # Optional: Configure USSD-specific settings
105
- config.use_session_config(boundaries: [:flow, :platform])
37
+ class RegistrationFlow < FlowChat::Flow
38
+ def main_page
39
+ name = app.screen(:name) { |prompt| prompt.ask "What's your name?" }
40
+
41
+ email = app.screen(:email) do |prompt|
42
+ prompt.ask "Your email?", validate: ->(input) { "Invalid email" unless input.include?("@") }
106
43
  end
107
44
 
108
- processor.run SurveyFlow, :start
45
+ app.say "Welcome #{name}!"
109
46
  end
110
47
  end
111
48
  ```
112
49
 
113
- #### WhatsApp Controller
50
+ FlowChat re-runs this method from the top on every webhook. Each `screen` returns its stored answer when the session already holds one, and re-prompts when it does not, so the method reads as a straight-line script even though each turn is a separate stateless request. For the mechanics, see [How the replay engine works](../README.md#how-the-replay-engine-works).
114
51
 
115
- ```ruby
116
- # app/controllers/whatsapp_controller.rb
117
- class WhatsappController < ApplicationController
118
- skip_forgery_protection
119
-
120
- def webhook
121
- processor = FlowChat::Processor.new(self) do |config|
122
- config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi
123
- config.use_session_store FlowChat::Session::CacheSessionStore
124
- end
52
+ Put the flow anywhere Rails autoloads it, for example `app/flow_chat/registration_flow.rb`.
125
53
 
126
- processor.run SurveyFlow, :start
127
- rescue => e
128
- Rails.logger.error "WhatsApp error: #{e.message}"
129
- head :internal_server_error
130
- end
131
- end
132
- ```
54
+ ## Wire a controller
133
55
 
134
- #### Telegram Controller
56
+ Build a processor in your webhook action, choose a gateway and session store, and run the flow. Here `self` is the controller:
135
57
 
136
58
  ```ruby
137
- # app/controllers/telegram_controller.rb
138
- class TelegramController < ApplicationController
59
+ # app/controllers/ussd_controller.rb
60
+ class UssdController < ApplicationController
139
61
  skip_forgery_protection
140
62
 
141
63
  def webhook
142
64
  processor = FlowChat::Processor.new(self) do |config|
143
- config.use_gateway FlowChat::Telegram::Gateway::BotApi
144
- config.use_session_store FlowChat::Session::CacheSessionStore
145
- end
146
-
147
- processor.run SurveyFlow, :start
148
- rescue => e
149
- Rails.logger.error { "Telegram error: #{e.message}" }
150
- head :internal_server_error
151
- end
152
- end
153
- ```
154
-
155
- #### HTTP API Controller
156
-
157
- ```ruby
158
- # app/controllers/api/chat_controller.rb
159
- class Api::ChatController < ApplicationController
160
- before_action :authenticate_api_user # Your auth logic
161
-
162
- def message
163
- processor = FlowChat::Processor.new(self) do |config|
164
- config.use_gateway FlowChat::Http::Gateway::Simple
65
+ config.use_gateway FlowChat::Ussd::Gateway::Nalo
165
66
  config.use_session_store FlowChat::Session::CacheSessionStore
166
-
167
- # Session isolation per API user
168
- config.use_session_config(identifier: :user_id)
169
67
  end
170
68
 
171
- processor.run SurveyFlow, :start
172
- # Automatically returns JSON response
69
+ processor.run RegistrationFlow, :main_page
173
70
  end
174
71
  end
175
72
  ```
176
73
 
177
- ### 3. Add Routes
74
+ `skip_forgery_protection` is needed because gateway webhooks are POSTs from an external service that cannot carry a Rails CSRF token. Point a route at the action:
178
75
 
179
76
  ```ruby
180
77
  # config/routes.rb
181
- Rails.application.routes.draw do
182
- # USSD routes
183
- post '/ussd/nalo', to: 'ussd#nalo_webhook'
184
-
185
- # WhatsApp routes
186
- post '/whatsapp/webhook', to: 'whatsapp#webhook'
187
- get '/whatsapp/webhook', to: 'whatsapp#verify' # For webhook verification
188
-
189
- # Telegram routes
190
- post '/telegram/webhook', to: 'telegram#webhook'
191
-
192
- # API routes
193
- namespace :api do
194
- post '/chat/message', to: 'chat#message'
195
- end
196
- end
78
+ post "/ussd", to: "ussd#webhook"
197
79
  ```
198
80
 
199
- ### 4. Configuration
200
-
201
- #### Environment Variables
202
-
203
- Add these to your `.env` file:
81
+ To run the same flow on another platform, change only `config.use_gateway`. The gateway classes and their platform symbols are listed in the [README](../README.md#wiring-a-platform).
204
82
 
205
- ```bash
206
- # WhatsApp Configuration
207
- WHATSAPP_ACCESS_TOKEN=your_access_token
208
- WHATSAPP_PHONE_NUMBER_ID=your_phone_number_id
209
- WHATSAPP_VERIFY_TOKEN=your_verify_token
210
- WHATSAPP_APP_SECRET=your_app_secret
211
-
212
- # Telegram Configuration
213
- TELEGRAM_BOT_TOKEN=your_bot_token
214
- TELEGRAM_SECRET_TOKEN=your_webhook_secret # Optional: for webhook validation
215
- ```
83
+ ## Try it in the simulator
216
84
 
217
- #### FlowChat Configuration
218
-
219
- Create `config/initializers/flow_chat.rb`:
220
-
221
- ```ruby
222
- FlowChat::Config.logger = Rails.logger
223
- FlowChat::Config.cache = Rails.cache
224
-
225
- # USSD Configuration
226
- FlowChat::Config.ussd.pagination_page_size = 160 # Adjust for your network
227
- FlowChat::Config.ussd.pagination_next_option = "#"
228
- FlowChat::Config.ussd.pagination_back_option = "0"
229
-
230
- # WhatsApp Configuration
231
- FlowChat::Config.whatsapp.message_handling_mode = :inline # or :background
232
- ```
233
-
234
- ### 5. Test Your Application
235
-
236
- #### Using the Built-in Simulator
237
-
238
- Create a simple test in `rails console`:
239
-
240
- ```ruby
241
- # Start a simulator session
242
- simulator = FlowChat::Simulator.new(SurveyFlow, :start)
85
+ FlowChat ships a web simulator for driving flows locally without a real gateway. It requires `FlowChat::Config.simulator_secret` to be set (for example in your initializer). Once set, you can step through a flow from the browser during development. See [testing.md](testing.md) for how to mount and use it.
243
86
 
244
- # Simulate the conversation
245
- simulator.start
246
- # => "Welcome to our survey! What's your name?"
247
-
248
- simulator.send_message("John Doe")
249
- # => "Hi John Doe! Rate our service:"
250
-
251
- simulator.select_option("5")
252
- # => "Any additional feedback?"
253
-
254
- simulator.send_message("Great service, very helpful!")
255
- # => "Thank you John Doe! Your feedback (5⭐) has been recorded."
256
- ```
257
-
258
- #### Testing HTTP API
259
-
260
- ```bash
261
- # Test the HTTP endpoint
262
- curl -X POST http://localhost:3000/api/chat/message \
263
- -H "Content-Type: application/json" \
264
- -H "Authorization: Bearer your_api_token" \
265
- -d '{
266
- "user_id": "user123",
267
- "input": "Hello"
268
- }'
269
- ```
270
-
271
- #### Testing USSD (using simulator)
272
-
273
- Visit your Rails app with the simulator enabled:
274
-
275
- ```ruby
276
- # In development, add this to your controller
277
- processor = FlowChat::Processor.new(self, enable_simulator: true) do |config|
278
- config.use_gateway FlowChat::Ussd::Gateway::Nalo
279
- # ... other config
280
- end
281
- ```
282
-
283
- ## Key Concepts
284
-
285
- ### 1. Screen-Based Navigation
286
-
287
- FlowChat uses a **screen-based** approach where each `app.screen(:key)` represents a step in your conversation:
288
-
289
- - **Automatic State Management**: Each screen's result is automatically cached
290
- - **Navigation Stack**: FlowChat tracks where users are in the flow
291
- - **Resume Capability**: Users can return to where they left off
292
-
293
- ### 2. Platform Abstraction
294
-
295
- The same flow code works across all platforms because:
296
-
297
- - **Unified Prompts**: `prompt.ask()` and `prompt.select()` adapt to each platform
298
- - **Context Normalization**: All platforms provide consistent context (phone number, input, etc.)
299
- - **Flexible Rendering**: Emojis show on WhatsApp, get stripped for USSD
300
-
301
- ### 3. Pluggable Gateways
302
-
303
- Each platform can have multiple gateway implementations:
304
-
305
- ```ruby
306
- # USSD with different providers
307
- config.use_gateway FlowChat::Ussd::Gateway::Nalo
308
- # config.use_gateway FlowChat::Ussd::Gateway::Africaist
309
- # config.use_gateway YourCompany::Ussd::Gateway::CustomProvider
310
-
311
- # WhatsApp with different APIs
312
- config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi
313
- # config.use_gateway FlowChat::Whatsapp::Gateway::OnPremise
314
- # config.use_gateway YourCompany::Whatsapp::Gateway::Twilio
315
- ```
316
-
317
- ### 4. Session Management
318
-
319
- FlowChat provides flexible session configuration:
320
-
321
- ```ruby
322
- # Ephemeral sessions (restart on each request)
323
- config.use_session_config(identifier: :request_id)
324
-
325
- # Durable sessions (persist across timeouts using phone number)
326
- config.use_durable_sessions
327
-
328
- # Cross-platform sessions (same user, different platforms)
329
- config.use_cross_platform_sessions
330
-
331
- # Multi-tenant isolation
332
- config.use_url_isolation
333
- ```
334
-
335
- ## Next Steps
336
-
337
- Now that you have a basic multi-platform application running:
338
-
339
- 1. **Explore Platform Features**: Learn platform-specific capabilities
340
- - [USSD Development](platforms/ussd.md) - Pagination, choice mapping
341
- - [WhatsApp Development](platforms/whatsapp.md) - Rich media, templates
342
- - [Telegram Development](platforms/telegram.md) - Inline keyboards, media, callbacks
343
- - [HTTP Development](platforms/http.md) - API integration patterns
344
-
345
- 2. **Advanced Topics**:
346
- - [Session Management](../session-management.md) - Deep dive into session boundaries
347
- - [Middleware Development](../middleware.md) - Custom processing logic
348
- - [Gateway Development](../gateway-development.md) - Build your own platform support
349
-
350
- 3. **Production Deployment**:
351
- - [Configuration](../configuration.md) - Production-ready settings
352
- - [Background Jobs](../background-jobs.md) - Async processing for WhatsApp
353
- - [Testing](../testing.md) - Comprehensive testing strategies
354
-
355
- ## Common Patterns
356
-
357
- ### Validation and Transformation
358
-
359
- ```ruby
360
- app.screen(:phone) do |prompt|
361
- prompt.ask "Enter your phone number:",
362
- validate: ->(input) {
363
- return "Invalid phone format" unless input.match?(/^\+?[\d\s-()]+$/)
364
- parsed = Phonelib.parse(input)
365
- return "Invalid phone number" unless parsed.valid?
366
- nil
367
- },
368
- transform: ->(input) { Phonelib.parse(input).e164 }
369
- end
370
- ```
371
-
372
- ### Conditional Flow Logic
373
-
374
- ```ruby
375
- def main_menu
376
- user_type = determine_user_type(app.msisdn)
377
-
378
- if user_type == :premium
379
- premium_menu
380
- else
381
- basic_menu
382
- end
383
- end
384
- ```
385
-
386
- ### Error Handling
387
-
388
- ```ruby
389
- def payment_flow
390
- begin
391
- amount = app.screen(:amount) { |p| p.ask "Enter amount:" }
392
- process_payment(amount)
393
- app.say "Payment successful!"
394
- rescue PaymentError => e
395
- app.say "Payment failed: #{e.message}"
396
- end
397
- end
398
- ```
399
-
400
- ## Troubleshooting
401
-
402
- ### Common Issues
403
-
404
- 1. **Session Not Persisting**
405
- - Check your session store configuration
406
- - Verify session boundaries match your use case
407
-
408
- 2. **Gateway Errors**
409
- - Ensure environment variables are set correctly
410
- - Check gateway-specific configuration requirements
411
-
412
- 3. **Platform Differences**
413
- - Test flows on each platform's simulator
414
- - Be aware of character limits (USSD) vs rich features (WhatsApp)
415
-
416
- ### Debug Mode
417
-
418
- Enable comprehensive logging:
419
-
420
- ```ruby
421
- # config/initializers/flow_chat.rb
422
- FlowChat::Config.logger.level = Logger::DEBUG
423
-
424
- # In your flow
425
- Rails.logger.debug "Current screen: #{app.navigation_stack.last}"
426
- Rails.logger.debug "User input: #{app.input.inspect}"
427
- ```
87
+ ## Next steps
428
88
 
429
- Ready to build more sophisticated flows? Continue with the [Architecture Overview](architecture.md) to understand FlowChat's design principles.
89
+ - [configuration.md](configuration.md) for the full config surface and session options.
90
+ - [platforms/ussd.md](platforms/ussd.md), [platforms/whatsapp.md](platforms/whatsapp.md), and [platforms/telegram.md](platforms/telegram.md) for platform-specific behavior.