Skip to content
This repository was archived by the owner on Jul 30, 2026. It is now read-only.

merge: sync origin/main into personal/local-clients - #16

Merged
makoMakoGo merged 12 commits into
personal/local-clientsfrom
sync/origin-main-2026-05-19
May 19, 2026
Merged

merge: sync origin/main into personal/local-clients#16
makoMakoGo merged 12 commits into
personal/local-clientsfrom
sync/origin-main-2026-05-19

Conversation

@makoMakoGo

@makoMakoGo makoMakoGo commented May 19, 2026

Copy link
Copy Markdown
Owner

What Changed

Syncs upstream origin/main into the fork-only personal/local-clients branch through a temporary integration branch.

This brings in upstream changes through 270d64c, including:

  • TUI Minutely tab support behind minutelyTabEnabled.
  • Daily detail rows for selected dates.
  • Antigravity chunked identity probe decoding.
  • Gemini v0.39 stream-json compatibility fixes.
  • Hermes agent total accounting.
  • README/package version updates to 2.1.3.

Local Conflict Resolution

The merge conflicted in crates/tokscale-cli/src/tui/ui/header.rs.

Resolution keeps the local header click-area geometry implementation and integrates upstream visible-tab filtering so hidden tabs, especially the default-disabled Minutely tab, do not register stale click hitboxes.

Validation

  • cargo fmt
  • cargo test -p tokscale-cli tui::ui::header
  • cargo test

Summary by CodeRabbit

Release Notes v2.1.3

  • New Features

    • 新增可选的"分钟级"(Minutely)数据视图,可通过配置启用
    • Daily视图新增详情展开功能,支持按来源/供应商/模型聚合展示
    • 增强HTTP响应解析,改进容错性
  • Bug Fixes

    • 改进Gemini使用量计算中的缓存处理逻辑
    • 优化消息数据聚合算法
  • Documentation

    • 更新所有语言版本的README,补充新视图说明及快捷键指南
  • Chores

    • 版本号升级至2.1.3

Review Change Stack

IvGolovach and others added 11 commits May 18, 2026 14:51
fix(hermes): include agent metadata in parsed usage

Validation
* Validation tier: Tier 2 — Narrow runtime change, Hermes parser metadata only with targeted parser and TUI aggregation coverage.
* git diff --check: PASS
* git diff --cached --check: PASS
* cargo test -p tokscale-core --test hermes: PASS (3 passed)
* cargo test -p tokscale-cli tui::data::tests::test_aggregate_messages_builds_agent_usage: PASS (1 passed)
* cargo fmt --check: PASS
* Ledger: not applicable — not required for selected validation tier/change family.
* Version: not applicable — not required for selected validation tier/change family.
* Not run: full workspace tests — not required for selected validation tier; remote CI can provide broad proof.

Rollback
* git revert HEAD
Recover Gemini CLI stream-json stats that expose per-model token counts directly under stats.models rather than nested stats.models.*.tokens. Treat stream-json input as already net when only input is present while preserving cache-inclusive normalization for legacy tokens wrappers.

Validation:

- cargo fmt --check: PASS

- cargo test -p tokscale-core gemini: PASS, 32 passed; codebuff 0 passed, 10 filtered out; hermes 0 passed, 3 filtered out

- cargo test -p tokscale-core: PASS, 662 passed, 1 ignored; codebuff 10 passed; hermes 3 passed; doc-tests 0 passed

- cargo clippy -p tokscale-core --lib -- -D warnings: PASS

- git diff --check: PASS

- git diff --cached --check: PASS

- rg -n "[А-Яа-яЁё]" crates/tokscale-core/src/sessions/gemini.rs: PASS, no matches

Rollback:

- git revert HEAD
* fix(antigravity): decode chunked identity probe responses

* fix(antigravity): prefer Transfer-Encoding chunked over Content-Length

When a response advertises both Transfer-Encoding: chunked and
Content-Length, RFC 7230 §3.3.3 requires the receiver to ignore
Content-Length and decode the body as chunked. Both identity_probe_request
and rpc_request previously checked Content-Length first, so a server that
sent both headers would be decoded as a fixed-length read on top of chunk
framing and the body would silently desync.

Reorder both code paths to check chunked first, then fall back to
Content-Length, then to close-delimited reads. Add a regression test that
serves chunked Antigravity JSON alongside a bogus Content-Length: 1
header and asserts the marker is still detected.

---------

Co-authored-by: Junho Yeo <i@junho.io>
Validation
* Validation tier: Tier 2 — narrow runtime change, localized TUI state/footer behavior.
* git diff --check: PASS
* git diff --cached --check: PASS
* cargo test -p tokscale-cli test_initial_hourly_tab_uses_hourly_sort_default: PASS
* cargo test -p tokscale-cli test_switch_tab_preserves_daily_sort_after_hourly_roundtrip: PASS
* cargo test -p tokscale-cli test_current_count_label_matches_active_tab: PASS
* cargo test -p tokscale-cli --bin tokscale: PASS, 482 passed, 1 ignored
* cargo fmt --all --check: PASS
* rustup run stable cargo clippy -p tokscale-cli --bin tokscale --all-features -- -D warnings: PASS
* Ledger: not applicable — not required for selected validation tier/change family.
* Version: not applicable — not required for selected validation tier/change family.
* Not run: no frontend tests — not required for CLI TUI-only change.
* Additional note: cargo test -p tokscale-cli was attempted and failed only in pricing integration tests due external LiteLLM/OpenRouter fetch failures, outside this TUI diff.

Rollback
* git revert HEAD
* feat(tui): open daily detail rows for selected date

* fix(tui): re-anchor daily detail close + sanitize on data refresh

Two daily-detail state-machine fixes:

- close_daily_detail() re-anchors the Daily summary selection by date
  rather than by the cached list index. The user can change the Daily
  sort while in detail mode, which makes daily_list_selected_index
  stale; finding the date in the freshly-sorted list keeps the same
  day highlighted on close. The viewport is preserved when the
  restored row is still in it, otherwise centered around the row.

- update_data() exits detail mode when the refreshed UsageData no
  longer contains the date the user was viewing. Without this guard
  selected_daily_detail_date stays Some(...) while
  get_sorted_daily_detail_rows() returns empty, leaving the UI on a
  ghost detail view that cannot be navigated out of with arrow keys.

Tests:

- close after sort change: open detail on a target date, switch sort
  from Date to Cost, close, assert the restored selection still points
  at the original date.
- update_data drops detail when the selected date disappears, with
  daily_detail_date returning None and the detail rows empty.
- update_data preserves detail mode when the selected date still
  exists after the refresh.

---------

Co-authored-by: Junho Yeo <i@junho.io>
)

* feat(tui): add Minutely tab for per-minute token breakdown

* feat(settings): gate Minutely tab behind opt-in flag, default off

Add `minutelyTabEnabled` to settings.json (camelCase, default false) and
plumb it through three surfaces so users who do not need per-minute
aggregation do not pay for it:

- DataLoader skips the minute-bucket map in aggregate_messages when the
  flag is off. The flag is set via `with_minutely_enabled(bool)` builder
  on construction in App::new_with_cached_data.
- The header tab strip and click-area registration filter Tab::Minutely
  out when the flag is off, so the tab is invisible and not focusable
  via mouse.
- Tab / BackTab / Left / Right navigation skip Tab::Minutely when the
  flag is off via new App::next_visible_tab / prev_visible_tab helpers
  that walk Tab::next/prev until they land on a visible variant.
- App::new_with_cached_data clamps initial_tab to Tab::Overview when
  the requested tab is invisible under the current flag, so a stale
  TuiConfig pointing at Minutely cannot strand the user on a hidden
  view.

Tests:

- settings round-trip and default-false coverage in tui::settings.
- DataLoader::aggregate_messages aggregation gate: skip when flag off,
  run when flag on, group same-minute messages, split adjacent-minute
  messages, clamp negative tokens and cost to zero.
- App tab navigation: default cycle excludes Minutely; with the flag
  enabled the full 7-tab cycle is restored. Stale Tab::Minutely initial
  request clamps to Tab::Overview when the flag is off.
- Footer current_count_label keeps an explicit "(0 minutes)" arm,
  verified by a dedicated test that enables the flag.

---------

Co-authored-by: Patrick Kühn <patrick.kuehn@von-poll.com>
Co-authored-by: Junho Yeo <i@junho.io>
…ab (junhoyeo#568)

Add the new `minutelyTabEnabled` setting to the configuration tables in
README.md, README.ja.md, README.ko.md, and README.zh-cn.md, plus a short
"Enabling the Minutely tab" subsection (localized in each language)
explaining when to flip it on, the cost/benefit, the settings.json
snippet, and where the tab appears in the strip.

The English README also gets a small parenthetical in the features list
and the TUI features section noting that Minutely is opt-in. The
language READMEs already lag behind the English view list (some still
say "4 views"); a full re-sync of those is out of scope for this
change \u2014 this PR only adds the new flag's documentation.

Cross-references the existing `autoRefreshEnabled` setting for the
near-real-time monitoring use case.
…hoyeo#569)

The `1-6` tab-switch hint appears in every README but the TUI key
handler has no number-key branches \u2014 tab switching is only
`\u2190/\u2192/Tab/BackTab`. The same handler also exposes nine keys that the
docs never listed: `Enter`/`Esc`/`Backspace` for the daily-detail and
graph-cell flow added by junhoyeo#564, `Home`/`End` for list navigation, `j`
to jump to today, `y` to copy the selected row, `h` to toggle the
Overview chart granularity, `v` to toggle the Hourly Table/Profile
view, `Shift+R` to toggle auto-refresh, and `+`/`-` to adjust the
refresh interval. `Ctrl+C` was also missing.

Replace the keyboard navigation block in README.md, README.ja.md,
README.ko.md, and README.zh-cn.md with an accurate list covering these
keys. Each language version preserves the surrounding bullet ordering
so the diff is local.

The three language READMEs also still claimed "4 interactive views" in
the Features list at line 140 \u2014 missing Hourly and Agents (added long
before this round) and the new opt-in Minutely. Their TUI Features
subsection already said 6 views, so the inconsistency was only at the
top. Align the line 140 wording with the English version (six views +
opt-in Minutely) in all three translations.

No code changes; touches only README*.md.
@coderabbitai

coderabbitai Bot commented May 19, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 821d1e89-3516-43c3-9908-57c224cf3142

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sync/origin-main-2026-05-19

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new optional 'Minutely' view to the TUI, allowing users to track token usage with minute-level granularity. It also adds a detailed daily view accessible by pressing Enter on the Daily tab, improves Gemini usage parsing to handle new data formats, and enhances Antigravity connection handling with support for chunked transfer encoding. A redundant sorting operation was identified in the minutely rendering logic, as the data source is already a sorted collection.

Comment thread crates/tokscale-cli/src/tui/ui/minutely.rs Outdated
@makoMakoGo
makoMakoGo marked this pull request as ready for review May 19, 2026 13:25

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
crates/tokscale-core/src/sessions/gemini.rs (1)

526-528: 💤 Low value

考虑为 input_includes_cache 的启发式逻辑添加注释。

该条件较为微妙:net_input.is_none() 作为回退条件意味着"当无法找到 input 字段时,默认按 cache-inclusive 处理"——此时 input 为 0,归一化操作为无害的空操作。添加简短注释可帮助后续维护者理解这一设计意图。

💡 建议添加的注释
+    // Determine whether input includes cached tokens:
+    // - prompt/input_tokens/prompt_tokens are always cache-inclusive
+    // - input inside a tokens wrapper is treated as cache-inclusive
+    // - when no input field exists (net_input.is_none()), default to true as normalization of 0 is harmless
     input_includes_cache: prompt_input.is_some()
         || wrapper_input.is_some()
         || net_input.is_none(),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/tokscale-core/src/sessions/gemini.rs` around lines 526 - 528, Add a
short inline comment above the assignment to input_includes_cache explaining the
heuristic: that input_includes_cache is true if prompt_input or wrapper_input
exists, or if net_input is missing (net_input.is_none()) which intentionally
treats a missing input field as a cache-inclusive default because input will
normalize to zero and be a no-op; reference the symbols input_includes_cache,
prompt_input, wrapper_input, and net_input so readers understand the fallback
rationale.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/tokscale-cli/src/tui/cache.rs`:
- Around line 592-595: 反序列化时把 minutely 字段直接置空导致当 cache 被标为 Fresh(见 load_cache()
的返回/CacheState::Fresh)但用户启用了 minutelyTabEnabled 时会显示空的 Minutely
页;修复方式:在读取/构建缓存(load_cache()/缓存反序列化逻辑)时检测到 minutely.is_empty() 且
minutelyTabEnabled 为 true 时不要返回 Fresh,改为将缓存降级为
Stale/设置需要强制刷新标记(或触发自动填充流程),或者改为同时持久化 minutely;定位符参考 minutely
字段、load_cache()、minutelyTabEnabled 和
CacheState::Fresh(或相应的枚举/标志),在这些位置实现降级或强制刷新逻辑以避免展示空页。

---

Nitpick comments:
In `@crates/tokscale-core/src/sessions/gemini.rs`:
- Around line 526-528: Add a short inline comment above the assignment to
input_includes_cache explaining the heuristic: that input_includes_cache is true
if prompt_input or wrapper_input exists, or if net_input is missing
(net_input.is_none()) which intentionally treats a missing input field as a
cache-inclusive default because input will normalize to zero and be a no-op;
reference the symbols input_includes_cache, prompt_input, wrapper_input, and
net_input so readers understand the fallback rationale.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 748f709d-5295-4546-a5d0-5d941a56014d

📥 Commits

Reviewing files that changed from the base of the PR and between 8cacdd0 and bd49d8d.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (28)
  • Cargo.toml
  • README.ja.md
  • README.ko.md
  • README.md
  • README.zh-cn.md
  • crates/tokscale-cli/src/antigravity.rs
  • crates/tokscale-cli/src/tui/app.rs
  • crates/tokscale-cli/src/tui/cache.rs
  • crates/tokscale-cli/src/tui/data/mod.rs
  • crates/tokscale-cli/src/tui/settings.rs
  • crates/tokscale-cli/src/tui/ui/daily.rs
  • crates/tokscale-cli/src/tui/ui/footer.rs
  • crates/tokscale-cli/src/tui/ui/header.rs
  • crates/tokscale-cli/src/tui/ui/minutely.rs
  • crates/tokscale-cli/src/tui/ui/mod.rs
  • crates/tokscale-core/src/sessions/gemini.rs
  • crates/tokscale-core/src/sessions/hermes.rs
  • crates/tokscale-core/tests/hermes.rs
  • packages/cli-darwin-arm64/package.json
  • packages/cli-darwin-x64/package.json
  • packages/cli-linux-arm64-gnu/package.json
  • packages/cli-linux-arm64-musl/package.json
  • packages/cli-linux-x64-gnu/package.json
  • packages/cli-linux-x64-musl/package.json
  • packages/cli-win32-arm64-msvc/package.json
  • packages/cli-win32-x64-msvc/package.json
  • packages/cli/package.json
  • packages/tokscale/package.json

Comment thread crates/tokscale-cli/src/tui/cache.rs
@makoMakoGo
makoMakoGo merged commit 3b115f1 into personal/local-clients May 19, 2026
3 checks passed
@makoMakoGo
makoMakoGo deleted the sync/origin-main-2026-05-19 branch May 19, 2026 13:52
@makoMakoGo
makoMakoGo restored the sync/origin-main-2026-05-19 branch May 19, 2026 13:57
@makoMakoGo
makoMakoGo deleted the sync/origin-main-2026-05-19 branch May 19, 2026 14:22
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants