Add first-class model and tools fields to AgentCustomization#289
Merged
Conversation
A custom agent carries two pieces of behavior that `AgentCustomization` couldn't represent natively: a model override and a tool scope. Without native fields, a host had to drop them or stuff them into `_meta` (the agent host currently routes `session.custom_agents_updated` — which carries both — through `_meta.copilot.customAgents`). Both are general agent concepts, so they get first-class fields, matching the spec's established style for `RuleCustomization` (`alwaysApply`, `globs`). Add two optional fields to `AgentCustomization`: - `model?: string` — the model the agent is pinned to; absent means the agent inherits the session's default model. - `tools?: string[]` — an allowlist of tool names the agent is scoped to; absent means no restriction beyond the session default. The shapes mirror the upstream discovery payload (the runtime's `CustomAgentsUpdatedAgent` reports `model: string` and a list of tool names per agent). Both are optional, so existing producers and consumers are unaffected until they opt in. Regenerates the schema and all five client mirrors, updates the customizations guide, adds a cross-language round-trip conformance fixture exercising both fields, and lands CHANGELOG entries across the spec and every client. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR extends the protocol’s AgentCustomization type with two new optional, first-class fields—model?: string and tools?: string[]—so model pinning and tool allowlisting no longer need to be carried via _meta. The change is propagated through the JSON Schemas, all client mirrors, documentation, a new round-trip conformance fixture, and the relevant CHANGELOGs.
Changes:
- Added optional
modelandtoolsfields toAgentCustomizationin the TypeScript source-of-truth, then regenerated schemas and client mirrors. - Documented the new fields in the customizations guide.
- Added a cross-language round-trip fixture to ensure the fields survive decode/re-encode through the typed
ChildCustomizationunion.
Show a summary per file
| File | Description |
|---|---|
| types/test-cases/round-trips/027-agent-customization-model-and-tools.json | New round-trip fixture exercising model + tools on an AgentCustomization child. |
| types/channels-session/state.ts | Adds model?: string and tools?: string[] to AgentCustomization with JSDoc semantics. |
| schema/state.schema.json | Regenerated schema including the new AgentCustomization fields. |
| schema/notifications.schema.json | Regenerated schema including the new AgentCustomization fields. |
| schema/errors.schema.json | Regenerated schema including the new AgentCustomization fields. |
| schema/commands.schema.json | Regenerated schema including the new AgentCustomization fields. |
| schema/actions.schema.json | Regenerated schema including the new AgentCustomization fields. |
| docs/guide/customizations.md | Updates the child-type metadata table to include model? and tools? for agents. |
| clients/typescript/CHANGELOG.md | Adds Unreleased entry for the new AgentCustomization fields. |
| clients/swift/CHANGELOG.md | Adds Unreleased entry for the new AgentCustomization fields. |
| clients/swift/AgentHostProtocol/Sources/AgentHostProtocol/Generated/State.generated.swift | Regenerated Swift types including model and tools on AgentCustomization. |
| clients/rust/crates/ahp-types/src/state.rs | Regenerated Rust types including model and tools on AgentCustomization. |
| clients/rust/CHANGELOG.md | Adds Unreleased entry for the new AgentCustomization fields. |
| clients/kotlin/src/main/kotlin/com/microsoft/agenthostprotocol/generated/State.generated.kt | Regenerated Kotlin types including model and tools on AgentCustomization. |
| clients/kotlin/CHANGELOG.md | Adds Unreleased entry for the new AgentCustomization fields. |
| clients/go/CHANGELOG.md | Adds Unreleased entry for the new AgentCustomization fields. |
| clients/go/ahptypes/state.generated.go | Regenerated Go types including Model and Tools on AgentCustomization. |
| CHANGELOG.md | Adds Unreleased spec entry for the new AgentCustomization fields. |
Review details
- Files reviewed: 16/18 changed files
- Comments generated: 1
- Review effort level: Low
PR review flagged that an optional array serialized with Go's `omitempty` cannot distinguish an explicit empty list from absence, which could silently flip "no tools allowed" into "no restriction". Optional arrays diverge on `[]` across the generated clients by design (Go drops it via `omitempty`; Rust/Swift/Kotlin/TS preserve it), and this is the uniform, spec-wide convention for every optional array — the spec has no array-length constraints anywhere and encodes semantics in prose. The grounding producer never emits a meaningful empty list either: a wildcard `["*"]` resolves to "unset", so the only states it produces are absent (no restriction) or a non-empty allowlist. Rather than special-case this one field (a Go-only pointer would break the convention; `minItems` has no precedent and the generators don't emit it), define empty as equivalent to absent in the JSDoc. That makes the cross-client `[]` divergence semantically irrelevant — both encode "no restriction" — and Go's `omitempty` correct by definition. Regenerates the schema and all client mirrors so the clarified contract is mirrored everywhere. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
connor4312
approved these changes
Jun 30, 2026
Yoyokrazy
approved these changes
Jun 30, 2026
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.
Summary
Closes #286.
A custom agent carries two pieces of behavior that
AgentCustomizationcouldn't represent natively: a model override and a tool scope. With no native field for either, a host had to drop them or stuff them into_meta. Both are general agent-host concepts, not vendor-specific residue, so they get first-class fields — matching the spec's established style forRuleCustomization(alwaysApply,globs) rather than_meta.Adds two optional fields to
AgentCustomization:model?: string— the model the agent is pinned to. Absent ⇒ inherit the session's default model.tools?: string[]— an allowlist of tool names the agent is scoped to. Absent ⇒ no restriction beyond the session default.Both are optional, so existing producers and consumers are unaffected until they opt in.
Validation (everything verified against the source, not the issue text)
AgentCustomizationcarried onlydescription?+_meta?, and thatRuleCustomizationalready models behavior with first-class type-specific fields.copilot-agent-runtime: thesession.custom_agents_updatedpayload'sCustomAgentsUpdatedAgentreportsmodel: string("Model override for this agent, if set") andtools: string[] | null("List of tool names available to this agent, or null when all tools are available"), alongsideuserInvocable. Sostringandstring[]are the real upstream shapes — no speculative structured allow/deny scope was warranted.session.custom_agents_updatedthrough_meta.copilot.customAgentsbecause there's no native field. The issue's premise holds.Changes
types/channels-session/state.ts— the two new optional fields with JSDoc.docs/guide/customizations.md— updated the child-type metadata table.027-agent-customization-model-and-tools.jsonexercising both fields through the typedChildCustomizationunion.types/**change ripples to all six).Tests
Ran each client's suite against the regenerated types + new fixture — all green, including the round-trip corpus in every language:
go test ./...✅cargo test✅swift test(107 tests) ✅./gradlew test(JDK 17) ✅ — fixture027-...explicitly PASSEDnpm test(59 tests) ✅npm run typecheck && lint && verify:release-metadata && verify:changelog✅ (266 type/reducer tests pass directly; thec8coverage wrapper is broken under Node v26 in this environment, unrelated to this change)The protocol version was intentionally not bumped.