diff --git a/apps/storybook/src/decorators/withLimitedWidth.tsx b/apps/storybook/src/decorators/withLimitedWidth.tsx new file mode 100644 index 00000000000..93b98ea6461 --- /dev/null +++ b/apps/storybook/src/decorators/withLimitedWidth.tsx @@ -0,0 +1,11 @@ +import type { Decorator } from "@storybook/react" + +// Function that creates a decorator with a limited width container +// Provides consistent centering and configurable max-width constraint +export const withLimitedWidth = (maxWidth: number = 600): Decorator => { + return (Story) => ( +
+ +
+ ) +} diff --git a/apps/storybook/stories/AutoApproveMenu.stories.tsx b/apps/storybook/stories/AutoApproveMenu.stories.tsx new file mode 100644 index 00000000000..8c4420b1374 --- /dev/null +++ b/apps/storybook/stories/AutoApproveMenu.stories.tsx @@ -0,0 +1,20 @@ +import type { Meta, StoryObj } from "@storybook/react" +import AutoApproveMenu from "../../../webview-ui/src/components/chat/AutoApproveMenu" +import { withLimitedWidth } from "../src/decorators/withLimitedWidth" + +const meta: Meta = { + title: "Chat/AutoApproveMenu", + component: AutoApproveMenu, + decorators: [withLimitedWidth(400)], +} + +export default meta +type Story = StoryObj + +export const Collapsed: Story = {} + +export const Expanded: Story = { + args: { + initialExpanded: true, + }, +} diff --git a/apps/storybook/stories/AutoApproveSettings.stories.tsx b/apps/storybook/stories/AutoApproveSettings.stories.tsx new file mode 100644 index 00000000000..b8bbf9002e3 --- /dev/null +++ b/apps/storybook/stories/AutoApproveSettings.stories.tsx @@ -0,0 +1,16 @@ +import type { Meta, StoryObj } from "@storybook/react" +import { AutoApproveSettings } from "../../../webview-ui/src/components/settings/AutoApproveSettings" +import { withLimitedWidth } from "../src/decorators/withLimitedWidth" + +const meta: Meta = { + title: "Settings/AutoApproveSettings", + component: AutoApproveSettings, + decorators: [withLimitedWidth(600)], +} + +export default meta +type Story = StoryObj + +export const Default: Story = { + args: {}, +} diff --git a/apps/storybook/stories/DecoratedVSCodeTextField.stories.tsx b/apps/storybook/stories/DecoratedVSCodeTextField.stories.tsx new file mode 100644 index 00000000000..280d6dfc61e --- /dev/null +++ b/apps/storybook/stories/DecoratedVSCodeTextField.stories.tsx @@ -0,0 +1,61 @@ +import type { Meta, StoryObj } from "@storybook/react" +import { DecoratedVSCodeTextField } from "../../../webview-ui/src/components/common/DecoratedVSCodeTextField" + +const meta: Meta = { + title: "Component/DecoratedVSCodeTextField", + component: DecoratedVSCodeTextField, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + argTypes: { + placeholder: { + control: "text", + description: "Placeholder text for the input", + }, + value: { + control: "text", + description: "Current value of the input", + }, + disabled: { + control: "boolean", + description: "Whether the input is disabled", + }, + leftNodes: { + control: false, + description: "Array of React nodes to display on the left side of the input", + }, + rightNodes: { + control: false, + description: "Array of React nodes to display on the right side of the input", + }, + }, +} + +export default meta +type Story = StoryObj + +export const Default: Story = { + args: { + value: "", + placeholder: "Enter text...", + }, +} + +export const WithBothNodes: Story = { + args: { + value: "", + placeholder: "0.00", + leftNodes: [$], + rightNodes: [USD], + }, +} + +export const Disabled: Story = { + args: { + placeholder: "Disabled input", + leftNodes: [$], + disabled: true, + value: "100.00", + }, +} diff --git a/packages/types/src/global-settings.ts b/packages/types/src/global-settings.ts index a712e370145..2b82f5653b3 100644 --- a/packages/types/src/global-settings.ts +++ b/packages/types/src/global-settings.ts @@ -69,6 +69,7 @@ export const globalSettingsSchema = z.object({ commandTimeoutAllowlist: z.array(z.string()).optional(), preventCompletionWithOpenTodos: z.boolean().optional(), allowedMaxRequests: z.number().nullish(), + allowedMaxCost: z.number().nullish(), // kilocode_change autoCondenseContext: z.boolean().optional(), autoCondenseContextPercent: z.number().optional(), maxConcurrentFileReads: z.number().optional(), diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index f684b565898..8ff6aa2a979 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -165,6 +165,7 @@ export class Task extends EventEmitter { api: ApiHandler private static lastGlobalApiRequestTime?: number private consecutiveAutoApprovedRequestsCount: number = 0 + private consecutiveAutoApprovedCost: number = 0 // kilocode_change /** * Reset the global API request timestamp. This should only be used for testing. @@ -2100,13 +2101,32 @@ export class Task extends EventEmitter { this.consecutiveAutoApprovedRequestsCount++ if (this.consecutiveAutoApprovedRequestsCount > maxRequests) { - const { response } = await this.ask("auto_approval_max_req_reached", JSON.stringify({ count: maxRequests })) + const { response } = await this.ask( + "auto_approval_max_req_reached", + JSON.stringify({ count: maxRequests, type: "requests" }), + ) // If we get past the promise, it means the user approved and did not start a new task if (response === "yesButtonClicked") { this.consecutiveAutoApprovedRequestsCount = 0 } } + // Check if we've reached the maximum allowed cost + const maxCost = state?.allowedMaxCost || Infinity + const currentCost = getApiMetrics(this.combineMessages(this.clineMessages.slice(1))).totalCost + this.consecutiveAutoApprovedCost = currentCost + + if (this.consecutiveAutoApprovedCost > maxCost) { + const { response } = await this.ask( + "auto_approval_max_req_reached", + JSON.stringify({ count: maxCost.toFixed(2), type: "cost" }), + ) + // If we get past the promise, it means the user approved and did not start a new task + if (response === "yesButtonClicked") { + this.consecutiveAutoApprovedCost = 0 + } + } + const metadata: ApiHandlerCreateMessageMetadata = { mode: mode, taskId: this.taskId, diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 7caa91e2fcf..96aeac6ef98 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -1440,6 +1440,7 @@ export class ClineProvider alwaysAllowSubtasks, alwaysAllowUpdateTodoList, allowedMaxRequests, + allowedMaxCost, // kilocode_change autoCondenseContext, autoCondenseContextPercent, soundEnabled, @@ -1539,6 +1540,7 @@ export class ClineProvider alwaysAllowSubtasks: alwaysAllowSubtasks ?? true, alwaysAllowUpdateTodoList: alwaysAllowUpdateTodoList ?? true, allowedMaxRequests, + allowedMaxCost, // kilocode_change autoCondenseContext: autoCondenseContext ?? true, autoCondenseContextPercent: autoCondenseContextPercent ?? 100, uriScheme: vscode.env.uriScheme, @@ -1725,6 +1727,7 @@ export class ClineProvider followupAutoApproveTimeoutMs: stateValues.followupAutoApproveTimeoutMs ?? 60000, diagnosticsEnabled: stateValues.diagnosticsEnabled ?? true, allowedMaxRequests: stateValues.allowedMaxRequests, + allowedMaxCost: stateValues.allowedMaxCost, // kilocode_change autoCondenseContext: stateValues.autoCondenseContext ?? true, autoCondenseContextPercent: stateValues.autoCondenseContextPercent ?? 100, taskHistory: stateValues.taskHistory, diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index bcead83fc9a..7124c3d2b59 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -350,6 +350,12 @@ export const webviewMessageHandler = async ( await updateGlobalState("allowedMaxRequests", message.value) await provider.postStateToWebview() break + // kilocode_change start + case "allowedMaxCost": + await updateGlobalState("allowedMaxCost", message.value) + await provider.postStateToWebview() + break + // kilocode_change end case "alwaysAllowSubtasks": await updateGlobalState("alwaysAllowSubtasks", message.bool) await provider.postStateToWebview() diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index e745fd1dc87..36eecbe13f7 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -249,6 +249,7 @@ export type ExtensionState = Pick< | "allowedCommands" | "deniedCommands" | "allowedMaxRequests" + | "allowedMaxCost" // kilocode_change | "browserToolEnabled" | "browserViewportSize" | "showAutoApproveMenu" // kilocode_change diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index c393d7a0e6b..d26de8fa3cc 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -90,6 +90,7 @@ export interface WebviewMessage { | "alwaysAllowMcp" | "alwaysAllowModeSwitch" | "allowedMaxRequests" + | "allowedMaxCost" // kilocode_change | "alwaysAllowSubtasks" | "alwaysAllowUpdateTodoList" | "autoCondenseContext" diff --git a/webview-ui/src/components/chat/AutoApproveMenu.tsx b/webview-ui/src/components/chat/AutoApproveMenu.tsx index 5705ecdbf45..c7804050a2f 100644 --- a/webview-ui/src/components/chat/AutoApproveMenu.tsx +++ b/webview-ui/src/components/chat/AutoApproveMenu.tsx @@ -6,23 +6,25 @@ import { vscode } from "@src/utils/vscode" import { useExtensionState } from "@src/context/ExtensionStateContext" import { useAppTranslation } from "@src/i18n/TranslationContext" import { AutoApproveToggle, AutoApproveSetting, autoApproveSettingsConfig } from "../settings/AutoApproveToggle" -import { MaxRequestsInput } from "../settings/MaxRequestsInput" // kilocode_change +import { MaxLimitInputs } from "../settings/MaxLimitInputs" // kilocode_change import { StandardTooltip } from "@src/components/ui" import { useAutoApprovalState } from "@src/hooks/useAutoApprovalState" import { useAutoApprovalToggles } from "@src/hooks/useAutoApprovalToggles" interface AutoApproveMenuProps { style?: React.CSSProperties + initialExpanded?: boolean } -const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => { - const [isExpanded, setIsExpanded] = useState(false) +const AutoApproveMenu = ({ style, initialExpanded = false }: AutoApproveMenuProps) => { + const [isExpanded, setIsExpanded] = useState(initialExpanded) const { autoApprovalEnabled, setAutoApprovalEnabled, alwaysApproveResubmit, allowedMaxRequests, + allowedMaxCost, // kilcode_change setAlwaysAllowReadOnly, setAlwaysAllowWrite, setAlwaysAllowExecute, @@ -34,6 +36,7 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => { setAlwaysAllowFollowupQuestions, setAlwaysAllowUpdateTodoList, setAllowedMaxRequests, + setAllowedMaxCost, // kilcode_change } = useExtensionState() const { t } = useAppTranslation() @@ -245,9 +248,11 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => { {/* kilocode_change start */} - setAllowedMaxRequests(value)} + allowedMaxCost={allowedMaxCost ?? undefined} + onMaxRequestsChange={(value) => setAllowedMaxRequests(value)} + onMaxCostChange={(value) => setAllowedMaxCost(value)} /> {/* kilocode_change end */} diff --git a/webview-ui/src/components/chat/AutoApprovedRequestLimitWarning.tsx b/webview-ui/src/components/chat/AutoApprovedRequestLimitWarning.tsx index 1c454e0082f..6f348a99954 100644 --- a/webview-ui/src/components/chat/AutoApprovedRequestLimitWarning.tsx +++ b/webview-ui/src/components/chat/AutoApprovedRequestLimitWarning.tsx @@ -12,18 +12,31 @@ type AutoApprovedRequestLimitWarningProps = { export const AutoApprovedRequestLimitWarning = memo(({ message }: AutoApprovedRequestLimitWarningProps) => { const [buttonClicked, setButtonClicked] = useState(false) - const { count } = JSON.parse(message.text ?? "{}") + const { count, type = "requests" } = JSON.parse(message.text ?? "{}") // kilcode_change if (buttonClicked) { return null } + // kilcode_change start + const isCostLimit = type === "cost" + const titleKey = isCostLimit + ? "ask.autoApprovedCostLimitReached.title" + : "ask.autoApprovedRequestLimitReached.title" + const descriptionKey = isCostLimit + ? "ask.autoApprovedCostLimitReached.description" + : "ask.autoApprovedRequestLimitReached.description" + const buttonKey = isCostLimit + ? "ask.autoApprovedCostLimitReached.button" + : "ask.autoApprovedRequestLimitReached.button" + // kilcode_change end + return ( <>
- +
@@ -37,7 +50,7 @@ export const AutoApprovedRequestLimitWarning = memo(({ message }: AutoApprovedRe justifyContent: "center", }}>
- +
- + diff --git a/webview-ui/src/components/chat/__tests__/AutoApproveMenu.spec.tsx b/webview-ui/src/components/chat/__tests__/AutoApproveMenu.spec.tsx index 185e5eeec6e..bff5a32cad7 100644 --- a/webview-ui/src/components/chat/__tests__/AutoApproveMenu.spec.tsx +++ b/webview-ui/src/components/chat/__tests__/AutoApproveMenu.spec.tsx @@ -34,7 +34,6 @@ vi.mock("@src/i18n/TranslationContext", () => ({ "settings:autoApprove.updateTodoList.label": "Update todo list", "settings:autoApprove.apiRequestLimit.title": "API request limit", "settings:autoApprove.apiRequestLimit.unlimited": "Unlimited", - "settings:autoApprove.apiRequestLimit.description": "Limit the number of API requests", "settings:autoApprove.readOnly.outsideWorkspace": "Also allow outside workspace", "settings:autoApprove.write.outsideWorkspace": "Also allow outside workspace", "settings:autoApprove.write.delay": "Delay", diff --git a/webview-ui/src/components/common/DecoratedVSCodeTextField.tsx b/webview-ui/src/components/common/DecoratedVSCodeTextField.tsx new file mode 100644 index 00000000000..7bec2e193e2 --- /dev/null +++ b/webview-ui/src/components/common/DecoratedVSCodeTextField.tsx @@ -0,0 +1,95 @@ +// kilcode_change - new file +import { cn } from "@/lib/utils" +import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" +import { forwardRef, useCallback, useRef, ReactNode, ComponentRef, ComponentProps } from "react" + +// Type for web components that have shadow DOM +interface WebComponentWithShadowRoot extends HTMLElement { + shadowRoot: ShadowRoot | null +} + +export interface VSCodeTextFieldWithNodesProps extends ComponentProps { + leftNodes?: ReactNode[] + rightNodes?: ReactNode[] +} + +function VSCodeTextFieldWithNodesInner( + props: VSCodeTextFieldWithNodesProps, + forwardedRef: React.Ref, +) { + const { className, style, "data-testid": dataTestId, leftNodes, rightNodes, ...restProps } = props + + const inputRef = useRef(null) + const vscodeFieldRef = useRef(null) + + // Callback ref to get access to the underlying input element. + // VSCodeTextField doesn't expose this directly so we have to query for it! + const handleVSCodeFieldRef = useCallback( + (element: ComponentRef) => { + vscodeFieldRef.current = element + if (!element) return + + const webComponent = element as unknown as WebComponentWithShadowRoot + const inputElement = + webComponent.shadowRoot?.querySelector?.("input") || webComponent.querySelector?.("input") + if (inputElement && inputElement instanceof HTMLInputElement) { + inputRef.current = inputElement + if (typeof forwardedRef === "function") { + forwardedRef?.(inputElement) + } else if (forwardedRef) { + ;(forwardedRef as React.MutableRefObject).current = inputElement + } + } + }, + [forwardedRef], + ) + + const focusInput = useCallback(async () => { + if (inputRef.current && document.activeElement !== inputRef.current) { + setTimeout(() => { + inputRef.current?.focus() + }) + } + }, []) + + const hasLeftNodes = leftNodes && leftNodes.filter(Boolean).length > 0 + const hasRightNodes = rightNodes && rightNodes.filter(Boolean).length > 0 + + return ( +
+ {hasLeftNodes && ( +
{leftNodes}
+ )} + + + + {hasRightNodes && ( +
{rightNodes}
+ )} + + {/* Absolutely positioned focus border overlay */} +
+
+ ) +} + +export const DecoratedVSCodeTextField = forwardRef(VSCodeTextFieldWithNodesInner) diff --git a/webview-ui/src/components/common/FormattedTextField.tsx b/webview-ui/src/components/common/FormattedTextField.tsx new file mode 100644 index 00000000000..9fcedf64112 --- /dev/null +++ b/webview-ui/src/components/common/FormattedTextField.tsx @@ -0,0 +1,85 @@ +// kilocode_change - new file +import { useCallback, forwardRef } from "react" +import { DecoratedVSCodeTextField, VSCodeTextFieldWithNodesProps } from "./DecoratedVSCodeTextField" + +export interface InputFormatter { + /** + * Parse the raw input string into the typed value + */ + parse: (input: string) => T | undefined + + /** + * Format the typed value for display in the input field + */ + format: (value: T | undefined) => string + + /** + * Filter/transform the input as the user types (optional) + */ + filter?: (input: string) => string +} + +interface FormattedTextFieldProps extends Omit { + value: T | undefined + onValueChange: (value: T | undefined) => void + formatter: InputFormatter +} + +function FormattedTextFieldInner( + { value, onValueChange, formatter, ...restProps }: FormattedTextFieldProps, + forwardedRef: React.Ref, +) { + const handleInput = useCallback( + (e: React.FormEvent) => { + const input = e.target as HTMLInputElement + + if (formatter.filter) { + input.value = formatter.filter(input.value) + } + + const parsedValue = formatter.parse(input.value) + onValueChange(parsedValue) + }, + [formatter, onValueChange], + ) + + const displayValue = formatter.format(value) + + return +} + +export const FormattedTextField = forwardRef(FormattedTextFieldInner as any) as ( + props: FormattedTextFieldProps & { ref?: React.Ref }, +) => React.ReactElement + +// Common formatters for reuse +export const unlimitedIntegerFormatter: InputFormatter = { + parse: (input: string) => { + if (input.trim() === "") return undefined + const value = parseInt(input) + return !isNaN(value) && value > 0 ? value : undefined + }, + format: (value: number | undefined) => { + return value === undefined || value === Infinity ? "" : value.toString() + }, + filter: (input: string) => input.replace(/[^0-9]/g, ""), +} + +export const unlimitedDecimalFormatter: InputFormatter = { + parse: (input: string) => { + if (input.trim() === "") return undefined + const value = parseFloat(input) + return !isNaN(value) && value >= 0 ? value : undefined + }, + format: (value: number | undefined) => { + return value === undefined || value === Infinity ? "" : value.toString() + }, + filter: (input: string) => { + let cleanValue = input.replace(/[^0-9.]/g, "") + const parts = cleanValue.split(".") + if (parts.length > 2) { + cleanValue = parts[0] + "." + parts.slice(1).join("") + } + return cleanValue + }, +} diff --git a/webview-ui/src/components/common/__tests__/DecoratedVSCodeTextField.spec.tsx b/webview-ui/src/components/common/__tests__/DecoratedVSCodeTextField.spec.tsx new file mode 100644 index 00000000000..81dd541bd03 --- /dev/null +++ b/webview-ui/src/components/common/__tests__/DecoratedVSCodeTextField.spec.tsx @@ -0,0 +1,78 @@ +import { render, screen } from "@testing-library/react" +import { DecoratedVSCodeTextField } from "../DecoratedVSCodeTextField" + +describe("DecoratedVSCodeTextField", () => { + test("renders without nodes as standard VSCodeTextField", () => { + render() + + const input = screen.getByTestId("test-input") + expect(input).toBeInTheDocument() + }) + + test("renders with left nodes", () => { + render( + $]} + />, + ) + + const input = screen.getByTestId("test-input") + expect(input).toBeInTheDocument() + + // Check that the dollar sign is rendered + expect(screen.getByText("$")).toBeInTheDocument() + }) + + test("renders with right nodes", () => { + render( + USD]} + />, + ) + + const input = screen.getByTestId("test-input") + expect(input).toBeInTheDocument() + + // Check that the USD text is rendered + expect(screen.getByText("USD")).toBeInTheDocument() + }) + + test("renders with both left and right nodes", () => { + render( + $]} + rightNodes={[USD]} + />, + ) + + const input = screen.getByTestId("test-input") + expect(input).toBeInTheDocument() + + // Check that both nodes are rendered + expect(screen.getByText("$")).toBeInTheDocument() + expect(screen.getByText("USD")).toBeInTheDocument() + }) + + test("handles multiple left nodes", () => { + render( + 🔍, Search]} + />, + ) + + const input = screen.getByTestId("test-input") + expect(input).toBeInTheDocument() + + // Check that both left nodes are rendered + expect(screen.getByText("🔍")).toBeInTheDocument() + expect(screen.getByText("Search")).toBeInTheDocument() + }) +}) diff --git a/webview-ui/src/components/common/__tests__/FormattedTextField.spec.tsx b/webview-ui/src/components/common/__tests__/FormattedTextField.spec.tsx new file mode 100644 index 00000000000..04c9feec6b4 --- /dev/null +++ b/webview-ui/src/components/common/__tests__/FormattedTextField.spec.tsx @@ -0,0 +1,114 @@ +// kilocode_change - new file +import React from "react" +import { describe, it, expect, vi } from "vitest" +import { render, screen, fireEvent } from "@testing-library/react" +import { FormattedTextField, unlimitedIntegerFormatter } from "../FormattedTextField" + +// Mock VSCodeTextField to render as regular HTML input for testing +vi.mock("@vscode/webview-ui-toolkit/react", () => ({ + VSCodeTextField: ({ value, onInput, placeholder, "data-testid": dataTestId }: any) => ( + onInput({ target: { value: e.target.value } })} + placeholder={placeholder} + data-testid={dataTestId} + /> + ), +})) + +describe("FormattedTextField", () => { + describe("unlimitedIntegerFormatter", () => { + it("should parse valid integers", () => { + expect(unlimitedIntegerFormatter.parse("123")).toBe(123) + expect(unlimitedIntegerFormatter.parse("1")).toBe(1) + }) + + it("should return undefined for empty input (unlimited)", () => { + expect(unlimitedIntegerFormatter.parse("")).toBeUndefined() + expect(unlimitedIntegerFormatter.parse(" ")).toBeUndefined() + }) + + it("should return undefined for invalid inputs", () => { + expect(unlimitedIntegerFormatter.parse("0")).toBeUndefined() + expect(unlimitedIntegerFormatter.parse("-5")).toBeUndefined() + expect(unlimitedIntegerFormatter.parse("abc")).toBeUndefined() + }) + + it("should format numbers correctly, treating undefined/Infinity as empty", () => { + expect(unlimitedIntegerFormatter.format(123)).toBe("123") + expect(unlimitedIntegerFormatter.format(undefined)).toBe("") + expect(unlimitedIntegerFormatter.format(Infinity)).toBe("") + }) + + it("should filter non-numeric characters", () => { + expect(unlimitedIntegerFormatter.filter?.("123abc")).toBe("123") + expect(unlimitedIntegerFormatter.filter?.("a1b2c3")).toBe("123") + }) + }) + + describe("FormattedTextField component", () => { + it("should render with correct initial value", () => { + const mockOnChange = vi.fn() + render( + , + ) + + const input = screen.getByTestId("test-input") as HTMLInputElement + expect(input.value).toBe("123") + }) + + it("should render as HTML input (mock verification)", () => { + const mockOnChange = vi.fn() + render( + , + ) + + const input = screen.getByTestId("test-input") + expect(input.tagName).toBe("INPUT") + expect(input).toHaveAttribute("type", "text") + }) + + it("should call onValueChange when input changes", () => { + const mockOnChange = vi.fn() + render( + , + ) + + const input = screen.getByTestId("test-input") + fireEvent.change(input, { target: { value: "456" } }) + expect(mockOnChange).toHaveBeenCalledWith(456) + }) + + it("should apply input filtering", () => { + const mockOnChange = vi.fn() + render( + , + ) + + const input = screen.getByTestId("test-input") as HTMLInputElement + fireEvent.change(input, { target: { value: "123abc" } }) + expect(mockOnChange).toHaveBeenCalledWith(123) + }) + }) +}) diff --git a/webview-ui/src/components/settings/AutoApproveSettings.tsx b/webview-ui/src/components/settings/AutoApproveSettings.tsx index 3d2f9b832dc..e3abf783adf 100644 --- a/webview-ui/src/components/settings/AutoApproveSettings.tsx +++ b/webview-ui/src/components/settings/AutoApproveSettings.tsx @@ -10,7 +10,7 @@ import { SetCachedStateField } from "./types" import { SectionHeader } from "./SectionHeader" import { Section } from "./Section" import { AutoApproveToggle } from "./AutoApproveToggle" -import { MaxRequestsInput } from "./MaxRequestsInput" // kilocode_change +import { MaxLimitInputs } from "./MaxLimitInputs" // kilocode_change import { useExtensionState } from "@/context/ExtensionStateContext" import { useAutoApprovalState } from "@/hooks/useAutoApprovalState" import { useAutoApprovalToggles } from "@/hooks/useAutoApprovalToggles" @@ -33,6 +33,7 @@ type AutoApproveSettingsProps = HTMLAttributes & { followupAutoApproveTimeoutMs?: number allowedCommands?: string[] allowedMaxRequests?: number | undefined // kilocode_change + allowedMaxCost?: number | undefined // kilocode_change showAutoApproveMenu?: boolean // kilocode_change deniedCommands?: string[] setCachedStateField: SetCachedStateField< @@ -52,6 +53,7 @@ type AutoApproveSettingsProps = HTMLAttributes & { | "followupAutoApproveTimeoutMs" | "allowedCommands" | "allowedMaxRequests" // kilocode_change + | "allowedMaxCost" // kilocode_change | "showAutoApproveMenu" // kilocode_change | "deniedCommands" | "alwaysAllowUpdateTodoList" @@ -76,6 +78,7 @@ export const AutoApproveSettings = ({ alwaysAllowUpdateTodoList, allowedCommands, allowedMaxRequests, // kilocode_change + allowedMaxCost, // kilocode_change showAutoApproveMenu, // kilocode_change deniedCommands, setCachedStateField, @@ -176,9 +179,11 @@ export const AutoApproveSettings = ({ onToggle={(key, value) => setCachedStateField(key, value)} /> {/* kilocode_change start */} - setCachedStateField("allowedMaxRequests", value)} + allowedMaxCost={allowedMaxCost} + onMaxRequestsChange={(value) => setCachedStateField("allowedMaxRequests", value)} + onMaxCostChange={(value) => setCachedStateField("allowedMaxCost", value)} /> {/* kilocode_change end */} diff --git a/webview-ui/src/components/settings/MaxCostInput.tsx b/webview-ui/src/components/settings/MaxCostInput.tsx new file mode 100644 index 00000000000..a6cce64247c --- /dev/null +++ b/webview-ui/src/components/settings/MaxCostInput.tsx @@ -0,0 +1,43 @@ +// kilocode_change - new file +import { useTranslation } from "react-i18next" +import { vscode } from "@/utils/vscode" +import { useCallback } from "react" +import { FormattedTextField, unlimitedDecimalFormatter } from "../common/FormattedTextField" + +interface MaxCostInputProps { + allowedMaxCost?: number + onValueChange: (value: number | undefined) => void + className?: string +} + +export function MaxCostInput({ allowedMaxCost, onValueChange, className }: MaxCostInputProps) { + const { t } = useTranslation() + + const handleValueChange = useCallback( + (value: number | undefined) => { + onValueChange(value) + vscode.postMessage({ type: "allowedMaxCost", value }) + }, + [onValueChange], + ) + + return ( +
+
+ +
{t("settings:autoApprove.apiCostLimit.title")}
+
+
+ $]} + /> +
+
+ ) +} diff --git a/webview-ui/src/components/settings/MaxLimitInputs.tsx b/webview-ui/src/components/settings/MaxLimitInputs.tsx new file mode 100644 index 00000000000..544bd516852 --- /dev/null +++ b/webview-ui/src/components/settings/MaxLimitInputs.tsx @@ -0,0 +1,33 @@ +// kilocode_change - new file +import React from "react" +import { useTranslation } from "react-i18next" +import { MaxRequestsInput } from "./MaxRequestsInput" +import { MaxCostInput } from "./MaxCostInput" + +export interface MaxLimitInputsProps { + allowedMaxRequests?: number + allowedMaxCost?: number + onMaxRequestsChange: (value: number | undefined) => void + onMaxCostChange: (value: number | undefined) => void +} + +export const MaxLimitInputs: React.FC = ({ + allowedMaxRequests, + allowedMaxCost, + onMaxRequestsChange, + onMaxCostChange, +}) => { + const { t } = useTranslation() + + return ( +
+
+ + +
+
+ {t("settings:autoApprove.maxLimits.description")} +
+
+ ) +} diff --git a/webview-ui/src/components/settings/MaxRequestsInput.tsx b/webview-ui/src/components/settings/MaxRequestsInput.tsx index dae59ebc06a..c8af8fb473f 100644 --- a/webview-ui/src/components/settings/MaxRequestsInput.tsx +++ b/webview-ui/src/components/settings/MaxRequestsInput.tsx @@ -1,11 +1,10 @@ -// kilocode_change - new file -import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" import { useTranslation } from "react-i18next" import { vscode } from "@/utils/vscode" import { useCallback } from "react" +import { FormattedTextField, unlimitedIntegerFormatter } from "../common/FormattedTextField" interface MaxRequestsInputProps { - allowedMaxRequests?: number | undefined + allowedMaxRequests?: number onValueChange: (value: number | undefined) => void className?: string } @@ -13,38 +12,30 @@ interface MaxRequestsInputProps { export function MaxRequestsInput({ allowedMaxRequests, onValueChange, className }: MaxRequestsInputProps) { const { t } = useTranslation() - const handleInput = useCallback( - (e: any) => { - const input = e.target as HTMLInputElement - input.value = input.value.replace(/[^0-9]/g, "") - const value = parseInt(input.value) - const parsedValue = !isNaN(value) && value > 0 ? value : undefined - onValueChange(parsedValue) - vscode.postMessage({ type: "allowedMaxRequests", value: parsedValue }) + const handleValueChange = useCallback( + (value: number | undefined) => { + onValueChange(value) + vscode.postMessage({ type: "allowedMaxRequests", value }) }, [onValueChange], ) - const inputValue = (allowedMaxRequests ?? Infinity) === Infinity ? "" : allowedMaxRequests?.toString() - return ( -
+
{t("settings:autoApprove.apiRequestLimit.title")}
-
-
- {t("settings:autoApprove.apiRequestLimit.description")} -
) } diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index c3d1bd8a8f7..4ad287b8c59 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -148,6 +148,7 @@ const SettingsView = forwardRef(({ onDone, t allowedCommands, deniedCommands, allowedMaxRequests, + allowedMaxCost, // kilocode_change language, alwaysAllowBrowser, alwaysAllowExecute, @@ -346,6 +347,7 @@ const SettingsView = forwardRef(({ onDone, t vscode.postMessage({ type: "allowedCommands", commands: allowedCommands ?? [] }) vscode.postMessage({ type: "deniedCommands", commands: deniedCommands ?? [] }) vscode.postMessage({ type: "allowedMaxRequests", value: allowedMaxRequests ?? undefined }) + vscode.postMessage({ type: "allowedMaxCost", value: allowedMaxCost ?? undefined }) // kilocode_change vscode.postMessage({ type: "autoCondenseContext", bool: autoCondenseContext }) vscode.postMessage({ type: "autoCondenseContextPercent", value: autoCondenseContextPercent }) vscode.postMessage({ type: "browserToolEnabled", bool: browserToolEnabled }) @@ -726,6 +728,7 @@ const SettingsView = forwardRef(({ onDone, t followupAutoApproveTimeoutMs={followupAutoApproveTimeoutMs} allowedCommands={allowedCommands} allowedMaxRequests={allowedMaxRequests ?? undefined} + allowedMaxCost={allowedMaxCost ?? undefined} // kilocode_change deniedCommands={deniedCommands} setCachedStateField={setCachedStateField} /> diff --git a/webview-ui/src/components/settings/__tests__/MaxCostInput.spec.tsx b/webview-ui/src/components/settings/__tests__/MaxCostInput.spec.tsx new file mode 100644 index 00000000000..bdf509b9e77 --- /dev/null +++ b/webview-ui/src/components/settings/__tests__/MaxCostInput.spec.tsx @@ -0,0 +1,85 @@ +// kilocode_change - new file +import { render, screen, fireEvent } from "@testing-library/react" +import { vi } from "vitest" +import { MaxCostInput } from "../MaxCostInput" + +vi.mock("@/utils/vscode", () => ({ + vscode: { postMessage: vi.fn() }, +})) + +vi.mock("react-i18next", () => ({ + useTranslation: () => { + const translations: Record = { + "settings:autoApprove.apiCostLimit.title": "Max API Cost", + "settings:autoApprove.apiCostLimit.unlimited": "Unlimited", + } + return { t: (key: string) => translations[key] || key } + }, +})) + +describe("MaxCostInput", () => { + const mockOnValueChange = vi.fn() + + beforeEach(() => { + mockOnValueChange.mockClear() + }) + + it("shows empty input when allowedMaxCost is undefined", () => { + render() + + const input = screen.getByPlaceholderText("Unlimited") + expect(input).toHaveValue("") + }) + + it("shows formatted cost value when allowedMaxCost is provided", () => { + render() + + const input = screen.getByPlaceholderText("Unlimited") + expect(input).toHaveValue("5.5") + }) + + it("calls onValueChange when input changes", () => { + render() + + const input = screen.getByPlaceholderText("Unlimited") + fireEvent.input(input, { target: { value: "10.25" } }) + + expect(mockOnValueChange).toHaveBeenCalledWith(10.25) + }) + + it("calls onValueChange with undefined when input is cleared", () => { + render() + + const input = screen.getByPlaceholderText("Unlimited") + fireEvent.input(input, { target: { value: "" } }) + + expect(mockOnValueChange).toHaveBeenCalledWith(undefined) + }) + + it("handles decimal input correctly", () => { + render() + + const input = screen.getByPlaceholderText("Unlimited") + fireEvent.input(input, { target: { value: "2.99" } }) + + expect(mockOnValueChange).toHaveBeenCalledWith(2.99) + }) + + it("accepts zero as a valid value", () => { + render() + + const input = screen.getByPlaceholderText("Unlimited") + fireEvent.input(input, { target: { value: "0" } }) + + expect(mockOnValueChange).toHaveBeenCalledWith(0) + }) + + it("allows typing decimal values starting with zero", () => { + render() + + const input = screen.getByPlaceholderText("Unlimited") + fireEvent.input(input, { target: { value: "0.15" } }) + + expect(mockOnValueChange).toHaveBeenCalledWith(0.15) + }) +}) diff --git a/webview-ui/src/components/settings/__tests__/MaxRequestsInput.spec.tsx b/webview-ui/src/components/settings/__tests__/MaxRequestsInput.spec.tsx index df652b86db3..69d7961026b 100644 --- a/webview-ui/src/components/settings/__tests__/MaxRequestsInput.spec.tsx +++ b/webview-ui/src/components/settings/__tests__/MaxRequestsInput.spec.tsx @@ -1,4 +1,3 @@ -// kilocode_change - new file import { render, screen, fireEvent } from "@testing-library/react" import { vi } from "vitest" import { MaxRequestsInput } from "../MaxRequestsInput" @@ -10,9 +9,8 @@ vi.mock("@/utils/vscode", () => ({ vi.mock("react-i18next", () => ({ useTranslation: () => { const translations: Record = { - "settings:autoApprove.apiRequestLimit.title": "Max API Requests", + "settings:autoApprove.apiRequestLimit.title": "Max Count", "settings:autoApprove.apiRequestLimit.unlimited": "Unlimited", - "settings:autoApprove.apiRequestLimit.description": "Limit the number of API requests", } return { t: (key: string) => translations[key] || key } }, diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index cc5f7b98026..aaf5eee1489 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -88,6 +88,7 @@ export interface ExtensionStateContextType extends ExtensionState { setAllowedCommands: (value: string[]) => void setDeniedCommands: (value: string[]) => void setAllowedMaxRequests: (value: number | undefined) => void + setAllowedMaxCost: (value: number | undefined) => void // kilocode_change setSoundEnabled: (value: boolean) => void setSoundVolume: (value: number) => void terminalShellIntegrationTimeout?: number @@ -467,6 +468,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode setAllowedCommands: (value) => setState((prevState) => ({ ...prevState, allowedCommands: value })), setDeniedCommands: (value) => setState((prevState) => ({ ...prevState, deniedCommands: value })), setAllowedMaxRequests: (value) => setState((prevState) => ({ ...prevState, allowedMaxRequests: value })), + setAllowedMaxCost: (value) => setState((prevState) => ({ ...prevState, allowedMaxCost: value })), // kilocode_change setSoundEnabled: (value) => setState((prevState) => ({ ...prevState, soundEnabled: value })), setSoundVolume: (value) => setState((prevState) => ({ ...prevState, soundVolume: value })), setTtsEnabled: (value) => setState((prevState) => ({ ...prevState, ttsEnabled: value })), diff --git a/webview-ui/src/i18n/locales/ar/chat.json b/webview-ui/src/i18n/locales/ar/chat.json index e3d286ff599..4d1f3aa48ef 100644 --- a/webview-ui/src/i18n/locales/ar/chat.json +++ b/webview-ui/src/i18n/locales/ar/chat.json @@ -345,6 +345,11 @@ "title": "تم بلوغ حد الطلبات الموافق عليها تلقائياً", "description": "Kilo Code وصل لحد {{count}} طلب API موافَق عليه تلقائيًا. تبي تعيد العداد وتكمل المهمة؟", "button": "إعادة الضبط والمتابعة" + }, + "autoApprovedCostLimitReached": { + "button": "إعادة ضبط والاستمرار", + "title": "تم الوصول إلى حد التكلفة المعتمدة تلقائيًا", + "description": "وصل كيلو كود إلى حد التكلفة المعتمد تلقائيًا البالغ ${{count}}. هل ترغب في إعادة ضبط التكلفة والمتابعة بالمهمة؟" } }, "indexingStatus": { diff --git a/webview-ui/src/i18n/locales/ar/settings.json b/webview-ui/src/i18n/locales/ar/settings.json index d298284c763..f34de6cbb5e 100644 --- a/webview-ui/src/i18n/locales/ar/settings.json +++ b/webview-ui/src/i18n/locales/ar/settings.json @@ -201,12 +201,18 @@ }, "apiRequestLimit": { "title": "أقصى الطلبات", - "description": "عدد طلبات API قبل طلب الموافقة.", "unlimited": "غير محدود" }, "toggleAriaLabel": "تبديل الموافقة التلقائية", "disabledAriaLabel": "الموافقة التلقائية معطلة - اختر الخيارات أولاً", - "selectOptionsFirst": "اختر خيار واحد على الأقل أدناه لتفعيل الموافقة التلقائية" + "apiCostLimit": { + "title": "Cost màxim de l'API", + "unlimited": "Il·limitat" + }, + "selectOptionsFirst": "اختر خيار واحد على الأقل أدناه لتفعيل الموافقة التلقائية", + "maxLimits": { + "description": "قم تلقائياً بإجراء الطلبات حتى هذه الحدود قبل طلب الموافقة للاستمرار." + } }, "providers": { "providerDocumentation": "توثيق {{provider}}", diff --git a/webview-ui/src/i18n/locales/ca/chat.json b/webview-ui/src/i18n/locales/ca/chat.json index a479da777b5..338277915c4 100644 --- a/webview-ui/src/i18n/locales/ca/chat.json +++ b/webview-ui/src/i18n/locales/ca/chat.json @@ -319,6 +319,11 @@ "title": "S'ha arribat al límit de sol·licituds aprovades automàticament", "description": "Kilo Code ha arribat al límit aprovat automàticament de {{count}} sol·licitud(s) d'API. Vols reiniciar el comptador i continuar amb la tasca?", "button": "Reiniciar i continuar" + }, + "autoApprovedCostLimitReached": { + "title": "S'ha assolit el límit de cost d'aprovació automàtica", + "description": "Kilo Code ha arribat al límit de cost aprovat automàticament de ${{count}}. Voldríeu restablir el cost i continuar amb la tasca?", + "button": "Restablir i continuar" } }, "codebaseSearch": { diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index c22554aeb33..f65e6e5c389 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -199,10 +199,16 @@ }, "apiRequestLimit": { "title": "Màximes Sol·licituds", - "description": "Fes aquesta quantitat de sol·licituds API automàticament abans de demanar aprovació per continuar amb la tasca.", "unlimited": "Il·limitat" }, - "selectOptionsFirst": "Seleccioneu almenys una opció a continuació per activar l'aprovació automàtica" + "apiCostLimit": { + "title": "Cost màxim de l'API", + "unlimited": "Il·limitat" + }, + "selectOptionsFirst": "Seleccioneu almenys una opció a continuació per activar l'aprovació automàtica", + "maxLimits": { + "description": "Realitza sol·licituds automàticament fins a aquests límits abans de demanar aprovació per continuar." + } }, "providers": { "providerDocumentation": "Documentació de {{provider}}", diff --git a/webview-ui/src/i18n/locales/cs/chat.json b/webview-ui/src/i18n/locales/cs/chat.json index d1e936faf79..66e6f1bc0e6 100644 --- a/webview-ui/src/i18n/locales/cs/chat.json +++ b/webview-ui/src/i18n/locales/cs/chat.json @@ -341,6 +341,11 @@ "title": "Dosažen limit automaticky schválených požadavků", "description": "Kilo Code dosáhl automaticky schváleného limitu {{count}} API požadavků. Chceš resetovat počítadlo a pokračovat v úkolu?", "button": "Resetovat a pokračovat" + }, + "autoApprovedCostLimitReached": { + "title": "Dosažen limit automaticky schválených nákladů", + "description": "Kilo Code dosáhl automaticky schváleného limitu nákladů ${{count}}. Chcete resetovat náklady a pokračovat v úkolu?", + "button": "Resetovat a pokračovat" } }, "indexingStatus": { diff --git a/webview-ui/src/i18n/locales/cs/settings.json b/webview-ui/src/i18n/locales/cs/settings.json index 3b6a5f5e158..eef8fb6ca27 100644 --- a/webview-ui/src/i18n/locales/cs/settings.json +++ b/webview-ui/src/i18n/locales/cs/settings.json @@ -201,12 +201,18 @@ }, "apiRequestLimit": { "title": "Maximální počet žádostí", - "description": "Automaticky provést tento počet žádostí API předtím, než požádá o schválení k pokračování v úkolu.", "unlimited": "Neomezeně" }, + "apiCostLimit": { + "unlimited": "Neomezený", + "title": "Maximální náklady na API" + }, "toggleAriaLabel": "Přepnout automatické schválení", "disabledAriaLabel": "Automatické schválení zakázáno - nejprve vyberte možnosti", - "selectOptionsFirst": "Vyberte alespoň jednu možnost níže pro povolení automatického schválení" + "selectOptionsFirst": "Vyberte alespoň jednu možnost níže pro povolení automatického schválení", + "maxLimits": { + "description": "Automatické odesílání požadavků až do těchto limitů, než bude vyžadováno schválení k pokračování." + } }, "providers": { "providerDocumentation": "Dokumentace {{provider}}", diff --git a/webview-ui/src/i18n/locales/de/chat.json b/webview-ui/src/i18n/locales/de/chat.json index b27f34b3cf3..bd93fde783c 100644 --- a/webview-ui/src/i18n/locales/de/chat.json +++ b/webview-ui/src/i18n/locales/de/chat.json @@ -319,6 +319,11 @@ "title": "Limit für automatisch genehmigte Anfragen erreicht", "description": "Kilo Code hat das automatisch genehmigte Limit von {{count}} API-Anfrage(n) erreicht. Möchtest du den Zähler zurücksetzen und mit der Aufgabe fortfahren?", "button": "Zurücksetzen und fortfahren" + }, + "autoApprovedCostLimitReached": { + "title": "Automatisch genehmigte Kostengrenze erreicht", + "description": "Kilo Code hat das automatisch genehmigte Kostenlimit von ${{count}} erreicht. Möchten Sie die Kosten zurücksetzen und mit der Aufgabe fortfahren?", + "button": "Zurücksetzen und Fortfahren" } }, "codebaseSearch": { diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index 63e958d1589..45fb8b4ac5e 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -199,10 +199,16 @@ }, "apiRequestLimit": { "title": "Maximale Anfragen", - "description": "Automatisch so viele API-Anfragen stellen, bevor du um die Erlaubnis gebeten wirst, mit der Aufgabe fortzufahren.", "unlimited": "Unbegrenzt" }, - "selectOptionsFirst": "Wähle mindestens eine Option unten aus, um die automatische Genehmigung zu aktivieren" + "apiCostLimit": { + "title": "Max API-Kosten", + "unlimited": "Unbegrenzt" + }, + "selectOptionsFirst": "Wähle mindestens eine Option unten aus, um die automatische Genehmigung zu aktivieren", + "maxLimits": { + "description": "Stellt automatisch Anfragen bis zu diesen Limits, bevor um Genehmigung zur Fortsetzung gebeten wird." + } }, "providers": { "providerDocumentation": "{{provider}}-Dokumentation", diff --git a/webview-ui/src/i18n/locales/en/chat.json b/webview-ui/src/i18n/locales/en/chat.json index 6f9786c4fae..a9a0b31ba83 100644 --- a/webview-ui/src/i18n/locales/en/chat.json +++ b/webview-ui/src/i18n/locales/en/chat.json @@ -337,6 +337,11 @@ "title": "Auto-Approved Request Limit Reached", "description": "Kilo Code has reached the auto-approved limit of {{count}} API request(s). Would you like to reset the count and proceed with the task?", "button": "Reset and Continue" + }, + "autoApprovedCostLimitReached": { + "title": "Auto-Approved Cost Limit Reached", + "description": "Kilo Code has reached the auto-approved cost limit of ${{count}}. Would you like to reset the cost and proceed with the task?", + "button": "Reset and Continue" } }, "indexingStatus": { diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index 611eeca0f0e..58b37e872a8 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -196,10 +196,16 @@ "description": "Automatically update the to-do list without requiring approval" }, "apiRequestLimit": { - "title": "Max Requests", - "description": "Automatically make this many API requests before asking for approval to continue with the task.", + "title": "Max Count", "unlimited": "Unlimited" }, + "apiCostLimit": { + "title": "Max Cost", + "unlimited": "Unlimited" + }, + "maxLimits": { + "description": "Automatically make requests up to these limits before asking for approval to continue." + }, "toggleAriaLabel": "Toggle auto-approval", "disabledAriaLabel": "Auto-approval disabled - select options first", "selectOptionsFirst": "Select at least one option below to enable auto-approval" diff --git a/webview-ui/src/i18n/locales/es/chat.json b/webview-ui/src/i18n/locales/es/chat.json index 31a8fb446f9..a51ac14e63b 100644 --- a/webview-ui/src/i18n/locales/es/chat.json +++ b/webview-ui/src/i18n/locales/es/chat.json @@ -319,6 +319,11 @@ "title": "Límite de Solicitudes Auto-aprobadas Alcanzado", "description": "Kilo Code ha alcanzado el límite auto-aprobado de {{count}} solicitud(es) API. ¿Deseas reiniciar el contador y continuar con la tarea?", "button": "Reiniciar y Continuar" + }, + "autoApprovedCostLimitReached": { + "title": "Límite de Costos con Aprobación Automática Alcanzado", + "button": "Restablecer y continuar", + "description": "Kilo Code ha alcanzado el límite de costo aprobado automáticamente de ${{count}}. ¿Le gustaría restablecer el costo y continuar con la tarea?" } }, "codebaseSearch": { diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index 5602a755633..431f8106f81 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -199,10 +199,16 @@ }, "apiRequestLimit": { "title": "Solicitudes máximas", - "description": "Realizar automáticamente esta cantidad de solicitudes a la API antes de pedir aprobación para continuar con la tarea.", "unlimited": "Ilimitado" }, - "selectOptionsFirst": "Selecciona al menos una opción a continuación para habilitar la aprobación automática" + "apiCostLimit": { + "unlimited": "Ilimitado", + "title": "Costo máximo de API" + }, + "selectOptionsFirst": "Selecciona al menos una opción a continuación para habilitar la aprobación automática", + "maxLimits": { + "description": "Realizar automáticamente solicitudes hasta estos límites antes de pedir aprobación para continuar." + } }, "providers": { "providerDocumentation": "Documentación de {{provider}}", diff --git a/webview-ui/src/i18n/locales/fr/chat.json b/webview-ui/src/i18n/locales/fr/chat.json index 8130c85cdfc..d7e35ba3876 100644 --- a/webview-ui/src/i18n/locales/fr/chat.json +++ b/webview-ui/src/i18n/locales/fr/chat.json @@ -319,6 +319,11 @@ "title": "Limite de requêtes auto-approuvées atteinte", "description": "Kilo Code a atteint la limite auto-approuvée de {{count}} requête(s) API. Souhaitez-vous réinitialiser le compteur et poursuivre la tâche ?", "button": "Réinitialiser et continuer" + }, + "autoApprovedCostLimitReached": { + "title": "Limite de coûts auto-approuvés atteinte", + "button": "Réinitialiser et continuer", + "description": "Kilo Code a atteint la limite de coût à approbation automatique de ${{count}}. Souhaitez-vous réinitialiser le coût et poursuivre la tâche ?" } }, "codebaseSearch": { diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index e323eb9f2f5..019c265e65c 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -200,8 +200,14 @@ }, "apiRequestLimit": { "title": "Requêtes maximales", - "description": "Effectuer automatiquement ce nombre de requêtes API avant de demander l'approbation pour continuer la tâche.", "unlimited": "Illimité" + }, + "apiCostLimit": { + "title": "Coût API Max", + "unlimited": "Illimité" + }, + "maxLimits": { + "description": "Effectuer automatiquement des demandes jusqu'à ces limites avant de demander l'approbation pour continuer." } }, "providers": { diff --git a/webview-ui/src/i18n/locales/hi/chat.json b/webview-ui/src/i18n/locales/hi/chat.json index 4737e5b1b94..3e1fc2770ef 100644 --- a/webview-ui/src/i18n/locales/hi/chat.json +++ b/webview-ui/src/i18n/locales/hi/chat.json @@ -319,6 +319,11 @@ "title": "स्वत:-स्वीकृत अनुरोध सीमा पहुंची", "description": "Kilo Code {{count}} API अनुरोध(धों) की स्वत:-स्वीकृत सीमा तक पहुंच गया है। क्या आप गणना को रीसेट करके कार्य जारी रखना चाहते हैं?", "button": "रीसेट करें और जारी रखें" + }, + "autoApprovedCostLimitReached": { + "button": "रीसेट करें और जारी रखें", + "description": "किलो कोड ${{count}} की स्वतः-अनुमोदित लागत सीमा तक पहुँच गया है। क्या आप लागत को रीसेट करके कार्य जारी रखना चाहेंगे?", + "title": "स्वतः-स्वीकृत लागत सीमा तक पहुंच गए" } }, "codebaseSearch": { diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index 28aae9bab3b..554f75aee2d 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -199,10 +199,16 @@ }, "apiRequestLimit": { "title": "अधिकतम अनुरोध", - "description": "कार्य जारी रखने के लिए अनुमति मांगने से पहले स्वचालित रूप से इतने API अनुरोध करें।", "unlimited": "असीमित" }, - "selectOptionsFirst": "स्वतः-अनुमोदन सक्षम करने के लिए नीचे से कम से कम एक विकल्प चुनें" + "apiCostLimit": { + "title": "अधिकतम API लागत", + "unlimited": "असीमित" + }, + "selectOptionsFirst": "स्वतः-अनुमोदन सक्षम करने के लिए नीचे से कम से कम एक विकल्प चुनें", + "maxLimits": { + "description": "स्वचालित रूप से जारी रखने के लिए अनुमोदन मांगने से पहले इन सीमाओं तक अनुरोध करें।" + } }, "providers": { "providerDocumentation": "{{provider}} दस्तावेज़ीकरण", diff --git a/webview-ui/src/i18n/locales/id/chat.json b/webview-ui/src/i18n/locales/id/chat.json index e5d1013d8ab..95566c5ca7f 100644 --- a/webview-ui/src/i18n/locales/id/chat.json +++ b/webview-ui/src/i18n/locales/id/chat.json @@ -334,6 +334,11 @@ "title": "Batas Permintaan yang Disetujui Otomatis Tercapai", "description": "Kilo Code telah mencapai batas {{count}} permintaan API yang disetujui otomatis. Apakah kamu ingin mengatur ulang hitungan dan melanjutkan tugas?", "button": "Atur Ulang dan Lanjutkan" + }, + "autoApprovedCostLimitReached": { + "title": "Batas Biaya yang Disetujui Otomatis Telah Tercapai", + "button": "Atur Ulang dan Lanjutkan", + "description": "Kilo Code telah mencapai batas biaya yang disetujui otomatis sebesar ${{count}}. Apakah Anda ingin mengatur ulang biaya dan melanjutkan tugas ini?" } }, "indexingStatus": { diff --git a/webview-ui/src/i18n/locales/id/settings.json b/webview-ui/src/i18n/locales/id/settings.json index ebfefd58e97..9556ca3b6c5 100644 --- a/webview-ui/src/i18n/locales/id/settings.json +++ b/webview-ui/src/i18n/locales/id/settings.json @@ -199,10 +199,16 @@ }, "apiRequestLimit": { "title": "Permintaan Maks", - "description": "Secara otomatis membuat sejumlah permintaan API ini sebelum meminta persetujuan untuk melanjutkan tugas.", "unlimited": "Tidak terbatas" }, - "selectOptionsFirst": "Pilih setidaknya satu opsi di bawah ini untuk mengaktifkan persetujuan otomatis" + "apiCostLimit": { + "title": "Biaya API Maksimum", + "unlimited": "Tidak terbatas" + }, + "selectOptionsFirst": "Pilih setidaknya satu opsi di bawah ini untuk mengaktifkan persetujuan otomatis", + "maxLimits": { + "description": "Secara otomatis membuat permintaan hingga batas ini sebelum meminta persetujuan untuk melanjutkan." + } }, "providers": { "providerDocumentation": "Dokumentasi {{provider}}", diff --git a/webview-ui/src/i18n/locales/it/chat.json b/webview-ui/src/i18n/locales/it/chat.json index 993a7ae26fb..ac264a49fc8 100644 --- a/webview-ui/src/i18n/locales/it/chat.json +++ b/webview-ui/src/i18n/locales/it/chat.json @@ -319,6 +319,11 @@ "title": "Limite di Richieste Auto-approvate Raggiunto", "description": "Kilo Code ha raggiunto il limite auto-approvato di {{count}} richiesta/e API. Vuoi reimpostare il contatore e procedere con l'attività?", "button": "Reimposta e Continua" + }, + "autoApprovedCostLimitReached": { + "title": "Limite di costo auto-approvato raggiunto", + "description": "Kilo Code ha raggiunto il limite di costo approvato automaticamente di ${{count}}. Desideri reimpostare il costo e procedere con l'attività?", + "button": "Resetta e Continua" } }, "codebaseSearch": { diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index 32bd6f2f598..17c83313666 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -199,10 +199,16 @@ }, "apiRequestLimit": { "title": "Richieste massime", - "description": "Esegui automaticamente questo numero di richieste API prima di chiedere l'approvazione per continuare con l'attività.", "unlimited": "Illimitato" }, - "selectOptionsFirst": "Seleziona almeno un'opzione qui sotto per abilitare l'approvazione automatica" + "apiCostLimit": { + "unlimited": "Illimitato", + "title": "Costo API Massimo" + }, + "selectOptionsFirst": "Seleziona almeno un'opzione qui sotto per abilitare l'approvazione automatica", + "maxLimits": { + "description": "Effettua automaticamente richieste fino a questi limiti prima di chiedere l'approvazione per continuare." + } }, "providers": { "providerDocumentation": "Documentazione {{provider}}", diff --git a/webview-ui/src/i18n/locales/ja/chat.json b/webview-ui/src/i18n/locales/ja/chat.json index 4ec89f818bc..c5d85bde6aa 100644 --- a/webview-ui/src/i18n/locales/ja/chat.json +++ b/webview-ui/src/i18n/locales/ja/chat.json @@ -319,6 +319,11 @@ "title": "自動承認リクエスト制限に達しました", "description": "Kilo Codeは{{count}}件のAPI自動承認リクエスト制限に達しました。カウントをリセットしてタスクを続行しますか?", "button": "リセットして続行" + }, + "autoApprovedCostLimitReached": { + "title": "自動承認コスト上限に達しました", + "description": "Kilo Codeは自動承認コスト上限の${{count}}に達しました。コストをリセットしてタスクを続行しますか?", + "button": "リセットして続行" } }, "codebaseSearch": { diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index d2dab4b2272..9f4e7962376 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -199,10 +199,16 @@ }, "apiRequestLimit": { "title": "最大リクエスト数", - "description": "タスクを続行するための承認を求める前に、自動的にこの数のAPIリクエストを行います。", "unlimited": "無制限" }, - "selectOptionsFirst": "自動承認を有効にするには、以下のオプションを少なくとも1つ選択してください" + "apiCostLimit": { + "title": "最大 API コスト", + "unlimited": "無制限" + }, + "selectOptionsFirst": "自動承認を有効にするには、以下のオプションを少なくとも1つ選択してください", + "maxLimits": { + "description": "これらの制限まで自動的にリクエストを行い、続行の承認を求める前に処理します。" + } }, "providers": { "providerDocumentation": "{{provider}}のドキュメント", diff --git a/webview-ui/src/i18n/locales/ko/chat.json b/webview-ui/src/i18n/locales/ko/chat.json index 62105f92d3e..5c925bdef06 100644 --- a/webview-ui/src/i18n/locales/ko/chat.json +++ b/webview-ui/src/i18n/locales/ko/chat.json @@ -319,6 +319,11 @@ "title": "자동 승인 요청 한도 도달", "description": "Kilo Code가 {{count}}개의 API 요청(들)에 대한 자동 승인 한도에 도달했습니다. 카운트를 재설정하고 작업을 계속하시겠습니까?", "button": "재설정 후 계속" + }, + "autoApprovedCostLimitReached": { + "title": "자동 승인 비용 한도 도달", + "description": "Kilo Code가 자동 승인 비용 한도인 ${{count}}에 도달했습니다. 비용을 재설정하고 작업을 계속하시겠습니까?", + "button": "재설정하고 계속하기" } }, "codebaseSearch": { diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index 7adb792be2a..024b3805bc8 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -199,10 +199,16 @@ }, "apiRequestLimit": { "title": "최대 요청 수", - "description": "작업을 계속하기 위한 승인을 요청하기 전에 자동으로 이 수의 API 요청을 수행합니다.", "unlimited": "무제한" }, - "selectOptionsFirst": "자동 승인을 활성화하려면 아래에서 하나 이상의 옵션을 선택하세요" + "apiCostLimit": { + "title": "최대 API 비용", + "unlimited": "무제한" + }, + "selectOptionsFirst": "자동 승인을 활성화하려면 아래에서 하나 이상의 옵션을 선택하세요", + "maxLimits": { + "description": "승인을 요청하기 전에 이러한 한도까지 자동으로 요청합니다." + } }, "providers": { "providerDocumentation": "{{provider}} 문서", diff --git a/webview-ui/src/i18n/locales/nl/chat.json b/webview-ui/src/i18n/locales/nl/chat.json index 4c7ecd15c0d..18df604b4fa 100644 --- a/webview-ui/src/i18n/locales/nl/chat.json +++ b/webview-ui/src/i18n/locales/nl/chat.json @@ -319,6 +319,11 @@ "title": "Limiet voor automatisch goedgekeurde verzoeken bereikt", "description": "Kilo Code heeft de automatisch goedgekeurde limiet van {{count}} API-verzoek(en) bereikt. Wil je de teller resetten en doorgaan met de taak?", "button": "Resetten en doorgaan" + }, + "autoApprovedCostLimitReached": { + "title": "Limiet voor Automatisch Goedgekeurde Kosten Bereikt", + "button": "Resetten en Doorgaan", + "description": "Kilo Code heeft de automatisch goedgekeurde kostenlimiet van ${{count}} bereikt. Wilt u de kosten resetten en doorgaan met de taak?" } }, "codebaseSearch": { diff --git a/webview-ui/src/i18n/locales/nl/settings.json b/webview-ui/src/i18n/locales/nl/settings.json index 550620ae62c..a73bfa41efb 100644 --- a/webview-ui/src/i18n/locales/nl/settings.json +++ b/webview-ui/src/i18n/locales/nl/settings.json @@ -199,10 +199,16 @@ }, "apiRequestLimit": { "title": "Maximale verzoeken", - "description": "Voer automatisch dit aantal API-verzoeken uit voordat om goedkeuring wordt gevraagd om door te gaan met de taak.", "unlimited": "Onbeperkt" }, - "selectOptionsFirst": "Selecteer ten minste één optie hieronder om automatische goedkeuring in te schakelen" + "apiCostLimit": { + "unlimited": "Onbeperkt", + "title": "Maximale API-kosten" + }, + "selectOptionsFirst": "Selecteer ten minste één optie hieronder om automatische goedkeuring in te schakelen", + "maxLimits": { + "description": "Automatisch verzoeken indienen tot deze limieten voordat om goedkeuring wordt gevraagd om door te gaan." + } }, "providers": { "providerDocumentation": "{{provider}} documentatie", diff --git a/webview-ui/src/i18n/locales/pl/chat.json b/webview-ui/src/i18n/locales/pl/chat.json index 6399fd0e388..ebfa5210204 100644 --- a/webview-ui/src/i18n/locales/pl/chat.json +++ b/webview-ui/src/i18n/locales/pl/chat.json @@ -319,6 +319,11 @@ "title": "Osiągnięto limit automatycznie zatwierdzonych żądań", "description": "Kilo Code osiągnął automatycznie zatwierdzony limit {{count}} żądania/żądań API. Czy chcesz zresetować licznik i kontynuować zadanie?", "button": "Zresetuj i kontynuuj" + }, + "autoApprovedCostLimitReached": { + "title": "Osiągnięto limit automatycznie zatwierdzanych kosztów", + "button": "Zresetuj i kontynuuj", + "description": "Kilo Code osiągnął automatyczny limit kosztów w wysokości ${{count}}. Czy chcesz zresetować koszt i kontynuować zadanie?" } }, "codebaseSearch": { diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index 719c7017ccf..746f82f9da4 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -199,10 +199,16 @@ }, "apiRequestLimit": { "title": "Maksymalna liczba żądań", - "description": "Automatycznie wykonaj tyle żądań API przed poproszeniem o zgodę na kontynuowanie zadania.", "unlimited": "Bez limitu" }, - "selectOptionsFirst": "Wybierz co najmniej jedną opcję poniżej, aby włączyć automatyczne zatwierdzanie" + "apiCostLimit": { + "unlimited": "Bez limitu", + "title": "Maksymalny koszt API" + }, + "selectOptionsFirst": "Wybierz co najmniej jedną opcję poniżej, aby włączyć automatyczne zatwierdzanie", + "maxLimits": { + "description": "Automatycznie składaj zapytania do tych limitów przed poproszeniem o zgodę na kontynuację." + } }, "providers": { "providerDocumentation": "Dokumentacja {{provider}}", diff --git a/webview-ui/src/i18n/locales/pt-BR/chat.json b/webview-ui/src/i18n/locales/pt-BR/chat.json index 4760c357dc5..580ba065f47 100644 --- a/webview-ui/src/i18n/locales/pt-BR/chat.json +++ b/webview-ui/src/i18n/locales/pt-BR/chat.json @@ -319,6 +319,11 @@ "title": "Limite de Solicitações Auto-aprovadas Atingido", "description": "Kilo Code atingiu o limite auto-aprovado de {{count}} solicitação(ões) de API. Deseja redefinir a contagem e prosseguir com a tarefa?", "button": "Redefinir e Continuar" + }, + "autoApprovedCostLimitReached": { + "title": "Limite de Custo Aprovado Automaticamente Atingido", + "button": "Reiniciar e Continuar", + "description": "O Kilo Code atingiu o limite de custo aprovado automaticamente de ${{count}}. Você gostaria de redefinir o custo e continuar com a tarefa?" } }, "codebaseSearch": { diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index bc6fa757ef8..3366178c181 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -199,10 +199,16 @@ }, "apiRequestLimit": { "title": "Máximo de Solicitações", - "description": "Fazer automaticamente este número de requisições à API antes de pedir aprovação para continuar com a tarefa.", "unlimited": "Ilimitado" }, - "selectOptionsFirst": "Selecione pelo menos uma opção abaixo para habilitar a aprovação automática" + "apiCostLimit": { + "title": "Custo Máximo de API", + "unlimited": "Ilimitado" + }, + "selectOptionsFirst": "Selecione pelo menos uma opção abaixo para habilitar a aprovação automática", + "maxLimits": { + "description": "Faça automaticamente solicitações até estes limites antes de pedir aprovação para continuar." + } }, "providers": { "providerDocumentation": "Documentação do {{provider}}", diff --git a/webview-ui/src/i18n/locales/ru/chat.json b/webview-ui/src/i18n/locales/ru/chat.json index ceb9b50222c..ced1004fc03 100644 --- a/webview-ui/src/i18n/locales/ru/chat.json +++ b/webview-ui/src/i18n/locales/ru/chat.json @@ -319,6 +319,11 @@ "title": "Достигнут лимит автоматически одобренных запросов", "description": "Kilo Code достиг автоматически одобренного лимита в {{count}} API-запрос(ов). Хотите сбросить счетчик и продолжить задачу?", "button": "Сбросить и продолжить" + }, + "autoApprovedCostLimitReached": { + "title": "Достигнут лимит автоматически одобренных расходов", + "button": "Сбросить и Продолжить", + "description": "Kilo Code достиг автоматически утвержденного лимита стоимости в ${{count}}. Хотите ли вы сбросить стоимость и продолжить выполнение задачи?" } }, "codebaseSearch": { diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json index e7758b73a16..a303e1a0e2d 100644 --- a/webview-ui/src/i18n/locales/ru/settings.json +++ b/webview-ui/src/i18n/locales/ru/settings.json @@ -199,10 +199,16 @@ }, "apiRequestLimit": { "title": "Максимум запросов", - "description": "Автоматически выполнять это количество API-запросов перед запросом разрешения на продолжение задачи.", "unlimited": "Без ограничений" }, - "selectOptionsFirst": "Выберите хотя бы один вариант ниже, чтобы включить автоодобрение" + "apiCostLimit": { + "title": "Максимальная стоимость API", + "unlimited": "Безлимитный" + }, + "selectOptionsFirst": "Выберите хотя бы один вариант ниже, чтобы включить автоодобрение", + "maxLimits": { + "description": "Автоматически выполнять запросы до этих лимитов перед запросом на разрешение продолжить." + } }, "providers": { "providerDocumentation": "Документация {{provider}}", diff --git a/webview-ui/src/i18n/locales/th/chat.json b/webview-ui/src/i18n/locales/th/chat.json index ff921389b39..3a7e6da0cf7 100644 --- a/webview-ui/src/i18n/locales/th/chat.json +++ b/webview-ui/src/i18n/locales/th/chat.json @@ -337,6 +337,11 @@ "title": "ถึงขีดจำกัดคำขอที่อนุมัติอัตโนมัติ", "description": "Kilo Code ถึงขีดจำกัดการอนุมัติอัตโนมัติของ {{count}} คำขอ API คุณต้องการรีเซ็ตจำนวนและดำเนินการต่อกับงานหรือไม่?", "button": "รีเซ็ตและดำเนินการต่อ" + }, + "autoApprovedCostLimitReached": { + "title": "ถึงขีดจำกัดค่าใช้จ่ายที่อนุมัติอัตโนมัติแล้ว", + "button": "รีเซ็ตและดำเนินการต่อ", + "description": "Kilo Code ได้ถึงขีดจำกัดค่าใช้จ่ายที่อนุมัติอัตโนมัติที่ ${{count}} แล้ว คุณต้องการรีเซ็ตค่าใช้จ่ายและดำเนินงานต่อหรือไม่?" } }, "indexingStatus": { diff --git a/webview-ui/src/i18n/locales/th/settings.json b/webview-ui/src/i18n/locales/th/settings.json index 6fbcf5c8d9a..861fe5a99d2 100644 --- a/webview-ui/src/i18n/locales/th/settings.json +++ b/webview-ui/src/i18n/locales/th/settings.json @@ -197,12 +197,18 @@ }, "apiRequestLimit": { "title": "คำขอสูงสุด", - "description": "ส่งคำขอ API จำนวนนี้โดยอัตโนมัติก่อนที่จะขออนุมัติเพื่อดำเนินการต่อกับงาน", + "unlimited": "ไม่จำกัด" + }, + "apiCostLimit": { + "title": "ค่าใช้จ่าย API สูงสุด", "unlimited": "ไม่จำกัด" }, "toggleAriaLabel": "สลับการอนุมัติอัตโนมัติ", "disabledAriaLabel": "การอนุมัติอัตโนมัติถูกปิดใช้งาน - เลือกตัวเลือกก่อน", - "selectOptionsFirst": "เลือกอย่างน้อยหนึ่งตัวเลือกด้านล่างเพื่อเปิดใช้งานการอนุมัติอัตโนมัติ" + "selectOptionsFirst": "เลือกอย่างน้อยหนึ่งตัวเลือกด้านล่างเพื่อเปิดใช้งานการอนุมัติอัตโนมัติ", + "maxLimits": { + "description": "ดำเนินการส่งคำร้องขอโดยอัตโนมัติจนถึงขีดจำกัดเหล่านี้ ก่อนที่จะขออนุมัติเพื่อดำเนินการต่อ" + } }, "providers": { "providerDocumentation": "เอกสารประกอบ {{provider}}", diff --git a/webview-ui/src/i18n/locales/tr/chat.json b/webview-ui/src/i18n/locales/tr/chat.json index d83206514e1..b0f23dd18c6 100644 --- a/webview-ui/src/i18n/locales/tr/chat.json +++ b/webview-ui/src/i18n/locales/tr/chat.json @@ -319,6 +319,11 @@ "title": "Otomatik Onaylanan İstek Limiti Aşıldı", "description": "Kilo Code, {{count}} API isteği/istekleri için otomatik onaylanan limite ulaştı. Sayacı sıfırlamak ve göreve devam etmek istiyor musunuz?", "button": "Sıfırla ve Devam Et" + }, + "autoApprovedCostLimitReached": { + "title": "Otomatik Onaylanan Maliyet Sınırına Ulaşıldı", + "button": "Sıfırla ve Devam Et", + "description": "Kilo Code otomatik olarak onaylanmış ${{count}} maliyet sınırına ulaştı. Maliyeti sıfırlamak ve göreve devam etmek ister misiniz?" } }, "codebaseSearch": { diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index acada18f1d6..55b5a4b1de6 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -199,10 +199,16 @@ }, "apiRequestLimit": { "title": "Maksimum İstek", - "description": "Göreve devam etmek için onay istemeden önce bu sayıda API isteği otomatik olarak yap.", "unlimited": "Sınırsız" }, - "selectOptionsFirst": "Otomatik onayı etkinleştirmek için aşağıdan en az bir seçenek seçin" + "apiCostLimit": { + "title": "Maksimum API Maliyeti", + "unlimited": "Sınırsız" + }, + "selectOptionsFirst": "Otomatik onayı etkinleştirmek için aşağıdan en az bir seçenek seçin", + "maxLimits": { + "description": "Bu limitlere kadar olan istekleri devam etmek için onay istemeden otomatik olarak yap." + } }, "providers": { "providerDocumentation": "{{provider}} Dokümantasyonu", diff --git a/webview-ui/src/i18n/locales/uk/chat.json b/webview-ui/src/i18n/locales/uk/chat.json index 6a0bc30db27..02963335f87 100644 --- a/webview-ui/src/i18n/locales/uk/chat.json +++ b/webview-ui/src/i18n/locales/uk/chat.json @@ -339,6 +339,11 @@ "title": "Досягнуто ліміт автосхвалених запитів", "description": "Kilo Code досяг автосхваленого ліміту {{count}} API запит(ів). Хочеш скинути лічильник і продовжити завдання?", "button": "Скинути і продовжити" + }, + "autoApprovedCostLimitReached": { + "title": "Досягнуто ліміт автоматично схвалених витрат", + "button": "Скинути та продовжити", + "description": "Kilo Code досяг автоматично схваленого ліміту витрат у розмірі ${{count}}. Бажаєте скинути витрати та продовжити виконання завдання?" } }, "indexingStatus": { diff --git a/webview-ui/src/i18n/locales/uk/settings.json b/webview-ui/src/i18n/locales/uk/settings.json index c1fba9d8d8e..5b67f37161b 100644 --- a/webview-ui/src/i18n/locales/uk/settings.json +++ b/webview-ui/src/i18n/locales/uk/settings.json @@ -203,12 +203,18 @@ }, "apiRequestLimit": { "title": "Максимальна кількість запитів", - "description": "Автоматично робити таку кількість запитів API, перш ніж запитувати дозвіл на продовження завдання.", "unlimited": "Необмежено" }, + "apiCostLimit": { + "unlimited": "Безлімітно", + "title": "Максимальна вартість API" + }, "toggleAriaLabel": "Перемкнути автоматичне затвердження", "disabledAriaLabel": "Автоматичне затвердження вимкнено - спочатку виберіть параметри", - "selectOptionsFirst": "Виберіть принаймні один параметр нижче, щоб увімкнути автоматичне затвердження" + "selectOptionsFirst": "Виберіть принаймні один параметр нижче, щоб увімкнути автоматичне затвердження", + "maxLimits": { + "description": "Автоматично робити запити до цих лімітів, перш ніж запитувати дозвіл на продовження." + } }, "providers": { "providerDocumentation": "Документація {{provider}}", diff --git a/webview-ui/src/i18n/locales/vi/chat.json b/webview-ui/src/i18n/locales/vi/chat.json index a8494517de7..a687660eb4c 100644 --- a/webview-ui/src/i18n/locales/vi/chat.json +++ b/webview-ui/src/i18n/locales/vi/chat.json @@ -319,6 +319,11 @@ "title": "Đã Đạt Giới Hạn Yêu Cầu Tự Động Phê Duyệt", "description": "Kilo Code đã đạt đến giới hạn tự động phê duyệt là {{count}} yêu cầu API. Bạn có muốn đặt lại bộ đếm và tiếp tục nhiệm vụ không?", "button": "Đặt lại và Tiếp tục" + }, + "autoApprovedCostLimitReached": { + "title": "Đạt Giới Hạn Chi Phí Tự Động Phê Duyệt", + "description": "Kilo Code đã đạt đến giới hạn chi phí tự động phê duyệt là ${{count}}. Bạn có muốn đặt lại chi phí và tiếp tục với tác vụ không?", + "button": "Đặt lại và Tiếp tục" } }, "codebaseSearch": { diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index 36c64c1c6da..232dfec4812 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -199,10 +199,16 @@ }, "apiRequestLimit": { "title": "Số lượng yêu cầu tối đa", - "description": "Tự động thực hiện số lượng API request này trước khi yêu cầu phê duyệt để tiếp tục với nhiệm vụ.", "unlimited": "Không giới hạn" }, - "selectOptionsFirst": "Chọn ít nhất một tùy chọn bên dưới để bật tự động phê duyệt" + "apiCostLimit": { + "title": "Chi phí API Tối đa", + "unlimited": "Không giới hạn" + }, + "selectOptionsFirst": "Chọn ít nhất một tùy chọn bên dưới để bật tự động phê duyệt", + "maxLimits": { + "description": "Tự động thực hiện các yêu cầu trong phạm vi các giới hạn này trước khi xin phê duyệt để tiếp tục." + } }, "providers": { "providerDocumentation": "Tài liệu {{provider}}", diff --git a/webview-ui/src/i18n/locales/zh-CN/chat.json b/webview-ui/src/i18n/locales/zh-CN/chat.json index 7df08f820e6..7cf66f0426d 100644 --- a/webview-ui/src/i18n/locales/zh-CN/chat.json +++ b/webview-ui/src/i18n/locales/zh-CN/chat.json @@ -319,6 +319,11 @@ "title": "已达自动批准请求限制", "description": "Kilo Code 已达到 {{count}} 次 API 请求的自动批准限制。您想重置计数并继续任务吗?", "button": "重置并继续" + }, + "autoApprovedCostLimitReached": { + "title": "已达到自动批准费用限额", + "button": "重置并继续", + "description": "Kilo Code 已达到自动批准的成本限制 ${{count}}。您想要重置成本并继续执行任务吗?" } }, "codebaseSearch": { diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index a10224746e9..ea64348ebd2 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -200,10 +200,16 @@ }, "apiRequestLimit": { "title": "最大请求数", - "description": "在请求批准以继续执行任务之前,自动发出此数量的 API 请求。", "unlimited": "无限制" }, - "selectOptionsFirst": "请至少选择以下一个选项以启用自动批准" + "apiCostLimit": { + "title": "最大 API 成本", + "unlimited": "无限" + }, + "selectOptionsFirst": "请至少选择以下一个选项以启用自动批准", + "maxLimits": { + "description": "在请求批准继续之前,自动发出请求直至达到这些限制。" + } }, "providers": { "providerDocumentation": "{{provider}} 文档", diff --git a/webview-ui/src/i18n/locales/zh-TW/chat.json b/webview-ui/src/i18n/locales/zh-TW/chat.json index ae51cf535bf..134d18065a1 100644 --- a/webview-ui/src/i18n/locales/zh-TW/chat.json +++ b/webview-ui/src/i18n/locales/zh-TW/chat.json @@ -319,6 +319,11 @@ "title": "已達自動核准請求限制", "description": "Kilo Code 已達到 {{count}} 次 API 請求的自動核准限制。您想要重設計數並繼續工作嗎?", "button": "重設並繼續" + }, + "autoApprovedCostLimitReached": { + "button": "重置并继续", + "title": "已达到自动批准成本限制", + "description": "Kilo Code 已达到自动批准的费用限制 ${{count}}。您是否想重置费用并继续执行任务?" } }, "codebaseSearch": { diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index 803b67ff571..19bab330208 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -199,10 +199,16 @@ }, "apiRequestLimit": { "title": "最大請求數", - "description": "在請求批准以繼續執行工作之前,自動發出此數量的 API 請求。", "unlimited": "無限制" }, - "selectOptionsFirst": "請至少選擇以下一個選項以啟用自動核准" + "apiCostLimit": { + "title": "最大 API 成本", + "unlimited": "无限制" + }, + "selectOptionsFirst": "請至少選擇以下一個選項以啟用自動核准", + "maxLimits": { + "description": "在请求获得批准继续之前,自动发出请求直至达到这些限制。" + } }, "providers": { "providerDocumentation": "{{provider}} 文件",