diff --git a/CHANGELOG.md b/CHANGELOG.md index 84f94fbda0..2e53e9f87d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## [v5.3.1] - 2026-01-30 + +### Added + +- Retry button for 5xx streaming failures + +### Fixed + +- Fixed use_skill tool being included in system prompt when no skills are available + +--- + ## [v5.3.0] - 2026-01-29 ### Added diff --git a/src/api/providers/openrouter.ts b/src/api/providers/openrouter.ts index 37e603537e..77cc036051 100644 --- a/src/api/providers/openrouter.ts +++ b/src/api/providers/openrouter.ts @@ -230,7 +230,9 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH if (this.providerName == "KiloCode" && isAnyRecognizedKiloCodeError(error)) { throw error } - throw new Error(makeOpenRouterErrorReadable(error)) + const err = new Error(makeOpenRouterErrorReadable(error)) as any + err.status = error?.status || error?.code + throw err // kilocode_change end } @@ -247,7 +249,9 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH if ("error" in chunk) { const error = chunk.error as { message?: string; code?: number } console.error(`OpenRouter API Error: ${error?.code} - ${error?.message}`) - throw new Error(`OpenRouter API Error ${error?.code}: ${error?.message}`) + const err = new Error(`OpenRouter API Error ${error?.code}: ${error?.message}`) as any + err.status = error?.code + throw err } // kilocode_change start @@ -332,7 +336,9 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH } catch (error) { console.error("OpenRouter API Error:", error) let errorMessage = makeOpenRouterErrorReadable(error) - throw new Error(errorMessage) + const err = new Error(errorMessage) as any + err.status = error?.status || error?.code + throw err } if (lastUsage) { @@ -411,7 +417,9 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH if ("error" in response) { const error = response.error as { message?: string; code?: number } - throw new Error(`OpenRouter API Error ${error?.code}: ${error?.message}`) + const err = new Error(`OpenRouter API Error ${error?.code}: ${error?.message}`) as any + err.status = error?.code + throw err } const completion = response as OpenAI.Chat.ChatCompletion diff --git a/src/api/providers/utils/openai-error-handler.ts b/src/api/providers/utils/openai-error-handler.ts index 90be81f7c4..7ea8771834 100644 --- a/src/api/providers/utils/openai-error-handler.ts +++ b/src/api/providers/utils/openai-error-handler.ts @@ -17,13 +17,19 @@ export function handleOpenAIError(error: unknown, providerName: string): Error { // Invalid character/ByteString conversion error in API key if (msg.includes("Cannot convert argument to a ByteString")) { - return new Error(i18n.t("common:errors.api.invalidKeyInvalidChars")) + const err = new Error(i18n.t("common:errors.api.invalidKeyInvalidChars")) as any + err.status = (error as any).status + return err } // For other Error instances, wrap with provider-specific prefix - return new Error(`${providerName} completion error: ${msg}`) + const err = new Error(`${providerName} completion error: ${msg}`) as any + err.status = (error as any).status + return err } // Non-Error: wrap with provider-specific prefix - return new Error(`${providerName} completion error: ${String(error)}`) + const err = new Error(`${providerName} completion error: ${String(error)}`) as any + err.status = (error as any).status + return err } diff --git a/src/core/prompts/system.ts b/src/core/prompts/system.ts index 25bce28dac..8641363586 100644 --- a/src/core/prompts/system.ts +++ b/src/core/prompts/system.ts @@ -78,7 +78,10 @@ async function getSkillsSection(workspacePath: string): Promise { .join("\n") return `You are provided Skills below, these skills are to be used by you as per your descretion. The purpose of these skills is to provide you additional niche context for you tasks. You might get skills for React, Security or even third-party tools. Use the tool use_skill to get the skill context: -${skillList}` +${skillList} + +IMPORTANT: Skills are not tool calls such as read_file_with_content. +` } const applyDiffToolDescription = ` diff --git a/src/core/prompts/tools/index.ts b/src/core/prompts/tools/index.ts index ccd92c3199..f99da4262e 100644 --- a/src/core/prompts/tools/index.ts +++ b/src/core/prompts/tools/index.ts @@ -35,6 +35,7 @@ import { getGenerateImageDescription } from "./generate-image" import { getCheckPastChatMemoriesDescription } from "./check-past-chat-memories" import { getUseSkillDescription } from "./use-skill" import { CodeIndexManager } from "../../../services/code-index/manager" +import { discoverSkills } from "../../tools/skills" // kilocode_change start: Morph fast apply import { isFastApplyAvailable } from "../../tools/editFileTool" @@ -177,6 +178,12 @@ export async function getToolDescriptionsForMode( tools.delete("run_slash_command") } + // Conditionally exclude use_skill if no skills are available + const skills = await discoverSkills({ workspacePath: cwd }) + if (skills.length === 0) { + tools.delete("use_skill") + } + // Map tool descriptions for allowed tools const descriptions = await Promise.all( Array.from(tools).map(async (toolName) => { diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 049138cd1c..136564df5e 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -3289,7 +3289,11 @@ export class Task extends EventEmitter implements TaskLike { } // kilocode_change end // note that this api_req_failed ask is unique in that we only present this option if the api hasn't streamed any content yet (ie it fails on the first chunk due), as it would allow them to hit a retry button. However if the api failed mid-stream, it could be in any arbitrary state where some tools may have executed, so that error is handled differently and requires cancelling the task entirely. - if (autoApprovalEnabled && alwaysApproveResubmit) { + + // Check if this is a 5xx error - always show retry dialog for server errors + const isServerError = error.status && error.status >= 500 && error.status < 600 + + if (autoApprovalEnabled && alwaysApproveResubmit && !isServerError) { let errorMsg if (error.error?.metadata?.raw) { @@ -3403,7 +3407,11 @@ export class Task extends EventEmitter implements TaskLike { } // kilocode_change end + // Check if this is a 5xx error - always show retry dialog for server errors + const isServerError = error.status && Number(error.status) >= 500 && Number(error.status) < 600 + // For mid-stream failures, show the retry dialog to allow user to retry + // Always show retry dialog for 5xx server errors const { response } = await this.ask( "api_req_failed", error.message ?? JSON.stringify(serializeError(error), null, 2), diff --git a/src/package.json b/src/package.json index fce02c300f..e71b2dc674 100644 --- a/src/package.json +++ b/src/package.json @@ -3,7 +3,7 @@ "displayName": "%extension.displayName%", "description": "%extension.description%", "publisher": "matterai", - "version": "5.3.0", + "version": "5.3.1", "icon": "assets/icons/matterai-ic.png", "galleryBanner": { "color": "#FFFFFF", diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index 1babf2a09a..a7d92214f7 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -1740,6 +1740,20 @@ export const ChatRowContent = ({ switch (message.ask) { case "mistake_limit_reached": return + case "api_req_failed": + return ( + + ) case "command": return (
- {errorTitle}
@@ -154,11 +154,8 @@ export const ErrorRow = memo(
- {/* Error Icon */} - - {/* Error Title */} {errorTitle && ( @@ -219,6 +216,25 @@ export const ErrorRow = memo(
)} + {/* Retry Button - outside tooltip, below error row */} + {type === "api_failure" && message?.includes("Provider error:") && ( +
+ { + // This will be handled by the parent component + vscode.postMessage({ + type: "askResponse", + askResponse: "yesButtonClicked", + }) + }}> + + {t("chat:retry.title")} + +
+ )} +