Commit 100b23

2026-03-20 05:02:42 Claude (MCP): [mcp] docs: add Directed Message Routing feature spec
/dev/null .. Minsky/Directed_Message_Routing.md
@@ 0,0 1,109 @@
+ ---
+ category: feature
+ tags: [agents, irc, supervisor]
+ last_updated: 2026-03-20
+ confidence: high
+ ---
+
+ # Directed Message Routing
+
+ Event-driven delivery of IRC messages to specific agents, replacing poll-only communication.
+
+ ## Problem
+
+ Agents only see IRC messages when the supervisor's 30-second heartbeat prompts them to `read_messages`. If the PM or EM posts a message mentioning an agent by nick, it sits unread until the next poll. This creates up to 30 seconds of latency on directed communication and makes the system feel unresponsive.
+
+ The `IrcWatcher` already has an event-driven IRC connection that fires instantly on `PRIVMSG`. It currently only handles `TASK:` prefixes. It should also route messages that mention a specific agent.
+
+ ## Design
+
+ Extend `IrcWatcher` to detect nick mentions and notify the supervisor, which immediately sends the message content to the targeted agent.
+
+ ### Detection
+
+ A message is "directed" if it contains `@AgentName` where `AgentName` is a currently active agent nick. The watcher already has access to a callback — add a second callback for directed messages alongside the existing `on_task`.
+
+ ```python
+ class IrcWatcher:
+ def __init__(
+ self,
+ server: str,
+ port: int,
+ nick: str,
+ channels: list[str],
+ on_task: Callable[[str, str, str], None],
+ on_mention: Callable[[str, str, str, str], None],
+ # on_mention(channel, sender, mentioned_nick, full_message)
+ ) -> None:
+ ```
+
+ ### Routing
+
+ In `_handle_message`, after the existing `TASK:` check:
+
+ 1. Scan the message for `@{nick}` where nick is in the set of active agent names
+ 2. If found, call `on_mention(channel, sender_nick, mentioned_nick, message)`
+ 3. A message can match both `TASK:` and `@mention` — that's fine, they serve different purposes
+
+ The supervisor registers `on_mention` and calls `agent.send()` with the message content, bypassing the poll loop:
+
+ ```python
+ def _on_mention(self, channel: str, sender: str, target_nick: str, message: str) -> None:
+ agent = self.agents.get(target_nick)
+ if agent:
+ asyncio.get_running_loop().create_task(
+ agent.session.send(
+ f"[IRC {channel}] <{sender}> {message}\n\n"
+ "Respond in the appropriate channel."
+ )
+ )
+ ```
+
+ ### Active nick registry
+
+ The `IrcWatcher` needs to know which nicks are active to avoid matching mentions of non-agent names. The supervisor already tracks this in `self.agents` (keyed by name). Add a method to update the watcher's nick set:
+
+ ```python
+ # On IrcWatcher
+ def update_active_nicks(self, nicks: set[str]) -> None:
+ self._active_nicks = nicks
+
+ # Supervisor calls this after spawn/kill/shift-change
+ self._watcher.update_active_nicks(set(self.agents.keys()))
+ ```
+
+ ### Deduplication
+
+ Without guard, an agent could receive the same message twice: once via directed routing, once via the next heartbeat's `read_messages`. Two options:
+
+ 1. **Track delivered message timestamps per agent.** The supervisor records the timestamp of the last directed delivery. The heartbeat prompt changes to "Check your IRC channels for messages since {timestamp}."
+ 2. **Accept duplicates.** Agents are LLMs — seeing a message twice is redundant but not harmful. The simpler option for MVP.
+
+ **Recommendation:** Accept duplicates for MVP. Add dedup later if it becomes noisy.
+
+ ## Channels to watch
+
+ The watcher currently joins only `#standup-{slug}`. For mention routing to work across channels, it should also join `#project-{slug}` and any `#work-{task_id}` channels that get created. The supervisor should call `watcher.join_channel()` when creating work channels for new tasks.
+
+ ## Scope
+
+ ### In scope
+ - Extend `IrcWatcher._handle_message` with nick-mention detection
+ - Add `on_mention` callback to `IrcWatcher.__init__`
+ - Add `update_active_nicks()` method
+ - Wire `Supervisor._on_mention` to call `agent.send()`
+ - Update active nicks on spawn/kill/shift-change
+ - Join `#project-{slug}` in addition to `#standup-{slug}` on startup
+ - Tests for mention detection regex
+
+ ### Out of scope
+ - DM (private message) routing — agents only operate in channels for now
+ - Message deduplication — accept duplicates for MVP
+ - Priority/interruption — directed messages queue behind any in-progress `send()`
+ - Rate limiting — a burst of mentions could queue many `send()` calls
+
+ ## Files to change
+
+ - `supervisor/src/minsky_supervisor/irc_watcher.py` — add mention detection, `on_mention` callback, `update_active_nicks()`
+ - `supervisor/src/minsky_supervisor/supervisor.py` — wire `_on_mention`, call `update_active_nicks()` after agent lifecycle changes, join `#project-{slug}`
+ - `supervisor/tests/test_irc_watcher.py` — add mention detection tests
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9