Skip to content

feat: declare per-model capabilities on session/new - #24

Merged
zxch3n merged 1 commit into
mainfrom
feat/declare-model-capabilities
Sep 5, 2026
Merged

feat: declare per-model capabilities on session/new#24
zxch3n merged 1 commit into
mainfrom
feat/declare-model-capabilities

Conversation

@zxch3n

@zxch3n zxch3n commented Sep 4, 2026

Copy link
Copy Markdown

Why

configOptions is rebuilt on every model switch: the Fast toggle is present
only while supportsFastMode, and the effort list belongs to the current model.
A client reading the session/new snapshot therefore learns nothing about any
other model — and ACP has no request that asks.

Lody was reading those options as an agent-wide capability catalog, so selecting
a model other than the probed one produced answers like "this agent has no fast
mode" for a model that does, or the reverse.

What

Attach the catalog the SDK already handed us to the session/new response:

{
  "sessionId": "",
  "modes": { /* unchanged */ },
  "configOptions": [ /* unchanged */ ],
  "_meta": {
    "lody": {
      "modelCapabilities": {
        "version": 1,
        "models": {
          "sonnet": { "effortValues": ["low", "medium", "high"], "fastMode": true },
          "fable":  { "fastMode": false }
        }
      }
    }
  }
}

Built from the ModelInfo[] already in hand — supportedEffortLevels (only
when supportsEffort) and supportsFastMode. No new source of truth, no extra
request. effortValues is omitted rather than emptied for a model that does not
support effort, so "no effort control" and "an empty list" stay distinguishable.

Contract

Self-declared and advisory. It may describe a control the snapshot does not
carry; it never grants permission, authorizes a value, or rejects one. The live
session state remains the authority.

A model absent from models is unknown, not unsupported. The consumer
therefore reads the catalog whole or ignores it whole, and treats it as stale
after a TTL or a change of adapter version.

Emitted only when the model list is non-empty, so a client that ignores _meta
sees a byte-identical response.

Consumer

Read by Lody in LodyAI/Lody#333, which ships first and reads an absent
declaration as "nothing to say" — so this can merge on its own schedule.

Test

npx tsc --noEmit, npx eslint src/acp-agent.ts, and prettier --check clean.

🤖 Generated with Claude Code

`configOptions` is rebuilt per model, so a client reading it learns nothing
about any other model — and ACP has no request that asks. Attach the catalog
we already hold as `_meta.lody.modelCapabilities`
(`{ version: 1, models: { <id>: { effortValues?, fastMode? } } }`).

Self-declared and advisory: the live session state stays the authority.

Model: claude-opus-5[1m]

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@zxch3n
zxch3n merged commit 94223f1 into main Sep 5, 2026
1 check passed
@zxch3n
zxch3n deleted the feat/declare-model-capabilities branch September 5, 2026 01:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant