Repository navigation
Releases: ChiR24/opencode-tps-meter
Release list
v0.4.1 — Accurate TPS, subagent throughput, cache hit rate, theme colors
The throughput figures are now measured correctly, and three community features
land alongside. Tested on OpenCode v1 and v2. Install with
opencode-tps-meter@latest.
Accurate throughput
- No more five-digit spikes. Hosts batch output before publishing it (v2
flushes every ~100ms), and the first batch of a stream was divided by the few
milliseconds before the meter's first update, so readings like10364appeared,
most visibly as a subagent's frozen average. Each chunk is now rated over the
interval it was actually generated in, and the opening chunk counts toward the
total but never toward a rate. - The live rate is right from the start. It used to begin at a fixed 100 TPS
and climb toward the real figure over several seconds on fast models. It is now
exact from the second chunk (about 100ms in) at any speed, with no smoothing lag. - Settled averages describe what streamed. A step's average is its streamed
tokens over the time they streamed, corrected by the provider's count only when
the two agree within 2x. Provider totals that include output the host never
streamed, such as hidden reasoning, no longer inflate the rate. - Tool-call arguments count as output (v2). The text a model writes for a tool
call (a file write, an edit) is now counted, so a step that only calls a tool has
a real reading instead of none. - Calibration persists and is weighted by size (v2). The live estimate's
per-model correction is saved, so new sessions start calibrated, and small steps
no longer swing it. Steps whose reasoning count the provider leaves ambiguous are
left out. - A stalled stream shows its rate falling instead of holding its last value.
- One clock per measurement. v1 no longer divides by server completion time
minus a local first-token time. - The v1 toast keeps its main line on the primary session (a subagent's chunk
used to relabel the subagent's rate as the main one) and no longer drops an
update that lands inside its throttle window. - Dashboard.
genis replaced bytools, the time a turn spent running tools,
since tool time is never inside a step's rate window. The per-model ledger starts
fresh, as its old best/mean figures were recorded with the spiking formula. - Same-named subagents in the footer get a short id suffix, e.g.
explore(9B5c),
and a step that ends on an error now settles the reading and still counts its
usage.
Added
- Multi-session footer (#8, @ondrejch). While subagents run, the footer
switches to an aggregate line, e.g.TPS Σ 318 | main 63 | explore 91 | +2.
Σ sums every session generating right now, and each agent is labeled by name.
Columns keep their spawn order, so they don't swap places. A subagent stays
visible through long tool calls and drops off about 4s after it finishes.
Works on both hosts: v1 resolves the session tree fromsession.created/
session.updatedandapi.state.session, v2 fromctx.data.session. - Prompt cache hit rate (#9, @Anigx). The footer shows
cache N% · session cache N%: the cached share of the prompt for the last
step and for the whole session, computed as
cache.read / (input + cache.read + cache.write). It also appears in the v2
dashboard and the v1 final-stats toast. The figures appear once a session's
first step has finished. Toggle withshowCacheHit(defaulttrue) or
TPS_METER_SHOW_CACHE_HIT.
Fixed
- Theme colors on current v2 hosts (#10, @dragonish). v2 renamed its theme
tokens totext.base/text.muted/feedback.<kind>.base. The meter now
resolves those names, falling back to the older ones, instead of rendering in
the host's default color. The theme is also read live, so a theme switch
recolors the meter without restarting the TUI. - Cache figures after a tool-call-only step. A step that ends in a tool call
without streaming any text now updates the footer's cache figures on both
hosts, instead of leaving the previous step's numbers on screen.
Notes
- New config option:
showCacheHit(defaulttrue), env
TPS_METER_SHOW_CACHE_HIT. No other config changes. - Thanks to @ondrejch, @Anigx and @dragonish for the contributions.
v0.4.0 - OpenCode v2 support (stable)
v0.4.0 — OpenCode v2 support (stable)
Promotes v0.4.0-beta.1 to stable with loader fixes verified end to end against
OpenCode 2.0.2: the server reports
"features":{"server":true,"tui":true},"state":{"status":"active"} for the
plugin, and the meter renders in the TUI prompt footer. Install with
opencode-tps-meter@latest. Everything in the beta.1 notes below carries over.
🐞 Fixed since beta.1
- Removed the
tui: trueflag from the server plugin definition. The loader
treats anytuikey as a legacy handler and throws
Plugin <spec> has invalid tui exporton a non-function, aborting the whole
plugin load with no meter. ./serverresolves to the dual-hostdist/index.mjsagain ({ id, server, setup }), the only shape valid on both loaders. The v2-only
dist/v2/server.mjs({ id, setup }) fails the v1server()requirement.
The/v2exports remain for programmaticimportonly.- Local
file://installs must target<package>/dist, not the repo root.
The 2.0.2 resolver handles local directories by file path (<dir>/server,
<dir>/index,<dir>/tui) and ignorespackage.json#exportsthere, so the
repo root (no rootindex.*) is silently skipped.dist/contains both
index.mjs(server) andtui.mjs(TUI). - A build-invariants regression test now locks both entrypoints against both
loaders, including a direct guard ondist/v2/server.mjs.
📝 Notes
- OpenCode 2.x ships its own per-message
tok/sreadout next to each assistant
reply. To show only this plugin's meter, set"tps": falseundersessionin
cli.json. To keep the built-in readout instead, run/tps hidden. - Local installs should register
file:///<path>/opencode-tps-meter/distin
opencode.json(plugin),cli.json(plugins), andtui.json(plugin).
v0.4.0-beta.1 — OpenCode v2 support (beta)
Prerelease. This targets opencode2, which is itself beta and whose plugin API is documented as subject to change before 2.0 is stable. Published under the beta dist-tag — latest stays on 0.3.1.
npm install opencode-tps-meter@betaOpenCode v2 support
One package now serves both generations. v1 reads server/tui, v2 reads setup, and v1 behaviour is unchanged.
v2 deleted the entire message.* event family the meter was built on, so this is a port rather than a version bump:
| v1 | v2 |
|---|---|
message.part.delta (field text) |
session.text.delta |
message.part.updated (reasoning) |
session.reasoning.delta |
message.updated (completed) |
session.step.ended |
message.updated (info.tokens) |
session.usage.updated |
session.idle |
unchanged |
New on v2
Measurements v1 could not make, because its events carried no timestamps and StepStartPart carried neither model nor agent:
- Calibrated live rate — each step compares streamed tokens against the provider's count and learns a per-model correction
- Generation vs end-to-end TPS — tool execution subtracted, so a turn that waited on a shell command still reports the model's real rate
- Time to first token, measured on the host clock
- Hidden overhead — the auto-title and compaction tokens no step reports
- Per-subagent breakdown in the sidebar, using the real session tree
- Durable ledger and
/tpsdashboard, persisted and shared across windows
Performance
Delta counting re-read the whole accumulated response on every chunk — O(n²) per response.
| word heuristic | before | after |
|---|---|---|
| 186k chars (v2 path) | 10,223 ms | 12.2 ms |
| 62k chars (v1 path) | 1,571 ms, +11.7 MB | 10.4 ms |
Live updates were also being dropped by a clock mismatch in the publish throttle: 8 updates at p50 184 ms over 120 deltas became 120 updates at p50 15.3 ms.
Both fixes benefit v1 as well.
Known limitations
- Registering only in
opencode.jsonloads the server half but not the TUI meter. Point~/.config/opencode/cli.jsonat the built file for now:{ "plugins": ["file:///abs/path/to/opencode-tps-meter/dist/tui.mjs"] } - The footer meter is confirmed working on
opencode2beta. The dashboard, sidebar panel and calibration are covered by tests but have not been exercised in a live TUI.
Feedback welcome — especially from anyone already running opencode2.
v0.3.1
Changes
- Keeps the persistent TUI TPS meter visible when assistant responses transition into tool calls.
- Keeps the latest TUI TPS status visible after idle/non-streaming phases.
- Adds configurable initial display latency with
initialDisplayDelayMs/TPS_METER_INITIAL_DISPLAY_DELAY_MSand a 10ms default. - Improves live stream token counting and fallback delta/full-part deduplication.
v0.3.0
Highlights
- Add persistent OpenCode TUI TPS meter via the
opencode-tps-meter/tuientrypoint. - Disable legacy toast popups by default; opt back in with
toastFallbackorTPS_METER_TOAST_FALLBACK=true. - Add per-session final TPS persistence, delta/full-part dedupe, lifecycle cleanup, package export, docs, and test coverage.
v0.2.1
What's Changed
message.part.delta event support
The event handler now processes message.part.delta events alongside message.part.updated. This new event type carries only { sessionID, messageID, partID, field, delta } without a full Part object. The handleMessagePartUpdated function was extracted from the plugin closure and updated with a two-path dispatch: if a Part object is present, it uses the existing accumulated-text-delta logic; if only delta + sessionID + messageID are present, it uses the delta string directly. The event switch now matches both "message.part.updated" and "message.part.delta".
MessageEvent type expanded
The MessageEvent.type union in types.ts now includes "message.part.delta". The MessageEvent.properties shape gained three optional fields: messageID, partID, and field — used by the delta-only event path.
Background agent detection reverted to cross-session only
getAllActiveAgentsGlobally no longer includes same-session agents with messageAgentCache entries. The isSameSessionAgent check was removed — only trackers where sessionId !== primarySessionId are identified as background agents. isPrimarySessionActive no longer skips trackers with cached agent names; any tracker with recent activity in the primary session counts as primary activity.
Primary session assignment simplified
The message.part.updated handler no longer checks messageAgentCache before setting primarySessionId. The first session to receive any assistant token chunk becomes the primary session, unconditionally.
Multi-agent tests aligned with cross-session design
Tests were using same-session agents, but the code only detects background agents across sessions. Tests 1, 2, and 4 now use separate session IDs per agent and send primary part.updated tokens first to establish primarySessionId. Test 3 (agents-only) updated: the original code always shows the primary TPS line when the primary session has tokens, so the test now verifies agent labels appear alongside the primary line rather than expecting no primary line.
Version bump
package.json version updated from 0.2.0 to 0.2.1.
Full Changelog: v0.2.0...v0.2.1
v0.2.0: Realistic TPS and Better Agent IDs
Improvements
Fixed
- Inflated TPS values — Changed denominator from to . This prevents 300+ TPS spikes when tokens arrive in bursts by accounting for idle time that naturally lowers the rate.
- Identical agent labels — All agents showed because OpenCode message IDs share the prefix. Now strips this prefix and shows 6 differentiating characters.
Changed
- Faster timing constants for more responsive feel:
- : 250ms → 150ms (40% faster initial display)
- : 500ms → 250ms (more responsive smoothing)
- : 150ms → 80ms (nearly 2x faster UI updates)
- : 0.1s → 0.3s (stable baseline)
- Reduced toast throttling — multiplier: 3x → 2x
Commits
- fix: correct TPS calculation to use elapsed time from oldest entry
- feat: improve TPS responsiveness with faster timing constants
- fix: improve agent ID display and reduce UI throttling
- docs: update README timing and bump version to 0.2.0
0.1.9
v0.1.8 - Agent Detection Improvements
Improvements
- Improved agent name detection from multiple sources
- Better handling of missing agent metadata
- Added debug logging for troubleshooting
Bug Fixes
- Fixed
buildAgentLabelto handle missing type gracefully - Improved
getAllActiveAgentsGloballyto get agent name from cache when metadata is missing
v0.1.7 - Bug Fixes
Bug Fixes
- Agent name caching fixed - Previously the cache was never populated because
info.agentis anAgentIdentityobject (not a string). Now correctly extracts name from:agent.name > agent.type > agentType > agent.id - Color coding in agent-only mode - Previously showed "slow" (red) because TPS was 0. Now uses max agent TPS for color determination
- formatAgentLine respects config - Now properly uses
showInstant,showAverage,showTotalTokenssettings instead of always showing all
Files Changed
src/index.ts- Fixed agent name extraction from AgentIdentity objectsrc/ui.ts- Fixed color coding logic and formatAgentLine config respect