opencode-context-indicator 1.7.0 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +49 -12
- package/README.md +11 -8
- package/index.js +3 -4
- package/lib/limits.js +10 -10
- package/lib/v2.js +56 -10
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,40 @@ Notable user-facing changes per release. Dates are commit days (UTC).
|
|
|
4
4
|
The package also ships a dual V1/V2 plugin — see the README for the surface
|
|
5
5
|
matrix; everything below concerns the V2 path unless stated otherwise.
|
|
6
6
|
|
|
7
|
+
## 1.7.1 — 2026-10-29
|
|
8
|
+
|
|
9
|
+
- **Fixed: the live log no longer prints `ctx=0` above real categories.** A hook
|
|
10
|
+
breakdown whose instance has no request counters (a mirror that only saw the
|
|
11
|
+
compaction, so `input`/`ctx` are still 0) used to be printed as-is: the header
|
|
12
|
+
contradicted the category lines below it. Such an entry now takes the same
|
|
13
|
+
path as a missing/fallback one and borrows the hook-measured `state.json`
|
|
14
|
+
snapshot whole (categories, counters, residual and a `snapshot=` stamp; a
|
|
15
|
+
snapshot without a timestamp is still marked `snapshot=unknown`). A counter is
|
|
16
|
+
knowledge only when it is positive: a borrowed entry whose own counters are 0,
|
|
17
|
+
or a ctx-only snapshot with no input, prints the unknown counter as `?`, and
|
|
18
|
+
the residual as `n/a (input unknown on this instance)` instead of a fabricated
|
|
19
|
+
`0` (`other = input - sum` needs an input). The same rule now applies to a
|
|
20
|
+
local hook entry that knows `ctx` but not `input`. T36 covers all four cases:
|
|
21
|
+
whole-entry borrow, no snapshot, zero-counter snapshot, ctx-only snapshot
|
|
22
|
+
(86 checks).
|
|
23
|
+
- **Fixed: the compaction reserve is 15 % again (1.7.0 regression).** 1.7.0
|
|
24
|
+
switched the denominator to a 10 % share on the strength of an unreleased
|
|
25
|
+
branch head. Every RELEASED host from 2.1.4 through 2.2.2 (2.1.4, 2.2.0,
|
|
26
|
+
2.2.1, 2.2.2) still has `RESERVE_SHARE = 0.15` in
|
|
27
|
+
`packages/core/src/session/compaction.ts` (verified tag by tag), so on those
|
|
28
|
+
hosts 1.7.0 overstated the ceiling and the `compact at` line by ~5 % of the
|
|
29
|
+
window (~26 k tokens at 512 k) and therefore under-reported every `%`. The
|
|
30
|
+
plugin now mirrors `max(floor(window * 0.15), window >= 32_000 ? 16_000 : 0)`
|
|
31
|
+
again; the formula, the 16 k floor and the `2 * RESERVE_MIN` gate are
|
|
32
|
+
unchanged. Docs, the T16/T26 tables and the seed expectations are back on the
|
|
33
|
+
15 % numbers.
|
|
34
|
+
- Dev: the isolated e2e (plugin load + state write) was re-run against the
|
|
35
|
+
**2.2.2** Desktop CLI (`opencode v2.2.2`); the Desktop 2.2.2 `app.asar` still
|
|
36
|
+
carries the `@opencode/plugin/gui` / `@opencode/plugin/rpc` bridge and the
|
|
37
|
+
`ComposerPill` / `Rpc.define` / `session.panel` symbols the experimental panel
|
|
38
|
+
uses, and `opencode-cli.exe` still exposes `opentui:runtime-module:` plus the
|
|
39
|
+
`node_modules` exclusion filter.
|
|
40
|
+
|
|
7
41
|
## 1.7.0 — 2026-10-29
|
|
8
42
|
|
|
9
43
|
Desktop GUI release (experimental): the plugin now has a surface in the
|
|
@@ -60,14 +94,14 @@ OpenCode Desktop app, not just in the terminal.
|
|
|
60
94
|
its `ctx`/`input` counters, tagged `snapshot=<updatedAt>` — instead of this
|
|
61
95
|
instance's fallback-shaped view. Repeated compactions of one session are also
|
|
62
96
|
no longer collapsed into a single note when the host omits the event id.
|
|
63
|
-
- **Reserve share corrected
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
97
|
+
- **Reserve share (claim in 1.7.0 was wrong; corrected in 1.7.1).** This
|
|
98
|
+
release switched the denominator to a 10 % share; no host in the verified
|
|
99
|
+
range 2.1.4 through 2.2.2 used it. `calculateCeiling()` in those tags keeps
|
|
100
|
+
`RESERVE_SHARE = 0.15`, so 1.7.0 overstated the ceiling (and the `compact at`
|
|
101
|
+
line) by ~5 % of the window (~26 k tokens at 512 k) on them and therefore
|
|
102
|
+
under-reported every `%`. The 1.6.0 note that the ceiling was unchanged in
|
|
103
|
+
2.2.0 was right (the formula and the `2 * RESERVE_MIN` gate did stay); the
|
|
104
|
+
share line here was the wrong part.
|
|
71
105
|
- Dev: `lib/format.js` (pure `fmt`/`num`) extracted so the GUI bundle has no
|
|
72
106
|
`node:*` import; gate (a) now expands `dist/`, gate (e)/(f) scan `gui/` and the
|
|
73
107
|
GUI build config, new gate (g) validates the built CJS bundle (app-provided
|
|
@@ -100,11 +134,13 @@ Host alignment release: opencode 2.2.0 moved TUI plugins to OpenTUI 0.7.
|
|
|
100
134
|
opencode 2.0.16+. The TUI module now ships compiled against OpenTUI 0.7, so
|
|
101
135
|
the sidebar and the `/cx` panel require **opencode ≥ 2.2.0** and cannot load on
|
|
102
136
|
an older host. Older-host installation was not re-verified for this release.
|
|
103
|
-
- **Compaction ceiling unchanged (
|
|
137
|
+
- **Compaction ceiling unchanged (this note was right; 1.7.0 briefly wrong).**
|
|
104
138
|
`calculateCeiling()` in 2.2.0 is identical
|
|
105
139
|
to 2.1.x, so the `0.15 / 16k / 32k` reserve formula and every `%` denominator
|
|
106
140
|
are unaffected. (2.2.0 changed how a *compaction request* is sized - measured
|
|
107
|
-
prompt instead of characters - not the trigger.
|
|
141
|
+
prompt instead of characters - not the trigger. 1.7.0 later claimed the share
|
|
142
|
+
had dropped to 10 %; see 1.7.1 - no host in the verified range
|
|
143
|
+
2.1.4-2.2.2 uses it.)
|
|
108
144
|
- Dev: `drift-watch` is green again (host 2.2.0, OpenTUI 0.7.0); `tsc`,
|
|
109
145
|
`prepublish` and the isolated e2e pass against the 2.2.0 CLI.
|
|
110
146
|
|
|
@@ -150,8 +186,9 @@ Fix-and-hygiene release after a full-project review. No new commands.
|
|
|
150
186
|
|
|
151
187
|
- **Compaction ceiling aligned with opencode 2.1.4.** `lib/limits.js`
|
|
152
188
|
now mirrors `calculateCeiling()` exactly: the reserve is
|
|
153
|
-
`max(floor(window * 0.15), window >= 2 * 16_000 ? 16_000 : 0)` (
|
|
154
|
-
|
|
189
|
+
`max(floor(window * 0.15), window >= 2 * 16_000 ? 16_000 : 0)` (the plugin's
|
|
190
|
+
older builds used 10 %; no host in the verified range uses it - the share
|
|
191
|
+
stayed 0.15 there),
|
|
155
192
|
and an override buffer is honored **only while it is smaller than the
|
|
156
193
|
window** — a buffer configured for a larger model is ignored instead of
|
|
157
194
|
leaving zero room. The `compact at` line and the `%` denominator now match the
|
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ sidebar** and a **zero-LLM `/cx` breakdown panel** with measured contributors
|
|
|
16
16
|
and a redacted export, and an **experimental Desktop GUI part**
|
|
17
17
|
(`./gui-experimental`: a side-panel tab and a composer pill). The V2 path is
|
|
18
18
|
**tested against opencode 2.2.0** (the isolated load + state-write e2e was also
|
|
19
|
-
re-run against the 2.2.1 Desktop CLI); the V1 path against 1.18.29+.
|
|
19
|
+
re-run against the 2.2.1 and 2.2.2 Desktop CLI); the V1 path against 1.18.29+.
|
|
20
20
|
|
|
21
21
|
> **V1 is feature-frozen.** It is kept working for compatibility (bug fixes
|
|
22
22
|
> only); all new features land in the V2 path.
|
|
@@ -72,11 +72,13 @@ For every served request the plugin estimates how the context window is split:
|
|
|
72
72
|
duplicate group), reported honestly as *"N redundant tool outputs ≈ X tokens"*
|
|
73
73
|
— never as "the model wasted effort". Findings are per-session and **path-free
|
|
74
74
|
in the persisted/exported form**.
|
|
75
|
-
* The denominator is the **opencode 2.
|
|
76
|
-
limit.input || limit.context`; `reserve = max(floor(window * 0.
|
|
75
|
+
* The denominator is the **opencode 2.x compaction ceiling**: `window =
|
|
76
|
+
limit.input || limit.context`; `reserve = max(floor(window * 0.15), window >=
|
|
77
77
|
2 * 16_000 ? 16_000 : 0)`; the ceiling is `window - reserve`. This mirrors
|
|
78
|
-
`calculateCeiling()` in `packages/core/src/session/compaction.ts
|
|
79
|
-
|
|
78
|
+
`calculateCeiling()` in `packages/core/src/session/compaction.ts`, whose
|
|
79
|
+
`RESERVE_SHARE` is 0.15 in every released host from 2.1.4 through 2.2.2 (the
|
|
80
|
+
unreleased `v2` branch has a different share; the plugin follows the release,
|
|
81
|
+
not the branch). When opencode
|
|
80
82
|
carries a `compaction.buffer` configured in its own settings, the plugin
|
|
81
83
|
cannot read that value (it is not exposed to plugins), so the default
|
|
82
84
|
reserve formula is used — set `OPENCODE_CONTEXT_INDICATOR_COMPACT_BUFFER`
|
|
@@ -635,7 +637,8 @@ the sidebar instead.
|
|
|
635
637
|
and toasts keep working).
|
|
636
638
|
* **OpenCode ≥ 2.2.0** for the V2 path with the TUI surfaces (built and verified
|
|
637
639
|
against **2.2.0**; the isolated load + state-write e2e also passes on the
|
|
638
|
-
**2.2.
|
|
640
|
+
**2.2.2** Desktop CLI, the latest at the time of writing). The server-only V2
|
|
641
|
+
path keeps working on
|
|
639
642
|
2.0.16+; the TUI module pins the OpenTUI generation it was compiled against,
|
|
640
643
|
so a host on OpenTUI 0.6 (opencode ≤ 2.1.4) will not load the TUI surfaces -
|
|
641
644
|
neither the sidebar nor the `/cx` panel - from a 1.6.x install.
|
|
@@ -724,7 +727,7 @@ caller.
|
|
|
724
727
|
|
|
725
728
|
```bash
|
|
726
729
|
npm run build:gui # build dist/gui-experimental.cjs (required by the gate)
|
|
727
|
-
npm run test:prepublish # fast gate: tarball + tui.tsx + resolve + T1-
|
|
730
|
+
npm run test:prepublish # fast gate: tarball + tui.tsx + resolve + T1-T36 unit checks + GUI bundle
|
|
728
731
|
npm run test:types # tsc for the TUI entry AND the Desktop GUI part
|
|
729
732
|
npm run test:e2e # full E2E: isolated plugin load + one "Say OK" round-trip
|
|
730
733
|
npm run test:e2e:quick # E2E load-only, no LLM call
|
|
@@ -747,7 +750,7 @@ STEP 80 s, ~3 min total.
|
|
|
747
750
|
| (a) tarball composition | `package.json` `files[]` expands to the base files plus every `lib/*` module (count asserted dynamically); `docs/` may contain images only; `dist/` may contain only the built GUI bundle | no test/node_modules/tsconfig in the tarball, and no published export pointing at nothing |
|
|
748
751
|
| (b) pragma regression | esbuild `--jsx=automatic` → `@opentui/solid/jsx-runtime` present, `react/jsx-runtime` absent | a removed/broken pragma kills the sidebar on npm install |
|
|
749
752
|
| (c) resolve chain | replica install: host-bridged + peer packages resolve; local tui.tsx relative imports resolve via node from the entry (resolve-bridge.cjs) | broken peer/optional dep or a bad local import silently breaks the sidebar |
|
|
750
|
-
| (d) unit harness T1-
|
|
753
|
+
| (d) unit harness T1-T36 | limit/agent/state fix assertions plus measureContributors, redacted-export, shared category-helper, sanitizer/unicode, prompt-cache, waste-detector, compaction-reserve, top-consumer, diff, subagent-rollup and Desktop-RPC checks (86 total; T30 schema-vs-builder drift, T31 registration + handler, T32-T36 fallback-vs-hook category priority across the report, the shared state file and the live log (T36: a counter-less hook entry borrows the snapshot or prints `?`), T25c/T25d compaction-hook arming, T34 cross-instance summary merge) | regressions in modelLimits, writeStateFile, hydrate, contributors, export, subagent lifetime, and a GUI/server wire mismatch |
|
|
751
754
|
| (e) corporate-identifier scan | no forbidden identifiers in the published surface (`index.js`, `tui.tsx`, `lib/*`, `gui/*`, `README.md`, `CHANGELOG.md`, `package.json`, the GUI build config) or the test tree | compliance |
|
|
752
755
|
| (f) non-English scan | no non-ASCII letters (`\p{L}` above U+007F) in the same published surface and test tree (`docs/` is image-only and is not text-scanned) | English-only source rule |
|
|
753
756
|
| (g) Desktop GUI bundle | `dist/gui-experimental.cjs` exists and is a non-trivial single-file CJS bundle: no ESM `import`, no dynamic `import()`, no Node built-in, and every app-provided module (`solid-js`, `solid-js/web`, `@opencode/plugin/gui`, `@opencode/plugin/rpc`) still a `require()` - no bundled second copy, no React/OpenTUI/bare-`opencode` import | a stale or hand-edited bundle, or a build that inlines the app instance, breaks the Desktop panel at load time |
|
package/index.js
CHANGED
|
@@ -127,10 +127,9 @@
|
|
|
127
127
|
*
|
|
128
128
|
* - Metric: the native opencode overflow count — tokens.total when present,
|
|
129
129
|
* else input + output + cache.read + cache.write — compared against the
|
|
130
|
-
* model's compaction ceiling (opencode calculateCeiling,
|
|
131
|
-
* window = limit.input || limit.context, reserve = max(
|
|
132
|
-
* when window >= 32k), usable = window - reserve
|
|
133
|
-
* 2.1.x). Percentages are NOT
|
|
130
|
+
* model's compaction ceiling (opencode calculateCeiling, released 2.x):
|
|
131
|
+
* window = limit.input || limit.context, reserve = max(15% of window, 16k
|
|
132
|
+
* when window >= 32k), usable = window - reserve. Percentages are NOT
|
|
134
133
|
* clamped at 100% - exceeding the ceiling must stay visible.
|
|
135
134
|
* - Category estimates use a unicode-aware heuristic (see estimateTokens):
|
|
136
135
|
* Cyrillic ~2.5 chars/token, CJK ~1.5, latin/ASCII ~4. These are ESTIMATES,
|
package/lib/limits.js
CHANGED
|
@@ -12,11 +12,11 @@
|
|
|
12
12
|
// auto-compaction trigger ceiling as window - reserve, where
|
|
13
13
|
// window = limit.input || limit.context (null/zero falls through to the context
|
|
14
14
|
// window) and
|
|
15
|
-
// reserve = max(floor(window * 0.
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
15
|
+
// reserve = max(floor(window * 0.15), window >= 2 * 16_000 ? 16_000 : 0)
|
|
16
|
+
// `RESERVE_SHARE = 0.15` in every RELEASED host from 2.1.4 through 2.2.2
|
|
17
|
+
// (verified tag by tag; the `2 * RESERVE_MIN` gate is older and unchanged). The
|
|
18
|
+
// unreleased `v2` branch HEAD has an inline `0.1` instead - do NOT mirror a
|
|
19
|
+
// branch head: a wrong reserve makes every displayed percentage wrong.
|
|
20
20
|
// A compaction.buffer configured in opencode itself is NOT visible to the
|
|
21
21
|
// plugin (the plugin cannot read opencode's internal compaction config); the
|
|
22
22
|
// env var OPENCODE_CONTEXT_INDICATOR_COMPACT_BUFFER overrides the reserve for
|
|
@@ -85,11 +85,11 @@ function getModelLimit(providerID, modelID) {
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
// Compute the reserve for a given window, matching opencode's
|
|
88
|
-
// packages/core/src/session/compaction.ts calculateCeiling()
|
|
89
|
-
//
|
|
88
|
+
// packages/core/src/session/compaction.ts calculateCeiling() as RELEASED
|
|
89
|
+
// (RESERVE_SHARE = 0.15 in 2.1.4 .. 2.2.2; the branch head is not a release).
|
|
90
90
|
// Returns null when
|
|
91
91
|
// window is not a finite positive number.
|
|
92
|
-
const RESERVE_SHARE = 0.
|
|
92
|
+
const RESERVE_SHARE = 0.15; // core: RESERVE_SHARE in every released 2.x
|
|
93
93
|
const RESERVE_MIN = 16_000; // core: RESERVE_MIN
|
|
94
94
|
function reserveFor(window, bufferOverride) {
|
|
95
95
|
if (
|
|
@@ -109,7 +109,7 @@ function reserveFor(window, bufferOverride) {
|
|
|
109
109
|
return bufferOverride;
|
|
110
110
|
}
|
|
111
111
|
// Default reserve formula (mirror of opencode core):
|
|
112
|
-
// max(floor(window * 0.
|
|
112
|
+
// max(floor(window * 0.15), window >= 2 * RESERVE_MIN ? RESERVE_MIN : 0)
|
|
113
113
|
// The threshold is 2 * RESERVE_MIN (32_000), not RESERVE_MIN.
|
|
114
114
|
return Math.max(
|
|
115
115
|
Math.floor(window * RESERVE_SHARE),
|
|
@@ -117,7 +117,7 @@ function reserveFor(window, bufferOverride) {
|
|
|
117
117
|
);
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
// Native opencode compaction ceiling (calculateCeiling,
|
|
120
|
+
// Native opencode compaction ceiling (calculateCeiling, released 2.x):
|
|
121
121
|
// window = limit.input || limit.context (zero/null falls through to context)
|
|
122
122
|
// reserve = reserveFor(window) [or bufferOverride when provided]
|
|
123
123
|
// usable = window - reserve
|
package/lib/v2.js
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
extractToolTarget,
|
|
16
16
|
fmt,
|
|
17
17
|
isHookMeasuredEntry,
|
|
18
|
+
isHookSource,
|
|
18
19
|
isSafeSessionKey,
|
|
19
20
|
oneLine,
|
|
20
21
|
safeReclaim,
|
|
@@ -741,7 +742,21 @@ function buildLiveLinesV2(sessionID, modelID, providerID, inputTokens, ctxTokens
|
|
|
741
742
|
let snapInputTokens = null;
|
|
742
743
|
let snapCtxTokens = null;
|
|
743
744
|
let snapAt = null;
|
|
744
|
-
|
|
745
|
+
let borrowedSnapshot = false;
|
|
746
|
+
// A hook breakdown whose instance has NO request counters is just as
|
|
747
|
+
// unprintable as a fallback: the categories are measured, but the total this
|
|
748
|
+
// instance knows is 0 (it never completed a step - e.g. a mirror that only
|
|
749
|
+
// saw the compaction hook). Such an entry also takes the state.json path, so
|
|
750
|
+
// the header and the categories come from ONE measurement instead of a
|
|
751
|
+
// `ctx=0` header sitting above non-zero categories.
|
|
752
|
+
const haveLocalCounters =
|
|
753
|
+
(typeof inputTokens === "number" && inputTokens > 0) ||
|
|
754
|
+
(typeof ctxTokens === "number" && ctxTokens > 0);
|
|
755
|
+
if (
|
|
756
|
+
!bd ||
|
|
757
|
+
bd.source === "client-context" ||
|
|
758
|
+
(isHookSource(bd.source) && !haveLocalCounters)
|
|
759
|
+
) {
|
|
745
760
|
const entry = isSafeSessionKey(sessionID)
|
|
746
761
|
? readStateSnapshot()[sessionID]
|
|
747
762
|
: null;
|
|
@@ -750,13 +765,33 @@ function buildLiveLinesV2(sessionID, modelID, providerID, inputTokens, ctxTokens
|
|
|
750
765
|
snapInputTokens = safeSaneOrNull(entry.input);
|
|
751
766
|
snapCtxTokens = safeSaneOrNull(entry.ctx);
|
|
752
767
|
snapAt = typeof entry.updatedAt === "string" ? entry.updatedAt : null;
|
|
768
|
+
borrowedSnapshot = true;
|
|
753
769
|
}
|
|
754
770
|
}
|
|
755
771
|
const source = bd?.source || "none";
|
|
756
772
|
const rt = typeof reasoningTokens === "number" ? reasoningTokens : 0;
|
|
757
773
|
const effInput = snapInputTokens != null ? snapInputTokens : inputTokens;
|
|
758
774
|
const effCtx = snapCtxTokens != null ? snapCtxTokens : ctxTokens;
|
|
759
|
-
|
|
775
|
+
// Only a POSITIVE counter is knowledge. A zero is exactly what the broken
|
|
776
|
+
// case looks like (a state entry written by the trailing-event fallback of a
|
|
777
|
+
// mirror instance carries `ctx: 0, input: 0`), and treating it as "known"
|
|
778
|
+
// would reprint the very `ctx=0` this path exists to avoid.
|
|
779
|
+
const haveSnapCounters =
|
|
780
|
+
(typeof snapInputTokens === "number" && snapInputTokens > 0) ||
|
|
781
|
+
(typeof snapCtxTokens === "number" && snapCtxTokens > 0);
|
|
782
|
+
// Still unknown (no counters here AND no usable hook measurement on disk):
|
|
783
|
+
// print "?" rather than a fabricated 0 that contradicts the categories below.
|
|
784
|
+
const countersKnown = haveLocalCounters || haveSnapCounters;
|
|
785
|
+
// `other = input - sum` needs the input itself; a ctx-only snapshot (the
|
|
786
|
+
// compaction hook knows ctx but a mirror never saw the request) cannot
|
|
787
|
+
// produce an honest residual.
|
|
788
|
+
const inputKnown =
|
|
789
|
+
countersKnown && typeof effInput === "number" && effInput > 0;
|
|
790
|
+
const total = !countersKnown
|
|
791
|
+
? null
|
|
792
|
+
: typeof effCtx === "number" && effCtx > 0
|
|
793
|
+
? effCtx
|
|
794
|
+
: effInput;
|
|
760
795
|
const denom =
|
|
761
796
|
usableInfo != null
|
|
762
797
|
? usableInfo.usable
|
|
@@ -764,13 +799,19 @@ function buildLiveLinesV2(sessionID, modelID, providerID, inputTokens, ctxTokens
|
|
|
764
799
|
? limit
|
|
765
800
|
: null;
|
|
766
801
|
const pct =
|
|
767
|
-
denom != null
|
|
802
|
+
denom != null && total != null
|
|
803
|
+
? ` (${((total / denom) * 100).toFixed(1)}%)`
|
|
804
|
+
: "";
|
|
768
805
|
// The reader must be able to tell a fresh local measurement from a borrowed
|
|
769
|
-
// snapshot (and from how old it is).
|
|
770
|
-
|
|
806
|
+
// snapshot (and from how old it is). A snapshot without a timestamp still
|
|
807
|
+
// must not look local.
|
|
808
|
+
const snapNote = borrowedSnapshot
|
|
809
|
+
? ` snapshot=${snapAt || "unknown"}`
|
|
810
|
+
: "";
|
|
771
811
|
|
|
812
|
+
const shownTotal = total != null ? fmt(total) : "?";
|
|
772
813
|
lines.push(
|
|
773
|
-
`[${ts}] session=${sessionID} model=${modelID} ctx=${
|
|
814
|
+
`[${ts}] session=${sessionID} model=${modelID} ctx=${shownTotal}/${denom != null ? fmt(denom) : "?"}${pct} count=${shownTotal} usable=${usableInfo != null ? fmt(usableInfo.usable) : "?"} limit=${limit != null ? fmt(limit) : "?"} maxOutput=${usableInfo != null ? fmt(usableInfo.maxOutput) : "?"} reserved=${usableInfo != null ? fmt(usableInfo.reserved) : "?"} input=${inputKnown ? fmt(effInput) : "?"}${snapNote}`,
|
|
774
815
|
);
|
|
775
816
|
|
|
776
817
|
// Residual = input minus every measured category (shared normaliser; `bd`
|
|
@@ -778,10 +819,11 @@ function buildLiveLinesV2(sessionID, modelID, providerID, inputTokens, ctxTokens
|
|
|
778
819
|
// breakdown taken from state.json carries the residual the HOOK computed
|
|
779
820
|
// against the input IT saw; this instance's `inputTokens` can differ (a mirror
|
|
780
821
|
// that only saw the trailing event has 0), so that value is used instead.
|
|
781
|
-
const other =
|
|
782
|
-
typeof bd?.otherChars === "number"
|
|
822
|
+
const other = inputKnown
|
|
823
|
+
? typeof bd?.otherChars === "number"
|
|
783
824
|
? bd.otherChars
|
|
784
|
-
: breakdownCategories(bd, effInput).other
|
|
825
|
+
: breakdownCategories(bd, effInput).other
|
|
826
|
+
: null;
|
|
785
827
|
|
|
786
828
|
if (bd) {
|
|
787
829
|
lines.push(` user ${fmt(bd.userChars || 0)} (est tokens)`);
|
|
@@ -811,7 +853,11 @@ function buildLiveLinesV2(sessionID, modelID, providerID, inputTokens, ctxTokens
|
|
|
811
853
|
lines.push(` system n/a (hook did not run)`);
|
|
812
854
|
lines.push(` tool schemas n/a`);
|
|
813
855
|
}
|
|
814
|
-
lines.push(
|
|
856
|
+
lines.push(
|
|
857
|
+
other != null
|
|
858
|
+
? ` other ${fmt(other)} (= input - sum of estimates)`
|
|
859
|
+
: ` other n/a (input unknown on this instance)`,
|
|
860
|
+
);
|
|
815
861
|
lines.push(` source ${source}`);
|
|
816
862
|
|
|
817
863
|
// Provider-reported prompt cache (EXACT, not an estimate; a subset of input —
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-context-indicator",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"description": "Real-time context-window usage for OpenCode: per-category token breakdown, /context-diff, a zero-LLM /cx panel with per-session cost and a searchable family-session picker, a TUI sidebar, and an experimental Desktop panel. Dual V1 + V2 plugin entry.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|