Skip to content

fix(cli): include subagent costs in session total - #9448

Merged
alex-alecu merged 12 commits into
mainfrom
fix/costs-of-subagents
Apr 27, 2026
Merged

fix(cli): include subagent costs in session total#9448
alex-alecu merged 12 commits into
mainfrom
fix/costs-of-subagents

Conversation

@alex-alecu

@alex-alecu alex-alecu commented Apr 24, 2026

Copy link
Copy Markdown
Contributor

Why

Fixes #6321

When a session asked the AI to split its work into helper agents, the price shown at the bottom of the screen only counted the main chat and ignored everything the helpers spent. This made the running total look much smaller than it really was.

What changed

Each helper agent now reports its total spending back to the line of the conversation that started it, and the system adds that amount on top of whatever that line already cost. Helpers that launch their own helpers work the same way — each layer rolls up into the one above it — so by the time you look at the main session, the number includes every helper, no matter how deep. Resuming a paused helper only adds what was spent this time around, not a duplicate of the older amount. The price shown in the terminal footer, the sidebar, the web view, and the editor side panel are all updated automatically because they read from the same number.

How to test

  1. Start a new chat and ask the assistant to use a few helper agents to do something (for example: "Use 3 explore subagents in parallel to look at different parts of the codebase").
  2. Wait for all the helpers to finish.
  3. Check the cost shown in the terminal footer and the sidebar — it should now include the helpers' spending, not just the main chat's.
  4. Try a task that uses nested helpers (a helper that spawns another helper) and confirm the total includes every level.

Demo

Screenshot 2026-04-24 at 19 03 03

Bun dev (this fix)

Screenshot 2026-04-24 at 19 04 40

Kilo 7.2.22

Screenshot 2026-04-24 at 19 05 23

When a session spawned subagents via the task tool, the displayed cost reflected only the parent's own LLM steps. Propagate each subagent's total up to the invoking assistant message so every UI sums the full tree.
Comment thread packages/opencode/src/tool/task.ts
@kilo-code-bot

kilo-code-bot Bot commented Apr 24, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (8 files)
  • .changeset/fix-subagent-cost-propagation.md
  • packages/opencode/src/cli/cmd/stats.ts
  • packages/opencode/src/kilocode/session/cost-propagation.ts
  • packages/opencode/src/session/processor.ts
  • packages/opencode/src/session/prompt.ts
  • packages/opencode/src/tool/task.ts
  • packages/opencode/test/kilocode/cost-propagation.test.ts
  • packages/opencode/test/kilocode/stats-subagent-cost.test.ts

Reviewed by gpt-5.4-2026-03-05 · 2,353,038 tokens

finish-step and cleanup previously re-wrote the parent assistant message with a stale in-memory cost, clobbering the subagent cost written by task.ts during tool execution. Refresh from DB before each write so the propagated cost is preserved across steps. Verified live via side-by-side `kilo serve` vs dev `bun serve` with 2 parallel subagents; parent now shows own LLM + children, matching real spend.
Comment thread packages/opencode/src/session/processor.ts Outdated
The task tool propagates each child session's total cost into the parent's tool-wrapper assistant message. Counting both root and child sessions in `kilo stats` would double-count that contribution. Filter `getAllSessions()` to root sessions (parent_id IS NULL), matching how the TUI session list and web sidebar already treat child sessions.
Comment thread packages/opencode/src/cli/cmd/stats.ts Outdated
@alex-alecu

Copy link
Copy Markdown
Contributor Author

VS Code

On this branch with the fix - $0.99

Screenshot 2026-04-25 at 10 16 49

On main - $0.54

Screenshot 2026-04-25 at 10 18 31

Comment thread packages/opencode/src/cli/cmd/stats.ts Outdated
Comment on lines +198 to +205
const cost = (() => {
const parts = message.parts.filter(
(part: MessageV2.Part): part is MessageV2.StepFinishPart => part.type === "step-finish",
)
if (parts.length === 0) return message.info.cost || 0
return parts.reduce((sum: number, part: MessageV2.StepFinishPart) => sum + part.cost, 0)
})()
if (!session.parentID) sessionCost += message.info.cost || 0

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Just some code golfing clarity suggestion. Take it or leave it!

There's a lot of unnecessary code here we can make tighter. For instance, the type of message is already known and each message part is already expressed as a discriminated union on part.type, so there's no need to express this type guard here:

(part: MessageV2.Part): part is MessageV2.StepFinishPart => part.type === "step-finish"

You can remove all of the types and it works the same:

(part) => part.type === "step-finish"

And since we're already using .filter and .reduce, we may as well lean into that style and chain them together:

Suggested change
const cost = (() => {
const parts = message.parts.filter(
(part: MessageV2.Part): part is MessageV2.StepFinishPart => part.type === "step-finish",
)
if (parts.length === 0) return message.info.cost || 0
return parts.reduce((sum: number, part: MessageV2.StepFinishPart) => sum + part.cost, 0)
})()
if (!session.parentID) sessionCost += message.info.cost || 0
const cost = message.parts
.filter(part => part.type === "step-finish")
.reduce((sum, part) => sum + part.cost, message.info.cost || 0)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in 1eaf409.

@alex-alecu

Copy link
Copy Markdown
Contributor Author

Blocked before running requested workflow: the worktree was already dirty at startup, so I did not run merge-with-main, fix-review, tests, or any commands that could overwrite user work. Dirty files detected:

  • .kilo/package-lock.json
  • .kilocode/package-lock.json
  • packages/opencode/test/session/prompt-effect.test.ts

Per the safety rules, these pre-existing changes need to be resolved or confirmed before continuing.

Add missing 'variant' property to task tool metadata in prompt-effect test, required after upstream added model variant support to the task tool's ExecuteResult type.
@alex-alecu
alex-alecu merged commit 5be897c into main Apr 27, 2026
15 checks passed
@alex-alecu
alex-alecu deleted the fix/costs-of-subagents branch April 27, 2026 10:30
teknium1 added a commit to NousResearch/hermes-agent that referenced this pull request Apr 28, 2026
)

* Port from Kilo-Org/kilocode#9448: roll up subagent costs into parent session total

Child subagents built by delegate_task() each track their own
session_estimated_cost_usd, but the parent agent's total never folded
those numbers in.  On runs where the parent mostly delegates and the
children do the expensive work, the footer/UI was reporting a fraction
of the actual spend — sometimes $0.00 when the parent itself made no
billed calls.

Fix:
- Capture each child's session_estimated_cost_usd into _child_cost_usd
  on the result entry (before child.close() drops the counter).
- After the existing subagent_stop hook loop, sum the children's costs
  and add the total to parent.session_estimated_cost_usd.
- Promote session_cost_source from 'none' -> 'subagent' when the parent
  had no direct spend but children did, so the UI doesn't label the
  total as having unknown provenance.  Real sources (openrouter,
  anthropic, etc.) are preserved.

Nested orchestrator -> worker trees roll up naturally: each layer's own
delegate_task() folds its direct children in, and when the orchestrator
itself returns, its parent folds the orchestrator's now-inflated total
on top.

Internal fields (_child_cost_usd, _child_role) are stripped from the
results dict before it's serialised back to the model — same contract
as _child_role already followed.

Tests: TestSubagentCostRollup (5 cases) covers single-child, batch,
zero-cost-children, preserved-source, and legacy-fixture paths.

Source: Kilo-Org/kilocode#9448

* fix(web): scope dashboard config Reset button to the current tab

Reported by @ykmfb001 via X: clicking 'Restore Defaults' (恢复默认值) on
the Auxiliary page wiped the entire config.yaml to defaults, not just
the auxiliary section. The button sits next to the category tabs and
users reasonably assumed 'reset this tab', not 'reset everything'.

Changes:
- handleReset now scopes to the fields in the current view:
  active category's fields (form mode) or search-matched fields
  (search mode). Only those keys are copied from defaults; the rest
  of the config is left alone.
- Added a window.confirm() with the scope name before applying.
- Button is hidden in YAML mode (scoping doesn't apply there).
- Tooltip/aria-label now name the scope, e.g. 'Reset Auxiliary to
  defaults'.
- i18n: new resetScopeTooltip / confirmResetScope / resetScopeToast
  strings in en + zh; resetDefaults key preserved for compat.
cluricaun28 pushed a commit to cluricaun28/Logos that referenced this pull request Apr 28, 2026
…813)

* Port from Kilo-Org/kilocode#9448: roll up subagent costs into parent session total

Child subagents built by delegate_task() each track their own
session_estimated_cost_usd, but the parent agent's total never folded
those numbers in.  On runs where the parent mostly delegates and the
children do the expensive work, the footer/UI was reporting a fraction
of the actual spend — sometimes $0.00 when the parent itself made no
billed calls.

Fix:
- Capture each child's session_estimated_cost_usd into _child_cost_usd
  on the result entry (before child.close() drops the counter).
- After the existing subagent_stop hook loop, sum the children's costs
  and add the total to parent.session_estimated_cost_usd.
- Promote session_cost_source from 'none' -> 'subagent' when the parent
  had no direct spend but children did, so the UI doesn't label the
  total as having unknown provenance.  Real sources (openrouter,
  anthropic, etc.) are preserved.

Nested orchestrator -> worker trees roll up naturally: each layer's own
delegate_task() folds its direct children in, and when the orchestrator
itself returns, its parent folds the orchestrator's now-inflated total
on top.

Internal fields (_child_cost_usd, _child_role) are stripped from the
results dict before it's serialised back to the model — same contract
as _child_role already followed.

Tests: TestSubagentCostRollup (5 cases) covers single-child, batch,
zero-cost-children, preserved-source, and legacy-fixture paths.

Source: Kilo-Org/kilocode#9448

* fix(web): scope dashboard config Reset button to the current tab

Reported by @ykmfb001 via X: clicking 'Restore Defaults' (恢复默认值) on
the Auxiliary page wiped the entire config.yaml to defaults, not just
the auxiliary section. The button sits next to the category tabs and
users reasonably assumed 'reset this tab', not 'reset everything'.

Changes:
- handleReset now scopes to the fields in the current view:
  active category's fields (form mode) or search-matched fields
  (search mode). Only those keys are copied from defaults; the rest
  of the config is left alone.
- Added a window.confirm() with the scope name before applying.
- Button is hidden in YAML mode (scoping doesn't apply there).
- Tooltip/aria-label now name the scope, e.g. 'Reset Auxiliary to
  defaults'.
- i18n: new resetScopeTooltip / confirmResetScope / resetScopeToast
  strings in en + zh; resetDefaults key preserved for compat.
adurham added a commit to adurham/hermes-agent that referenced this pull request May 4, 2026
…e progress

Three feedback items rolled into one focused change:

1. Cost tracking — the per-child cost rollup at the end of delegate_task()
   already existed (Kilo-Org/kilocode#9448 port) but the user couldn't see
   anything about subagent spend until the parent's session footer at end of
   turn. Now we emit visible status:
   - Every 30s during a child run (heartbeat-piggyback): model · tool ·
     iteration · elapsed · running tokens · running cost.
   - On each child completion: status icon · model · duration · tokens · cost.
   - On full delegate_task() return: N/M subagents ok · total duration ·
     batch child cost · session running total.
   All routed through _emit_status() so the lines reach both CLI scrollback
   and TUI/gateway status channels (per the lifecycle visibility convention
   in the hermes-agent-internals skill).

2. Per-call model selection — added 'model' to the delegate_task tool schema
   at both the top level and inside tasks[]. Precedence:
   per-task → top-level → delegation.model config → parent's model. Lets
   the orchestrator right-size each child (Haiku for retrieval/grep, Sonnet
   for analysis, Opus for deep reasoning) without editing config.yaml.
   Plumbed through to _build_child_agent's existing 'model' kwarg — the
   credential resolution path already supports per-child models, this just
   exposes the knob.

3. Progress visibility — until now, a long delegate_task showed only the
   parent's spinner_text "🔀 delegate ... 506.2s" with no signal about what
   the subagent was doing. The heartbeat thread already polled
   child.get_activity_summary() for the GATEWAY's _touch_activity (so
   inactivity timeout doesn't fire), but it was invisible to the user. Now
   the same poll cycle also routes a human-readable line through
   _emit_status. Same pattern as the streaming-stall heartbeat fix
   (commit 03890b6c) — piggyback existing thread, no new cadence.

Tests: tests/tools/test_delegate.py 121/121 pass.

User-visible diff (during a 3-child delegate run):

  ┊ 🔀 [0] claude-haiku-4-5 · salesforce_fetch_case (iter 2/30) · 30s elapsed | 4,210↓/127↑ tok | $0.0008
  ┊ 🔀 [1] claude-sonnet-4-6 · search_files (iter 5/30) · 60s elapsed | 18,332↓/892↑ tok | $0.0772
  ┊ ✅ subagent [0] claude-haiku-4-5 · completed in 47.2s | 9,217↓/411↑ tok | $0.0019
  ┊ ✅ subagent [1] claude-sonnet-4-6 · completed in 142.8s | 38,201↓/2,103↑ tok | $0.1462
  ┊ 🔀 delegate done · 3 subagents ok · 603.2s · children=$0.4287 · session=$0.7912

Replaces the prior 1-line "🔀 delegate ... 506.2s" with no cost insight.
02356abc pushed a commit to 02356abc/hermes-agent that referenced this pull request May 14, 2026
…sResearch#16813)

* Port from Kilo-Org/kilocode#9448: roll up subagent costs into parent session total

Child subagents built by delegate_task() each track their own
session_estimated_cost_usd, but the parent agent's total never folded
those numbers in.  On runs where the parent mostly delegates and the
children do the expensive work, the footer/UI was reporting a fraction
of the actual spend — sometimes $0.00 when the parent itself made no
billed calls.

Fix:
- Capture each child's session_estimated_cost_usd into _child_cost_usd
  on the result entry (before child.close() drops the counter).
- After the existing subagent_stop hook loop, sum the children's costs
  and add the total to parent.session_estimated_cost_usd.
- Promote session_cost_source from 'none' -> 'subagent' when the parent
  had no direct spend but children did, so the UI doesn't label the
  total as having unknown provenance.  Real sources (openrouter,
  anthropic, etc.) are preserved.

Nested orchestrator -> worker trees roll up naturally: each layer's own
delegate_task() folds its direct children in, and when the orchestrator
itself returns, its parent folds the orchestrator's now-inflated total
on top.

Internal fields (_child_cost_usd, _child_role) are stripped from the
results dict before it's serialised back to the model — same contract
as _child_role already followed.

Tests: TestSubagentCostRollup (5 cases) covers single-child, batch,
zero-cost-children, preserved-source, and legacy-fixture paths.

Source: Kilo-Org/kilocode#9448

* fix(web): scope dashboard config Reset button to the current tab

Reported by @ykmfb001 via X: clicking 'Restore Defaults' (恢复默认值) on
the Auxiliary page wiped the entire config.yaml to defaults, not just
the auxiliary section. The button sits next to the category tabs and
users reasonably assumed 'reset this tab', not 'reset everything'.

Changes:
- handleReset now scopes to the fields in the current view:
  active category's fields (form mode) or search-matched fields
  (search mode). Only those keys are copied from defaults; the rest
  of the config is left alone.
- Added a window.confirm() with the scope name before applying.
- Button is hidden in YAML mode (scoping doesn't apply there).
- Tooltip/aria-label now name the scope, e.g. 'Reset Auxiliary to
  defaults'.
- i18n: new resetScopeTooltip / confirmResetScope / resetScopeToast
  strings in en + zh; resetDefaults key preserved for compat.
dannyJ848 pushed a commit to dannyJ848/hermes-agent that referenced this pull request May 17, 2026
…sResearch#16813)

* Port from Kilo-Org/kilocode#9448: roll up subagent costs into parent session total

Child subagents built by delegate_task() each track their own
session_estimated_cost_usd, but the parent agent's total never folded
those numbers in.  On runs where the parent mostly delegates and the
children do the expensive work, the footer/UI was reporting a fraction
of the actual spend — sometimes $0.00 when the parent itself made no
billed calls.

Fix:
- Capture each child's session_estimated_cost_usd into _child_cost_usd
  on the result entry (before child.close() drops the counter).
- After the existing subagent_stop hook loop, sum the children's costs
  and add the total to parent.session_estimated_cost_usd.
- Promote session_cost_source from 'none' -> 'subagent' when the parent
  had no direct spend but children did, so the UI doesn't label the
  total as having unknown provenance.  Real sources (openrouter,
  anthropic, etc.) are preserved.

Nested orchestrator -> worker trees roll up naturally: each layer's own
delegate_task() folds its direct children in, and when the orchestrator
itself returns, its parent folds the orchestrator's now-inflated total
on top.

Internal fields (_child_cost_usd, _child_role) are stripped from the
results dict before it's serialised back to the model — same contract
as _child_role already followed.

Tests: TestSubagentCostRollup (5 cases) covers single-child, batch,
zero-cost-children, preserved-source, and legacy-fixture paths.

Source: Kilo-Org/kilocode#9448

* fix(web): scope dashboard config Reset button to the current tab

Reported by @ykmfb001 via X: clicking 'Restore Defaults' (恢复默认值) on
the Auxiliary page wiped the entire config.yaml to defaults, not just
the auxiliary section. The button sits next to the category tabs and
users reasonably assumed 'reset this tab', not 'reset everything'.

Changes:
- handleReset now scopes to the fields in the current view:
  active category's fields (form mode) or search-matched fields
  (search mode). Only those keys are copied from defaults; the rest
  of the config is left alone.
- Added a window.confirm() with the scope name before applying.
- Button is hidden in YAML mode (scoping doesn't apply there).
- Tooltip/aria-label now name the scope, e.g. 'Reset Auxiliary to
  defaults'.
- i18n: new resetScopeTooltip / confirmResetScope / resetScopeToast
  strings in en + zh; resetDefaults key preserved for compat.
jliounis pushed a commit to jliounis/kilocode that referenced this pull request May 18, 2026
fix(cli): include subagent costs in session total
gweeteve pushed a commit to gweeteve/hermes-agent that referenced this pull request Jun 2, 2026
…sResearch#16813)

* Port from Kilo-Org/kilocode#9448: roll up subagent costs into parent session total

Child subagents built by delegate_task() each track their own
session_estimated_cost_usd, but the parent agent's total never folded
those numbers in.  On runs where the parent mostly delegates and the
children do the expensive work, the footer/UI was reporting a fraction
of the actual spend — sometimes $0.00 when the parent itself made no
billed calls.

Fix:
- Capture each child's session_estimated_cost_usd into _child_cost_usd
  on the result entry (before child.close() drops the counter).
- After the existing subagent_stop hook loop, sum the children's costs
  and add the total to parent.session_estimated_cost_usd.
- Promote session_cost_source from 'none' -> 'subagent' when the parent
  had no direct spend but children did, so the UI doesn't label the
  total as having unknown provenance.  Real sources (openrouter,
  anthropic, etc.) are preserved.

Nested orchestrator -> worker trees roll up naturally: each layer's own
delegate_task() folds its direct children in, and when the orchestrator
itself returns, its parent folds the orchestrator's now-inflated total
on top.

Internal fields (_child_cost_usd, _child_role) are stripped from the
results dict before it's serialised back to the model — same contract
as _child_role already followed.

Tests: TestSubagentCostRollup (5 cases) covers single-child, batch,
zero-cost-children, preserved-source, and legacy-fixture paths.

Source: Kilo-Org/kilocode#9448

* fix(web): scope dashboard config Reset button to the current tab

Reported by @ykmfb001 via X: clicking 'Restore Defaults' (恢复默认值) on
the Auxiliary page wiped the entire config.yaml to defaults, not just
the auxiliary section. The button sits next to the category tabs and
users reasonably assumed 'reset this tab', not 'reset everything'.

Changes:
- handleReset now scopes to the fields in the current view:
  active category's fields (form mode) or search-matched fields
  (search mode). Only those keys are copied from defaults; the rest
  of the config is left alone.
- Added a window.confirm() with the scope name before applying.
- Button is hidden in YAML mode (scoping doesn't apply there).
- Tooltip/aria-label now name the scope, e.g. 'Reset Auxiliary to
  defaults'.
- i18n: new resetScopeTooltip / confirmResetScope / resetScopeToast
  strings in en + zh; resetDefaults key preserved for compat.
the-grove-ai pushed a commit to the-grove-ai/hermes-autonomaton-refactor that referenced this pull request Jun 2, 2026
…813)

* Port from Kilo-Org/kilocode#9448: roll up subagent costs into parent session total

Child subagents built by delegate_task() each track their own
session_estimated_cost_usd, but the parent agent's total never folded
those numbers in.  On runs where the parent mostly delegates and the
children do the expensive work, the footer/UI was reporting a fraction
of the actual spend — sometimes $0.00 when the parent itself made no
billed calls.

Fix:
- Capture each child's session_estimated_cost_usd into _child_cost_usd
  on the result entry (before child.close() drops the counter).
- After the existing subagent_stop hook loop, sum the children's costs
  and add the total to parent.session_estimated_cost_usd.
- Promote session_cost_source from 'none' -> 'subagent' when the parent
  had no direct spend but children did, so the UI doesn't label the
  total as having unknown provenance.  Real sources (openrouter,
  anthropic, etc.) are preserved.

Nested orchestrator -> worker trees roll up naturally: each layer's own
delegate_task() folds its direct children in, and when the orchestrator
itself returns, its parent folds the orchestrator's now-inflated total
on top.

Internal fields (_child_cost_usd, _child_role) are stripped from the
results dict before it's serialised back to the model — same contract
as _child_role already followed.

Tests: TestSubagentCostRollup (5 cases) covers single-child, batch,
zero-cost-children, preserved-source, and legacy-fixture paths.

Source: Kilo-Org/kilocode#9448

* fix(web): scope dashboard config Reset button to the current tab

Reported by @ykmfb001 via X: clicking 'Restore Defaults' (恢复默认值) on
the Auxiliary page wiped the entire config.yaml to defaults, not just
the auxiliary section. The button sits next to the category tabs and
users reasonably assumed 'reset this tab', not 'reset everything'.

Changes:
- handleReset now scopes to the fields in the current view:
  active category's fields (form mode) or search-matched fields
  (search mode). Only those keys are copied from defaults; the rest
  of the config is left alone.
- Added a window.confirm() with the scope name before applying.
- Button is hidden in YAML mode (scoping doesn't apply there).
- Tooltip/aria-label now name the scope, e.g. 'Reset Auxiliary to
  defaults'.
- i18n: new resetScopeTooltip / confirmResetScope / resetScopeToast
  strings in en + zh; resetDefaults key preserved for compat.
waefrebeorn pushed a commit to waefrebeorn/slermes that referenced this pull request Jul 2, 2026
…sResearch#16813)

* Port from Kilo-Org/kilocode#9448: roll up subagent costs into parent session total

Child subagents built by delegate_task() each track their own
session_estimated_cost_usd, but the parent agent's total never folded
those numbers in.  On runs where the parent mostly delegates and the
children do the expensive work, the footer/UI was reporting a fraction
of the actual spend — sometimes $0.00 when the parent itself made no
billed calls.

Fix:
- Capture each child's session_estimated_cost_usd into _child_cost_usd
  on the result entry (before child.close() drops the counter).
- After the existing subagent_stop hook loop, sum the children's costs
  and add the total to parent.session_estimated_cost_usd.
- Promote session_cost_source from 'none' -> 'subagent' when the parent
  had no direct spend but children did, so the UI doesn't label the
  total as having unknown provenance.  Real sources (openrouter,
  anthropic, etc.) are preserved.

Nested orchestrator -> worker trees roll up naturally: each layer's own
delegate_task() folds its direct children in, and when the orchestrator
itself returns, its parent folds the orchestrator's now-inflated total
on top.

Internal fields (_child_cost_usd, _child_role) are stripped from the
results dict before it's serialised back to the model — same contract
as _child_role already followed.

Tests: TestSubagentCostRollup (5 cases) covers single-child, batch,
zero-cost-children, preserved-source, and legacy-fixture paths.

Source: Kilo-Org/kilocode#9448

* fix(web): scope dashboard config Reset button to the current tab

Reported by @ykmfb001 via X: clicking 'Restore Defaults' (恢复默认值) on
the Auxiliary page wiped the entire config.yaml to defaults, not just
the auxiliary section. The button sits next to the category tabs and
users reasonably assumed 'reset this tab', not 'reset everything'.

Changes:
- handleReset now scopes to the fields in the current view:
  active category's fields (form mode) or search-matched fields
  (search mode). Only those keys are copied from defaults; the rest
  of the config is left alone.
- Added a window.confirm() with the scope name before applying.
- Button is hidden in YAML mode (scoping doesn't apply there).
- Tooltip/aria-label now name the scope, e.g. 'Reset Auxiliary to
  defaults'.
- i18n: new resetScopeTooltip / confirmResetScope / resetScopeToast
  strings in en + zh; resetDefaults key preserved for compat.
later0day pushed a commit to later0day/hermes-agent that referenced this pull request Jul 4, 2026
…session total

Child subagents built by delegate_task() each track their own
session_estimated_cost_usd, but the parent agent's total never folded
those numbers in.  On runs where the parent mostly delegates and the
children do the expensive work, the footer/UI was reporting a fraction
of the actual spend — sometimes $0.00 when the parent itself made no
billed calls.

Fix:
- Capture each child's session_estimated_cost_usd into _child_cost_usd
  on the result entry (before child.close() drops the counter).
- After the existing subagent_stop hook loop, sum the children's costs
  and add the total to parent.session_estimated_cost_usd.
- Promote session_cost_source from 'none' -> 'subagent' when the parent
  had no direct spend but children did, so the UI doesn't label the
  total as having unknown provenance.  Real sources (openrouter,
  anthropic, etc.) are preserved.

Nested orchestrator -> worker trees roll up naturally: each layer's own
delegate_task() folds its direct children in, and when the orchestrator
itself returns, its parent folds the orchestrator's now-inflated total
on top.

Internal fields (_child_cost_usd, _child_role) are stripped from the
results dict before it's serialised back to the model — same contract
as _child_role already followed.

Tests: TestSubagentCostRollup (5 cases) covers single-child, batch,
zero-cost-children, preserved-source, and legacy-fixture paths.

Source: Kilo-Org/kilocode#9448
t7tran pushed a commit to t7tran/kilocode that referenced this pull request Aug 14, 2026
fix(cli): include subagent costs in session total
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TUI cost of session doesn't include cost of subagents

2 participants