Skip to content

feat(cli): combine elapsed + timeout in shell time indicator#3512

Merged
wenshao merged 2 commits into
QwenLM:mainfrom
wenshao:feat/shell-time-display
Apr 23, 2026
Merged

feat(cli): combine elapsed + timeout in shell time indicator#3512
wenshao merged 2 commits into
QwenLM:mainfrom
wenshao:feat/shell-time-display

Conversation

@wenshao

@wenshao wenshao commented Apr 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

Render shell tools that have an explicit timeout as (elapsed · timeout N) inline with the Running… status from t=0, instead of splitting the information across the right-aligned elapsed indicator and the ShellStatsBar row.

Before

▶ Running bash: sleep 10                                10s
  <streaming>
  +N lines  timeout 5.0s  2.3 KB

(elapsed shown after a 3s quiet threshold, timeout shown on a separate stats row below)

After

▶ Running bash: sleep 10                (10s · timeout 5s)
  <streaming>
  +N lines  2.3 KB

(elapsed + timeout combined inline from t=0; sub-second precision preserved for non-integer timeouts)

What changed

  • formatters: add hideTrailingZeros option so whole seconds render as 5s (not 5.0s) while 5.5s stays intact.
  • ToolElapsedTime: accept optional timeoutMs. When present, skip the 3s quiet threshold and render (elapsed · timeout N); when absent, keep the original quiet-then-elapsed behavior so fast non-shell tools stay visually calm.
  • ToolMessage: extract timeoutMs from AnsiOutputDisplay and feed it to ToolElapsedTime.
  • ShellStatsBar: drop its timeoutMs field (now inline above); keeps +N lines and memory usage only.
  • Unify both modes on formatDuration so hour-range output is consistent (1h 2m 6s across timeout and no-timeout paths).

Test plan

  • packages/cli/src/ui/utils/formatters.test.ts — 5 new cases covering hideTrailingZeros (whole seconds, fractional seconds, ms-range, multi-unit)
  • packages/cli/src/ui/components/shared/ToolElapsedTime.test.tsx — 8 cases covering Pending/Executing/Success, the 3s quiet threshold, combined format from t=0, fractional timeout (5.5s), minute-range (1m 5s · timeout 5m), and the non-positive-timeout fallback
  • Full CLI regression: 4316 pass / 7 skipped / 0 fail

Manual TTY verification (tmux)

Mounted ToolElapsedTime inside a 140×24 tmux pane via a small Ink harness that exercises Pending/Executing/Success and feeds various timeoutMs combinations, then captured frames at specific offsets.

1. Initial frame (t=0) — combined format visible from the first tick; no-timeout row stays quiet:

t=0s
[1] static Pending:                                       (empty)
[2] no-timeout (3s quiet → elapsed):
[3] timeout=30s (from t=0):                                (0s · timeout 30s)
[4] timeoutMs=5500 (fractional):                           (0s · timeout 5.5s)
[5] cycled status (Pending→Exec→Success):
[6] mid-flight timeout (undefined→10s @ t=2s):
[7] timeout=0 (ignored, falls back to 3s quiet):
[8] negative timeout (defensive):

2. Past the 3s threshold (t≈9s) — quiet row becomes visible; combined rows advance; mid-flight row has received the new timeoutMs and switched into combined mode; defensive fallbacks behave like the no-timeout path:

t=9s
[1] static Pending:                                       (empty)
[2] no-timeout (3s quiet → elapsed):                       9s
[3] timeout=30s (from t=0):                                (9s · timeout 30s)
[4] timeoutMs=5500 (fractional):                           (9s · timeout 5.5s)
[6] mid-flight timeout (undefined→10s @ t=2s):             (9s · timeout 10s)
[7] timeout=0 (ignored, falls back to 3s quiet):           9s
[8] negative timeout (defensive):                          9s

3. Hour range (t=1h 2m 6s) — both modes render consistently via formatDuration:

[2] no-timeout:                                            1h 2m 6s
[3] timeout=30s:                                           (1h 2m 6s · timeout 30s)
[4] timeoutMs=5500 (fractional):                           (1h 2m 6s · timeout 5.5s)
[5] cycled status (Pending→Exec→Success):                  (1h 2m 6s · timeout 15s)

4. Status cycling — the row in [5] flips Pending → Executing → Success on a 9s cycle and confirms that transitioning out of Executing hides the indicator, while re-entering Executing resets the elapsed counter (executionStartTime change re-inits the effect):

sample 1 (t=24s): [5] (5s · timeout 15s)   # mid-Executing
sample 2 (t=26s): [5]                      # Success/Pending → hidden
sample 3 (t=28s): [5] (0s · timeout 15s)   # re-entered Executing, counter reset
sample 4 (t=30s): [5] (2s · timeout 15s)   # still Executing, advancing

5. Defensive edgestimeoutMs={0} and timeoutMs={-500} fall back to the no-timeout path (rows [7] / [8] above), i.e. quiet for the first 3s and plain elapsed afterward. No crash or garbled label.

Render shell tools that have an explicit timeout as
`(elapsed · timeout N)` inline with the Running… status from t=0,
instead of splitting the information across the right-aligned elapsed
indicator and the ShellStatsBar row.

- formatters: add a `hideTrailingZeros` option so whole seconds render
  as `5s` rather than `5.0s` while fractional values like `5.5s` stay
  intact
- ToolElapsedTime: accept optional `timeoutMs`; when set, skip the 3s
  quiet threshold and render the combined `(elapsed · timeout N)` label
- ToolMessage: extract `timeoutMs` from AnsiOutputDisplay and feed it
  to ToolElapsedTime
- ShellStatsBar: drop its `timeoutMs` field (now inline); keeps
  `+N lines` and memory usage only
- Unify both modes on `formatDuration` so hour-range output is
  consistent (`1h 2m 6s` across timeout and no-timeout paths)

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Compact mode still drops the timeout budget for shell tools. This change removes timeout rendering from ShellStatsBar and wires timeoutMs only through the expanded ToolMessage path, but CompactToolGroupDisplay still renders ToolElapsedTime without timeoutMs. As a result, compact tool groups fall back to the old elapsed-only behavior and no longer show the configured timeout. Please thread timeoutMs through the compact path as well so both views implement the new behavior consistently.

— gpt-5.4 via Qwen Code /review

The combined `(elapsed · timeout N)` format introduced in the previous
commit was only wired through the expanded ToolMessage path. Compact
tool groups kept rendering ToolElapsedTime without timeoutMs, so shell
tools displayed in compact mode silently dropped the timeout budget.

- CompactToolGroupDisplay: add getShellTimeoutMs() to pull timeoutMs
  off the active tool's AnsiOutputDisplay result (same shape used by
  ToolMessage) and feed it to ToolElapsedTime
- add CompactToolGroupDisplay.test.tsx covering the three paths:
  ansi display with timeoutMs, ansi display without timeoutMs, and
  non-ansi resultDisplay (string)
@wenshao

wenshao commented Apr 22, 2026

Copy link
Copy Markdown
Collaborator Author

Good catch — fixed in 5de1944. CompactToolGroupDisplay now extracts timeoutMs from the active tool's AnsiOutputDisplay (same shape used by ToolMessage) and threads it into ToolElapsedTime, so compact tool groups render (elapsed · timeout N) consistently with the expanded path. Added a focused test file (CompactToolGroupDisplay.test.tsx, 3 cases) covering the three branches: ansi display with timeoutMs, ansi display without timeoutMs, and non-ansi resultDisplay.

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

No issues found. LGTM! ✅ — gpt-5.4 via Qwen Code /review

@tanzhenxin tanzhenxin added the type/feature-request New feature or enhancement request label Apr 22, 2026

@tanzhenxin tanzhenxin left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Review

Nice cleanup — inline (elapsed · timeout N) reads much better than the previous split across the right-aligned elapsed and the stats-bar timeout, and unifying both modes on formatDuration removes the minute/hour-range divergence. Tests look reasonable.

Verdict

APPROVE

@wenshao wenshao merged commit 69da115 into QwenLM:main Apr 23, 2026
13 checks passed
TaimoorSiddiquiOfficial pushed a commit to TaimoorSiddiquiOfficial/HopCode that referenced this pull request Apr 23, 2026
Port upstream QwenLM/qwen-code improvements to HopCode:

fix(core): scope StreamingToolCallParser per stream (QwenLM#3516)
- Add ConverterStreamContext interface with per-stream StreamingToolCallParser
- Add createStreamContext() factory on OpenAIContentConverter
- convertOpenAIChunkToGemini() now takes ctx as explicit arg
- Drop shared streamingToolCallParser instance field and resetStreamingToolCalls()
- ContentGenerationPipeline creates one context per stream entry
- Update all tests to use createStreamContext() API
- Fixes concurrent subagent streams corrupting each other (NO_RESPONSE_TEXT)

feat(cli): combine elapsed + timeout in shell time indicator (QwenLM#3512)
- formatters: add FormatDurationOptions with hideTrailingZeros option
- ToolElapsedTime: accept optional timeoutMs; render (elapsed · timeout N)
- ToolMessage: extract timeoutMs from AnsiOutputDisplay, feed to ToolElapsedTime
- CompactToolGroupDisplay: add getShellTimeoutMs() helper, thread timeoutMs
- AnsiOutput: drop timeoutMs from ShellStatsBarProps (now inline in elapsed)

fix(cli): stabilize resume callback deps (QwenLM#3533)
- Destructure historyManager into clearItems/loadHistory stable refs
- Use hasHistoryManager boolean guard in useCallback dep array

chore: bump version 0.14.41 -> 0.15.1 across all packages

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
TaimoorSiddiquiOfficial pushed a commit to TaimoorSiddiquiOfficial/HopCode that referenced this pull request Apr 23, 2026
…3512)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
chiga0 pushed a commit that referenced this pull request Apr 24, 2026
* feat(cli): combine elapsed + timeout in shell time indicator

Render shell tools that have an explicit timeout as
`(elapsed · timeout N)` inline with the Running… status from t=0,
instead of splitting the information across the right-aligned elapsed
indicator and the ShellStatsBar row.

- formatters: add a `hideTrailingZeros` option so whole seconds render
  as `5s` rather than `5.0s` while fractional values like `5.5s` stay
  intact
- ToolElapsedTime: accept optional `timeoutMs`; when set, skip the 3s
  quiet threshold and render the combined `(elapsed · timeout N)` label
- ToolMessage: extract `timeoutMs` from AnsiOutputDisplay and feed it
  to ToolElapsedTime
- ShellStatsBar: drop its `timeoutMs` field (now inline); keeps
  `+N lines` and memory usage only
- Unify both modes on `formatDuration` so hour-range output is
  consistent (`1h 2m 6s` across timeout and no-timeout paths)

* feat(cli): thread shell timeoutMs through compact tool group display

The combined `(elapsed · timeout N)` format introduced in the previous
commit was only wired through the expanded ToolMessage path. Compact
tool groups kept rendering ToolElapsedTime without timeoutMs, so shell
tools displayed in compact mode silently dropped the timeout budget.

- CompactToolGroupDisplay: add getShellTimeoutMs() to pull timeoutMs
  off the active tool's AnsiOutputDisplay result (same shape used by
  ToolMessage) and feed it to ToolElapsedTime
- add CompactToolGroupDisplay.test.tsx covering the three paths:
  ansi display with timeoutMs, ansi display without timeoutMs, and
  non-ansi resultDisplay (string)
xaelistic pushed a commit to xaelistic/qwen-code that referenced this pull request Jun 7, 2026
…3512)

* feat(cli): combine elapsed + timeout in shell time indicator

Render shell tools that have an explicit timeout as
`(elapsed · timeout N)` inline with the Running… status from t=0,
instead of splitting the information across the right-aligned elapsed
indicator and the ShellStatsBar row.

- formatters: add a `hideTrailingZeros` option so whole seconds render
  as `5s` rather than `5.0s` while fractional values like `5.5s` stay
  intact
- ToolElapsedTime: accept optional `timeoutMs`; when set, skip the 3s
  quiet threshold and render the combined `(elapsed · timeout N)` label
- ToolMessage: extract `timeoutMs` from AnsiOutputDisplay and feed it
  to ToolElapsedTime
- ShellStatsBar: drop its `timeoutMs` field (now inline); keeps
  `+N lines` and memory usage only
- Unify both modes on `formatDuration` so hour-range output is
  consistent (`1h 2m 6s` across timeout and no-timeout paths)

* feat(cli): thread shell timeoutMs through compact tool group display

The combined `(elapsed · timeout N)` format introduced in the previous
commit was only wired through the expanded ToolMessage path. Compact
tool groups kept rendering ToolElapsedTime without timeoutMs, so shell
tools displayed in compact mode silently dropped the timeout budget.

- CompactToolGroupDisplay: add getShellTimeoutMs() to pull timeoutMs
  off the active tool's AnsiOutputDisplay result (same shape used by
  ToolMessage) and feed it to ToolElapsedTime
- add CompactToolGroupDisplay.test.tsx covering the three paths:
  ansi display with timeoutMs, ansi display without timeoutMs, and
  non-ansi resultDisplay (string)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type/feature-request New feature or enhancement request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants