Refactor channel interactions into adapter pattern (Phase 1)#160
Refactor channel interactions into adapter pattern (Phase 1)#160imonlinux wants to merge 3 commits into
Conversation
Extract the per-channel Slack if-ladder in router.onMessage into a uniform
adapter interface that each channel implements as a factory. No behavior
change — existing Slack tests must pass unchanged.
**Core abstraction (src/channels/interaction-adapter.ts):**
- ChannelInteractionInstance: Per-message adapter with lifecycle hooks
- ChannelInteractionFactory: Given InboundMessage, return Instance or null
- ChannelInteractionRegistry: Iterates factories, builds instances for each message
**Lifecycle hooks (in order called):**
1. onTurnStart() — awaited before runtime
2. onRuntimeEvent(event) — for each runtime event
3. onTurnEnd({text, isError}) — awaited after runtime
4. deliverResponse({text, isError}) — optional, true claims response
5. dispose() — cleanup, always called
**Slack adapter (src/channels/slack-interaction.ts):**
- createSlackInteractionFactory(slackChannel) returns factory
- Direct lift of existing Slack logic (no behavior change)
- Preserves status reactions, progress streaming, feedback buttons
**src/index.ts changes:**
- Import ChannelInteractionRegistry and createSlackInteractionFactory
- Register Slack factory with registry on startup
- Replace Slack-specific setup in router.onMessage with adapter loop
- Telegram and other channels preserved unchanged
**Benefits:**
- New channels: implement adapter interface, no src/index.ts edits
- Consistent lifecycle: all channels use same hooks
- Testable: each adapter tested in isolation
- Smaller core: orchestration loop becomes channel-agnostic
**Tests:**
- interaction-adapter.test.ts: Registry lifecycle, factory behavior
- slack-interaction.test.ts: Slack adapter preserves existing behavior
This is Phase 1 of the Telegram parity plan. The architectural foundation
for adding new channels without core code changes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Move interactionRegistry declaration outside if(slackChannel) block for proper scope - Remove stale statusReactions?.dispose() call (variable no longer exists) - Remove unused imports: createStatusReactionController, formatToolActivity, createProgressStream - Update createSlackInteractionFactory to accept SlackTransport union type - Add optional chaining (?.) for all adapter method calls per TypeScript strict mode - Fix test event objects to match RuntimeEvent type (remove sessionId)
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d727e6e90e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } | ||
| }, | ||
|
|
||
| async deliverResponse({ text }): Promise<boolean> { |
There was a problem hiding this comment.
Restore Slack terminal reactions
Before this refactor, every Slack turn finalized the status reaction with setDone() or setError() after runtime.handleMessage returned. The Slack adapter now goes straight from runtime events to response delivery without implementing onTurnEnd, so successful Slack requests can be left showing the queued/thinking/tool emoji, and responses like the session-busy Error: path that do not emit a runtime error event no longer get the warning reaction. Please finalize statusReactions in the adapter's turn-end hook before delivering/disposing.
Useful? React with 👍 / 👎.
Refactor channel interactions into adapter pattern (Phase 1)
Extract the per-channel Slack if-ladder in router.onMessage into a uniform adapter interface that each channel implements as a factory. No behavior change — existing Slack tests must pass unchanged.
Core abstraction (src/channels/interaction-adapter.ts):
Lifecycle hooks (in order called):
Slack adapter (src/channels/slack-interaction.ts):
src/index.ts changes:
Benefits:
Tests:
This is Phase 1 of the Telegram Feature Parity with Slack Channel project
This refactoring establishes the architectural foundation for a comprehensive 7-phase implementation approach to achieve full feature parity between Telegram and Slack channels:
Phase 2: Interaction Features - Progressive updates, feedback buttons, reaction-as-feedback
Phase 3: Owner Access Control - Reject non-owner DMs with configurable messages
Phase 4: Hardening & Reliability - Message splitting, retry/backoff, security audit
Phase 5: Proactive Intro - First-run welcome messages with database idempotency
Phase 6: Documentation - Comprehensive 620-line setup guide and best practices
Phase 7: Webhook Transport - Production-ready webhook mode with security features
The adapter pattern enables each phase to be implemented as independent, testable channel enhancements without modifying core orchestration logic.
What Changed
5 files changed: 619 insertions(+), 73 deletions(-)
src/channels/interaction-adapter.ts(121 lines) - Core abstractionsrc/channels/slack-interaction.ts(130 lines) - Slack adapter implementationsrc/channels/__tests__/interaction-adapter.test.ts(119 lines) - Registry testssrc/channels/__tests__/slack-interaction.test.ts(208 lines) - Slack adapter testssrc/index.ts(-73/+114 lines) - Replaced Slack if-ladder with adapter loopNet: Cleaner architecture, no behavior change, all tests passing.
Why
Problem:
src/index.tshas grown a complex ladder ofif (isSlack)/if (isTelegram)conditionals for channel-specific rendering logic. Each new channel requires modifying the core orchestration loop, making the codebase harder to maintain and extend.Solution: Extract per-channel orchestration into a uniform adapter pattern. Each channel registers a factory that returns adapter instances with a shared lifecycle interface. New channels can be added by implementing the adapter interface without touching core code.
This architectural refactor enables the Telegram Feature Parity project and provides a sustainable pattern for future channel integrations.
How I Tested
Existing tests: All existing Slack and Telegram tests pass unchanged, confirming behavioral equivalence.
New comprehensive test coverage: 327 new lines of tests covering:
Verification:
bun testpasses all 1,819 tests.Checklist
bun test)bun run lint)bun run typecheck).envfiles includedCo-Authored-By: imonlinux <imonlinux@mcmurphys.net>
Co-Authored-By: Phantom <phantom@mcmurphys.net>