Skip to content

Commit 518a67e

Browse files
docs: update skills semantics to eager injection model
Update type comments, docs, and test descriptions to reflect that per-agent skills are eagerly injected into the agent's context at startup rather than filtered for invocation. Sub-agents do not inherit skills from the parent. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 3314a54 commit 518a67e

10 files changed

Lines changed: 27 additions & 25 deletions

File tree

docs/features/custom-agents.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ await using var session = await client.CreateSessionAsync(new SessionConfig
216216
| `prompt` | `string` || System prompt for the agent |
217217
| `mcpServers` | `object` | | MCP server configurations specific to this agent |
218218
| `infer` | `boolean` | | Whether the runtime can auto-select this agent (default: `true`) |
219-
| `skills` | `string[]` | | List of skill names available to this agent |
219+
| `skills` | `string[]` | | Skill names to preload into the agent's context at startup |
220220

221221
> **Tip:** A good `description` helps the runtime match user intent to the right agent. Be specific about the agent's expertise and capabilities.
222222
@@ -228,7 +228,7 @@ In addition to per-agent configuration above, you can set `agent` on the **sessi
228228

229229
## Per-Agent Skills
230230

231-
You can scope skills to individual agents using the `skills` property. Skills are **opt-in**agents have no access to skills by default. The skill names listed in `skills` are resolved from the session-level `skillDirectories`.
231+
You can preload skills into an agent's context using the `skills` property. When specified, the **full content** of each listed skill is eagerly injected into the agent's context at startup — the agent doesn't need to invoke a skill tool; the instructions are already present. Skills are **opt-in**: agents receive no skills by default, and sub-agents do not inherit skills from the parent. Skill names are resolved from the session-level `skillDirectories`.
232232

233233
```typescript
234234
const session = await client.createSession({
@@ -251,7 +251,7 @@ const session = await client.createSession({
251251
});
252252
```
253253

254-
In this example, `security-auditor` can invoke only `security-scan` and `dependency-check`, while `docs-writer` can invoke only `markdown-lint`. An agent without a `skills` field has no access to any skills.
254+
In this example, `security-auditor` starts with `security-scan` and `dependency-check` already injected into its context, while `docs-writer` starts with `markdown-lint`. An agent without a `skills` field receives no skill content.
255255

256256
## Selecting an Agent at Session Creation
257257

docs/features/skills.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ The markdown body contains the instructions that are injected into the session c
316316

317317
### Skills + Custom Agents
318318

319-
Skills can be scoped to individual custom agents using the `skills` property. Skills are **opt-in**agents get no skills by default. Skill names are resolved from the session-level `skillDirectories`.
319+
Skills listed in an agent's `skills` field are **eagerly preloaded**their full content is injected into the agent's context at startup, so the agent has access to the skill instructions immediately without needing to invoke a skill tool. Skill names are resolved from the session-level `skillDirectories`.
320320

321321
```typescript
322322
const session = await client.createSession({
@@ -331,7 +331,7 @@ const session = await client.createSession({
331331
});
332332
```
333333

334-
> **Note:** When `skills` is omitted, the agent has **no** access to skills. This is an opt-in model — you must explicitly list the skills each agent needs.
334+
> **Note:** Skills are opt-in — when `skills` is omitted, no skill content is injected. Sub-agents do not inherit skills from the parent; you must list them explicitly per agent.
335335
336336
### Skills + MCP Servers
337337

dotnet/src/Types.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1517,10 +1517,11 @@ public class CustomAgentConfig
15171517
public bool? Infer { get; set; }
15181518

15191519
/// <summary>
1520-
/// List of skill names available to this agent.
1521-
/// Skills are resolved by name from the session's loaded skill pool (configured via skillDirectories).
1522-
/// When set, only the listed skills can be invoked by this agent.
1523-
/// When omitted, the agent has no access to skills (opt-in model).
1520+
/// List of skill names to preload into this agent's context.
1521+
/// When set, the full content of each listed skill is eagerly injected into
1522+
/// the agent's context at startup. Skills are resolved by name from the
1523+
/// session's configured skill directories (skillDirectories).
1524+
/// When omitted, no skills are injected (opt-in model).
15241525
/// </summary>
15251526
[JsonPropertyName("skills")]
15261527
public List<string>? Skills { get; set; }

dotnet/test/SkillsTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ public async Task Should_Allow_Agent_With_Skills_To_Invoke_Skill()
110110

111111
Assert.Matches(@"^[a-f0-9-]+$", session.SessionId);
112112

113-
// The agent has Skills = ["test-skill"], so it should be able to invoke the skill
113+
// The agent has Skills = ["test-skill"], so the skill content is preloaded into its context
114114
var message = await session.SendAndWaitAsync(new MessageOptions { Prompt = "Say hello briefly using the test skill." });
115115
Assert.NotNull(message);
116116
Assert.Contains(SkillMarker, message!.Data.Content);
@@ -140,7 +140,7 @@ public async Task Should_Not_Provide_Skills_To_Agent_Without_Skills_Field()
140140

141141
Assert.Matches(@"^[a-f0-9-]+$", session.SessionId);
142142

143-
// The agent has no Skills field, so it should NOT have access to skills
143+
// The agent has no Skills field, so no skill content is injected
144144
var message = await session.SendAndWaitAsync(new MessageOptions { Prompt = "Say hello briefly using the test skill." });
145145
Assert.NotNull(message);
146146
Assert.DoesNotContain(SkillMarker, message!.Data.Content);

go/internal/e2e/skills_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ func TestSkills(t *testing.T) {
131131
t.Fatalf("Failed to create session: %v", err)
132132
}
133133

134-
// The agent has Skills: ["test-skill"], so it should be able to invoke the skill
134+
// The agent has Skills: ["test-skill"], so the skill content is preloaded into its context
135135
message, err := session.SendAndWait(t.Context(), copilot.MessageOptions{
136136
Prompt: "Say hello briefly using the test skill.",
137137
})
@@ -168,7 +168,7 @@ func TestSkills(t *testing.T) {
168168
t.Fatalf("Failed to create session: %v", err)
169169
}
170170

171-
// The agent has no Skills field, so it should NOT have access to skills
171+
// The agent has no Skills field, so no skill content is injected
172172
message, err := session.SendAndWait(t.Context(), copilot.MessageOptions{
173173
Prompt: "Say hello briefly using the test skill.",
174174
})

go/types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ type CustomAgentConfig struct {
416416
MCPServers map[string]MCPServerConfig `json:"mcpServers,omitempty"`
417417
// Infer indicates whether the agent should be available for model inference
418418
Infer *bool `json:"infer,omitempty"`
419-
// Skills is the list of skill names available to this agent (opt-in; omit for no skills)
419+
// Skills is the list of skill names to preload into this agent's context at startup (opt-in; omit for none)
420420
Skills []string `json:"skills,omitempty"`
421421
}
422422

nodejs/src/types.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,10 +1007,11 @@ export interface CustomAgentConfig {
10071007
*/
10081008
infer?: boolean;
10091009
/**
1010-
* List of skill names available to this agent.
1011-
* Skills are resolved by name from the session's loaded skill pool (configured via `skillDirectories`).
1012-
* When set, only the listed skills can be invoked by this agent.
1013-
* When omitted, the agent has no access to skills (opt-in model).
1010+
* List of skill names to preload into this agent's context.
1011+
* When set, the full content of each listed skill is eagerly injected into
1012+
* the agent's context at startup. Skills are resolved by name from the
1013+
* session's configured skill directories (`skillDirectories`).
1014+
* When omitted, no skills are injected (opt-in model).
10141015
*/
10151016
skills?: string[];
10161017
}

nodejs/test/e2e/skills.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ IMPORTANT: You MUST include the exact text "${SKILL_MARKER}" somewhere in EVERY
112112

113113
expect(session.sessionId).toBeDefined();
114114

115-
// The agent has skills: ["test-skill"], so it should be able to invoke the skill
115+
// The agent has skills: ["test-skill"], so the skill content is preloaded into its context
116116
const message = await session.sendAndWait({
117117
prompt: "Say hello briefly using the test skill.",
118118
});
@@ -140,7 +140,7 @@ IMPORTANT: You MUST include the exact text "${SKILL_MARKER}" somewhere in EVERY
140140

141141
expect(session.sessionId).toBeDefined();
142142

143-
// The agent has no skills field, so it should NOT have access to skills
143+
// The agent has no skills field, so no skill content is injected
144144
const message = await session.sendAndWait({
145145
prompt: "Say hello briefly using the test skill.",
146146
});

python/copilot/session.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -748,7 +748,7 @@ class CustomAgentConfig(TypedDict, total=False):
748748
# MCP servers specific to agent
749749
mcp_servers: NotRequired[dict[str, MCPServerConfig]]
750750
infer: NotRequired[bool] # Whether agent is available for model inference
751-
# List of skill names available to this agent (opt-in; omit for no skills)
751+
# Skill names to preload into this agent's context at startup (opt-in; omit for none)
752752
skills: NotRequired[list[str]]
753753

754754

python/e2e/test_skills.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ async def test_should_not_apply_skill_when_disabled_via_disabledskills(
8989
await session.disconnect()
9090

9191
async def test_should_allow_agent_with_skills_to_invoke_skill(self, ctx: E2ETestContext):
92-
"""Test that an agent with skills can invoke the specified skill"""
92+
"""Test that an agent with skills gets skill content preloaded into context"""
9393
skills_dir = create_skill_dir(ctx.work_dir)
9494
custom_agents: list[CustomAgentConfig] = [
9595
{
@@ -108,7 +108,7 @@ async def test_should_allow_agent_with_skills_to_invoke_skill(self, ctx: E2ETest
108108

109109
assert session.session_id is not None
110110

111-
# The agent has skills: ["test-skill"], so it should be able to invoke the skill
111+
# The agent has skills: ["test-skill"], so the skill content is preloaded into its context
112112
message = await session.send_and_wait("Say hello briefly using the test skill.")
113113
assert message is not None
114114
assert SKILL_MARKER in message.data.content
@@ -118,7 +118,7 @@ async def test_should_allow_agent_with_skills_to_invoke_skill(self, ctx: E2ETest
118118
async def test_should_not_provide_skills_to_agent_without_skills_field(
119119
self, ctx: E2ETestContext
120120
):
121-
"""Test that an agent without skills field gets no skills (opt-in model)"""
121+
"""Test that an agent without skills field gets no skill content (opt-in model)"""
122122
skills_dir = create_skill_dir(ctx.work_dir)
123123
custom_agents: list[CustomAgentConfig] = [
124124
{
@@ -136,7 +136,7 @@ async def test_should_not_provide_skills_to_agent_without_skills_field(
136136

137137
assert session.session_id is not None
138138

139-
# The agent has no skills field, so it should NOT have access to skills
139+
# The agent has no skills field, so no skill content is injected
140140
message = await session.send_and_wait("Say hello briefly using the test skill.")
141141
assert message is not None
142142
assert SKILL_MARKER not in message.data.content

0 commit comments

Comments
 (0)