Skip to content

[Bug] Gemini sessions & token usage never imported (3.20.3): .jsonl filtered out by extension check, and the $set incremental format is unparsed #7384

Description

@itswl

Self Checks / 自检

  • I have read the FAQ section in README.
  • I have searched for existing issues, including closed ones.

Related / 相关:#3938 reports the same symptom on 3.16.2 + Gemini CLI 0.45.2 (now stale). This issue adds the source-level root cause, which turns out to be two independent defects, plus measured data on how much usage is lost.
#3938 在 3.16.2 + Gemini CLI 0.45.2 上报告过同样的现象(现已被标记 stale)。本 issue 补充源码级根因——实际是两个独立缺陷——以及漏算量的实测数据。

CC Switch Version / 版本号

3.20.3

Operating System / 操作系统

macOS 14.4 (darwin 23.4.0, arm64)

Related App / 涉及应用

Gemini CLI

Steps to Reproduce / 重现步骤

  1. Install Gemini CLI 0.59.0 and run a few sessions so that ~/.gemini/tmp/<project>/chats/session-*.jsonl files exist.
  2. Open CC Switch with visibleApps.gemini: true and sessionAutoSyncEnabled: true.
  3. Open the Usage dashboard and the session history list.

Expected Behavior / 期望行为

Gemini CLI sessions appear in the session list, and their token usage is imported into the usage dashboard.

Gemini CLI 的会话出现在会话列表里,token 消耗被导入用量统计。

Actual Behavior / 实际行为

Neither happens. [GEMINI-SYNC] never appears in the log at all — not even a parse warning:

两者都没有发生。日志里 [GEMINI-SYNC] 一次都没出现过,连解析告警都没有:

$ for t in SESSION CODEX GEMINI OPENCODE PI; do printf "%-10s %s\n" "$t" "$(grep -c "\[$t-SYNC\]" ~/.cc-switch/logs/cc-switch.log)"; done
SESSION    12848
CODEX      511
GEMINI     0        <-- 33 session files on disk
OPENCODE   0
PI         7007

$ sqlite3 ~/.cc-switch/cc-switch.db "SELECT COUNT(*) FROM session_log_sync WHERE file_path LIKE '%.gemini%';"
0

Additional Context / 补充信息

Root cause / 根因

There are two independent defects. Fixing only the first still yields wrong numbers.

两个独立缺陷,只修第一个数字仍然是错的。

1. The extension filter rejects .jsonl

src-tauri/src/services/session_usage_gemini.rs:120

.map(|n| n.starts_with("session-") && n.ends_with(".json"))

"session-….jsonl".ends_with(".json") is false, so zero files are scanned. src-tauri/src/session_manager/providers/gemini.rs:42 has the same problem via path.extension() != Some("json").

Gemini CLI writes .jsonl — from its own bundle (@google/gemini-cli@0.59.0, bundle/chunk-YSBB75DZ.js):

`session-${safeSessionId}.jsonl`

2. The file body is no longer a single JSON object

Both parsers do a whole-file serde_json::from_str and then look for a top-level messages array (session_usage_gemini.rs:154-167, session_manager/providers/gemini.rs:59-65). Current Gemini CLI writes an append-only JSONL made of three record kinds:

两个解析器都对整个文件做 serde_json::from_str,再找顶层 messages 数组。现在的 Gemini CLI 写的是追加式 JSONL,由三类记录组成:

{"sessionId":"","projectHash":"","startTime":"","lastUpdated":"","kind":"main"}
{"$set":{"messages":[],"lastUpdated":""}}
{"id":"","type":"gemini","model":"","tokens":{"input":…,"output":…,"cached":…,"thoughts":…},"toolCalls":[]}

Renaming a real session file to .json so it passes filter #1 shows defect #2 on its own:

把一个真实会话文件改名成 .json 绕过缺陷 #1,缺陷 #2 就单独暴露出来:

[GEMINI-SYNC] Gemini 会话文件解析失败 …/session-…json:
              配置错误: JSON 解析失败: trailing characters at line 2 column 1

Two semantics that are easy to get wrong / 两处容易写错的语义

Both matter for token accuracy, and I verified them against 33 real session files:

两处都直接影响 token 准确性,已用 33 个真实会话文件验证:

  • $set.messages is a full replacement, not an append. When a session is resumed or compacted, the whole history is rewritten as a snapshot. In my data one snapshot carries 118 messages. Treating $set lines as "skip" loses all of them; treating them as "append" double-counts the overlap (one of my files has 8 ids present both in a snapshot and as standalone lines).
    $set.messages 是全量替换而非追加。 会话被 resume 或压缩时整段历史以快照重写。我的数据里有个快照带 118 条消息。把 $set 行当作"跳过"会丢掉全部;当作"追加"则会重复计算重叠部分(我有个文件里 8 个 id 同时出现在快照和独立行中)。

  • A standalone line with an existing id rewrites that message. Gemini CLI writes a message once without toolCalls, then rewrites it with the same id once the tool calls resolve — same timestamp, same tokens. 13 of 55 messages in one of my sessions are such rewrites.
    独立行若 id 已存在,是对该消息的改写。 Gemini CLI 先写一条不带 toolCalls 的消息,工具调用完成后用同一个 id 重写——时间戳和 tokens 都相同。我某个会话 55 条消息里有 13 条是这种改写。

Measured impact / 漏算量实测

Replaying my 33 session files with the correct semantics vs. skipping $set lines:

按正确语义回放我本机 33 个会话文件,对比"跳过 $set 行"的做法:

messages input output cached
correct ($set replaces + upsert by id) 213 10,232,753 136,973 7,427,224
skipping $set lines 155 6,152,380 91,558 4,057,979

That is 40% of input tokens lost, concentrated in one resumed session where 58 of 59 messages live only inside a $set.messages snapshot.

40% 的 input token 丢失,集中在一个 resume 过的会话里——59 条消息中有 58 条只存在于 $set.messages 快照中。

Note on pricing / 定价表补充

Even after parsing is fixed, cost shows $0 for most models: of the 5 models in my sessions, only gemini-3.5-flash exists in model-pricing.json. gemma-4-31b-it (my highest-volume model), gemini-2.5-pro, gemini-3.1-flash-lite and gemini-3.1-pro-preview have no pricing entry, so tokens will show but cost will not. Probably worth a separate issue.

即使解析修好,多数模型的费用仍显示 $0:我会话里的 5 个模型只有 gemini-3.5-flashmodel-pricing.json 中。gemma-4-31b-it(用量最大)、gemini-2.5-progemini-3.1-flash-litegemini-3.1-pro-preview 都没有定价条目,会有 token 数但算不出金额。这个大概值得单开一个 issue。

Prior art / 已有工作

PR #2771 addresses defect #1 and part of #2, but it is currently mergeable_state: dirty (it was written against get_sync_state, which main has since replaced with load_sync_cursors) and it skips $set lines, which is the 40% gap above. I have a fix that handles both defects and both semantics; I will open a PR referencing this issue.

PR #2771 处理了缺陷 #1 和部分 #2,但目前 mergeable_state: dirty(它基于 get_sync_state,而 main 已改为 load_sync_cursors),且它跳过 $set 行——就是上面那 40% 的缺口。我写了一个同时处理两个缺陷和两处语义的修复,会另开 PR 关联本 issue。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions