From 8e4081e6f1f4eb2166a991193c7f8a32a5fdf958 Mon Sep 17 00:00:00 2001 From: Mikael Hugo Date: Thu, 30 Apr 2026 19:33:16 +0200 Subject: [PATCH] =?UTF-8?q?test:=20Verified=20existing=20tests=20cover=20s?= =?UTF-8?q?kill=20proposal=20writer=20and=20all=20four=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SF-Task: S03/T02 --- .../interactive/components/tool-execution.ts | 24 ++++++++++++++++++- .../controllers/chat-controller.ts | 22 ++++++++++++++--- .../src/modes/interactive/interactive-mode.ts | 5 ++-- 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts b/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts index 27615d8da..6620cba1d 100644 --- a/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +++ b/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts @@ -98,6 +98,7 @@ function renderToolFrame( label: string; status: string; tone: ToolFrameTone; + modelLabel?: string; }, ): string[] { const outerWidth = Math.max(20, width); @@ -110,7 +111,24 @@ function renderToolFrame( const border = (s: string) => theme.fg(borderColor, s); const leftStyled = theme.fg(labelColor, theme.bold(`• ${opts.label}`)); - const rightStyled = theme.fg(statusColor, opts.status); + const statusStyled = theme.fg(statusColor, opts.status); + let rightStyled = statusStyled; + if (opts.modelLabel) { + const separatorStyled = theme.fg("dim", " · "); + const modelWidth = outerWidth + - visibleWidth(leftStyled) + - visibleWidth(separatorStyled) + - visibleWidth(statusStyled) + - 2; + if (modelWidth >= 8) { + const modelStyled = truncateToWidth( + theme.fg("dim", `model ${opts.modelLabel}`), + modelWidth, + theme.fg("dim", "…"), + ); + rightStyled = `${modelStyled}${separatorStyled}${statusStyled}`; + } + } const gap = Math.max(1, outerWidth - visibleWidth(leftStyled) - visibleWidth(rightStyled)); const headerRow = `${leftStyled}${" ".repeat(gap)}${rightStyled}`; const headerPad = Math.max(0, outerWidth - visibleWidth(headerRow)); @@ -173,6 +191,7 @@ function formatCompactArgs(args: unknown, expanded: boolean): string { export interface ToolExecutionOptions { showImages?: boolean; // default: true (only used if terminal supports images) + modelLabel?: string; } type WriteHighlightCache = { @@ -195,6 +214,7 @@ export class ToolExecutionComponent extends Container { private args: any; private expanded = false; private showImages: boolean; + private modelLabel?: string; private isPartial = true; private toolDefinition?: ToolDefinition; private ui: TUI; @@ -233,6 +253,7 @@ export class ToolExecutionComponent extends Container { this.toolName = toolName; this.args = args; this.showImages = options.showImages ?? true; + this.modelLabel = options.modelLabel?.trim() || undefined; this.toolDefinition = toolDefinition; this.ui = ui; this.cwd = cwd; @@ -532,6 +553,7 @@ export class ToolExecutionComponent extends Container { label: frameLabel, status: frameStatus, tone: frameTone, + modelLabel: this.modelLabel, }); return framed.length > 0 ? ["", ...framed] : framed; } diff --git a/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts b/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts index df23ee8fe..7ceedeb92 100644 --- a/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +++ b/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts @@ -43,6 +43,22 @@ function hasAssistantToolBlocks(message: { content: Array }): boolean { return message.content.some((c) => c.type === "toolCall" || c.type === "serverToolUse"); } +function modelLabelFromAssistant(message: any): string | undefined { + if (message?.role !== "assistant") return undefined; + const provider = typeof message.provider === "string" ? message.provider.trim() : ""; + const model = typeof message.model === "string" ? message.model.trim() : ""; + return provider && model ? `${provider}/${model}` : undefined; +} + +function modelLabelFromHost(host: InteractiveModeStateHost): string | undefined { + const streamingLabel = modelLabelFromAssistant(host.streamingMessage); + if (streamingLabel) return streamingLabel; + const model = host.session?.model; + const provider = typeof model?.provider === "string" ? model.provider.trim() : ""; + const id = typeof model?.id === "string" ? model.id.trim() : ""; + return provider && id ? `${provider}/${id}` : undefined; +} + // Pick the latest non-empty text block that appears strictly before the most // recent tool call. Text blocks that come after the last tool call are still // streaming live into the chat container, so mirroring them into the pinned @@ -253,7 +269,7 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & { const component = new ToolExecutionComponent( content.name, content.arguments, - { showImages: host.settingsManager.getShowImages() }, + { showImages: host.settingsManager.getShowImages(), modelLabel: modelLabelFromAssistant(host.streamingMessage) }, host.getRegisteredToolDefinition(content.name), host.ui, ); @@ -268,7 +284,7 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & { const component = new ToolExecutionComponent( content.name, content.input ?? {}, - { showImages: host.settingsManager.getShowImages() }, + { showImages: host.settingsManager.getShowImages(), modelLabel: modelLabelFromAssistant(host.streamingMessage) }, undefined, host.ui, ); @@ -627,7 +643,7 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & { const component = new ToolExecutionComponent( event.toolName, event.args, - { showImages: host.settingsManager.getShowImages() }, + { showImages: host.settingsManager.getShowImages(), modelLabel: modelLabelFromHost(host) }, host.getRegisteredToolDefinition(event.toolName), host.ui, ); diff --git a/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts b/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts index c6bebc813..cccde7adb 100644 --- a/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +++ b/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts @@ -2208,13 +2208,14 @@ export class InteractiveMode { // Assistant messages need special handling for tool calls if (message.role === "assistant") { this.addMessageToChat(message); + const modelLabel = message.provider && message.model ? `${message.provider}/${message.model}` : undefined; // Render tool call components for (const content of message.content) { if (content.type === "toolCall") { const component = new ToolExecutionComponent( content.name, content.arguments, - { showImages: this.settingsManager.getShowImages() }, + { showImages: this.settingsManager.getShowImages(), modelLabel }, this.getRegisteredToolDefinition(content.name), this.ui, ); @@ -2241,7 +2242,7 @@ export class InteractiveMode { const component = new ToolExecutionComponent( content.name, content.input ?? {}, - { showImages: this.settingsManager.getShowImages() }, + { showImages: this.settingsManager.getShowImages(), modelLabel }, undefined, this.ui, );