Skip to content

feat(chat): @-mention picker for agents, MCPs, and apps #115

Description

@sanjitkverma

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

  • Backend pytest covers agent task service, MCP extra-configs loader, and the new call_agent tool.
  • Frontend typecheck passes.
  • Type @, 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; expand call_agent tool call to see sub-agent panel; click 'View full trajectory' loads hidden sub-chat with breadcrumb.
  • Sub-chat does not appear in the chat sidebar.
  • @my-app run report action — agent calls invoke_app_action with the right app instance id from the hint block.
  • Negative: hand-craft mentions[].ref_id for an MCP the user does not own — worker logs skip, agent does not get the tool.
  • Negative: inspect sub-agent's tool registry — call_agent is not present (proves depth-1 is structural).
  • No regression: legacy chat send paths (channels, schedules, external_agent) work unchanged.

Metadata

Metadata

Assignees

No one assigned

    Labels

    agentsAI agent system issuesenhancementNew feature or requestfrontendReact frontend issuesopensourceSelf-contained issue suitable for external contributors

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions