Skip to content

Add first-class model and tools fields to AgentCustomization#289

Merged
connor4312 merged 2 commits into
mainfrom
colby-agent-customization-model-tools
Jun 30, 2026
Merged

Add first-class model and tools fields to AgentCustomization#289
connor4312 merged 2 commits into
mainfrom
colby-agent-customization-model-tools

Conversation

@colbylwilliams

Copy link
Copy Markdown
Member

Summary

Closes #286.

A custom agent carries two pieces of behavior that AgentCustomization couldn'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 for RuleCustomization (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)

  • Spec premise — Confirmed AgentCustomization carried only description? + _meta?, and that RuleCustomization already models behavior with first-class type-specific fields.
  • Vendor grounding — Confirmed in copilot-agent-runtime: the session.custom_agents_updated payload's CustomAgentsUpdatedAgent reports model: string ("Model override for this agent, if set") and tools: string[] | null ("List of tool names available to this agent, or null when all tools are available"), alongside userInvocable. So string and string[] are the real upstream shapes — no speculative structured allow/deny scope was warranted.
  • Carrier premise — Confirmed the AHP agent host currently routes session.custom_agents_updated through _meta.copilot.customAgents because there's no native field. The issue's premise holds.
  • Reducers — Customizations are stored wholesale via the existing upsert actions, so the new optional fields pass through with no reducer change.

Changes

  • types/channels-session/state.ts — the two new optional fields with JSDoc.
  • Regenerated the JSON Schema and all five client mirrors (Rust, Kotlin, Swift, TypeScript, Go).
  • docs/guide/customizations.md — updated the child-type metadata table.
  • Added cross-language round-trip conformance fixture 027-agent-customization-model-and-tools.json exercising both fields through the typed ChildCustomization union.
  • CHANGELOG entries across the spec and every client (a 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 go test ./...
  • Rust cargo test
  • Swift swift test (107 tests) ✅
  • Kotlin ./gradlew test (JDK 17) ✅ — fixture 027-... explicitly PASSED
  • TypeScript npm test (59 tests) ✅
  • Root npm run typecheck && lint && verify:release-metadata && verify:changelog ✅ (266 type/reducer tests pass directly; the c8 coverage wrapper is broken under Node v26 in this environment, unrelated to this change)

The protocol version was intentionally not bumped.

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>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 model and tools fields to AgentCustomization in 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 ChildCustomization union.
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

Comment thread clients/go/ahptypes/state.generated.go
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>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

  • Files reviewed: 16/18 changed files
  • Comments generated: 0 new
  • Review effort level: Low

@connor4312 connor4312 enabled auto-merge June 30, 2026 17:29
@connor4312 connor4312 merged commit b6fbec8 into main Jun 30, 2026
10 checks passed
@connor4312 connor4312 deleted the colby-agent-customization-model-tools branch June 30, 2026 17:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add first-class model and tools fields to AgentCustomization

4 participants