test: Verified existing tests cover skill proposal writer and all four…

SF-Task: S03/T02
This commit is contained in:
Mikael Hugo 2026-04-30 19:33:16 +02:00
parent 69be7aeeaa
commit 8e4081e6f1
3 changed files with 45 additions and 6 deletions

View file

@ -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;
}

View file

@ -43,6 +43,22 @@ function hasAssistantToolBlocks(message: { content: Array<any> }): 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,
);

View file

@ -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,
);