feat: show root orchestration cost tally for subtasks

When viewing a subtask spawned by an orchestrator, the TaskHeader now
displays the full orchestration cost (aggregated across all tasks in the
orchestration) alongside the current task cost. This gives users
visibility into total spend without having to navigate back to the
parent task.

Addresses #11928
This commit is contained in:
Roo Code 2026-03-16 14:43:01 +00:00
parent 44fd975b17
commit 472c3960b0
4 changed files with 84 additions and 2 deletions

View file

@ -505,7 +505,15 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
text: currentTaskItem.id,
})
}
}, [taskTs, currentTaskItem?.id, currentTaskItem?.childIds])
// When viewing a subtask, also request aggregated costs for the root orchestration task
const rootId = currentTaskItem?.rootTaskId
if (taskTs && rootId && rootId !== currentTaskItem?.id) {
vscode.postMessage({
type: "getTaskWithAggregatedCosts",
text: rootId,
})
}
}, [taskTs, currentTaskItem?.id, currentTaskItem?.childIds, currentTaskItem?.rootTaskId])
useEffect(() => {
if (isHidden) {
@ -1563,6 +1571,13 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
)
}
parentTaskId={currentTaskItem?.parentTaskId}
rootOrchestrationCost={
currentTaskItem?.rootTaskId &&
currentTaskItem.rootTaskId !== currentTaskItem.id &&
aggregatedCostsMap.has(currentTaskItem.rootTaskId)
? aggregatedCostsMap.get(currentTaskItem.rootTaskId)!.totalCost
: undefined
}
costBreakdown={
currentTaskItem?.id && aggregatedCostsMap.has(currentTaskItem.id)
? getCostBreakdownIfNeeded(aggregatedCostsMap.get(currentTaskItem.id)!, {

View file

@ -36,6 +36,7 @@ export interface TaskHeaderProps {
aggregatedCost?: number
hasSubtasks?: boolean
parentTaskId?: string
rootOrchestrationCost?: number
costBreakdown?: string
contextTokens: number
buttonsDisabled: boolean
@ -53,6 +54,7 @@ const TaskHeader = ({
aggregatedCost,
hasSubtasks,
parentTaskId,
rootOrchestrationCost,
costBreakdown,
contextTokens,
buttonsDisabled,
@ -315,6 +317,25 @@ const TaskHeader = ({
</StandardTooltip>
</>
)}
{typeof rootOrchestrationCost === "number" && rootOrchestrationCost > 0 && (
<>
<span>·</span>
<StandardTooltip
content={
<div>
{t("chat:costs.orchestrationTotal", {
cost: rootOrchestrationCost.toFixed(2),
})}
</div>
}
side="top"
sideOffset={8}>
<span className="text-vscode-descriptionForeground">
{t("chat:costs.orchestrationLabel")}: ${rootOrchestrationCost.toFixed(2)}
</span>
</StandardTooltip>
</>
)}
</div>
</div>
)}
@ -447,6 +468,28 @@ const TaskHeader = ({
</tr>
)}
{typeof rootOrchestrationCost === "number" && rootOrchestrationCost > 0 && (
<tr>
<th className="font-medium text-left align-top w-1 whitespace-nowrap pr-3 h-[24px]">
{t("chat:costs.orchestrationLabel")}
</th>
<td className="font-light align-top">
<StandardTooltip
content={
<div>
{t("chat:costs.orchestrationTotal", {
cost: rootOrchestrationCost.toFixed(2),
})}
</div>
}
side="top"
sideOffset={8}>
<span>${rootOrchestrationCost.toFixed(2)}</span>
</StandardTooltip>
</td>
</tr>
)}
{/* Size display */}
{!!currentTaskItem?.size && currentTaskItem.size > 0 && (
<tr>

View file

@ -470,4 +470,26 @@ describe("TaskHeader", () => {
expect(screen.getByText("0%")).toBeInTheDocument()
})
})
describe("Root orchestration cost", () => {
it("should display orchestration cost when rootOrchestrationCost is provided and > 0", () => {
renderTaskHeader({ rootOrchestrationCost: 1.5 })
expect(screen.getByText("chat:costs.orchestrationLabel: $1.50")).toBeInTheDocument()
})
it("should not display orchestration cost when rootOrchestrationCost is undefined", () => {
renderTaskHeader({ rootOrchestrationCost: undefined })
expect(screen.queryByText(/chat:costs.orchestrationLabel/)).not.toBeInTheDocument()
})
it("should not display orchestration cost when rootOrchestrationCost is 0", () => {
renderTaskHeader({ rootOrchestrationCost: 0 })
expect(screen.queryByText(/chat:costs.orchestrationLabel/)).not.toBeInTheDocument()
})
it("should display orchestration cost tooltip with correct text", () => {
renderTaskHeader({ rootOrchestrationCost: 2.75 })
expect(screen.getByText("chat:costs.orchestrationLabel: $2.75")).toBeInTheDocument()
})
})
})

View file

@ -389,7 +389,9 @@
"costs": {
"totalWithSubtasks": "Total Cost (including subtasks): ${{cost}}",
"total": "Total Cost: ${{cost}}",
"includesSubtasks": "Includes subtask costs"
"includesSubtasks": "Includes subtask costs",
"orchestrationLabel": "Orchestration",
"orchestrationTotal": "Full orchestration cost (all tasks): ${{cost}}"
},
"codeblock": {
"tooltips": {