Skip to content

feat(cli): add provider-agnostic channel bridge core (replacement)#114

Open
cdenneen wants to merge 1 commit intohappier-dev:devfrom
cdenneen:feat/channel-bridge-core-v2
Open

feat(cli): add provider-agnostic channel bridge core (replacement)#114
cdenneen wants to merge 1 commit intohappier-dev:devfrom
cdenneen:feat/channel-bridge-core-v2

Conversation

@cdenneen
Copy link

@cdenneen cdenneen commented Mar 6, 2026

Summary

  • rebased on latest upstream/dev
  • squashed into a single clean core commit
  • preserves provider-agnostic channel bridge core implementation and docs

Supersedes

Notes

  • Replacement branch: feat/channel-bridge-core-v2

@cdenneen
Copy link
Author

cdenneen commented Mar 6, 2026

@coderabbitai review

@cdenneen
Copy link
Author

cdenneen commented Mar 6, 2026

@greptile review

@coderabbitai
Copy link

coderabbitai bot commented Mar 6, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link

coderabbitai bot commented Mar 6, 2026

Warning

Rate limit exceeded

@cdenneen has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 1 minutes and 16 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b515c44a-7144-44db-a8f7-a1f4d3b75dd6

📥 Commits

Reviewing files that changed from the base of the PR and between cd992ae and ea92de6.

📒 Files selected for processing (4)
  • apps/cli/src/channels/core/channelBridgeWorker.test.ts
  • apps/cli/src/channels/core/channelBridgeWorker.ts
  • docs/README.md
  • docs/channel-bridge.md
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai
Copy link

coderabbitai bot commented Mar 6, 2026

Warning

Rate limit exceeded

@cdenneen has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 23 minutes and 12 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f980bd5b-9344-43a0-9f29-c52f8e484e1c

📥 Commits

Reviewing files that changed from the base of the PR and between cd992ae and ea92de6.

📒 Files selected for processing (4)
  • apps/cli/src/channels/core/channelBridgeWorker.test.ts
  • apps/cli/src/channels/core/channelBridgeWorker.ts
  • docs/README.md
  • docs/channel-bridge.md
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cdenneen
Copy link
Author

cdenneen commented Mar 6, 2026

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Mar 6, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 6, 2026

Greptile Summary

This PR introduces the provider-agnostic channel bridge core: a polling worker that maps external messaging-channel conversations (Telegram, Discord, etc.) to Happier sessions. It adds channelBridgeWorker.ts, its 1922-line test suite, and supporting documentation.

Key changes:

  • executeChannelBridgeTick — processes inbound messages from adapters, routes slash commands (/sessions, /attach, /detach, /session, /help, /start) with optional authorizeCommand gating, forwards non-command text into bound sessions, and delivers agent output back to channel conversations with monotonic cursor advancement.
  • startChannelBridgeWorker — wraps the tick in a single-flight interval loop, manages adapter lifecycle (deduped stop by object identity), and provides an idempotent stop() that correctly drains in-flight ticks via microtask ordering before stopping adapters.
  • createInMemoryChannelBindingStore — an injectable in-memory binding store for tests and lightweight deployments; real persistence is intended to be injected by callers.
  • createChannelBridgeInboundDeduper — time-bounded deduplication with FIFO eviction when the entry cap is exceeded, guarding against duplicate inbound messages across ticks.
  • Docsdocs/channel-bridge.md added and docs/README.md index updated.

The implementation is well-structured with excellent error isolation and comprehensive test coverage.

Confidence Score: 5/5

  • This PR is safe to merge; it introduces a well-tested, provider-agnostic core with no blocking issues.
  • The implementation is thorough and correct. The test suite is comprehensive (1922 lines covering happy paths, all error branches, deduplication, cursor semantics, and worker lifecycle). The at-least-once delivery semantics are intentional and properly documented. No logic bugs, security issues, or functional problems were found.
  • No files require special attention.

Important Files Changed

Filename Overview
apps/cli/src/channels/core/channelBridgeWorker.ts Core channel bridge worker: well-structured with good error isolation. The implementation correctly handles inbound message routing, slash commands, cursor persistence, and adapter lifecycle management with proper async handling throughout.
apps/cli/src/channels/core/channelBridgeWorker.test.ts 1922-line test suite with excellent coverage of inbound commands, outbound forwarding, cursor persistence, deduplication, stop/drain semantics, and edge cases (store failures, ambiguous sessions, duplicate adapters).
docs/channel-bridge.md New documentation accurately describing the core bridge runtime, tick loop behavior, binding model, and adapter contract. Correctly scopes out-of-scope items for follow-up PRs.
docs/README.md Updated index adds a reference to channel-bridge.md; no issues.

Sequence Diagram

sequenceDiagram
    participant L as SingleFlightLoop
    participant W as ChannelBridgeWorker (tick)
    participant A as Adapter(s)
    participant S as ChannelBindingStore
    participant D as ChannelBridgeDeps

    L->>W: runTick() [microtask]
    W->>A: pullInboundMessages() [timeout guarded]
    A-->>W: ChannelBridgeInboundMessage[]

    alt Message is slash command
        W->>D: authorizeCommand() [if not /help or /start]
        D-->>W: {allowed, message}
        alt authorized
            W->>S: getBinding / upsertBinding / removeBinding
            W->>A: sendMessage(reply)
        else denied
            W->>A: sendMessage(denial)
        end
    else Non-command message
        W->>S: getBinding(ref)
        S-->>W: ChannelSessionBinding | null
        alt bound
            W->>D: sendUserMessageToSession() [timeout guarded]
        else unbound
            W->>A: sendMessage("No session attached")
        end
    end

    W->>S: listBindings()
    S-->>W: ChannelSessionBinding[]

    loop for each binding
        W->>D: fetchAgentMessagesAfterSeq({sessionId, afterSeq})
        D-->>W: [{seq, text}]
        W->>W: sort by seq, filter seq > lastForwardedSeq
        loop for each new row
            W->>A: sendMessage({conversationId, threadId, text}) [timeout guarded]
            W->>S: updateLastForwardedSeq(binding, seq) [monotonic]
        end
    end
Loading

Last reviewed commit: ea92de6

@cdenneen cdenneen marked this pull request as ready for review March 6, 2026 17:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant