qwen-code/packages/cli/src
Shaojin Wen 6677ca1dd3
feat(web-shell): per-task token & time detail on completed todos (#5118)
* feat(web-shell): per-task token & time detail on completed todos

Expanding a completed task in the todo list now reveals when it ran (start /
end / duration) and what it spent: input / output / cached tokens, API time,
and tool time.

The agent stamps a cumulative-usage snapshot onto each todo (plan) update via
`_meta.stats`; the SDK normalizer carries it into the TodoWrite tool call's
rawOutput, and the web-shell diffs consecutive snapshots for tokens and API
time while summing transcript tool durations for tool time.

Works live (no polling race) and on /resume: tokens and tool time are
reconstructed from persisted usage metadata, while API time is live-only since
per-turn durations are not replayed. Sessions whose agent never stamped a
snapshot degrade gracefully to start/end + tool time.

* refactor(web-shell): show task duration inline on the end-time row

Trail the elapsed duration after the end time as a dimmed parenthetical
("12:34:15 (4m 14s)") instead of a separate row, since it's derived from the
start/end pair.

* fix(web-shell): correct per-task detail for reused todo ids; review follow-ups

- computeTodoDetails: when a completed id+content key restarts as in_progress (positional plan-N ids repeat across plans), reset the window so the new task diffs its own start instead of the prior task's far-earlier boundary — which rendered a cross-plan window with wildly inflated token/time numbers. Correct the todoStateKey JSDoc accordingly.
- Tool time: sort spans once and binary-search the task window instead of an O(todos x spans) scan per completed task.
- Tests: add the reuse-reset case, a windowed tool-time case, an SDK-normalizer -> extractTodoStats contract test (locks the stats passthrough so a field rename fails loudly), and a stopPropagation test (expander click must not bubble to the tool-row header).

* fix(web-shell): reset todo detail window on reopen via pending, not just direct

Track keys that have ever reached 'completed' instead of checking prev === 'completed': a reopened task can pass through 'pending' (completed → pending → in_progress), where prev at the re-activation is 'pending' and the direct check missed it, leaving a stale baseline that diffs across both runs. A pause/resume that never completed (in_progress → pending → in_progress) still keeps its first baseline, so its diff captures the whole task.

* fix(web-shell,cli): harden todo stats against NaN poisoning and partial snapshots

- MessageEmitter: only fold finite usage/duration values into the cumulative accumulator. A NaN/Infinity (incl. a NaN that survives `?? 0`) would poison the running total forever, making every later snapshot fail extractTodoStats and silently show 'not captured' for the rest of the session.
- extractTodoStats: require the token fields but default the live-only apiTimeMs to 0 when absent/non-finite, so a snapshot that omits it keeps its valid token counts instead of being dropped whole.
- computeTodoDetails: gate the start baseline on the stored value, not Map.has — a stats-less start (e.g. a plain plan message) recorded undefined, which Map.has treated as already-set, blocking a later stats-bearing snapshot from upgrading the baseline.
- Document the MessageEmitter-before-PlanEmitter ordering invariant in both emitters.
2026-06-15 15:40:06 +08:00
..
acp-integration feat(web-shell): per-task token & time detail on completed todos (#5118) 2026-06-15 15:40:06 +08:00
commands fix(serve): Add prompt queue backpressure (#5033) 2026-06-13 18:46:01 +08:00
config fix(core): Bound active tool result history (#5111) 2026-06-15 15:29:12 +08:00
core feat(cli): auto-detect terminal theme ('auto' or unset) (#3460) 2026-04-22 16:58:45 +08:00
dualOutput fix(dual-output): prevent FIFO blocking on startup when no reader connected (#4894) 2026-06-15 06:04:06 +08:00
export feat(vscode-companion): support /export session command (#2592) 2026-04-24 17:55:26 +08:00
i18n feat(cli): improve /copy command argumentHint and description (#5110) 2026-06-15 00:33:19 +08:00
nonInteractive feat(daemon): merge daemon-mode feature batch into main (#4490) 2026-06-12 00:34:49 +08:00
patches adh/bugfix/1563 (#4822) 2025-07-25 16:39:37 +00:00
remoteInput feat(cli): add dual-output sidecar mode for TUI (#3352) 2026-04-18 02:14:53 +08:00
serve feat(sdk,serve): DaemonTransport abstraction + ACP standard compliance (#5040) 2026-06-14 02:37:06 +08:00
services feat(cli): import Claude MCP servers (#5095) 2026-06-14 23:47:54 +08:00
startup feat(worktree): Phase D — startup --worktree flag + symlinkDirectories + PR refs (#4381) 2026-05-27 17:04:51 +08:00
test-utils refactor(core): remove GitService, migrate /restore to FileHistoryService (#4871) 2026-06-09 18:34:31 +08:00
ui fix(core): include response tokens in prompt estimate (#4525) 2026-06-15 02:45:05 +08:00
utils fix(core): remove unused debugResponses array and dead extractUsageFromGeminiClient (#4982) 2026-06-13 07:51:19 +08:00
gemini.test.tsx refactor(core): remove GitService, migrate /restore to FileHistoryService (#4871) 2026-06-09 18:34:31 +08:00
gemini.tsx feat(stats): add interactive /stats dashboard with cross-session tracking (#4779) 2026-06-10 14:59:08 +08:00
nonInteractiveCli.test.ts fix(core): Repair duplicate tool call IDs (#5107) 2026-06-14 23:53:36 +08:00
nonInteractiveCli.ts fix(core): Repair duplicate tool call IDs (#5107) 2026-06-14 23:53:36 +08:00
nonInteractiveCliCommands.test.ts feat(cli,web-shell): persist goal status in daemon transcript events (#5098) 2026-06-14 21:08:06 +08:00
nonInteractiveCliCommands.ts feat(cli,web-shell): persist goal status in daemon transcript events (#5098) 2026-06-14 21:08:06 +08:00
validateNonInterActiveAuth.test.ts feat(auth): discontinue Qwen OAuth free tier (2026-04-15 cutoff) (#3291) 2026-04-15 22:30:20 +08:00
validateNonInterActiveAuth.ts Merge branch 'main' into feat/debug-logging-refactor 2026-02-01 20:47:38 +08:00