feat(web): show tool call duration in the detail dialog#1036
Open
junmo-kim wants to merge 3 commits into
Open
Conversation
Show a completed tool's execution duration at the top of its detail dialog. The value is derived from the Claude entry's own timestamps (the execution machine's wall clock) rather than the hub's message-receive time, and is used only when both the tool_use and tool_result entries carry a real timestamp — otherwise it falls back to the hub receive times on both sides, so the two clocks are never mixed. Running/pending tools show nothing, the running-state live timer is unchanged, and clock skew is guarded against. Reuses the existing formatDuration formatter. No schema changes.
There was a problem hiding this comment.
Findings
- [Major] Result-before-call fallback can display
0.0sinstead of the hub-time duration — when atool_resultis reduced before itstool_use, the placeholder tool is created withstartedAtequal to the result message time. The later tool-use path now backfillsexecStartedAt, but it still does not backfillstartedAt; when exec timestamps are incomplete or missing,toolDurationMsfalls back tostartedAt ?? createdAt, so the newly added duration row uses the result timestamp for both ends. Evidenceweb/src/components/ToolCard/toolDuration.ts:22, related contextweb/src/chat/reducerTimeline.ts:896.
Suggested fix:if (block.tool.state === 'pending') { block.tool = { ...block.tool, state: 'running' } } setEarliestStartedAt(block, msg.createdAt) setEarliestExecStartedAt(block, msg.agentTimestamp ?? null)
Questions
- None.
Summary
- Review mode: initial
- One Major finding: the new displayed duration is wrong for the existing result-before-use ordering when the exec timestamp pair is unavailable or incomplete.
Testing
- Not run (review automation; PR content not executed). Add a reducer/helper test for result-before-use with missing exec start that asserts the hub fallback duration.
HAPI Bot
| const useExec = tool.execStartedAt != null && tool.execCompletedAt != null | ||
| const end = useExec ? tool.execCompletedAt : tool.completedAt | ||
| if (end == null) return null | ||
| const start = useExec ? tool.execStartedAt : (tool.startedAt ?? tool.createdAt) |
There was a problem hiding this comment.
[MAJOR] This fallback can still use the wrong start time for the result-before-use ordering. In that path, ensureToolBlock creates the placeholder from the tool_result, so startedAt is the result message time; when the later tool_use arrives, this PR backfills only execStartedAt, not the hub startedAt. If the exec pair is incomplete/missing, this line falls back to startedAt ?? createdAt and the dialog displays 0.0s instead of the hub receive window.
Suggested fix:
if (block.tool.state === 'pending') {
block.tool = { ...block.tool, state: 'running' }
}
setEarliestStartedAt(block, msg.createdAt)
setEarliestExecStartedAt(block, msg.agentTimestamp ?? null)When a tool_result entry is reduced before its tool_use, the tool block is created from the result, so the hub startedAt is the result receive time. The tool_use path only lowered the exec start, not the hub startedAt, so a timestamp-less pair (no exec duration available) fell back to startedAt === completedAt and the detail dialog showed 0.0s. Lower the hub startedAt to the earlier tool_use receive time as well.
There was a problem hiding this comment.
Findings
- None.
Questions
- None.
Summary
- Review mode: follow-up after new commits
- No high-confidence issues found in the latest full PR diff. The prior bot finding about result-before-use hub fallback has been addressed by backfilling
startedAtfrom the latertool_usepath. - Residual risk: I did not execute the PR tests in this automation pass; review was based on diff/source inspection only.
Testing
- Not run (review automation; PR content not executed).
HAPI Bot
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
While a tool call is running, its card header shows a live elapsed timer, but once the tool finishes that timer disappears — so there's no way to see how long a completed tool actually took. Showing it permanently on every card would be noisy, so it fits better in the detail dialog that opens on click.
Solution
Add a single Duration row at the top of the tool detail dialog for completed and errored tools. The value is derived from the Claude entry's own
timestampfields (the execution machine's wall clock, already present in the stored message payload) rather than the hub's message-receive time, so it reflects the tool's actual execution time rather than transport/queue timing. It is used only when both the tool_use and tool_result entries carry a real Claude timestamp; if either side is missing one (a hub-synthesized result for a denied/timed-out/cancelled tool, a malformed entry, or a non-Claude agent flavor), the duration falls back to the hub receive times on both sides — the two clocks are never mixed.Running and pending tools show nothing here (their live progress is already in the card header), the running-state live timer is unchanged, and clock skew is guarded against. Reuses the existing
formatDurationformatter. No DB/schema changes.Screenshots
Duration row in the detail dialog on a completed tool, from a live Claude session:
sleep 2— Duration 2.3sTests
Unit tests cover the ISO timestamp parser (valid/missing/malformed), the duration helper's both-or-neither selection (exec pair vs. hub pair, clock-skew guard, running/pending/instantaneous cases), and a render test for the dialog row. Reducer-level tests cover exec-timestamp recording distinct from the hub receive time, the non-Claude fallback, and the reorder case where the tool_result entry is processed before its tool_use. Manually verified end-to-end with a live Claude session in an isolated hub instance.
Follow-up: message-level Duration (replacing
turn_duration) and per-tool token counts are out of scope.