Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,23 @@ changes accumulate. Track in-flight protocol changes via PRs touching

Spec version: `0.5.2`

### Added

- Optional `enabled` flag on the child customizations (`AgentCustomization`,
`SkillCustomization`, `PromptCustomization`, `RuleCustomization`,
`HookCustomization`) so an individual child can be turned off independently of
its container; absent means enabled.
- `disableUserInvocation` on `SkillCustomization`, plus `disableModelInvocation`
and `disableUserInvocation` on `AgentCustomization`, giving custom agents and
skills a symmetric user/model invocation matrix.

### Changed

- `session/customizationToggled` now targets any top-level customization
(`plugin`, `directory`, or top-level `mcpServer`) or an individual child by
`id` and sets that entry's `enabled`; the effective state of a child is
`container.enabled && (child.enabled ?? true)`.

## [0.5.1] — 2026-07-02

Spec version: `0.5.1`
Expand Down
18 changes: 18 additions & 0 deletions clients/go/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,24 @@ tag whose matching `## [X.Y.Z]` heading is missing from this file.

## [Unreleased]

## [0.5.2] — Unreleased

Implements AHP 0.5.2.

### Added

- Optional `Enabled` field on the child customization types
(`AgentCustomization`, `SkillCustomization`, `PromptCustomization`,
`RuleCustomization`, `HookCustomization`).
- `DisableUserInvocation` on `SkillCustomization`, plus `DisableModelInvocation`
and `DisableUserInvocation` on `AgentCustomization`.

### Changed

- The `session/customizationToggled` reducer now toggles any top-level
customization (`plugin`, `directory`, or top-level `mcpServer`) or an
individual child by `id`, setting that entry's `Enabled`.

## [0.5.1] — 2026-07-02

Implements AHP 0.5.1.
Expand Down
30 changes: 30 additions & 0 deletions clients/go/ahp/reducers.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,23 @@ func setContainerEnabled(c *ahptypes.Customization, enabled bool) {
}
}

func setChildEnabled(c *ahptypes.ChildCustomization, enabled bool) {
switch v := c.Value.(type) {
case *ahptypes.AgentCustomization:
v.Enabled = &enabled
case *ahptypes.SkillCustomization:
v.Enabled = &enabled
case *ahptypes.PromptCustomization:
v.Enabled = &enabled
case *ahptypes.RuleCustomization:
v.Enabled = &enabled
case *ahptypes.HookCustomization:
v.Enabled = &enabled
case *ahptypes.McpServerCustomization:
v.Enabled = enabled
}
}

func applyToggle(list []ahptypes.Customization, id string, enabled bool) bool {
for i := range list {
got, ok := customizationID(list[i])
Expand All @@ -332,6 +349,19 @@ func applyToggle(list []ahptypes.Customization, id string, enabled bool) bool {
return true
}
}
for i := range list {
children := containerChildren(&list[i])
if children == nil {
continue
}
for j := range *children {
got, ok := childCustomizationID((*children)[j])
if ok && got == id {
setChildEnabled(&(*children)[j], enabled)
return true
}
}
}
return false
}

Expand Down
20 changes: 12 additions & 8 deletions clients/go/ahptypes/actions.generated.go
Original file line number Diff line number Diff line change
Expand Up @@ -824,17 +824,21 @@ type SessionCustomizationsChangedAction struct {
Customizations []Customization `json:"customizations"`
}

// A client toggled a container customization on or off.
//
// Targets a top-level container (plugin or directory) by `id`. Only
// containers have an `enabled` flag; children are always active when
// their container is enabled. Is a no-op when no matching container is
// found.
// A client toggled a customization on or off.
//
// Matches `id` against every top-level customization first — a plugin or
// directory container, or a bare top-level MCP server — then against the
// children inside each container (a skill, agent, or other entry), and
// sets the matched entry's `enabled` flag. Disabling a container still
// disables all of its children — the effective state of a child is
// `container.enabled && (child.enabled ?? true)` — so toggling a child
// only matters while its container is enabled. Is a no-op when no
// customization has the given `id`.
type SessionCustomizationToggledAction struct {
Type ActionType `json:"type"`
// The id of the container to toggle.
// The id of the container or child to toggle.
Id string `json:"id"`
// Whether to enable or disable the container.
// Whether to enable or disable the targeted customization.
Enabled bool `json:"enabled"`
}

Expand Down
92 changes: 82 additions & 10 deletions clients/go/ahptypes/state.generated.go
Original file line number Diff line number Diff line change
Expand Up @@ -2206,8 +2206,20 @@ type AgentCustomization struct {
// customization is a subset of a larger file (for example, one entry
// in an inline `mcpServers` block of a `plugins.json` manifest).
// Absent when the customization covers the whole resource.
Range *TextRange `json:"range,omitempty"`
Type CustomizationType `json:"type"`
Range *TextRange `json:"range,omitempty"`
// Whether this child is individually enabled. Absent means enabled, so a
// producer only needs to set it to surface a child that exists but is
// turned off on its own.
//
// This flag is independent of the parent container's: the **effective**
// enabled state of a child is
// `container.enabled && (child.enabled ?? true)`, so a disabled container
// disables every child regardless of each child's own flag.
//
// A child is turned on or off by id with
// {@link SessionCustomizationToggledAction | `session/customizationToggled`}.
Enabled *bool `json:"enabled,omitempty"`
Type CustomizationType `json:"type"`
// Short description of what the agent specializes in and when to
// invoke it. Sourced from the agent file's frontmatter `description`.
Description *string `json:"description,omitempty"`
Expand All @@ -2223,6 +2235,14 @@ type AgentCustomization struct {
// field rather than sending an empty array, so an empty list carries no
// meaning distinct from absence.
Tools []string `json:"tools,omitempty"`
// When `true`, the agent will not auto-delegate to this custom agent
// as a sub-agent; it can only be selected by the user. Absent or
// `false` means the agent may delegate to it.
DisableModelInvocation *bool `json:"disableModelInvocation,omitempty"`
// When `true`, the user cannot select this custom agent (for example,
// in a picker); it remains available for the agent to auto-delegate
// to. Absent or `false` means the user may select it.
DisableUserInvocation *bool `json:"disableUserInvocation,omitempty"`
// Additional provider-specific metadata for this custom agent.
//
// Mirrors the MCP `_meta` convention.
Expand Down Expand Up @@ -2256,15 +2276,31 @@ type SkillCustomization struct {
// customization is a subset of a larger file (for example, one entry
// in an inline `mcpServers` block of a `plugins.json` manifest).
// Absent when the customization covers the whole resource.
Range *TextRange `json:"range,omitempty"`
Type CustomizationType `json:"type"`
Range *TextRange `json:"range,omitempty"`
// Whether this child is individually enabled. Absent means enabled, so a
// producer only needs to set it to surface a child that exists but is
// turned off on its own.
//
// This flag is independent of the parent container's: the **effective**
// enabled state of a child is
// `container.enabled && (child.enabled ?? true)`, so a disabled container
// disables every child regardless of each child's own flag.
//
// A child is turned on or off by id with
// {@link SessionCustomizationToggledAction | `session/customizationToggled`}.
Enabled *bool `json:"enabled,omitempty"`
Type CustomizationType `json:"type"`
// Short description used for help text and auto-invocation matching.
// Sourced from the skill's frontmatter `description`.
Description *string `json:"description,omitempty"`
// When `true`, only the user can invoke this skill — the agent will not
// auto-invoke it. Sourced from the command skill's frontmatter
// `disable-model-invocation` flag.
DisableModelInvocation *bool `json:"disableModelInvocation,omitempty"`
// When `true`, the user cannot directly invoke this skill (for example,
// as a slash command); it remains available for the agent to
// auto-invoke. Absent or `false` means the user may invoke it.
DisableUserInvocation *bool `json:"disableUserInvocation,omitempty"`
}

// A prompt contributed by a plugin or directory.
Expand All @@ -2289,8 +2325,20 @@ type PromptCustomization struct {
// customization is a subset of a larger file (for example, one entry
// in an inline `mcpServers` block of a `plugins.json` manifest).
// Absent when the customization covers the whole resource.
Range *TextRange `json:"range,omitempty"`
Type CustomizationType `json:"type"`
Range *TextRange `json:"range,omitempty"`
// Whether this child is individually enabled. Absent means enabled, so a
// producer only needs to set it to surface a child that exists but is
// turned off on its own.
//
// This flag is independent of the parent container's: the **effective**
// enabled state of a child is
// `container.enabled && (child.enabled ?? true)`, so a disabled container
// disables every child regardless of each child's own flag.
//
// A child is turned on or off by id with
// {@link SessionCustomizationToggledAction | `session/customizationToggled`}.
Enabled *bool `json:"enabled,omitempty"`
Type CustomizationType `json:"type"`
// Short description of what the prompt does.
Description *string `json:"description,omitempty"`
}
Expand Down Expand Up @@ -2325,8 +2373,20 @@ type RuleCustomization struct {
// customization is a subset of a larger file (for example, one entry
// in an inline `mcpServers` block of a `plugins.json` manifest).
// Absent when the customization covers the whole resource.
Range *TextRange `json:"range,omitempty"`
Type CustomizationType `json:"type"`
Range *TextRange `json:"range,omitempty"`
// Whether this child is individually enabled. Absent means enabled, so a
// producer only needs to set it to surface a child that exists but is
// turned off on its own.
//
// This flag is independent of the parent container's: the **effective**
// enabled state of a child is
// `container.enabled && (child.enabled ?? true)`, so a disabled container
// disables every child regardless of each child's own flag.
//
// A child is turned on or off by id with
// {@link SessionCustomizationToggledAction | `session/customizationToggled`}.
Enabled *bool `json:"enabled,omitempty"`
Type CustomizationType `json:"type"`
// Description of what the rule enforces.
Description *string `json:"description,omitempty"`
// When `true`, the rule is always active (subject to `globs` if any).
Expand Down Expand Up @@ -2360,8 +2420,20 @@ type HookCustomization struct {
// customization is a subset of a larger file (for example, one entry
// in an inline `mcpServers` block of a `plugins.json` manifest).
// Absent when the customization covers the whole resource.
Range *TextRange `json:"range,omitempty"`
Type CustomizationType `json:"type"`
Range *TextRange `json:"range,omitempty"`
// Whether this child is individually enabled. Absent means enabled, so a
// producer only needs to set it to surface a child that exists but is
// turned off on its own.
//
// This flag is independent of the parent container's: the **effective**
// enabled state of a child is
// `container.enabled && (child.enabled ?? true)`, so a disabled container
// disables every child regardless of each child's own flag.
//
// A child is turned on or off by id with
// {@link SessionCustomizationToggledAction | `session/customizationToggled`}.
Enabled *bool `json:"enabled,omitempty"`
Type CustomizationType `json:"type"`
}

// An MCP server contributed by a plugin or directory.
Expand Down
18 changes: 18 additions & 0 deletions clients/kotlin/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,24 @@ versions (`*-SNAPSHOT`) are explicitly rejected by the publish pipeline; bump

## [Unreleased]

## [0.5.2] — Unreleased

Implements AHP 0.5.2.

### Added

- Optional `enabled` field on the child customization types
(`AgentCustomization`, `SkillCustomization`, `PromptCustomization`,
`RuleCustomization`, `HookCustomization`).
- `disableUserInvocation` on `SkillCustomization`, plus `disableModelInvocation`
and `disableUserInvocation` on `AgentCustomization`.

### Changed

- The `session/customizationToggled` reducer now toggles any top-level
customization (`plugin`, `directory`, or top-level `mcpServer`) or an
individual child by `id`, setting that entry's `enabled`.

## [0.5.1] — 2026-07-02

Implements AHP 0.5.1.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,16 @@ private fun withCustomizationEnabled(c: Customization, enabled: Boolean): Custom
is CustomizationUnknown -> c
}

private fun withChildCustomizationEnabled(c: ChildCustomization, enabled: Boolean): ChildCustomization = when (c) {
is ChildCustomizationAgent -> ChildCustomizationAgent(c.value.copy(enabled = enabled))
is ChildCustomizationSkill -> ChildCustomizationSkill(c.value.copy(enabled = enabled))
is ChildCustomizationPrompt -> ChildCustomizationPrompt(c.value.copy(enabled = enabled))
is ChildCustomizationRule -> ChildCustomizationRule(c.value.copy(enabled = enabled))
is ChildCustomizationHook -> ChildCustomizationHook(c.value.copy(enabled = enabled))
is ChildCustomizationMcpServer -> ChildCustomizationMcpServer(c.value.copy(enabled = enabled))
is ChildCustomizationUnknown -> c
}

private fun childCustomizationId(c: ChildCustomization): String? = when (c) {
is ChildCustomizationAgent -> c.value.id
is ChildCustomizationSkill -> c.value.id
Expand Down Expand Up @@ -597,10 +607,22 @@ public fun sessionReducer(state: SessionState, action: StateAction): SessionStat
val list = state.customizations
if (list == null) state else {
val idx = list.indexOfFirst { customizationId(it) == a.id }
if (idx < 0) state else {
if (idx >= 0) {
val updated = list.toMutableList()
updated[idx] = withCustomizationEnabled(updated[idx], a.enabled)
state.copy(customizations = updated)
} else run {
for (i in list.indices) {
val children = customizationChildren(list[i]) ?: continue
val childIdx = children.indexOfFirst { childCustomizationId(it) == a.id }
if (childIdx < 0) continue
val newChildren = children.toMutableList()
newChildren[childIdx] = withChildCustomizationEnabled(newChildren[childIdx], a.enabled)
val updated = list.toMutableList()
updated[i] = withCustomizationChildren(list[i], newChildren)
return@run state.copy(customizations = updated)
}
state
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -915,11 +915,11 @@ data class SessionCustomizationsChangedAction(
data class SessionCustomizationToggledAction(
val type: ActionType,
/**
* The id of the container to toggle.
* The id of the container or child to toggle.
*/
val id: String,
/**
* Whether to enable or disable the container.
* Whether to enable or disable the targeted customization.
*/
val enabled: Boolean
)
Expand Down
Loading