Summary
SessionState.customizations lets a host surface host-discovered skills and custom agents as native SkillCustomization / AgentCustomization children. In practice a host hits three gaps that force it to either drop entries or lose state, because the child-customization model is only half-built:
- No per-child enable/disable. A skill or agent that exists but is individually turned off (config-level) has no representation.
enabled lives only on container customizations, and SessionCustomizationToggled only reaches top-level containers — so an individually-disabled child can't be surfaced, only omitted.
- No user-invocation gate.
SkillCustomization has disableModelInvocation but no user-side counterpart, so "this skill is a user slash-command vs. model-auto-invoked" can't be expressed. A host that wants to render a command palette can't tell which entries are user-invocable.
- Asymmetric invocation model between Skill and Agent.
AgentCustomization has neither invocation gate, so "this agent is/isn't user-selectable" and "the model may/may not auto-delegate to it" are both unrepresentable.
The result is that disabled and non-user-invocable entries get dropped entirely — a settings UI can't list-and-re-enable a disabled skill, and a picker can't show (greyed out) the agents a user can't currently select.
Current state in the spec (0.4.0)
ChildCustomization variants (Agent, Skill, Prompt, Rule, Hook, McpServer) share id / uri / name / icons / range. None carries enabled.
SkillCustomization carries disableModelInvocation?: boolean.
AgentCustomization carries _meta but no invocation gates.
RuleCustomization models activation with first-class, type-specific fields (alwaysApply, globs) — i.e. the spec's established style is to model behavior with real fields, not a generic _meta slot.
- Container customizations (
PluginCustomization, DirectoryCustomization) carry enabled: boolean.
- The
SessionCustomizationToggled reducer matches a top-level container id and sets the container's enabled; it has no path to toggle an individual child.
So the spec already has (a) an enable/disable action and (b) one corner of an invocation model (Skill.disableModelInvocation). This proposal finishes both.
Proposal
A. Per-child enabled + child-addressable toggle
- Add
enabled?: boolean (absent ⇒ enabled) to the child customizations that can be individually disabled — at minimum SkillCustomization and AgentCustomization; ideally to the shared child base next to id/uri/name so it's uniform.
- Extend
SessionCustomizationToggled (and the reducer) so a child id can be toggled, not just a container. Today the reducer only matches top-level containers.
- Define precedence explicitly: a disabled container disables its children regardless of each child's
enabled (effective-enabled = container.enabled && child.enabled).
This lets a disabled-but-present customization be surfaced with accurate state instead of omitted, and generalizes cleanly — "toggle one skill/agent/rule without touching its siblings" is broadly useful.
Alternative considered: mirror the container convention with a required enabled: boolean on children. An optional field with absent⇒enabled is friendlier for existing producers (no behavior change unless they set it). Either is fine; flagging the choice.
B. A complete, symmetric invocation matrix on Skill and Agent
Add the missing gates so both child types carry the full {user, model} matrix, using the spec's existing negative-default-false convention (default = both can invoke):
| Field |
SkillCustomization |
AgentCustomization |
disableModelInvocation?: boolean |
exists |
add (gates model auto-delegation as a sub-agent) |
disableUserInvocation?: boolean |
add (gates user slash-command invocation) |
add (gates user selection in a picker) |
With (A) and (B), a host never has to drop an entry: a disabled skill is enabled: false; a non-user-invocable agent is disableUserInvocation: true; a model-only skill is disableUserInvocation: true (or a slash-only skill is disableModelInvocation: true). All are surfaced with faithful state.
Why first-class fields, not _meta
enabled and user/model invocation are general agent-host concepts, not vendor-specific. The spec already models this class of thing with real fields (Rule.alwaysApply/globs, Skill.disableModelInvocation). Routing them through _meta would defeat the purpose of having native customization types and fragment the same concept across every vendor's _meta namespace. _meta should stay reserved for genuinely provider-specific residue.
Concrete grounding (public, vendor SDK example)
A representative agent SDK already reports these per-entry on its discovery events, which is exactly the data with no AHP home:
session.skills_loaded skill entries report enabled and userInvocable (and a model-invocation flag is being added — see the companion SDK request).
session.custom_agents_updated agent entries report userInvocable, plus model and tools.
So the data exists at the source; only the AHP carrier fields are missing.
Backwards compatibility
All additions are additive and optional (booleans / optional fields), so existing producers and consumers are unaffected until they opt in. The one behavioral extension is teaching SessionCustomizationToggled to match child ids; that only changes behavior for ids that previously matched nothing.
Scope / related proposals
(A) and (B) are the core of this issue and hang together. Two adjacent improvements are tracked separately so each can land on its own:
Summary
SessionState.customizationslets a host surface host-discovered skills and custom agents as nativeSkillCustomization/AgentCustomizationchildren. In practice a host hits three gaps that force it to either drop entries or lose state, because the child-customization model is only half-built:enabledlives only on container customizations, andSessionCustomizationToggledonly reaches top-level containers — so an individually-disabled child can't be surfaced, only omitted.SkillCustomizationhasdisableModelInvocationbut no user-side counterpart, so "this skill is a user slash-command vs. model-auto-invoked" can't be expressed. A host that wants to render a command palette can't tell which entries are user-invocable.AgentCustomizationhas neither invocation gate, so "this agent is/isn't user-selectable" and "the model may/may not auto-delegate to it" are both unrepresentable.The result is that disabled and non-user-invocable entries get dropped entirely — a settings UI can't list-and-re-enable a disabled skill, and a picker can't show (greyed out) the agents a user can't currently select.
Current state in the spec (0.4.0)
ChildCustomizationvariants (Agent,Skill,Prompt,Rule,Hook,McpServer) shareid/uri/name/icons/range. None carriesenabled.SkillCustomizationcarriesdisableModelInvocation?: boolean.AgentCustomizationcarries_metabut no invocation gates.RuleCustomizationmodels activation with first-class, type-specific fields (alwaysApply,globs) — i.e. the spec's established style is to model behavior with real fields, not a generic_metaslot.PluginCustomization,DirectoryCustomization) carryenabled: boolean.SessionCustomizationToggledreducer matches a top-level container id and sets the container'senabled; it has no path to toggle an individual child.So the spec already has (a) an enable/disable action and (b) one corner of an invocation model (
Skill.disableModelInvocation). This proposal finishes both.Proposal
A. Per-child
enabled+ child-addressable toggleenabled?: boolean(absent ⇒ enabled) to the child customizations that can be individually disabled — at minimumSkillCustomizationandAgentCustomization; ideally to the shared child base next toid/uri/nameso it's uniform.SessionCustomizationToggled(and the reducer) so a childidcan be toggled, not just a container. Today the reducer only matches top-level containers.enabled(effective-enabled = container.enabled && child.enabled).This lets a disabled-but-present customization be surfaced with accurate state instead of omitted, and generalizes cleanly — "toggle one skill/agent/rule without touching its siblings" is broadly useful.
B. A complete, symmetric invocation matrix on Skill and Agent
Add the missing gates so both child types carry the full {user, model} matrix, using the spec's existing negative-default-false convention (default = both can invoke):
SkillCustomizationAgentCustomizationdisableModelInvocation?: booleandisableUserInvocation?: booleanWith (A) and (B), a host never has to drop an entry: a disabled skill is
enabled: false; a non-user-invocable agent isdisableUserInvocation: true; a model-only skill isdisableUserInvocation: true(or a slash-only skill isdisableModelInvocation: true). All are surfaced with faithful state.Why first-class fields, not
_metaenabledand user/model invocation are general agent-host concepts, not vendor-specific. The spec already models this class of thing with real fields (Rule.alwaysApply/globs,Skill.disableModelInvocation). Routing them through_metawould defeat the purpose of having native customization types and fragment the same concept across every vendor's_metanamespace._metashould stay reserved for genuinely provider-specific residue.Concrete grounding (public, vendor SDK example)
A representative agent SDK already reports these per-entry on its discovery events, which is exactly the data with no AHP home:
session.skills_loadedskill entries reportenabledanduserInvocable(and a model-invocation flag is being added — see the companion SDK request).session.custom_agents_updatedagent entries reportuserInvocable, plusmodelandtools.So the data exists at the source; only the AHP carrier fields are missing.
Backwards compatibility
All additions are additive and optional (booleans / optional fields), so existing producers and consumers are unaffected until they opt in. The one behavioral extension is teaching
SessionCustomizationToggledto match child ids; that only changes behavior for ids that previously matched nothing.Scope / related proposals
(A) and (B) are the core of this issue and hang together. Two adjacent improvements are tracked separately so each can land on its own:
modelandtoolsfields toAgentCustomization— behavior configuration (model override + tool scope).Customization::Groupcontainer for non-file-backed groupings.