Skip to content

feat(panel): add Host model picker under the composer - #91

Open
zzalancelot wants to merge 4 commits into
omdsh-dev:mainfrom
zzalancelot:feat/composer-model-menu
Open

zzalancelot wants to merge 4 commits into
omdsh-dev:mainfrom
zzalancelot:feat/composer-model-menu

Conversation

@zzalancelot

@zzalancelot zzalancelot commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Bridge maps session.models to Host Typert session/modelCatalog and adapts the catalog into the panel directory shape, including provisional (deferred) sessions.
  • Composer shows a dedicated row under the input with an upward-opening model menu instead of chip/tag buttons.
  • Connected Hosts surface their routable models (e.g. deepseek-official) without requiring a relay profile first.
image image

Test plan

  • Restart dsh web, reload the extension from ~/.dsh/browser-extension
  • Connect the panel and confirm the model row appears under the composer
  • Open the menu (opens upward) and select a Host model; confirm selection sticks for the next prompt
  • Confirm empty Host catalogs show the empty hint; Escape / outside click closes the menu
  • pnpm --filter @yuxianglin/dsh-bridge-browser test -- session-deferral.spec.ts remote-host-api.spec.ts
  • pnpm --filter dsh-browser-extension exec vitest run tests/panel-styles.spec.ts

RetriggerConfidence Score: 3/5

The PR does not yet appear safe to merge because two unresolved model-selection defects can misrepresent or ignore the model that will execute the next prompt.

Findings

  1. P1 Deferred selection failures are ignored
  2. P1 Host default replaces session selection

Summary

Adds a Host model picker beneath the composer and bridges Host model catalogs into the panel’s session-model directory shape.

  • Supports model discovery and deferred selection for provisional sessions.
  • Adds localized model-picker UI, upward-opening menu styles, and related tests.
  • Hardens selection-watcher cleanup and non-DOM behavior.
Diagram
sequenceDiagram
  participant Panel
  participant Deferral as Session Deferral
  participant Adapter as Remote Host Adapter
  participant Host
  Panel->>Deferral: session.models(sessionId)
  alt provisional session
    Deferral->>Adapter: session.models
    Adapter->>Host: session/modelCatalog
    Host-->>Adapter: host-wide catalog/default
    Adapter-->>Deferral: adapted directory
    Deferral-->>Panel: catalog plus pending selection
  else materialized session
    Deferral->>Adapter: session.models(sessionId)
    Adapter->>Host: session/modelCatalog
    Host-->>Panel: adapted host-wide directory
  end
  Panel->>Deferral: session.selectModel(selection)
  alt provisional session
    Deferral-->>Panel: selection recorded
    Panel->>Deferral: first session.prompt
    Deferral->>Host: session.create
    Deferral->>Host: session.selectModel
    Deferral->>Host: session.prompt
  else materialized session
    Deferral->>Host: session.selectModel
  end
Loading

Reviews (3) · Last reviewed commit: "fix(content): clear selection watcher ti..."

Map session.models to session/modelCatalog so connected Hosts expose
their routable models, and replace chip tags with an upward menu on a
dedicated row beneath the input.
Comment on lines +145 to +154
await api.call({
rpcId: crypto.randomUUID(),
method: 'session.selectModel',
payload: { sessionId, ...selection },
signal: call.signal,
})
} catch {
// The prompt still proceeds; the Host keeps its deployment default.
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Deferred selection failures are ignored

For a provisional session, the picker reports the model as selected before the Host applies it. When the first prompt materializes the session, this code ignores both a non-OK session.selectModel result and thrown errors, then sends the prompt anyway. If the Host rejects the model because its route is no longer available, the prompt silently runs with the deployment default even though the composer shows the requested model as active. Propagate the selection failure instead of sending the prompt with a different model.

Comment on lines +625 to +633
case 'session.models':
// Host Typert exposes session/modelCatalog (no args). Adapt ModelCatalog
// into the panel's { current, routable, groups } directory shape.
return {
namespace: 'session',
method: 'modelCatalog',
args: {},
adapt: adaptModelCatalog,
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Host default replaces session selection

session.models accepts a session ID, but this adapter discards it and maps the argument-less host-wide catalog's default model to the session-specific current field. When an existing session has selected another model, resuming or refreshing it therefore marks the deployment default as selected and misleads the user about which model the next prompt will use. The current model needs to come from session state, such as the model-selection projection, rather than modelCatalog.default.

@zzalancelot
zzalancelot force-pushed the feat/composer-model-menu branch from 83287c0 to 295a061 Compare September 16, 2026 11:43
Pending settle timeouts survived useRealTimers() and later hit
document after jsdom was torn down, which made the full vitest run
fail CI with an unhandled ReferenceError.
@zzalancelot
zzalancelot force-pushed the feat/composer-model-menu branch from 295a061 to 146b6bc Compare September 16, 2026 11:44
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