From 95ae59027916702dd11640c93be64ce2d7e7900c Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Thu, 30 Apr 2026 22:49:31 +0200 Subject: [PATCH] fix(coding-agent): refresh thinking border from extensions closes #3888 --- packages/coding-agent/CHANGELOG.md | 3 +++ packages/coding-agent/docs/extensions.md | 22 ++++++++++++++++++- .../coding-agent/src/core/agent-session.ts | 10 ++++++++- .../coding-agent/src/core/extensions/types.ts | 9 ++++++++ .../src/modes/interactive/interactive-mode.ts | 5 +++++ 5 files changed, 47 insertions(+), 2 deletions(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 87ddb815..66231011 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -8,6 +8,7 @@ - Added `message_end` extension result support for replacing finalized messages, enabling extensions to override assistant usage cost ([#3982](https://github.com/badlogic/pi-mono/issues/3982)). - Added top-level `name` support to `pi.registerProvider()` so extension-registered providers can show a friendly name in `/login` ([#3956](https://github.com/badlogic/pi-mono/issues/3956)). - Added `ctx.ui.getEditorComponent()` so extensions can wrap the currently configured custom editor factory ([#3935](https://github.com/badlogic/pi-mono/issues/3935)). +- Added a `thinking_level_select` extension event for observing thinking level changes ([#3888](https://github.com/badlogic/pi-mono/issues/3888)). ### Removed @@ -15,6 +16,8 @@ ### Fixed +- Fixed blocked `edit` tool results rendering the rejection reason twice after interactive extension confirmation ([#3830](https://github.com/badlogic/pi-mono/issues/3830)). +- Fixed extension-triggered thinking level changes refreshing the interactive editor border immediately ([#3888](https://github.com/badlogic/pi-mono/issues/3888)). - Fixed the coding-agent README See Also link to point at `@mariozechner/pi-agent-core` ([#4023](https://github.com/badlogic/pi-mono/issues/4023)). - Fixed `grep` and `find` tool argument injection for flag-like search patterns ([#4018](https://github.com/badlogic/pi-mono/issues/4018)). - Updated `@mariozechner/clipboard` to an attested release so package managers with trust policies do not reject installs ([#3946](https://github.com/badlogic/pi-mono/issues/3946)). diff --git a/packages/coding-agent/docs/extensions.md b/packages/coding-agent/docs/extensions.md index e20924c4..76a11c5d 100644 --- a/packages/coding-agent/docs/extensions.md +++ b/packages/coding-agent/docs/extensions.md @@ -40,6 +40,7 @@ See [examples/extensions/](../examples/extensions/) for working implementations. - [Resource Events](#resource-events) - [Session Events](#session-events) - [Agent Events](#agent-events) + - [Model Events](#model-events) - [Tool Events](#tool-events) - [ExtensionContext](#extensioncontext) - [ExtensionCommandContext](#extensioncommandcontext) @@ -323,8 +324,12 @@ user sends another prompt ◄───────────────── └─► session_tree /model or Ctrl+P (model selection/cycling) + ├─► thinking_level_select (if model change changes/clamps thinking level) └─► model_select +thinking level changes (settings, keybinding, pi.setThinkingLevel()) + └─► thinking_level_select + exit (Ctrl+C, Ctrl+D, SIGHUP, SIGTERM) └─► session_shutdown ``` @@ -649,6 +654,21 @@ 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. +#### thinking_level_select + +Fired when the thinking level changes. This is notification-only; handler return values are ignored. + +```typescript +pi.on("thinking_level_select", async (event, ctx) => { + // event.level - newly selected thinking level + // event.previousLevel - previous thinking level + + ctx.ui.setStatus("thinking", `thinking: ${event.level}`); +}); +``` + +Use this to update extension UI when `pi.setThinkingLevel()`, model changes, or built-in thinking-level controls change the active thinking level. + ### Tool Events #### tool_call @@ -1502,7 +1522,7 @@ if (model) { ### pi.getThinkingLevel() / pi.setThinkingLevel(level) -Get or set the thinking level. Level is clamped to model capabilities (non-reasoning models always use "off"). +Get or set the thinking level. Level is clamped to model capabilities (non-reasoning models always use "off"). Changes emit `thinking_level_select`. ```typescript const current = pi.getThinkingLevel(); // "off" | "minimal" | "low" | "medium" | "high" | "xhigh" diff --git a/packages/coding-agent/src/core/agent-session.ts b/packages/coding-agent/src/core/agent-session.ts index bf87179f..eb430a3c 100644 --- a/packages/coding-agent/src/core/agent-session.ts +++ b/packages/coding-agent/src/core/agent-session.ts @@ -120,6 +120,7 @@ export type AgentSessionEvent = } | { type: "compaction_start"; reason: "manual" | "threshold" | "overflow" } | { type: "session_info_changed"; name: string | undefined } + | { type: "thinking_level_changed"; level: ThinkingLevel } | { type: "compaction_end"; reason: "manual" | "threshold" | "overflow"; @@ -1505,7 +1506,8 @@ export class AgentSession { const effectiveLevel = availableLevels.includes(level) ? level : this._clampThinkingLevel(level, availableLevels); // Only persist if actually changing - const isChanging = effectiveLevel !== this.agent.state.thinkingLevel; + const previousLevel = this.agent.state.thinkingLevel; + const isChanging = effectiveLevel !== previousLevel; this.agent.state.thinkingLevel = effectiveLevel; @@ -1514,6 +1516,12 @@ export class AgentSession { if (this.supportsThinking() || effectiveLevel !== "off") { this.settingsManager.setDefaultThinkingLevel(effectiveLevel); } + this._emit({ type: "thinking_level_changed", level: effectiveLevel }); + void this._extensionRunner.emit({ + type: "thinking_level_select", + level: effectiveLevel, + previousLevel, + }); } } diff --git a/packages/coding-agent/src/core/extensions/types.ts b/packages/coding-agent/src/core/extensions/types.ts index 0df7649f..27643d8e 100644 --- a/packages/coding-agent/src/core/extensions/types.ts +++ b/packages/coding-agent/src/core/extensions/types.ts @@ -718,6 +718,13 @@ export interface ModelSelectEvent { source: ModelSelectSource; } +/** Fired when a new thinking level is selected */ +export interface ThinkingLevelSelectEvent { + type: "thinking_level_select"; + level: ThinkingLevel; + previousLevel: ThinkingLevel; +} + // ============================================================================ // User Bash Events // ============================================================================ @@ -958,6 +965,7 @@ export type ExtensionEvent = | ToolExecutionUpdateEvent | ToolExecutionEndEvent | ModelSelectEvent + | ThinkingLevelSelectEvent | UserBashEvent | InputEvent | ToolCallEvent @@ -1111,6 +1119,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: "thinking_level_select", handler: ExtensionHandler): void; on(event: "tool_call", handler: ExtensionHandler): void; on(event: "tool_result", handler: ExtensionHandler): void; on(event: "user_bash", 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 5b5ac0a9..3b2adb5d 100644 --- a/packages/coding-agent/src/modes/interactive/interactive-mode.ts +++ b/packages/coding-agent/src/modes/interactive/interactive-mode.ts @@ -2658,6 +2658,11 @@ export class InteractiveMode { this.ui.requestRender(); break; + case "thinking_level_changed": + this.footer.invalidate(); + this.updateEditorBorderColor(); + break; + case "message_start": if (event.message.role === "custom") { this.addMessageToChat(event.message);