Skip to content

Capability Negotiation

Z-M-Huang edited this page May 3, 2026 · 5 revisions

Capability Negotiation

How core matches a session's needs against a provider/model's capabilities at session start and on every /model or /provider switch.

contractVersion: 1.0.1


Intent

Provider and model capabilities are not static: a user may switch between a streaming-capable model and a non-streaming one, between a tool-calling model and one that cannot call tools, between a large-context model and a small one. A session depends on capabilities declared by its attached State Machine, configured Context Providers, and active Hooks. A mid-session switch that drops a required capability would silently break the session.

Capability Negotiation is the check that prevents that: every switch is a fail-fast on incompatibility. See Capability Mismatch Switch flow.


Declared capabilities

Per-model claims are registered against the bundled provider extensions and resolved per (providerId, modelId). The negotiator uses that registry as the source of truth for the selected pair; when the pair is unknown it falls back to a default capability vector whose promptCaching is probed (detect-on-use).

A provider's contracts/Providers shape carries a capabilities set. The common v1 capabilities:

classDiagram
    class ModelCapabilities {
        streaming : bool
        toolCalling : bool
        structuredOutput : bool
        multimodal : MultimodalKinds
        reasoning : bool
        contextWindow : int
        promptCaching : bool
    }
    class MultimodalKinds {
        image : bool
        audio : bool
        video : bool
    }
    ModelCapabilities --> MultimodalKinds
Loading

See Model Capabilities for per-capability semantics.


Required capabilities

A session's required set is computed from:

Source Contribution
Attached State Machine toolCalling: hard is always contributed — the stage pipeline's completion channel depends on strict tool calling (structured calls core can parse; malformed payloads returnable as tool-call results, not plain text). See Stage Definitions — Provider capability requirements. An SM may additionally contribute capabilities its stages rely on (e.g., streaming for token-stream hooks).
Active Context Providers Providers declaring they produce tool-hint or resource-binding content require tool-calling; ones with large contributions require a minimum context window.
Active Hooks Guard or transform hooks that operate on streaming token events require streaming.
Security mode allowlist with tool-oriented workflows requires tool-calling.

The session's required set is fixed at session start for the initial provider/model choice and re-evaluated on each switch. A provider/model that cannot satisfy toolCalling: hard fails the capability check on /sm attach and on /model or /provider switch while an SM is attached.

A subagent session inherits the parent's required capability set at spawn. If the orchestrator's LLM overrides the subagent's (providerId, modelId) via the delegate tool's model arg (see Subagent Sessions § Model selection), the new model must satisfy the same set. A mismatch fails the spawn with Subagent/ModelCapabilityMismatch before any IP request fires — distinct from the /model mid-session switch path, which negotiates against the orchestrator session's required set rather than a child's.


The negotiation flow

sequenceDiagram
    autonumber
    actor User
    participant Core
    participant NewProvider as Target provider
    User->>Core: /model small-gpt
    Core->>NewProvider: describeCapabilities(small-gpt)
    NewProvider-->>Core: capabilities
    Core->>Core: diff required vs capabilities
    alt compatible
        Core-->>User: switch accepted
    else missing capability
        Core-->>User: switch rejected<br/>(list what's missing)
    end
Loading

A rejected switch leaves the session on the previous choice. The user sees the specific missing capability and picks another target. Nothing silently downgrades.


What "required" really means

A capability may be required in one of three senses:

Level Meaning
hard Without it, the session cannot run correctly. Failure is fatal.
preferred Without it, the session runs in a degraded mode but runs. Failure is a warning.
probed Only relevant if a specific feature is invoked. Failure surfaces at the point of invocation.

A State Machine that advertises streaming as hard will reject a non-streaming model. A Context Provider that marks promptCaching as preferred will accept any model; it just won't get cache hits on the provider that lacks it.

Three levels × seven capabilities matrix

The table below shows every v1 capability against every requirement level and the negotiation outcome:

Capability hard miss result preferred miss result probed result
streaming ProviderCapability/MissingCapability — switch rejected preferred-unmet warning probe-pending warning
toolCalling ProviderCapability/MissingCapability — switch rejected preferred-unmet warning probe-pending warning
structuredOutput ProviderCapability/MissingCapability — switch rejected preferred-unmet warning probe-pending warning
multimodal ProviderCapability/MissingCapability — switch rejected preferred-unmet warning probe-pending warning
reasoning ProviderCapability/MissingCapability — switch rejected preferred-unmet warning probe-pending warning
contextWindow ProviderCapability/MissingCapability if claim < minimum preferred-unmet if claim < minimum probe-pending if claim is 'probed'
promptCaching ProviderCapability/MissingCapability — switch rejected preferred-unmet warning probe-pending warning

contextWindow differs from the other six: the provider claim is number | 'probed' (not a discrete level). A requirement may carry a minimum token count; the negotiator compares the numeric claim against it. A 'probed' claim is always deferred regardless of the requirement level.


Re-negotiation triggers

Trigger Behavior
/model switch Full re-negotiation against the target model.
/provider switch Full re-negotiation against the target provider's current choice model.
State Machine attach Recompute required set; re-negotiate against current provider/model.
Reload adding a new active Context Provider Recompute required set; re-negotiate.
Context window exhaustion mid-turn Not a negotiation trigger — that is a Compaction trigger.

Versioning note

Capability negotiation is runtime. Contract versioning (see Versioning and Compatibility) handles load-time compatibility. They are independent axes:

  • A session's contract versions may all be compatible with core, but the current model may drop a runtime capability the SM needs — that is a runtime fail.
  • A session's capabilities may all be satisfied, but the SM's contract version may be incompatible with core — that is a load-time fail.

Diagnostic shape

A failed or warned negotiation produces a diagnostic carrying:

Field Meaning
Target Provider + model the user asked to switch to.
Missing List of { capability, requiredBy } pairs.
Previous The session's pre-switch provider + model (still in force).
Suggestion Optional — a target that would satisfy the session.
paramsAffected Optional — list of { paramPath[], reason, activeModelId, currentValue, sourceLayer } entries enumerating session provider params that will not apply on the destination model. Surfaced for preferred-unmet and probe-pending cases (the switch is not blocked but params are dropped). currentValue is redacted via the same pipeline that produces audit redacted-deltas — secret-shaped values never appear verbatim. sourceLayer is one of defaultParams, launch, or /params.

The diagnostic emits on the event bus and into the Audit Trail.


Security notes

  • Provider-declared capabilities are untrusted input. A malicious provider may overstate its capabilities. Core treats declarations as hints and verifies them against known values where possible (contextWindow, multimodal). See Trust Model.
  • Downgrade requests are audited. A user accepting a switch despite a preferred capability loss lands in the audit trail so operators can see when sessions silently degraded.
  • No automatic fallback. Core does not pick a "close enough" model when the user's requested one is rejected; the user chooses explicitly.

Related pages


Changelog

1.0.0 — initial

  • Capability Negotiation spec as documented above. Declared v1 capability set; fail-fast on mid-session switch incompatibility; consumer-declared needs from SM, Context Providers, Hooks.

1.0.1 — paramsAffected diagnostic enrichment

  • Diagnostic shape gains an optional paramsAffected field enumerating session provider params that will not apply on the destination model after a /model or /provider switch. Surfaced for preferred-unmet and probe-pending cases. Field shape: { paramPath[], reason, activeModelId, currentValue, sourceLayer }[]. currentValue is redacted; sourceLayer is one of defaultParams / launch / /params.
  • No contract break: pre-existing consumers ignore the new field; the four pre-existing fields are unchanged.

Introduction

Reading

Core runtime

Contracts

Category contracts

Context

Security

Runtime behavior

Operations

Providers (bundled)

Integrations

Reference extensions

Tools

UI

Session Stores

Loggers

Providers

Hooks

Context Providers

Commands

Case studies

Flows

Maintainers

Clone this wiki locally