Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save dctmfoo/9998aa3f9efd8c89633da9bbd356831c to your computer and use it in GitHub Desktop.

Select an option

Save dctmfoo/9998aa3f9efd8c89633da9bbd356831c to your computer and use it in GitHub Desktop.

S19-01 — Main /model source-of-truth from SDK control-plane catalog (CLI auth only)

Status: DONE AssignedTo: opus DelegateModel: anthropic/claude-opus-4-6 (thinking: high) Scope: single-slice delivery in this file only (implementation + tests + verification + completion receipt)


1) Objective (no ambiguity)

Eliminate hardcoded Anthropic model version mapping for main-runtime model selection.

For this repo, the source of truth must be:

  • Claude Agent SDK control-plane model listing (CLI auth account-scoped availability)
  • Not API-key model endpoints
  • Not local hardcoded alias→version tables for Anthropic model selection

This slice must deliver:

  1. Main Telegram /model panel options are generated from SDK control-plane model listing.
  2. Main /model writes canonical model values using control-plane-resolved IDs.
  3. Anthropic alias/version pinning is no longer hardcoded in provider resolver path.
  4. Existing child runtime model flow remains unchanged.

2) Explicit user constraints

  • Runtime auth model is CLI/cloud auth (no API key flow as primary model source).
  • Do not rely on Anthropic REST /v1/models for primary selection logic.
  • Main model UX must reflect what the current authenticated SDK control plane allows.

3) Source-of-truth evidence

3.1 SDK control-plane capabilities

From @anthropic-ai/claude-agent-sdk types (sdk.d.ts):

  • query(...) returns Query
  • Query.supportedModels(): Promise<ModelInfo[]>
  • Query.initializationResult(): Promise<... models: ModelInfo[]>
  • SDKSystemMessage includes model: string in subtype: 'init'

These provide runtime model catalog + resolved model identity from the active authenticated control plane.

3.2 Why this slice is needed

Current repo state hardcodes Anthropic model IDs in:

  • src/providers/index.ts (sonnet, opus, haiku)
  • Main Telegram /model panel uses resolveModel(...) from those hardcoded mappings

This can drift from actual account-available control-plane models.


4) Scope boundaries

In scope

  • Main runtime model catalog sourcing for Telegram /model (non-child route).
  • Anthropic model resolution path used by main runtime config writes.
  • Removing hardcoded Anthropic version mapping in selection path.
  • Tests covering control-plane catalog path and main /model UX behavior.

Out of scope (must not change)

  • Child runtime model catalog/listing flow (sessions_models, runtime settings service).
  • /think behavior.
  • Voice/TTS/transcription behavior.
  • OpenRouter model strategy beyond current behavior.

5) Required functional behavior

5.1 Control-plane catalog service (new)

Implement a service that returns main-runtime model options from SDK control plane:

  • Uses query(...) + supportedModels() as primary listing.
  • For each selectable model value, resolves the actual init model ID by probing a short-lived query with options.model=<value> and reading first system:init message model.
  • Returns entries like:
    • selector (e.g. sonnet, opus, haiku, default, or other value returned)
    • displayName
    • description
    • resolvedModelId

Service requirements:

  • permissionMode: 'plan'
  • maxTurns: 1
  • short timeout guard
  • per-instance TTL cache (e.g. 10 min) to avoid repeated probe cost
  • fail-closed with explicit error text (no silent fallback to hardcoded mappings)

5.2 Main /model panel behavior

Main (non-child) /model panel must:

  • Build button rows from dynamic control-plane catalog entries (not fixed sonnet/opus/haiku array).
  • Display:
    • provider
    • configured model
    • resolved model
  • Button labels include selector + resolved ID (or display name + resolved ID).
  • Selected state compares against current resolved model ID.

5.3 Main /model <arg> apply behavior

When user passes /model <arg> in main chat:

  • If <arg> matches catalog selector → persist corresponding resolvedModelId.
  • If <arg> matches a catalog resolvedModelId exactly → persist it.
  • Else reject with warning including suggestion to run /model (panel refresh), no mutation.

5.4 Provider resolver behavior (Anthropic)

  • Remove static Anthropic alias→version table from resolveModel selection path.
  • Anthropic resolver must no longer pin sonnet/opus/haiku to local constants.
  • OpenRouter behavior may stay as-is in this slice.

6) Implementation outline

  1. Add src/providers/sdk-control-plane-model-catalog.ts:
    • listMainControlPlaneModels(...)
    • resolveControlPlaneModelId(...)
    • TTL cache read/write under .config/
  2. Update src/interfaces/telegram/adapter.ts:
    • Replace hardcoded main model option list with catalog service output.
    • Update apply path to resolve against catalog.
  3. Update src/providers/index.ts:
    • Remove hardcoded Anthropic version mapping in resolveModel path.
  4. Add tests:
    • new service unit tests
    • adapter main /model tests with mocked catalog service

Design note:

  • Keep a clear seam so tests do not call real SDK/network.

7) Files allowed to change

  1. src/providers/index.ts
  2. src/providers/sdk-control-plane-model-catalog.ts (new)
  3. src/providers/sdk-control-plane-model-catalog.test.ts (new)
  4. src/interfaces/telegram/adapter.ts
  5. src/interfaces/telegram/adapter.test.ts
  6. plan/slices/S19/S19-01-sdk-control-plane-model-catalog-main-model-ux.md (status/receipt updates only)

No other files unless required for compile correctness. If extra files are touched, record exact reason in receipt.


8) TDD plan (mandatory)

Follow strict RED → GREEN → REFACTOR in small vertical steps.

Minimum required tests:

  1. loads main model options from sdk control-plane catalog service
  2. main /model panel renders dynamic catalog entries instead of fixed alias list
  3. main /model <selector> persists control-plane resolved model id
  4. main /model <resolved-id> persists when present in catalog
  5. main /model rejects unknown input when absent from catalog
  6. main model callback applies selected catalog entry
  7. fails closed with warning when control-plane catalog cannot be loaded
  8. resolveModel anthropic path does not hardcode sonnet/opus/haiku snapshot ids

9) Verification commands (must run and report)

Focused:

pnpm vitest run src/providers/sdk-control-plane-model-catalog.test.ts src/interfaces/telegram/adapter.test.ts

Full gate:

pnpm test
pnpm build
pnpm readiness:debt:gate

All commands must pass.


10) Handoff format (required)

Return exactly:

  1. Functional result (operator-facing behavior changes)
  2. Files changed
  3. Tests added (exact names)
  4. Verification results for each required command
  5. Risk notes / deferred follow-ups

11) Acceptance checklist

  • Main /model options come from SDK control-plane listing.
  • Main /model no longer depends on hardcoded Anthropic alias mapping.
  • Main /model persists control-plane-resolved model IDs.
  • Unknown models fail closed with explicit warning.
  • Child runtime model behavior unchanged.
  • All required tests added and passing.
  • pnpm test, pnpm build, pnpm readiness:debt:gate pass.

12) Completion receipt (fill on finish)

  • Set Status to DONE
  • CompletedBy: opus
  • CompletedAt: 2026-02-19T13:55:00Z
  • CommitOrPR: pending
  • VerificationEvidence:
    • pnpm vitest run ...: PASS — 132 tests (9 catalog + 123 adapter), 0 failures
    • pnpm test: PASS — 2887 passed, 8 skipped, 0 failures (235 test files)
    • pnpm build: PASS — tsc + dashboard assets OK
    • pnpm readiness:debt:gate: PASS — debt gate status: PASS
  • ExtraFilesTouched (if any):
    • none
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment