- {/* kilocode_change: Show title with X button at the top */}
- {title && (
-
- )}
+ {/* Title with close button */}
-
setIsTaskExpanded(!isTaskExpanded)}>
- {/*
-
-
*/}
-
- {/*
- {t("chat:task.title")}
- {!isTaskExpanded && ":"}
- */}
- {!isTaskExpanded && (
-
-
-
- )}
-
+
+ {title || "New task"}
- {/*
-
- */}
+
- {/* Collapsed state: Track context and cost if we have any */}
- {!isTaskExpanded && contextWindow > 0 && (
-
- {/* {showTaskTimeline && (
-
- )} */}
-
- {/*
-
- {condenseButton}
-
- {!!totalCost && ${totalCost.toFixed(2)}}
-
*/}
-
- )}
- {/* Expanded state: Show task text and images */}
- {isTaskExpanded && (
- <>
-
setIsTaskExpanded(!isTaskExpanded)}
- ref={textContainerRef}
- className="text-vscode-font-size overflow-y-auto break-words break-anywhere relative">
-
-
-
-
- {task.images && task.images.length > 0 &&
}
-
- {/* {showTaskTimeline && (
-
- )} */}
-
- {/*
- {isTaskExpanded && contextWindow > 0 && (
-
-
-
- {t("chat:task.contextWindow")}
-
-
-
- {condenseButton}
-
- )}
-
-
- {t("chat:task.tokens")}
- {typeof tokensIn === "number" && tokensIn > 0 && (
-
-
- {formatLargeNumber(tokensIn)}
-
- )}
- {typeof tokensOut === "number" && tokensOut > 0 && (
-
-
- {formatLargeNumber(tokensOut)}
-
- )}
-
- {!totalCost &&
}
-
-
- {((typeof cacheReads === "number" && cacheReads > 0) ||
- (typeof cacheWrites === "number" && cacheWrites > 0)) && (
-
- {t("chat:task.cache")}
- {typeof cacheWrites === "number" && cacheWrites > 0 && (
-
-
- {formatLargeNumber(cacheWrites)}
-
- )}
- {typeof cacheReads === "number" && cacheReads > 0 && (
-
-
- {formatLargeNumber(cacheReads)}
-
- )}
-
- )}
-
- {!!totalCost && (
-
-
- {t("chat:task.apiCost")}
- ${totalCost?.toFixed(2)}
-
-
-
- )}
-
*/}
- >
- )}
-
+ {/*
*/}
)
}
diff --git a/webview-ui/src/components/kilocode/StickyUserMessage.tsx b/webview-ui/src/components/kilocode/StickyUserMessage.tsx
new file mode 100644
index 0000000000..5e93b6f8b1
--- /dev/null
+++ b/webview-ui/src/components/kilocode/StickyUserMessage.tsx
@@ -0,0 +1,126 @@
+// kilocode_change: new file - Sticky user message component for chat
+import { memo, useState } from "react"
+import type { ClineMessage } from "@roo-code/types"
+import { ReadOnlyChatText } from "../chat/ReadOnlyChatText"
+import { cn } from "@src/lib/utils"
+
+export interface StickyUserMessageProps {
+ task: ClineMessage
+ messages: (ClineMessage | ClineMessage[])[]
+ stickyIndex: number | null
+}
+
+/**
+ * StickyUserMessage - Displays the current "sticky" user message.
+ *
+ * This works like sticky headers on mobile UI:
+ * - Shows the initial prompt (task.text) by default
+ * - As user scrolls, it tracks which user message should be sticky
+ * - Uses CSS position: sticky to stay at the top of the scroll container
+ */
+const StickyUserMessage = ({ task, messages, stickyIndex }: StickyUserMessageProps) => {
+ const [isExpanded, setIsExpanded] = useState(false)
+
+ // Get the message to display based on stickyIndex
+ // stickyIndex: null = task prompt, number = index in messages array (groupedMessages)
+ const getStickyMessage = () => {
+ // If stickyIndex is null, show the initial task prompt
+ if (stickyIndex === null) {
+ return {
+ text: task?.text,
+ images: task?.images,
+ }
+ }
+
+ // Find the user_feedback message at the given index in groupedMessages
+ const msg = messages[stickyIndex]
+
+ if (!msg || Array.isArray(msg)) {
+ return {
+ text: task?.text,
+ images: task?.images,
+ }
+ }
+
+ if (msg.type === "say" && msg.say === "user_feedback") {
+ return {
+ text: msg.text,
+ images: msg.images,
+ }
+ }
+
+ // Fallback to task prompt
+ return {
+ text: task?.text,
+ images: task?.images,
+ }
+ }
+
+ const stickyMessage = getStickyMessage()
+
+ if (!stickyMessage?.text && !stickyMessage?.images?.length) {
+ return null
+ }
+
+ return (
+
+
setIsExpanded(!isExpanded)}>
+
+ {!isExpanded ? (
+
+
+
+ ) : (
+
+
+
+ )}
+
+
+ {isExpanded && stickyMessage.images && stickyMessage.images.length > 0 && (
+
+ {stickyMessage.images.slice(0, 4).map((img, i) => (
+

+ ))}
+ {stickyMessage.images.length > 4 && (
+
+{stickyMessage.images.length - 4} more
+ )}
+
+ )}
+
+ )
+}
+
+export default memo(StickyUserMessage)
diff --git a/webview-ui/src/components/kilocode/chat/ModelSelector.tsx b/webview-ui/src/components/kilocode/chat/ModelSelector.tsx
index e9db7171ca..8b27e43ecd 100644
--- a/webview-ui/src/components/kilocode/chat/ModelSelector.tsx
+++ b/webview-ui/src/components/kilocode/chat/ModelSelector.tsx
@@ -1,13 +1,14 @@
-import { useMemo } from "react"
-import { SelectDropdown, DropdownOptionType } from "@/components/ui"
+import { DropdownOption, DropdownOptionType, SelectDropdown, StandardTooltip } from "@/components/ui"
+import { usePreferredModels } from "@/components/ui/hooks/kilocode/usePreferredModels"
+import { Alert02Icon, Brain01Icon } from "@/utils/customIcons"
import { OPENROUTER_DEFAULT_PROVIDER_NAME, type ProviderSettings } from "@roo-code/types"
-import { vscode } from "@src/utils/vscode"
import { useAppTranslation } from "@src/i18n/TranslationContext"
import { cn } from "@src/lib/utils"
-import { prettyModelName, getModelCredits } from "../../../utils/prettyModelName"
+import { vscode } from "@src/utils/vscode"
+import { useMemo } from "react"
+import { prettyModelName } from "../../../utils/prettyModelName"
import { useProviderModels } from "../hooks/useProviderModels"
import { getModelIdKey, getSelectedModelId } from "../hooks/useSelectedModel"
-import { usePreferredModels } from "@/components/ui/hooks/kilocode/usePreferredModels"
interface ModelSelectorProps {
currentApiConfigName?: string
@@ -17,7 +18,8 @@ interface ModelSelectorProps {
export const ModelSelector = ({ currentApiConfigName, apiConfiguration, fallbackText }: ModelSelectorProps) => {
const { t } = useAppTranslation()
- const { provider, providerModels, providerDefaultModel, isLoading, isError } = useProviderModels(apiConfiguration)
+ const { provider, providerModels, providerDefaultModel, isLoading, isError, proModelIds, proModelsEnabled } =
+ useProviderModels(apiConfiguration)
const selectedModelId = getSelectedModelId({
provider,
apiConfiguration,
@@ -30,15 +32,22 @@ export const ModelSelector = ({ currentApiConfigName, apiConfiguration, fallback
const missingModelIds = modelsIds.indexOf(selectedModelId) >= 0 ? [] : [selectedModelId]
return missingModelIds.concat(modelsIds).map((modelId) => {
const baseLabel = providerModels[modelId]?.displayName ?? prettyModelName(modelId)
- const credits = getModelCredits(modelId)
- const label = credits ? `${baseLabel} ${credits}` : baseLabel
+ // const credits = getModelCredits(modelId)
+ // const label = credits ? `${baseLabel} ${credits}` : baseLabel
+ const label = baseLabel
+ // kilocode_change: Check if this is a pro model that's disabled
+ const isProModel = proModelIds?.includes(modelId)
+ const isProModelDisabled = isProModel && !proModelsEnabled
+
return {
value: modelId,
label,
type: DropdownOptionType.ITEM,
+ disabled: isProModelDisabled,
+ isProModelDisabled,
}
})
- }, [modelsIds, providerModels, selectedModelId])
+ }, [modelsIds, providerModels, selectedModelId, proModelIds, proModelsEnabled])
const disabled = isLoading || isError
@@ -61,6 +70,40 @@ export const ModelSelector = ({ currentApiConfigName, apiConfiguration, fallback
})
}
+ const renderItem = (option: DropdownOption & { isProModelDisabled?: boolean }) => {
+ return (
+
+
+
+ {option.isProModelDisabled && (
+
+ Pro models are only available on the Paid Plan
+
+
+ }>
+
+
+
+
+ )}
+
+ )
+ }
+
if (isLoading) {
return null
}
@@ -83,6 +126,7 @@ export const ModelSelector = ({ currentApiConfigName, apiConfiguration, fallback
)}
triggerIcon={true}
itemClassName="group"
+ renderItem={renderItem}
/>
)
}
diff --git a/webview-ui/src/components/kilocode/hooks/useProviderModels.ts b/webview-ui/src/components/kilocode/hooks/useProviderModels.ts
index 082edfee20..88e2381f09 100644
--- a/webview-ui/src/components/kilocode/hooks/useProviderModels.ts
+++ b/webview-ui/src/components/kilocode/hooks/useProviderModels.ts
@@ -1,4 +1,3 @@
-import { useMemo } from "react"
import {
type ProviderName,
type ProviderSettings,
@@ -323,22 +322,20 @@ export const useProviderModels = (apiConfiguration?: ProviderSettings) => {
})
: FALLBACK_MODELS
- // kilocode_change start: Filter out axon-code-2-pro if beta models are not enabled
- const filteredModels = useMemo(() => {
- if (!betaModelsEnabled) {
- // Filter out axon-code-2-pro when beta models are not enabled
- const { "axon-code-2-pro": _, ...rest } = models as ModelRecord
- return rest
- }
- return models
- }, [models, betaModelsEnabled])
+ // kilocode_change start: Always show all models including axon-code-2-pro
+ // Pro models are marked as disabled if betaModelsEnabled is false
+ const proModelIds = ["axon-code-2-pro"]
+ const proModelsEnabled = betaModelsEnabled
// kilocode_change end
return {
provider,
- providerModels: filteredModels as ModelRecord,
+ providerModels: models as ModelRecord,
providerDefaultModel: defaultModel,
isLoading: routerModels.isLoading,
isError: routerModels.isError,
+ // kilocode_change: pro models support
+ proModelIds,
+ proModelsEnabled,
}
}
diff --git a/webview-ui/src/components/kilocode/settings/providers/KiloCode.tsx b/webview-ui/src/components/kilocode/settings/providers/KiloCode.tsx
index fa74c65c4d..88c7fef0c4 100644
--- a/webview-ui/src/components/kilocode/settings/providers/KiloCode.tsx
+++ b/webview-ui/src/components/kilocode/settings/providers/KiloCode.tsx
@@ -9,8 +9,8 @@ import { ModelPicker } from "../../../settings/ModelPicker"
import { OrganizationSelector } from "../../common/OrganizationSelector"
import { getKiloCodeBackendSignInUrl } from "../../helpers"
import { useExtensionState } from "@/context/ExtensionStateContext"
-import { useMemo } from "react"
-import type { ModelRecord } from "@roo/api"
+import { ProfileData, WebviewMessage } from "@roo/WebviewMessage"
+import { useEffect, useRef, useState } from "react"
type KiloCodeProps = {
apiConfiguration: ProviderSettings
@@ -38,18 +38,79 @@ export const KiloCode = ({
kilocodeDefaultModel,
}: KiloCodeProps) => {
const { t } = useAppTranslation()
- const { betaModelsEnabled } = useExtensionState()
-
- // Filter out axon-code-2-pro if beta models are not enabled
- const filteredModels = useMemo(() => {
- const models = routerModels?.["kilocode-openrouter"] ?? {}
- if (!betaModelsEnabled) {
- // Filter out axon-code-2-pro when beta models are not enabled
- const { "axon-code-2-pro": _, ...rest } = models as ModelRecord
- return rest
+ const { betaModelsEnabled, clineMessages } = useExtensionState()
+
+ // Profile data state for usage info
+ const [profileData, setProfileData] = useState