From 9f29a364e9a7ffe1cdd5e2fe80dc6d4e4520fa07 Mon Sep 17 00:00:00 2001 From: Julien Chaumond Date: Wed, 20 May 2026 20:24:00 +0200 Subject: [PATCH 1/2] Add model_selector_open extension event Emitted (fire-and-forget) from showModelSelector before opening the picker. Extensions can refresh provider state on this event; updates land on the next picker open, not the current one. Co-Authored-By: julien-agent --- packages/coding-agent/src/core/extensions/types.ts | 7 +++++++ .../coding-agent/src/modes/interactive/interactive-mode.ts | 1 + 2 files changed, 8 insertions(+) diff --git a/packages/coding-agent/src/core/extensions/types.ts b/packages/coding-agent/src/core/extensions/types.ts index 58b85227378..0046d61385c 100644 --- a/packages/coding-agent/src/core/extensions/types.ts +++ b/packages/coding-agent/src/core/extensions/types.ts @@ -718,6 +718,11 @@ export interface ModelSelectEvent { source: ModelSelectSource; } +/** Fired when the interactive model picker opens. Dispatched fire-and-forget so handlers do not block the UI — any provider-state refresh (e.g. fetching a remote model list) will be reflected on the next open. */ +export interface ModelSelectorOpenEvent { + type: "model_selector_open"; +} + /** Fired when a new thinking level is selected */ export interface ThinkingLevelSelectEvent { type: "thinking_level_select"; @@ -965,6 +970,7 @@ export type ExtensionEvent = | ToolExecutionUpdateEvent | ToolExecutionEndEvent | ModelSelectEvent + | ModelSelectorOpenEvent | ThinkingLevelSelectEvent | UserBashEvent | InputEvent @@ -1119,6 +1125,7 @@ export interface ExtensionAPI { on(event: "tool_execution_update", handler: ExtensionHandler): void; on(event: "tool_execution_end", handler: ExtensionHandler): void; on(event: "model_select", handler: ExtensionHandler): void; + on(event: "model_selector_open", handler: ExtensionHandler): void; on(event: "thinking_level_select", handler: ExtensionHandler): void; on(event: "tool_call", handler: ExtensionHandler): void; on(event: "tool_result", handler: ExtensionHandler): void; diff --git a/packages/coding-agent/src/modes/interactive/interactive-mode.ts b/packages/coding-agent/src/modes/interactive/interactive-mode.ts index aa8db52b729..d9da66125a8 100644 --- a/packages/coding-agent/src/modes/interactive/interactive-mode.ts +++ b/packages/coding-agent/src/modes/interactive/interactive-mode.ts @@ -4078,6 +4078,7 @@ export class InteractiveMode { } private showModelSelector(initialSearchInput?: string): void { + void this.session.extensionRunner.emit({ type: "model_selector_open" }); this.showSelector((done) => { const selector = new ModelSelectorComponent( this.ui, From fd8b6bbce01d76b51f3a3adb2ab0d621855c8b5c Mon Sep 17 00:00:00 2001 From: Julien Chaumond Date: Wed, 20 May 2026 20:26:49 +0200 Subject: [PATCH 2/2] Document model_selector_open event in extensions.md Co-Authored-By: julien-agent --- packages/coding-agent/docs/extensions.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/coding-agent/docs/extensions.md b/packages/coding-agent/docs/extensions.md index ce922f2aeaf..87a660551f7 100644 --- a/packages/coding-agent/docs/extensions.md +++ b/packages/coding-agent/docs/extensions.md @@ -324,6 +324,7 @@ user sends another prompt ◄───────────────── └─► session_tree /model or Ctrl+P (model selection/cycling) + ├─► model_selector_open (when the picker opens, fire-and-forget) ├─► thinking_level_select (if model change changes/clamps thinking level) └─► model_select @@ -654,6 +655,19 @@ pi.on("model_select", async (event, ctx) => { Use this to update UI elements (status bars, footers) or perform model-specific initialization when the active model changes. +#### model_selector_open + +Fired when the interactive model picker opens (via `/model` or `Ctrl+P`). Dispatched fire-and-forget so the picker is never delayed by extension work, which means provider state changes made in this handler (e.g. re-registering a provider after a remote `/v1/models` fetch) are reflected on the **next** picker open, not the current one. + +```typescript +pi.on("model_selector_open", async () => { + // Refresh dynamically discovered models so the next open sees them. + await refreshRemoteModels(); +}); +``` + +Use this for providers backed by a live endpoint where the model list can change between sessions (e.g. a running llama-server with hot-swappable models). + #### thinking_level_select Fired when the thinking level changes. This is notification-only; handler return values are ignored.