Skip to content

feat: Add pluggable channel system for remote messaging via Telegram#410

Draft
edenreich wants to merge 9 commits intomainfrom
feat/channels-remote-messaging
Draft

feat: Add pluggable channel system for remote messaging via Telegram#410
edenreich wants to merge 9 commits intomainfrom
feat/channels-remote-messaging

Conversation

@edenreich
Copy link
Copy Markdown
Contributor

@edenreich edenreich commented Apr 9, 2026

Summary

  • Add a transport-agnostic channel abstraction that lets external messaging platforms (Telegram, WhatsApp, etc.) remote-control the agent
  • The infer channels-manager command runs as a standalone long-running daemon, completely decoupled from the agent
  • Each incoming message triggers infer agent --session-id <id> as a subprocess — the agent has no knowledge of channels

Architecture

Telegram Bot API  <-->  infer channels-manager (daemon)
                              |
                              v  (per message)
                        1. Check allowlist
                        2. Derive session ID (channel-telegram-<chatID>)
                        3. exec: infer agent --session-id <id> "<message>"
                        4. Parse JSON stdout
                        5. Send response via channel

What's included

  • Channel interface (domain.Channel) with Start/Send/Stop lifecycle
  • ChannelManager service — subprocess-based routing, per-sender mutex serialization, allowlist auth
  • Telegram channel using go-telegram/bot SDK (long-polling, no webhook needed)
  • infer channels-manager command — standalone daemon with signal handling
  • Session ID bug fix--session-id now persists the provided ID even when the session doesn't exist yet
  • Per-channel allowlist security (secure-by-default: empty list rejects all)
  • Full documentation (docs/channels.md), Docker Compose example, and test coverage

Key design decisions

  • Decoupled: Channel manager is a standalone process, not embedded in the agent. The agent remains simple with no channel-related logic
  • Subprocess per message: Each message spawns infer agent --session-id <id>, giving crash isolation and clean lifecycle
  • Deterministic sessions: Session ID is channel-{name}-{senderID}, so conversations persist across messages using the existing ConversationRepository and auto-compact
  • No new dependencies on agent internals: Removed MessageQueue/EventBridge coupling from the channel manager

TODOs

  • Test the example to ensure the functionality works as expected with telegram
  • Ensure there are not many processes running and exhausting the system - that the semaphore limit works as expected
  • Test the agents tasks delegation - for example setup a browser agent and make sure it can use it when sending a message on telegram for example to get an article from the internet etc..
  • Test with vision LLMs like Anthropic Opus that it can understand images I'm sending over telegram
  • Implement approval flow for tools in telegram
  • Refactor session rollover manager session groups - I'm not happy about the complexity, I need to refactor this

Closes #380

@edenreich edenreich marked this pull request as draft April 9, 2026 15:01
Add a transport-agnostic channel abstraction that lets external messaging
platforms (Telegram, WhatsApp, etc.) act as inbound/outbound bridges to
the agent. Channel messages flow through the existing ConversationRepository
and MessageQueue, reusing the agent's session management and auto-compaction.

- Channel interface (domain.Channel) with Start/Send/Stop lifecycle
- ChannelManager service with registry, auth allowlist, EventBridge routing
- Telegram channel implementation using go-telegram/bot SDK (long-polling)
- Per-channel allowlist security (secure-by-default: empty list rejects all)
- Full documentation, Docker Compose example, and test coverage

Closes #380
@edenreich edenreich force-pushed the feat/channels-remote-messaging branch from 5065eb3 to 9ce8847 Compare April 9, 2026 15:02
Adds a configurable max_workers setting (default: 5) that caps the number
of agent subprocesses running in parallel. New messages wait for a slot
when all workers are busy.
Split agent system prompt to custom instructions and system prompt configurations also allow the operator to completely remove the defaults when not needed.

It's essential for minimal tests of the agent.

Agent was tested via telegram and seems to work as expected now I might create an RC soon.
@edenreich
Copy link
Copy Markdown
Contributor Author

Here are some tests with tools...

image image image image

Previously, processSyncResponse saved content and tool_calls as separate
assistant messages. This broke the API contract where assistant(tool_calls)
must be immediately followed by tool(result), causing DeepSeek and other
providers to reject the conversation history with "insufficient tool
messages following tool_calls message".
…h command state management

- Add SessionRolloverManager for automatic conversation rollover on idle/context limits
- Introduce BackgroundTaskRegistry to unify A2A and shell task tracking
- Add BackgroundTasksWaiter for batch-mode task draining
- Fix bash command failure flow: carry all state in BashCommandCompletedEvent and
  emit side effects via tea.Sequence after EndToolExecution() state transition
- Decouple channel manager from agent, add Telegram example with A2A browser agent
- Add max_workers semaphore support for concurrent agent subprocesses
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.

[FEATURE] Support Whatsapp or Telegram for remote control

1 participant