Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/types/src/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const commandExecutionStatusSchema = z.discriminatedUnion("status", [
status: z.literal("started"),
pid: z.number().optional(),
command: z.string(),
startTime: z.number().optional(),
}),
z.object({
executionId: z.string(),
Expand Down
34 changes: 0 additions & 34 deletions src/api/providers/kilocode-models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,40 +61,6 @@ export const KILO_CODE_MODELS: Record<string, KiloCodeModel> = {
input_cache_writes: "0",
},
},
"axon-code": {
id: "axon-code",
name: "Axon Code 1.3",
description: "Axon Code is super intelligent LLM model for coding tasks",
input_modalities: ["text", "image"],
context_length: 200000,
max_output_length: 32768,
output_modalities: ["text"],
supported_sampling_parameters: [
"temperature",
"top_p",
"top_k",
"repetition_penalty",
"frequency_penalty",
"presence_penalty",
"seed",
"stop",
],
supported_features: ["tools", "structured_outputs", "web_search"],
openrouter: {
slug: "matterai/axon",
},
datacenters: [{ country_code: "US" }],
created: 1750426201,
owned_by: "matterai",
pricing: {
prompt: "0.0",
completion: "0.0",
image: "0",
request: "0",
input_cache_reads: "0",
input_cache_writes: "0",
},
},
"axon-code-2": {
id: "axon-code-2",
name: "Axon Code 2.1",
Expand Down
8 changes: 7 additions & 1 deletion src/core/tools/executeCommandTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,13 @@ export async function executeCommand(
},
onShellExecutionStarted: (pid: number | undefined) => {
console.log(`[executeCommand] onShellExecutionStarted: ${pid}`)
const status: CommandExecutionStatus = { executionId, status: "started", pid, command }
const status: CommandExecutionStatus = {
executionId,
status: "started",
pid,
command,
startTime: Date.now(),
}
provider?.postMessageToWebview({ type: "commandExecutionStatus", text: JSON.stringify(status) })
},
onShellExecutionComplete: (details: ExitCodeDetails) => {
Expand Down
2 changes: 1 addition & 1 deletion src/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"displayName": "%extension.displayName%",
"description": "%extension.description%",
"publisher": "matterai",
"version": "5.6.9",
"version": "5.7.0",
"icon": "assets/icons/matterai-ic.png",
"galleryBanner": {
"color": "#FFFFFF",
Expand Down
59 changes: 19 additions & 40 deletions webview-ui/src/components/chat/ChatRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1495,48 +1495,27 @@ export const ChatRowContent = ({

if (isOutOfCreditsMessage) {
return (
<div className="bg-[var(--vscode-editor-background)] ml-0 my-2 mr-2 rounded-xl p-4 border border-[var(--color-matterai-red)]">
<div
style={{
display: "flex",
alignItems: "center",
gap: "8px",
marginBottom: "12px",
}}>
<span
className="codicon codicon-warning"
style={{
color: "var(--color-matterai-red)",
fontSize: "18px",
}}></span>
<div
style={{
fontWeight: "bold",
fontSize: "14px",
color: "var(--vscode-foreground)",
}}>
Your plan is out of credits
<div className="w-full min-w-0 my-2 pr-1">
<div className="flex items-center justify-between rounded-md gap-2 px-3 py-2 bg-[var(--vscode-input-background)] border border-[var(--vscode-panel-border)]">
<div className="flex flex-col gap-2">
<span className="text-lg font-medium text-[var(--vscode-foreground)]">
You are out of Orbital Credits
</span>
<span className="text-md text-[var(--vscode-descriptionForeground)] max-w-[85%]">
To continue using Orbital, upgrade your plan or switch to Auto model.
</span>
</div>
<button
className="flex items-center gap-1 px-2.5 py-0.5 rounded-full bg-[var(--vscode-button-background)] hover:bg-[var(--vscode-button-hoverBackground)] text-[var(--vscode-button-foreground)] text-md font-medium transition-all duration-200 shrink-0"
onClick={() =>
vscode.postMessage({
type: "openExternal",
url: "https://app.matterai.so/orbital",
})
}>
Upgrade
</button>
</div>
<div
style={{
marginBottom: "16px",
color: "var(--vscode-foreground)",
fontSize: "13px",
}}>
Purchase or upgrade your paid plan to continue using the service.
</div>
<VSCodeButton
appearance="primary"
onClick={(e) => {
e.preventDefault()
vscode.postMessage({
type: "openInBrowser",
url: "https://app.matterai.so/billing?tab=axon-code",
})
}}>
Purchase / Upgrade Plan
</VSCodeButton>
</div>
)
}
Expand Down
57 changes: 55 additions & 2 deletions webview-ui/src/components/chat/ChatView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import { getAllModes } from "@roo/modes"
import { ProfileValidator } from "@roo/ProfileValidator"
import { safeJsonParse } from "@roo/safeJsonParse"
import { getLatestTodo } from "@roo/todo"
import { AudioType } from "@roo/WebviewMessage"
import { AudioType, ProfileData, WebviewMessage } from "@roo/WebviewMessage"

import { useSelectedModel } from "@src/components/ui/hooks/useSelectedModel"
import { useExtensionState } from "@src/context/ExtensionStateContext"
Expand Down Expand Up @@ -273,6 +273,36 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
return w.ICONS_BASE_URI || ""
})

// kilocode_change: Profile data state for usage tracking
const [profileData, setProfileData] = useState<ProfileData | null>(null)

// Fetch profile data for usage tracking
useEffect(() => {
if (apiConfiguration?.kilocodeToken) {
vscode.postMessage({ type: "fetchProfileDataRequest" })
}
}, [apiConfiguration?.kilocodeToken])

// Listen for profile data response
useEffect(() => {
const handleMessage = (event: MessageEvent<WebviewMessage>) => {
const message = event.data
if (message.type === "profileDataResponse") {
const payload = message.payload as any
if (payload?.success && payload.data) {
setProfileData(payload.data)
}
}
}

window.addEventListener("message", handleMessage)
return () => window.removeEventListener("message", handleMessage)
}, [])

// Check if usage is over 98% (near exhaustion warning)
const isUsageExhausted =
profileData && typeof profileData.usagePercentage === "number" && profileData.usagePercentage >= 98

const clineAskRef = useRef(clineAsk)
useEffect(() => {
clineAskRef.current = clineAsk
Expand Down Expand Up @@ -2664,11 +2694,34 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
</div>
)}

{/* kilocode_change: Show notification when monthly limit is exhausted */}
{isUsageExhausted && !task && (
<div className="w-full min-w-0 px-4 mb-4">
<div className="flex items-center justify-between rounded-md gap-2 px-3 py-2 bg-[var(--vscode-input-background)] border border-[var(--vscode-panel-border)]">
<div className="flex flex-col gap-2">
<span className="text-lg font-medium text-[var(--vscode-foreground)]">
You are out of Orbital Credits
</span>
<span className="text-md text-[var(--vscode-descriptionForeground)] max-w-[85%]">
To continue using Orbital, upgrade your plan or switch to Auto model.
</span>
</div>
<button
className="flex items-center gap-1 px-2.5 py-0.5 rounded-full bg-[var(--vscode-button-background)] hover:bg-[var(--vscode-button-hoverBackground)] text-[var(--vscode-button-foreground)] text-md font-medium transition-all duration-200 shrink-0"
onClick={() =>
vscode.postMessage({ type: "openExternal", url: "https://app.matterai.so/orbital" })
}>
Upgrade
</button>
</div>
</div>
)}

{!task && (
<div className={`w-full min-w-0 px-4 ${isReviewOnlyMode ? "mb-4" : "mb-1.5"}`}>
<VSCodeButton
appearance="secondary"
className="flex w-full min-w-full"
className="flex w-full min-w-full code-review-btn"
onClick={() => {
setShowSourceControl(true)
// If there's an error, automatically retry when opening
Expand Down
92 changes: 69 additions & 23 deletions webview-ui/src/components/chat/CommandExecution.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ChevronDown, OctagonX } from "lucide-react"
import { memo, useCallback, useMemo, useState } from "react"
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react"
import { useEvent } from "react-use"

import { CommandExecutionStatus, commandExecutionStatusSchema } from "@roo-code/types"
Expand Down Expand Up @@ -65,6 +65,9 @@ export const CommandExecution = memo(
const [isExpanded, setIsExpanded] = useState(terminalShellIntegrationDisabled)
const [streamingOutput, setStreamingOutput] = useState("")
const [status, setStatus] = useState<CommandExecutionStatus | null>(null)
const [elapsedSeconds, setElapsedSeconds] = useState<number>(0)
const [completedSeconds, setCompletedSeconds] = useState<number | null>(null)
const startTimeRef = useRef<number | null>(null)

// The command's output can either come from the text associated with the
// task message (this is the case for completed commands) or from the
Expand Down Expand Up @@ -136,6 +139,7 @@ export const CommandExecution = memo(

switch (data.status) {
case "started":
startTimeRef.current = data.startTime ?? null
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Logic Improvement

Issue: If data.startTime is undefined (e.g., from an older backend version or missing data), startTimeRef.current becomes null. This prevents the timer from starting, causing the UI to display Running Command for 0s indefinitely.

Fix: Fallback to Date.now() if data.startTime is not provided to ensure the timer always functions.

Impact: Improves backward compatibility and ensures the timer UI always works.

Suggested change
startTimeRef.current = data.startTime ?? null
startTimeRef.current = data.startTime ?? Date.now()

setStatus(data)
break
case "output":
Expand All @@ -156,45 +160,87 @@ export const CommandExecution = memo(

useEvent("message", onMessage)

// Timer effect for showing "Running for X seconds"
useEffect(() => {
if (status?.status === "started" && startTimeRef.current) {
const startTime = startTimeRef.current

// Update elapsed time every second
const interval = setInterval(() => {
const elapsed = Math.floor((Date.now() - startTime) / 1000)
setElapsedSeconds(elapsed)
}, 1000)

// Initial calculation
setElapsedSeconds(Math.floor((Date.now() - startTime) / 1000))

return () => clearInterval(interval)
}
}, [status])

// Calculate final duration when command completes
useEffect(() => {
if (status?.status === "exited" && startTimeRef.current) {
const elapsed = Math.floor((Date.now() - startTimeRef.current) / 1000)
setCompletedSeconds(elapsed)
}
}, [status])

return (
<>
<div className="flex flex-row items-center justify-between gap-2 mb-1">
<div className="flex flex-row items-center gap-2">
{icon}
{title}
{status?.status === "exited" && (
<div className="flex flex-row items-center gap-2 font-mono text-xs">
{/* Status display: running, completed, or initial */}
{status?.status === "started" ? (
<div className="flex flex-row items-center gap-2">
<div className="rounded-full size-2 bg-green-500 animate-pulse" />
<span className="font-medium">Running Command for {elapsedSeconds}s</span>
{status.pid && (
<span className="font-mono text-xs opacity-70">(PID: {status.pid})</span>
)}
</div>
) : status?.status === "exited" ? (
<div className="flex flex-row items-center gap-2">
<span className="font-medium">Command</span>
{completedSeconds !== null && (
<span className="font-mono text-xs opacity-70 pt-0.5">
Ran for {completedSeconds}s
</span>
)}
<StandardTooltip
content={t("chat.commandExecution.exitStatus", { exitStatus: status.exitCode })}>
<div
className={cn(
"rounded-full size-2",
"rounded-full size-2 mt-0.5",
status.exitCode === 0 ? "bg-green-600" : "bg-red-600",
)}
/>
</StandardTooltip>
</div>
) : (
<>
{icon}
{title}
</>
)}
</div>
<div className=" flex flex-row items-center justify-between gap-2 px-1">
<div className="flex flex-row items-center justify-between gap-2 px-1">
<div className="flex flex-row items-center gap-1">
{/* Abort button when running */}
{status?.status === "started" && (
<div className="flex flex-row items-center gap-2 font-mono text-xs">
{status.pid && <div className="whitespace-nowrap">(PID: {status.pid})</div>}
<StandardTooltip content={t("chat:commandExecution.abort")}>
<Button
variant="ghost"
size="icon"
onClick={() =>
vscode.postMessage({
type: "terminalOperation",
terminalOperation: "abort",
})
}>
<OctagonX className="size-4" />
</Button>
</StandardTooltip>
</div>
<StandardTooltip content={t("chat:commandExecution.abort")}>
<Button
variant="ghost"
size="icon"
onClick={() =>
vscode.postMessage({
type: "terminalOperation",
terminalOperation: "abort",
})
}>
<OctagonX className="size-4" />
</Button>
</StandardTooltip>
)}

{output.length > 0 && (
Expand Down
2 changes: 1 addition & 1 deletion webview-ui/src/components/kilocode/StickyUserMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ const StickyUserMessage = ({ task, messages, stickyIndex }: StickyUserMessagePro
return (
<div
className={cn(
"px-2 py-2 flex flex-col gap-1 relative ml-0.9 mr-1",
"px-2 py-2 flex flex-col gap-1 relative ml-0.9",
"rounded-lg",
"border border-[var(--vscode-activityBar-border)]",
"transition-all duration-150",
Expand Down
Loading
Loading