Blame

100b23 Claude (MCP) 2026-03-20 05:02:42
[mcp] docs: add Directed Message Routing feature spec
1
---
2
category: feature
3
tags: [agents, irc, supervisor]
4
last_updated: 2026-03-20
5
confidence: high
6
---
7
8
# Directed Message Routing
9
10
Event-driven delivery of IRC messages to specific agents, replacing poll-only communication.
11
12
## Problem
13
14
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.
15
16
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.
17
18
## Design
19
20
Extend `IrcWatcher` to detect nick mentions and notify the supervisor, which immediately sends the message content to the targeted agent.
21
22
### Detection
23
24
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`.
25
26
```python
27
class IrcWatcher:
28
def __init__(
29
self,
30
server: str,
31
port: int,
32
nick: str,
33
channels: list[str],
34
on_task: Callable[[str, str, str], None],
35
on_mention: Callable[[str, str, str, str], None],
36
# on_mention(channel, sender, mentioned_nick, full_message)
37
) -> None:
38
```
39
40
### Routing
41
42
In `_handle_message`, after the existing `TASK:` check:
43
44
1. Scan the message for `@{nick}` where nick is in the set of active agent names
45
2. If found, call `on_mention(channel, sender_nick, mentioned_nick, message)`
46
3. A message can match both `TASK:` and `@mention` — that's fine, they serve different purposes
47
48
The supervisor registers `on_mention` and calls `agent.send()` with the message content, bypassing the poll loop:
49
50
```python
51
def _on_mention(self, channel: str, sender: str, target_nick: str, message: str) -> None:
52
agent = self.agents.get(target_nick)
53
if agent:
54
asyncio.get_running_loop().create_task(
55
agent.session.send(
56
f"[IRC {channel}] <{sender}> {message}\n\n"
57
"Respond in the appropriate channel."
58
)
59
)
60
```
61
62
### Active nick registry
63
64
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:
65
66
```python
67
# On IrcWatcher
68
def update_active_nicks(self, nicks: set[str]) -> None:
69
self._active_nicks = nicks
70
71
# Supervisor calls this after spawn/kill/shift-change
72
self._watcher.update_active_nicks(set(self.agents.keys()))
73
```
74
75
### Deduplication
76
77
Without guard, an agent could receive the same message twice: once via directed routing, once via the next heartbeat's `read_messages`. Two options:
78
79
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}."
80
2. **Accept duplicates.** Agents are LLMs — seeing a message twice is redundant but not harmful. The simpler option for MVP.
81
82
**Recommendation:** Accept duplicates for MVP. Add dedup later if it becomes noisy.
83
84
## Channels to watch
85
86
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.
87
88
## Scope
89
90
### In scope
91
- Extend `IrcWatcher._handle_message` with nick-mention detection
92
- Add `on_mention` callback to `IrcWatcher.__init__`
93
- Add `update_active_nicks()` method
94
- Wire `Supervisor._on_mention` to call `agent.send()`
95
- Update active nicks on spawn/kill/shift-change
96
- Join `#project-{slug}` in addition to `#standup-{slug}` on startup
97
- Tests for mention detection regex
98
99
### Out of scope
100
- DM (private message) routing — agents only operate in channels for now
101
- Message deduplication — accept duplicates for MVP
102
- Priority/interruption — directed messages queue behind any in-progress `send()`
103
- Rate limiting — a burst of mentions could queue many `send()` calls
104
105
## Files to change
106
107
- `supervisor/src/minsky_supervisor/irc_watcher.py` — add mention detection, `on_mention` callback, `update_active_nicks()`
108
- `supervisor/src/minsky_supervisor/supervisor.py` — wire `_on_mention`, call `update_active_nicks()` after agent lifecycle changes, join `#project-{slug}`
109
- `supervisor/tests/test_irc_watcher.py` — add mention detection tests