Files
Codeman/src/model-config-resolvers.ts
T
Codeman maintainer 661fe3dc13 feat(sessions): a dsh session shows its route config's model while its screen names none
displayModel gains a `config` source, ranked below any report from the running
CLI and above the launch model: custom endpoint, then statusline or screen,
then config, then launch, then nothing. The screen still wins whenever it
names a model, since that is what the running TUI uses.

- Registry data: capabilities.modelDetect gains `configResolver`, a NAMED
  reader (src/model-config-resolvers.ts), like a launcher profile; dsh names
  'deepseek-route' (the reader from the previous commit). `screenLine` becomes
  optional; the schema refuses a modelDetect naming nothing, an unknown
  reader, or screenLines without a screenLine.
- Session: the reader runs from _withPaneLifecycle's finally, so at every pane
  start, attach and relaunch, with the session's own launch config
  (legacyConfigForMode) and env (its clamped overrides, then the server's), so
  a per-session DSH_HOME is the home read. Async; a read that lands after a
  newer one or after the session stopped is dropped; a remote or docker
  session reads nothing locally. A change emits displayModelChanged
  (broadcast and persist). Not restored after a restart: the next attach
  reads it again, and a restored screen value outranks it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 19:41:56 +02:00

50 lines
2.0 KiB
TypeScript

/**
* @fileoverview The config readers a CLI's registry entry may name for the model its
* session runs (`capabilities.modelDetect.configResolver`): the per-CLI behaviour lives
* here, keyed by name, so no code branches on a CLI id (like the launcher profiles in
* config/cli-registry/profiles.ts).
*
* A reader answers the model the CLI's own config pins for one session, or null when
* it pins none or the answer is in any doubt. It must be read-only, bounded (no
* synchronous filesystem call, nothing that can wait on a dead mount) and must return
* the model id alone, never another config value.
*
* @module model-config-resolvers
*/
import type { ModelConfigResolverName } from './config/cli-registry/types.js';
import { effectiveDshHome, readDeepSeekRouteModel } from './deepseek-route-config.js';
/** What a reader gets to know about the session. */
export interface ModelConfigContext {
/** The session's own launch config for its CLI (its `<Mode>Config`), if any. */
config: Record<string, unknown> | undefined;
/** The environment the session's CLI runs with (its own overrides, then the server's). */
env: (key: string) => string | undefined;
}
const RESOLVERS: Record<ModelConfigResolverName, (ctx: ModelConfigContext) => Promise<string | null>> = {
// dsh-TUI's route: the session's profile (else the one the launch boots, which the
// launch names from the server's own dsh home) read under the session's dsh home.
'deepseek-route': (ctx) =>
readDeepSeekRouteModel({
profile: ctx.config?.profile,
home: effectiveDshHome(ctx.env),
serverHome: effectiveDshHome((key) => process.env[key]),
}),
};
/**
* The model the named reader resolves for a session, or null.
*
* @param name a `configResolver` from the registry (schema-checked at load)
* @param ctx what the reader may know about the session
*/
export async function resolveConfigModel(
name: ModelConfigResolverName,
ctx: ModelConfigContext
): Promise<string | null> {
const resolver = RESOLVERS[name];
return resolver ? resolver(ctx) : null;
}