Summary
Add an @ mention autocomplete inside the chat input that lets a user reference, per-turn:
- Agents in their library —
@agent-slug runs that agent stateless on this turn and returns its output to the calling agent.
- MCPs —
@notion enables the Notion MCP for this turn only (not persisted on the agent record).
- Apps they have installed —
@my-app plants the app's slug + instance id + action list into the calling agent's context as a hint, so the agent calls invoke_app_action against the right instance.
A unified dropup above the textarea shows four sections (Agents, Apps, Connectors, Files) with theme-aware colored accents per kind. Disabled items render greyed but selectable. Works in light and dark mode via existing CSS variables.
The ChatInput component is shared between the agent chat page and the builder workspace surface, so one implementation covers both.
Wire format
Mentions ride alongside message as structured metadata, not embedded regex tokens:
interface ChatMention {
kind: 'agent' | 'mcp' | 'app';
ref_id: string; // marketplace agent id | user MCP config id | app instance id
display: string; // human token e.g. '@coworker'
offset: number; // position in message
}
interface AgentChatRequest {
// ...existing...
mentions?: ChatMention[];
}
The agent task payload gets three optional list fields plus parent_task_id, all defaulted, so legacy callers (channels, schedules, external_agent, automations) are unaffected.
Behavior at run time
@app — context hint, no tool change. Lean system-prompt block per app: slug, instance_id, action names + 1-line descriptions. Schemas pulled lazily by invoke_app_action. Per-app cost ~30-60 tokens.
@mcp — tool injection for this turn. A new MCP manager helper loads the user's MCP configs by id, validates ownership + is_active=True, runs the existing discovery path, registers tools into the run's tool registry. Deduped against MCPs already assigned to the agent so we don't pay schema tokens twice.
@agent — stateless sub-call. New call_agent tool, only registered when mention_agent_ids is non-empty. Validates the requested agent_id is in the authorized list, creates a hidden disposable chat tagged with parent_task_id and a new is_subagent_run=true flag, dispatches a fresh task with chat_history=[] and mention_agent_ids=[] (depth-1 cap is structural — sub-agent never gets the tool, so recursion is impossible). Returns { ok, output, duration_seconds, agent_slug, sub_chat_id }.
Sub-agent drill-in (in scope)
The hidden disposable sub-chats are reachable via the existing 'expand tool call' button on the parent's call_agent step:
- Expanded panel shows sub-agent name, input, output, duration, step count.
- 'View full trajectory' link routes to the sub-chat detail page; only chat list queries filter
is_subagent_run.
- Sub-chat detail page renders a 'Sub-agent run from · ' breadcrumb for bidirectional navigation.
Sub-runs do not appear in the chat sidebar.
Context-budget impact
| Mention |
System prompt |
Tool registry |
Transcript |
@app |
Slug + instance id + action names + 1-line descs (~30-60 tok/app) |
unchanged |
nothing until invoked |
@mcp |
nothing |
MCP tool schemas (varies by server, deduped vs already-assigned) |
nothing until invoked |
@agent |
call_agent description + authorized agent slugs (~100-200 tok total) |
+1 tool: call_agent |
sub-agent's final output verbatim |
call_agent tool is conditionally registered — plain chats see zero added tokens.
Schema changes
- Agent task payload: add
mention_agent_ids, mention_mcp_config_ids, mention_app_instance_ids, parent_task_id (all optional/defaulted).
- Chat model: add
parent_task_id (nullable UUID) and is_subagent_run (bool default false). Alembic migration. All chat-list queries filter is_subagent_run = false; chat-detail endpoint surfaces parent_task_id + resolved parent_chat_id for the breadcrumb.
Out of scope (v1)
- Rich chip rendering inside the textarea (would require swapping textarea for contenteditable).
- Streaming sub-agent steps live into the parent UI (drill-in to completed sub-chat is in scope).
- Depth > 1 delegation. Sub-agent does not get
call_agent registered, so it cannot delegate further. Structural, not numeric.
- A separate 'Sub-runs' sidebar filter view. Drill-in covers v1 audit needs.
Test plan
Summary
Add an
@mention autocomplete inside the chat input that lets a user reference, per-turn:@agent-slugruns that agent stateless on this turn and returns its output to the calling agent.@notionenables the Notion MCP for this turn only (not persisted on the agent record).@my-appplants the app's slug + instance id + action list into the calling agent's context as a hint, so the agent callsinvoke_app_actionagainst the right instance.A unified dropup above the textarea shows four sections (Agents, Apps, Connectors, Files) with theme-aware colored accents per kind. Disabled items render greyed but selectable. Works in light and dark mode via existing CSS variables.
The
ChatInputcomponent is shared between the agent chat page and the builder workspace surface, so one implementation covers both.Wire format
Mentions ride alongside
messageas structured metadata, not embedded regex tokens:The agent task payload gets three optional list fields plus
parent_task_id, all defaulted, so legacy callers (channels, schedules, external_agent, automations) are unaffected.Behavior at run time
@app— context hint, no tool change. Lean system-prompt block per app:slug,instance_id, action names + 1-line descriptions. Schemas pulled lazily byinvoke_app_action. Per-app cost ~30-60 tokens.@mcp— tool injection for this turn. A new MCP manager helper loads the user's MCP configs by id, validates ownership +is_active=True, runs the existing discovery path, registers tools into the run's tool registry. Deduped against MCPs already assigned to the agent so we don't pay schema tokens twice.@agent— stateless sub-call. Newcall_agenttool, only registered whenmention_agent_idsis non-empty. Validates the requested agent_id is in the authorized list, creates a hidden disposable chat tagged withparent_task_idand a newis_subagent_run=trueflag, dispatches a fresh task withchat_history=[]andmention_agent_ids=[](depth-1 cap is structural — sub-agent never gets the tool, so recursion is impossible). Returns{ ok, output, duration_seconds, agent_slug, sub_chat_id }.Sub-agent drill-in (in scope)
The hidden disposable sub-chats are reachable via the existing 'expand tool call' button on the parent's
call_agentstep:is_subagent_run.Sub-runs do not appear in the chat sidebar.
Context-budget impact
@app@mcp@agentcall_agentdescription + authorized agent slugs (~100-200 tok total)+1tool:call_agentcall_agenttool is conditionally registered — plain chats see zero added tokens.Schema changes
mention_agent_ids,mention_mcp_config_ids,mention_app_instance_ids,parent_task_id(all optional/defaulted).parent_task_id(nullable UUID) andis_subagent_run(bool default false). Alembic migration. All chat-list queries filteris_subagent_run = false; chat-detail endpoint surfacesparent_task_id+ resolvedparent_chat_idfor the breadcrumb.Out of scope (v1)
call_agentregistered, so it cannot delegate further. Structural, not numeric.Test plan
call_agenttool.@, verify 4-section dropup with light/dark theme parity and disabled items greyed.@notion summarise <url>— worker logs Notion tool registered for that run only.@coworker what's in your README?— sub-agent reply included; expandcall_agenttool call to see sub-agent panel; click 'View full trajectory' loads hidden sub-chat with breadcrumb.@my-app run report action— agent callsinvoke_app_actionwith the right app instance id from the hint block.mentions[].ref_idfor an MCP the user does not own — worker logs skip, agent does not get the tool.call_agentis not present (proves depth-1 is structural).