feat(chat): always-on system context manual injected into every agent dispatch#295
Conversation
…ispatch Adds agent_manual.py with build_manual() — a pure function that returns a per-agent operating manual (≤500 tokens) covering @-mention routing, project task verbs, and lead/non-lead status. The router prepends it as role:system before the conversation history on every chat dispatch so agents always know the channel primitives without per-session briefs.
📝 WalkthroughWalkthroughA new Changes
Sequence DiagramsequenceDiagram
actor User
participant Router as AgentChatRouter
participant ManualBuilder as build_manual()
participant Context as Message Context
participant Queue as bridge.enqueue_user_message
User->>Router: sends message in channel
Router->>ManualBuilder: invoke for each recipient<br/>(channel, agent_name, leads)
ManualBuilder->>ManualBuilder: analyze channel type & role
ManualBuilder-->>Router: return agent-specific manual text
Router->>Context: prepend {"role": "system",<br/>"content": manual} to context
Router->>Queue: enqueue message with<br/>per-recipient agent_context
Queue-->>User: message routed to recipient<br/>with context-injected instructions
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Review rate limit: 9/10 reviews remaining, refill in 6 minutes. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@tinyagentos/agent_chat_router.py`:
- Around line 171-172: The current code unconditionally prepends
build_manual(channel, agent_name, leads) into agent_context which injects
channel-routing and visibility rules into threaded deliveries; modify the logic
where agent_context is built (the lines that set manual_text and agent_context)
to detect when thread_id is set and either (a) call a new thread-specific manual
generator (e.g., build_thread_manual(channel, agent_name, leads, thread_id))
that omits or replaces routing/visibility sections, or (b) call build_manual
with a flag (e.g., build_manual(..., for_thread=True)) so it suppresses
routing/visibility content for threads; ensure resolve_thread_recipients()
remains the single source of truth for visibility when thread_id is present and
use that branch to produce the appropriate manual_text or no routing text at all
before composing agent_context.
In `@tinyagentos/agent_manual.py`:
- Around line 97-122: build_manual currently ignores settings["response_mode"]
and always injects _SECTION_MENTIONS for group/topic channels; update
build_manual to read response_mode = settings.get("response_mode", "lively") (or
similar) and branch when channel_type in ("group","topic") to append the correct
mention/visibility copy based on response_mode (e.g., append
_SECTION_MENTIONS_QUIET when response_mode == "quiet" and append the existing
_SECTION_MENTIONS or a new _SECTION_MENTIONS_LIVELY when response_mode ==
"lively"), leaving DM and A2A logic (project_id/is_a2a, _SECTION_TASK_VERBS,
_SECTION_LEAD_IS/_SECTION_NON_LEAD, _SECTION_QUICK_REF) unchanged; add any new
constants for the alternative text if needed and ensure references use settings
and response_mode so the manual matches the router behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 9cb34d8f-86ed-4fb5-8c84-4da8cf98e60c
📒 Files selected for processing (4)
tests/test_agent_chat_router.pytests/test_agent_manual.pytinyagentos/agent_chat_router.pytinyagentos/agent_manual.py
| manual_text = build_manual(channel, agent_name, leads) | ||
| agent_context = [{"role": "system", "content": manual_text}, *context] |
There was a problem hiding this comment.
Avoid injecting the channel-routing manual into thread deliveries as-is.
When thread_id is set, Lines 54-58 route through resolve_thread_recipients() instead of the normal channel quiet/lively rules. Prepending the same manual here means a threaded recipient can be told they receive every channel message or only @-mentions, even though visibility is now governed by the thread resolver. Please add a thread-specific manual branch, or suppress the routing/visibility sections for threaded dispatches.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tinyagentos/agent_chat_router.py` around lines 171 - 172, The current code
unconditionally prepends build_manual(channel, agent_name, leads) into
agent_context which injects channel-routing and visibility rules into threaded
deliveries; modify the logic where agent_context is built (the lines that set
manual_text and agent_context) to detect when thread_id is set and either (a)
call a new thread-specific manual generator (e.g., build_thread_manual(channel,
agent_name, leads, thread_id)) that omits or replaces routing/visibility
sections, or (b) call build_manual with a flag (e.g., build_manual(...,
for_thread=True)) so it suppresses routing/visibility content for threads;
ensure resolve_thread_recipients() remains the single source of truth for
visibility when thread_id is present and use that branch to produce the
appropriate manual_text or no routing text at all before composing
agent_context.
| channel_type = channel.get("type") or "" | ||
| project_id = channel.get("project_id") | ||
| settings = channel.get("settings") or {} | ||
| is_a2a = settings.get("kind") == "a2a" | ||
|
|
||
| # DM: header + stub only. | ||
| if channel_type == "dm": | ||
| return _HEADER + _DM_STUB | ||
|
|
||
| # Group / topic channels. | ||
| parts = [_HEADER] | ||
|
|
||
| # Section 1: @-mention routing (group and topic channels). | ||
| if channel_type in ("group", "topic"): | ||
| parts.append(_SECTION_MENTIONS) | ||
|
|
||
| # Sections 2 + 3: project a2a only. | ||
| if project_id and is_a2a: | ||
| parts.append(_SECTION_TASK_VERBS) | ||
| if agent_name in leads: | ||
| parts.append(_SECTION_LEAD_IS) | ||
| else: | ||
| parts.append(_SECTION_NON_LEAD) | ||
|
|
||
| # Section 4: always for non-DM. | ||
| parts.append(_SECTION_QUICK_REF) |
There was a problem hiding this comment.
Make the manual respect response_mode.
build_manual() never looks at settings["response_mode"], but Line 67 and Lines 96-99 in tinyagentos/agent_chat_router.py still fan lively channels out to every non-muted member. In a lively group/A2A channel this manual can tell agents “A reply with no @-tag reaches NO ONE” and “You only see messages where you're explicitly @-tagged” even though the router does the opposite. Please branch the mention/visibility copy on quiet vs lively mode before injecting it.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tinyagentos/agent_manual.py` around lines 97 - 122, build_manual currently
ignores settings["response_mode"] and always injects _SECTION_MENTIONS for
group/topic channels; update build_manual to read response_mode =
settings.get("response_mode", "lively") (or similar) and branch when
channel_type in ("group","topic") to append the correct mention/visibility copy
based on response_mode (e.g., append _SECTION_MENTIONS_QUIET when response_mode
== "quiet" and append the existing _SECTION_MENTIONS or a new
_SECTION_MENTIONS_LIVELY when response_mode == "lively"), leaving DM and A2A
logic (project_id/is_a2a, _SECTION_TASK_VERBS,
_SECTION_LEAD_IS/_SECTION_NON_LEAD, _SECTION_QUICK_REF) unchanged; add any new
constants for the alternative text if needed and ensure references use settings
and response_mode so the manual matches the router behavior.
Summary
tinyagentos/agent_manual.pywithbuild_manual(channel, agent_name, leads) -> str— a pure function, no IO, no globals beyond constant strings. Returns a markdown operating manual covering @-mention routing, project kanban verbs, and lead/non-lead status, branched per channel type and agent role.agent_name in leads.role: "system"atcontext[0]inagent_chat_router.py(afterbuild_context_window, before the per-recipient loop). Built per-agent so lead vs non-lead text is correct for each recipient.tests/test_agent_manual.pyand 3 new end-to-end tests intests/test_agent_chat_router.py. All 49 tests in both files pass. 199 tests pass across the wider suite (1 unrelated disk-full environmental error intest_routes_beads.py).Token count
Longest branch (project a2a, lead agent): 417 estimated tokens (words × 1.3 heuristic; 1869 chars, 321 words). Well within the 500-token budget.
Hook point
tinyagentos/agent_chat_router.pylines 145–170 (aftercontext = build_context_window(...), inside the per-recipientfor agent_name in recipients:loop, beforebridge.enqueue_user_message). Theleadslist is extracted fromsettings.get("leads")once before the loop;build_manualis called per-agent so each agent gets the correct lead/non-lead branch.No conflicts with in-flight PRs
feat/taos-assistant) — touchesdesktop/src/,tinyagentos/routes/taos_agent.py, and docs. No overlap.feat/image-gen-lifecycle-fixes) — touchestinyagentos/routes/models.pyandtinyagentos/services/rknn_sd_server.py. No overlap.Test plan
pytest tests/test_agent_manual.py— 24 tests covering all channel type branches, empty leads list, token budgetpytest tests/test_agent_chat_router.py— all pre-existing tests pass; 3 new e2e tests verify manual injected at context[0] with correct lead/non-lead textbuild_manualis a pure function (no IO, no side effects)role: "system"in routerSummary by CodeRabbit