Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions packages/coding-agent/docs/extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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.
Expand Down
7 changes: 7 additions & 0 deletions packages/coding-agent/src/core/extensions/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -965,6 +970,7 @@ export type ExtensionEvent =
| ToolExecutionUpdateEvent
| ToolExecutionEndEvent
| ModelSelectEvent
| ModelSelectorOpenEvent
| ThinkingLevelSelectEvent
| UserBashEvent
| InputEvent
Expand Down Expand Up @@ -1119,6 +1125,7 @@ export interface ExtensionAPI {
on(event: "tool_execution_update", handler: ExtensionHandler<ToolExecutionUpdateEvent>): void;
on(event: "tool_execution_end", handler: ExtensionHandler<ToolExecutionEndEvent>): void;
on(event: "model_select", handler: ExtensionHandler<ModelSelectEvent>): void;
on(event: "model_selector_open", handler: ExtensionHandler<ModelSelectorOpenEvent>): void;
on(event: "thinking_level_select", handler: ExtensionHandler<ThinkingLevelSelectEvent>): void;
on(event: "tool_call", handler: ExtensionHandler<ToolCallEvent, ToolCallEventResult>): void;
on(event: "tool_result", handler: ExtensionHandler<ToolResultEvent, ToolResultEventResult>): void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading