Skip to content

Add JetBrains Junie client support - #727

Merged
junhoyeo merged 2 commits into
junhoyeo:mainfrom
Haehnchen:feature/junie
Jun 21, 2026
Merged

Add JetBrains Junie client support#727
junhoyeo merged 2 commits into
junhoyeo:mainfrom
Haehnchen:feature/junie

Conversation

@Haehnchen

@Haehnchen Haehnchen commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

This add support for JetBrains Junie.

image

Summary by cubic

Adds first-class support for JetBrains Junie. Tokscale now scans ~/.junie/sessions/*/events.jsonl, imports usage with embedded costs (falls back to pricing when missing), and surfaces it in CLI, TUI, and the web UI.

  • New Features
    • Core: Registered junie; scans events.jsonl, parses usage events, deduplicates, infers provider, marks turn starts, preserves modelUsage.cost; tests added.
    • CLI: New --client junie, proper display name and logo; tests added.
    • TUI: Added Junie with color #7B61FF and hotkey p.
    • Web UI: Junie display name, logo (https://github.com/JetBrains.png), color, and type added.
    • Docs: Updated README and localized READMEs (ja/ko/zh) with Junie paths and inclusion in supported client lists.

Written for commit e60a16f. Summary will update on new commits.

Review in cubic

@vercel

vercel Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
tokscale Ignored Ignored Preview Jun 20, 2026 11:32pm

Request Review

@junhoyeo junhoyeo left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Reviewed via multi-agent review + local build/test/clippy + frontend parity test; full CI now green across all platforms (the first-time-contributor gate had blocked the native builds — I approved them and they pass). Adds JetBrains Junie end-to-end with exhaustive registry parity (Rust define_clients! Junie=32, COUNT->33; constants.ts/types.ts/SourceLogo.tsx; clientRegistry.test.ts passes), robust events.jsonl parsing, and embedded-cost preservation (reprice only when absent). Synced the ja/ko/zh READMEs with the English Junie entries. Thanks for the contribution — LGTM!

@junhoyeo
junhoyeo merged commit 633ea94 into junhoyeo:main Jun 21, 2026
17 checks passed
junhoyeo added a commit that referenced this pull request Jun 22, 2026
…bal ordinal

The file-global `usage_ordinal` gave a replayed identical
`LlmResponseMetadataEvent` a new suffix (:0 then :1), so both the original
and the replay passed the in-file `seen` set and the cross-file
`should_keep_deduped_message` filter — double-counting the same tokens/cost.

Revert the uniqueness component to the row's position within the event's own
`modelUsage` array. A replayed event reproduces the same dedup key (collapsed
to one), while multiple distinct rows inside one event keep distinct indices.
Genuinely-distinct calls already differ via `timestampMs` in the key, so the
#727 undercount fix is preserved.

Constraint: cross-file Junie dedup relies on dedup_key being stable across replays
Rejected: process-global monotonic counter | breaks replay collapse (regresses this PR's own fix)
Confidence: high
Scope-risk: narrow
Not-tested: two byte-identical events with identical timestamps are treated as a replay (indistinguishable from a true replay)
junhoyeo added a commit that referenced this pull request Jun 22, 2026
…749)

* fix(junie): unique dedup ordinal, scoped turn-start, kind-based skip

Three confirmed bugs in the Junie session parser (PR #727):

(a) The dedup key embedded `usage_index` from `usages.iter().enumerate()`,
which resets to 0 on every `modelUsage` row. Two distinct LLM calls with
identical token counts produced the same key and the second was silently
dropped. Replace it with a file-global monotonic ordinal so every usage
row across the whole session earns a unique key.

(b) `pending_turn_start` was set by a UserPromptEvent and only cleared when
a usage message was actually pushed. A prompt that produced no counted
usage leaked the flag onto a later, unrelated turn's usage event. Clear it
once per usage event (the first response event consumes the prompt's
turn-start) and mark only the first emitted message of that event.

(c) SKIP_EVENT_KINDS was matched via `line.contains(kind)`, a raw substring
search that could drop legitimate usage/prompt lines merely mentioning a
skipped kind in their text. Match on the parsed event kind instead, keeping
the substring filter only as a cheap pre-parse guard.

Adds regression tests, including two distinct usage rows with identical
token counts that must both be counted (fails without the ordinal fix).

Confidence: high
Scope-risk: narrow
Not-tested: real multi-megabyte Junie state snapshots (perf path)

* fix(junie): derive dedup suffix from within-event index, not file-global ordinal

The file-global `usage_ordinal` gave a replayed identical
`LlmResponseMetadataEvent` a new suffix (:0 then :1), so both the original
and the replay passed the in-file `seen` set and the cross-file
`should_keep_deduped_message` filter — double-counting the same tokens/cost.

Revert the uniqueness component to the row's position within the event's own
`modelUsage` array. A replayed event reproduces the same dedup key (collapsed
to one), while multiple distinct rows inside one event keep distinct indices.
Genuinely-distinct calls already differ via `timestampMs` in the key, so the
#727 undercount fix is preserved.

Constraint: cross-file Junie dedup relies on dedup_key being stable across replays
Rejected: process-global monotonic counter | breaks replay collapse (regresses this PR's own fix)
Confidence: high
Scope-risk: narrow
Not-tested: two byte-identical events with identical timestamps are treated as a replay (indistinguishable from a true replay)
pinion05 added a commit to pinion05/tokscale that referenced this pull request Jun 23, 2026
Adds the JetBrains Junie coding agent as a first-class client.

- Session source: ~/.junie/sessions/*/events.jsonl (ClientId::Junie = 32, COUNT -> 33)
- Parser reads modelUsage cost/tokens/turn-start with provider inference; preserves Junie's authoritative per-call cost and reprices only when absent; tolerant of malformed lines and large state snapshots
- Full registry parity: frontend constants (display name, logo, color), SUPPORTED_CLIENT_TYPES, TUI client UI + hotkey; clientRegistry.test.ts green
- Docs: README EN + ja/ko/zh client tables, multi-platform list, --client values, data-location table
makoMakoGo added a commit to makoMakoGo/tokscale that referenced this pull request Jun 23, 2026
ported from upstream junhoyeo#727
ported from upstream junhoyeo#749
t1000040 pushed a commit to tmobi-internal/tokscale that referenced this pull request Jun 30, 2026
Adds the JetBrains Junie coding agent as a first-class client.

- Session source: ~/.junie/sessions/*/events.jsonl (ClientId::Junie = 32, COUNT -> 33)
- Parser reads modelUsage cost/tokens/turn-start with provider inference; preserves Junie's authoritative per-call cost and reprices only when absent; tolerant of malformed lines and large state snapshots
- Full registry parity: frontend constants (display name, logo, color), SUPPORTED_CLIENT_TYPES, TUI client UI + hotkey; clientRegistry.test.ts green
- Docs: README EN + ja/ko/zh client tables, multi-platform list, --client values, data-location table
t1000040 pushed a commit to tmobi-internal/tokscale that referenced this pull request Jun 30, 2026
…unhoyeo#749)

* fix(junie): unique dedup ordinal, scoped turn-start, kind-based skip

Three confirmed bugs in the Junie session parser (PR junhoyeo#727):

(a) The dedup key embedded `usage_index` from `usages.iter().enumerate()`,
which resets to 0 on every `modelUsage` row. Two distinct LLM calls with
identical token counts produced the same key and the second was silently
dropped. Replace it with a file-global monotonic ordinal so every usage
row across the whole session earns a unique key.

(b) `pending_turn_start` was set by a UserPromptEvent and only cleared when
a usage message was actually pushed. A prompt that produced no counted
usage leaked the flag onto a later, unrelated turn's usage event. Clear it
once per usage event (the first response event consumes the prompt's
turn-start) and mark only the first emitted message of that event.

(c) SKIP_EVENT_KINDS was matched via `line.contains(kind)`, a raw substring
search that could drop legitimate usage/prompt lines merely mentioning a
skipped kind in their text. Match on the parsed event kind instead, keeping
the substring filter only as a cheap pre-parse guard.

Adds regression tests, including two distinct usage rows with identical
token counts that must both be counted (fails without the ordinal fix).

Confidence: high
Scope-risk: narrow
Not-tested: real multi-megabyte Junie state snapshots (perf path)

* fix(junie): derive dedup suffix from within-event index, not file-global ordinal

The file-global `usage_ordinal` gave a replayed identical
`LlmResponseMetadataEvent` a new suffix (:0 then :1), so both the original
and the replay passed the in-file `seen` set and the cross-file
`should_keep_deduped_message` filter — double-counting the same tokens/cost.

Revert the uniqueness component to the row's position within the event's own
`modelUsage` array. A replayed event reproduces the same dedup key (collapsed
to one), while multiple distinct rows inside one event keep distinct indices.
Genuinely-distinct calls already differ via `timestampMs` in the key, so the
junhoyeo#727 undercount fix is preserved.

Constraint: cross-file Junie dedup relies on dedup_key being stable across replays
Rejected: process-global monotonic counter | breaks replay collapse (regresses this PR's own fix)
Confidence: high
Scope-risk: narrow
Not-tested: two byte-identical events with identical timestamps are treated as a replay (indistinguishable from a true replay)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants