Render copilot skill invocations as a tool call#312557
Merged
roblourens merged 2 commits intomainfrom Apr 26, 2026
Merged
Conversation
The Copilot SDK now exposes a skill tool that the model uses to load SKILL.md files. Until now this surfaced in the agent host as the default tool UI, which doesn't fit. This change does three things: - Hide the raw `skill` tool call (added to HIDDEN_TOOL_NAMES). - Synthesize a `tool_start`/`tool_complete` pair from the SDK's `skill.invoked` lifecycle event so we can show 'Reading [name]' with a link to the skill file. Wired in both the live event path and the history-replay path so reconnect renders identically. - Filter out the synthetic `user.message` events the SDK sometimes injects with skill content (data.source !== 'user'). Client side, the chat adapter now tags markdown links pointing at a SKILL.md file with `?vscodeLinkType=skill` so they render as the inline skill pill instead of a plain markdown anchor. Doing this on the client (rather than at the agent host) keeps the agent host host-agnostic and also upgrades older sessions and other providers that emit SKILL.md links. The link rewriter also now preserves the original link text rather than always collapsing to `[](newHref)`, so a skill named 'plan' shows up as 'Reading [plan]' rather than 'Reading [SKILL.md]'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates the Copilot agent-host and client rendering pipeline to treat Copilot SDK “skill” usage as a first-class tool-style progress entry (“Reading skill …”), including clickable links to SKILL.md, and improves markdown link rewriting so original link text is preserved and skill links are tagged for richer rendering.
Changes:
- Hide raw
skilltool calls and synthesizetool_start/tool_completeevents fromskill.invoked(live + replay). - Filter SDK-injected
user.messageevents (e.g. skill content injection) using a newsourcepredicate. - Preserve markdown link text during rewrite and append
vscodeLinkType=skillforSKILL.mdlinks; update tests accordingly.
Show a summary per file
| File | Description |
|---|---|
| src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/stateToProgressAdapter.ts | Preserves rewritten link labels and tags SKILL.md links with vscodeLinkType=skill. |
| src/vs/workbench/contrib/chat/test/browser/agentSessions/stateToProgressAdapter.test.ts | Updates expectations for preserved link text in rewritten markdown. |
| src/vs/platform/agentHost/node/copilot/copilotToolDisplay.ts | Adds hidden skill tool and synthesizes tool events for skill.invoked. |
| src/vs/platform/agentHost/node/copilot/mapSessionEvents.ts | Filters synthetic user messages and maps skill.invoked into synthesized tool events during replay. |
| src/vs/platform/agentHost/node/copilot/copilotAgentSession.ts | Emits synthesized skill tool events on the live event stream. |
| src/vs/platform/agentHost/test/node/copilotToolDisplay.test.ts | Adds unit coverage for hidden tool + synthesized skill tool event output. |
| src/vs/platform/agentHost/test/node/mapSessionEvents.test.ts | Adds replay-path coverage for skill synthesis and synthetic user message filtering. |
Copilot's findings
- Files reviewed: 7/7 changed files
- Comments generated: 3
- Add escapeMarkdownLinkLabel helper in htmlContent.ts: only escapes
'\' and ']' so link labels rendered without re-parsing markdown
(e.g. the chat skill pill) don't show literal backslashes for safe
characters like '-' or '.'.
- Use it in synthesizeSkillToolEvents and stateToProgressAdapter's
rewriteLinkTokenRaw instead of the over-eager escapeMarkdownSyntaxTokens.
- Hash the path/name fallback in getSkillSyntheticToolCallId so the
synthesized toolCallId never contains '/' (which breaks
ChatResponseResource.createUri paths).
- Update copilotToolDisplay/mapSessionEvents test snapshots to match
the actual emitted strings ('Reading skill [plan](...)' /
'Read skill [plan](...)') and the new hash-based fallback id.
- Add htmlContent unit tests for escapeMarkdownLinkLabel.
(Written by Copilot)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
bhavyaus
approved these changes
Apr 26, 2026
roblourens
added a commit
that referenced
this pull request
Apr 26, 2026
* Restore empty link text for non-skill agent-host links PR #312557 changed all agent-host link rewriting to preserve the original markdown label, which made every read-tool link render as a plain blue markdown link instead of the rich inline anchor / file widget. Only skill links need a non-empty label (so the skill pill can show the skill name); restore the empty-label behavior for everything else. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address Copilot review: preserve image alt text, add skill/image tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The Copilot SDK now exposes a
skilltool that the model uses to loadSKILL.mdfiles. Until now this surfaced in the agent host as the default tool UI, which doesn't fit. This PR replaces it with a proper "Reading skill" entry that links to the skill file.(Written by Copilot)
Agent host (
src/vs/platform/agentHost)skilltool call by addingSkilltoHIDDEN_TOOL_NAMES.tool_start/tool_completepair from the SDK'sskill.invokedlifecycle event. The synth helper lives incopilotToolDisplay.tsand is called from both:copilotAgentSession.ts(_subscribeToEvents), andmapSessionEvents.ts,so reconnect/replay renders identically. The synthetic
toolCallIdissynth-skill-<eventId | path | name>for stability across replay.user.messageevents the SDK sometimes injects with skill content. NewisSyntheticUserMessagepredicate keys offdata.source(skill-injected messages havesource: "skill-<name>"; real user messages either lacksourceor set it to"user").Client side (
stateToProgressAdapter.ts)[](newHref). It now preserves the original link text, so a skill namedplanshows up asReading [plan]instead ofReading [SKILL.md].isSkillFileUricheck: when a rewritten link points at aSKILL.mdbasename, append?vscodeLinkType=skillso the chat renderer's inline anchor widget shows the rich skill pill rather than a plain markdown anchor. Doing this client-side (rather than at the agent host) keeps the host VS-Code-agnostic and automatically upgrades older sessions and other providers that emitSKILL.mdlinks.Tests
skill eventssuites incopilotToolDisplay.test.tsandmapSessionEvents.test.tscovering hidden tool, synth output (with-path and no-path), synthetic user message filtering, and stable toolCallId.stateToProgressAdapter.test.tssnapshots to expect preserved link text and thevscodeLinkType=skilltagging.All 118 affected tests green; type-check clean.