Skip to content

feat(runtime): expose session models on getStatus#300

Open
DaniAkash wants to merge 2 commits intoopenclaw:mainfrom
DaniAkash:feat/expose-session-models
Open

feat(runtime): expose session models on getStatus#300
DaniAkash wants to merge 2 commits intoopenclaw:mainfrom
DaniAkash:feat/expose-session-models

Conversation

@DaniAkash
Copy link
Copy Markdown

What

Adds an optional models field to AcpRuntimeStatus that surfaces the model ids the agent advertised at session-creation time, plus a small drive-by fix so first-session-creation captures models the same way the reconnect path already does.

Why

The data already lands in NewSessionResponse.models from the ACP SDK and is partly persisted on record.acpx.{available_models, current_model_id} — but the public runtime API never exposed it. The CLI's status command reaches into the private record directly (cli/status-command.ts:120); programmatic consumers can't.

Downstream acpx-ai-provider (the Vercel AI SDK adapter) needs this to expose getAvailableModelIds() so chat / picker UIs built on AI SDK can discover what each agent supports without reaching into private state. Gives every host the same capability the CLI already has.

This change is strictly additive at every layer — no public API breaks, no persisted-state schema changes, no wire-protocol changes.

Public surface

export type AcpRuntimeSessionModels = {
  currentModelId?: string;
  availableModelIds: string[];
};

export type AcpRuntimeStatus = {
  // …existing…
  models?: AcpRuntimeSessionModels;   // ← NEW
};

models is undefined when the agent didn't advertise any (Gemini CLI, custom adapters that omit NewSessionResponse.models).

Drive-by: capture models on first session creation

manager.ensureSession already calls applyConfigOptionsToRecord(record, sessionResult) to capture sessionResult.configOptions, but silently drops sessionResult.models — only the reconnect path (reconnect.ts:375) calls syncAdvertisedModelState. Added one line right after applyConfigOptionsToRecord to capture models on first creation too, mirroring the reconnect path. Same idempotent helper, same field on the record — just plugs the gap so models are visible before the first reconnect.

What's deliberately not in this PR

  • Display names. The ACP SDK provides { modelId, name } on availableModels but name is dropped at persistence time today (mode-preference.ts:137). Surfacing names would either require persistence schema changes (avoided here for back-compat safety) or a parallel optional field. Worth a separate follow-up if there's user demand — for now hosts render the id as the label.
  • Public setSessionModel method. setConfigOption('model', value) does NOT route to setSessionModel today — it goes through the generic setSessionConfigOption path. Adding a public model-switching method is a separate concern; this PR only exposes read state.

Tests

  • 3 new integration cases in test/runtime-manager.test.ts:
    • getStatus populates models from NewSessionResponse.models
    • getStatus omits models when the agent didn't advertise any
    • getStatus.models survives a save / reload cycle (proves the field is read out of the persisted record, not just in-process state)
  • pnpm check (format + typecheck + lint + build + viewer + test:coverage) passes locally — 605 tests, 0 failures.

Files touched

File Change LoC
src/runtime/public/contract.ts Add AcpRuntimeSessionModels; add models? to AcpRuntimeStatus ~22
src/runtime/engine/manager.ts New buildModelsField helper; spread it into getStatus; capture models on first ensureSession ~28
src/runtime.ts Re-export AcpRuntimeSessionModels ~1
test/runtime-manager.test.ts 3 new test cases + helper factory ~128

Total: 4 files, ~177 LoC, ~72% tests.

Backwards compatibility

Consumer Impact
Existing CLI users None. CLI's status command reaches into record.acpx?.available_models directly and is untouched.
Existing programmatic consumers None unless they read getStatus().models. The new field is ?: … so destructuring continues to work.
On-disk session records Schema unchanged. Old records load as today, new records persist as today.
acpx-ai-provider (downstream) None until it adopts this version — separate PR there.

DaniAkash added 2 commits May 7, 2026 11:15
Adds an optional `models` field to `AcpRuntimeStatus` that surfaces
the model ids the agent advertised at session-creation time. The data
already lands in `NewSessionResponse.models` and is partly persisted
on `record.acpx.{available_models, current_model_id}` — the public
runtime API just never exposed it. The CLI's status command reaches
into the private record directly today; this gives every host the
same capability.

Strictly additive at every layer:

- `AcpRuntimeStatus.models?: AcpRuntimeSessionModels` (new optional
  field on an existing type)
- `AcpRuntimeSessionModels = { currentModelId?, availableModelIds }`
  (new exported type)
- No persisted-state schema changes — `available_models` stays
  `string[]`
- No wire-protocol changes
- No CLI changes — its existing private-record path keeps working

Display names are deliberately not surfaced here. The ACP SDK
provides `{ modelId, name }` but `name` is dropped at persistence
time today (`mode-preference.ts:137`). Surfacing names would either
require schema changes or a parallel optional field — worth a
separate follow-up if there's user demand.

Drive-by fix: `manager.ensureSession` was already capturing
`sessionResult.configOptions` via `applyConfigOptionsToRecord` but
silently dropping `sessionResult.models` — only the reconnect path
called `syncAdvertisedModelState`. Added one line to capture
models on first session creation too, mirroring the reconnect path.

Driving consumer: `acpx-ai-provider` (the Vercel AI SDK adapter)
needs this to expose `getAvailableModelIds()` so chat / picker UIs
built on AI SDK can discover what each agent supports without
reaching into private state.

Tests:

- `getStatus` populates `models` from `NewSessionResponse.models`
- `getStatus` omits `models` when the agent didn't advertise any
- `getStatus.models` survives a save/reload cycle

`pnpm check` (format + typecheck + lint + build + viewer +
test:coverage) passes locally; 605 tests, 0 failures.
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