# GitHub Copilot Auto routing contracts

Target: official DSH / Windows Desktop `0.2.0-rc.2`. Implementation is plugin-only.
For ordinary use, start with [task assessment and explanations](./auto-task-routing.md)
and [parent following](./parent-model-follow.md).

## User contract

The managed `github-copilot-preview` group provides **Auto · Balance** (`auto`),
**Auto · Efficiency** and **Auto · Intelligence** in Core's flat picker.
Only a real model selection exits Auto. Native selection also saves the future
global default; the plugin does not change that Core behavior.

Each admitted Core turn resolves to one real account model. Tool continuations,
native request retries and compaction retries retain it; a new user or Goal
turn can choose differently. Transport, replay, native Usage and billing retain
the actual model identity. Auto never crosses providers/accounts or changes an
explicit real-model selection.

The virtual preference normally remains the Session's pending selection while actual
request headers record real models. A strict plugin-owned public Session projection
also retains the latest owner-explicit native `model/selection`: if a virtual
request header consumes native pending, later concrete headers cannot erase that
Auto intent. Cold refolding restores intent, not old decisions or reasons.
Copied fork/child prefixes alone do not establish an owner-explicit choice;
native pending and the existing parent-following rules remain independent.
Missing or malformed continuity evidence produces a named diagnostic instead of
guessing Auto from a real model. A new explicit fixed or other-provider selection
replaces intent for future turns, even after its native pending is consumed.
Already admitted Auto turns retain their real route through late picker changes.
This projection neither writes history/settings nor changes native projection ownership.
For an otherwise unselected Session that
inherits default Auto, the first request persists the exact virtual
`model/selection` intent. A later explicit selection wins. Existing fixed
Sessions are not converted because the global default changes.

## Eligibility and input fit

Current authenticated account metadata establishes entitlement, endpoint,
context/input/output limits, tool/image support and reasoning capabilities.
Missing/invalid metadata produces diagnostics, not guessed capabilities.
Exact exclusions and independent account/proof/cancellation guards apply first.

The public native TokenMeter estimates every entered message, including
reasoning and tool history. Its current-surface measurement is an envelope
floor, not a substitute for candidate-specific final serialization.
Missing/invalid measurement fails with
`COPILOT_AUTO_TOKEN_METER_UNAVAILABLE` /
`COPILOT_AUTO_TOKEN_ESTIMATE_INVALID`.

Candidates fit when estimated input does not exceed the hard budget after
output reservation and safety allowance. If none fits, choose the largest
input budget for native recovery and explain whether content is fixed,
compaction unavailable, already attempted, or eligible. This is not permission
to dispatch an oversized request. The native managed `inspectRequest` guard
remains authoritative after final framing/attachment projection.

Task preference uses supplier `model_picker_category`, not capacity, model
names or reasoning effort as quality rankings. [Task policy](./auto-task-routing.md)
owns the demand matrix, default semantic assessment, category fallback,
finite continuity and stable positive-weight same-category allocation.
User-marked high cost is orthogonal to demand and category; see
[high-cost requirements](./auto-high-cost.md). Future policy changes must read
the [latest review](./auto-iteration-review.json) before implementation.

## Images, compaction and search

Image eligibility includes historical user/tool image blocks in all entered
messages, not only current user text. Text-only candidates are removed; Auto
advertises image input only with an eligible image model. Actual fixed/Auto
dispatch independently checks explicit account MIME restrictions against
Core's projected image. Missing MIME lists stay unverified.
[Image admission limits](./image-input-compatibility.md).

Auto does not trim history or own compaction transactions. Estimated pressure
uses the initiating Agent's public bound recovery service and preserves the
same real model. If the summary input already exceeds every eligible budget,
stock one-shot compaction cannot rescue it. [Budget admission](./copilot-compaction.md)
and [manual recovery](./manual-compaction-recovery.md) describe separate paths.

Chat Auto does not own independent hosted search. A nonempty `searchModel`
override remains authoritative; otherwise that provider uses bounded
account-owned Responses selection and capability proof. Auto-following search
sees the initiating real request route. No virtual model reaches transport,
and the final query is not replayed across candidates.
[Search routing](./session-search-routing.md).

## Attribution and explanation

Completed replies show **Auto (preference)** with captured reasons, **Manual**
only with explicit fixed-selection evidence, or **Selection unknown** when a
successful read has no record. Failed reads show **Selection unavailable → Retry**.
Retry rereads the same Session/turn; it never runs inference.

The public assistant-actions slot adds the plugin item after native Usage/time.
It cannot change the native fixed-height parent row and adds no Model details
or footer button. The existing info dialog is **Turn model and selection evidence**.
When native Usage lacks complete model routes, model evidence comes first and the
original selection explanation remains in a collapsed disclosure below:

- **Recorded models** lists independent successful same-turn `assistant/message`
  sources, preserving multiple models/providers. Failed attempts, retries and
  paged/missing history retain an explicit incomplete-attribution warning; known
  sources do not prove which models every failed attempt used.
- **Requested model** is the fallback when no successful source exists. An
  independent public Session projection folds native `turn/start`, `step/start`,
  `request/header`, `step/end` and `turn/end`; only headers inside a recorded open
  step bind to that turn. Native headers have no turn field and are not emitted
  for every unchanged request. Therefore absent headers stay unknown, never
  inherited from an earlier turn or today's `Session.requestHeader()`. Configuration
  proves neither dispatch nor execution, usage or billing.
- Missing/invalid evidence stays **Unknown**. Requested-model read failures are
  distinct from missing evidence and have same-Session/turn read-only Retry.
  Selection mode and model evidence remain independent.

Complete native Usage routes avoid redundant model presentation. A turn without
a closing message has no assistant-actions anchor; pure failed turns without one
are not promised an injected entry. Optional conversation projections do not
control selection mounting. The request projection has its own `stateVersion: 1`,
retains at most 128 turns and 32 distinct routes per turn (overflow invalidates
that turn), and stores no message/tool/replay/credential content. Cold folding
uses existing recorded events only, not custom events or history conversion.

The bounded Host store retains 64 Agents and 128 turns per Agent. Disposal,
eviction and restart lose new records. Compatible historical Auto events remain
readable; missing historical preference/reasons are not reconstructed.
New optional `github-copilot/auto-model-decision` events are **not** emitted:
official rc.2 `Session.append()` has no public ignorable-envelope option.

`TurnSelectionController` extends public `TypertRemoteService` with `@Remote get`.
Client calls carry explicit viewed Session ID and turn, with no automatic
Client scope projection that would remove the ID. Native Host Agent lookup,
access checks and strict codecs remain. Additive `requestedModels` preserves the
existing `get` descriptor and strict selection codec unchanged. Actual Client/Host gateway regressions
cover reachability, bound contexts and missing/denied identities; mock RPC
success is not sufficient evidence.

Captured evidence includes task/category, eligible/fitting counts,
fallback/continuity and optional auxiliary milestones. It proves a decision,
not execution, current occupancy, usage or supplier billing. Native accounting
remains independent. [Usage limits](./copilot-usage.md).

### Recovering affected histories

Only histories containing the old incompatible decision event need this
compatibility utility. Upgrading prevents new writes but does not repair old logs.
From a source checkout, use Node 24 LTS and the pinned frozen dependencies:

```powershell
# Read-only validation; no conversation content is printed.
node scripts\repair-auto-model-history.mjs 'C:\absolute\path\session.v4.jsonl.zstd'

# Optional detached copy in a NEW private directory, never the live source.
node scripts\repair-auto-model-history.mjs 'C:\absolute\path\session.v4.jsonl.zstd' --write-copy 'C:\private\new-recovery-directory'
```

The utility validates the entire v4 log with the official rc.2 catalog, marks
only recognized decision envelopes `ignorable: true`, and preserves all other
logical fields. Unknown events, malformed relationships, torn tails and inputs
over 128 MiB fail closed. Copy mode refuses existing destinations and creates a
byte-exact original, validated copy and SHA-256 receipt; keep both logs private.

The corrected writer preserves a separate first zstd header frame for Desktop
discovery. The original utility combined header/events and could make a copy
disappear despite logical validation. `reframedHeader: true` reports physical
repair; zero changed event sequences alone is not readability proof.

**Applying a copy requires separate approval and stopped writers.** Recheck the
live source against `originalSha256`; regenerate if changed. Replace only that
log, compare `repairedSha256` and reopen with the official reader. Do not replay
business requests as a test. The utility never installs, restarts, writes the
source, clears credentials or performs automatic migration.

## Requested parent-to-child selection inheritance

The implemented opt-in [profile-wide switch](./parent-model-follow.md) follows
supported children on each new turn. Fixed parent selection is passed directly;
Auto passes the exact preference and evaluates the child's own context.
Child-owned explicit selections and admitted-turn freezing remain authoritative.
Legacy `parentModelFollow` bindings stay independent.

With both policies off, ordinary native children retain Core's creation-time
route snapshot. Native resolved options/descriptor v3 do not retain whether an
equal creation route was an explicit override. Enabling the plugin policy
authorizes replacing that snapshot; it does not reconstruct original intent,
fabricate descriptor provenance or rewrite Team labels.

The original [investigation #229](https://github.com/cloga/dsh-github-copilot/issues/229)
and [one-switch follow-up #234](https://github.com/cloga/dsh-github-copilot/issues/234)
record the design history. Upstream native rules/settings UI is separate work,
not a dependency or claimed shipped feature of this plugin.

## Model exclusions and richer picker boundary

**Settings → Models → GitHub Copilot → Manage → Model preferences** offers
search, All/Enabled/Excluded filters and immediate Exclude/Restore actions.
Temporarily absent excluded IDs remain restorable. Selected models can also be
excluded; legacy `lockedModelIds` remains empty.

`setModelExcluded(modelId, excluded)` returns a narrow strict preferences result
after path-level native CAS persistence. It reads no credentials, discovers no
models and enumerates no Sessions. Unknown settings make rows read-only with
named diagnostics; status-only Retry cannot imply an empty exclusion set.
The hidden volatile Config leaf persists through native projection/restart.

Directory, Auto pools/modalities, search facts and new-turn admission remove
exact excluded IDs. An admitted Session/turn/model with the exact native request
signal may continue steps/retries; `turn/end` clears admission. New turns,
unbound calls and stale picker rows fail `COPILOT_PREVIEW_MODEL_EXCLUDED`.
Account/proof/cancellation guards remain revocable. A fixed selection is never
changed automatically; restore it or select another model before the next turn.
Zero eligible candidates fails `COPILOT_AUTO_NO_ELIGIBLE_MODEL`.

Core owns flat grouping and picker search. The old
[nested-picker prototype](../output/auto-model-mockup/tiers.html) is historical
design exploration: its capacity quality scores, nested menu and simulated
responses are not implementation or model evaluations.

## Public seams and evidence limits

`PreviewAdapter.listModels/resolveModel`, `agent/pre-step`, public model-selection
middleware and `agent/request` compose the real route before request-header
persistence. Unresolved virtual Auto fails in adapter preparation/streaming.
Per-Agent/turn caching, native adapter leases, OAuth, replay and cancellation
keep their existing owners.

Required unchanged rc.2 source/published-artifact fixtures on Windows/Linux
cover eligibility, real headers, durable intent, multi-step/retry freezing,
images, native compaction, exclusions and gateway access. They do not establish
live model availability, quality, latency, price, account access or Desktop
activation. No Core/dependency/private-registry/shared-catalog/history patch is
permitted. Measured ranking, cross-provider Auto, failure-driven model switching,
side-effect replay and native picker redesign remain out of scope.
