Summary
When a user invokes /agent <name> to switch to a custom agent at the session root, no dedicated event is emitted. The only signal that an agent switch occurred is the <agent_instructions> block silently prepended to user.message.data.transformedContent. This makes it difficult for downstream tooling to reliably track which custom agent is active.
Current behavior
/agent review-pr-comments rewrites the next user message by prepending <agent_instructions>...</agent_instructions> into transformedContent
- There is no
session.agent_changed or session.agent_invoked event in events.jsonl
subagent.selected exists but fires rarely (21 events across 920+ sessions in one user's history) and doesn't appear to be the canonical marker for root-level /agent switches
session.mode_changed and session.model_change exist for other session-level state changes, but agents have no equivalent
Impact
- Session analytics: Tools like copilot-session-tools that enrich
events.jsonl into queryable databases cannot reliably count or filter by which custom agent was used. The only workaround is parsing <agent_instructions> blocks out of transformedContent, which is fragile and undocumented.
- Skill auditing: When auditing skill/agent effectiveness, the lack of a clean event means agent usage counts are dramatically undercounted (in one audit,
review-pr-comments showed 0 dispatches via task tool but actually had 390 root-level invocations hidden in transformedContent).
- Observability: Users and platform teams can't answer "which custom agents are actually being used?" without deep parsing of message content.
Proposed solution
Emit a new event when /agent <name> is invoked at the session root:
{"type": "session.agent_invoked", "agentName": "review-pr-comments", "agentSource": "user", "timestamp": "..."}
Fields:
agentName: The agent identifier as it appears in the .agent.md filename
agentSource: "user" for custom agents in ~/.copilot/agents/, "project" for repo-level agents, "builtin" for platform agents
This would parallel the existing session.mode_changed and session.model_change events.
Workaround (current)
Parse transformedContent for <agent_instructions> blocks and extract the agent name — but this is fragile, undocumented, and requires access to the full message payload rather than just the event stream.
Summary
When a user invokes
/agent <name>to switch to a custom agent at the session root, no dedicated event is emitted. The only signal that an agent switch occurred is the<agent_instructions>block silently prepended touser.message.data.transformedContent. This makes it difficult for downstream tooling to reliably track which custom agent is active.Current behavior
/agent review-pr-commentsrewrites the next user message by prepending<agent_instructions>...</agent_instructions>intotransformedContentsession.agent_changedorsession.agent_invokedevent inevents.jsonlsubagent.selectedexists but fires rarely (21 events across 920+ sessions in one user's history) and doesn't appear to be the canonical marker for root-level/agentswitchessession.mode_changedandsession.model_changeexist for other session-level state changes, but agents have no equivalentImpact
events.jsonlinto queryable databases cannot reliably count or filter by which custom agent was used. The only workaround is parsing<agent_instructions>blocks out oftransformedContent, which is fragile and undocumented.review-pr-commentsshowed 0 dispatches viatasktool but actually had 390 root-level invocations hidden intransformedContent).Proposed solution
Emit a new event when
/agent <name>is invoked at the session root:{"type": "session.agent_invoked", "agentName": "review-pr-comments", "agentSource": "user", "timestamp": "..."}Fields:
agentName: The agent identifier as it appears in the.agent.mdfilenameagentSource:"user"for custom agents in~/.copilot/agents/,"project"for repo-level agents,"builtin"for platform agentsThis would parallel the existing
session.mode_changedandsession.model_changeevents.Workaround (current)
Parse
transformedContentfor<agent_instructions>blocks and extract the agent name — but this is fragile, undocumented, and requires access to the full message payload rather than just the event stream.