@cortexkit/opencode-magic-context 0.21.6 → 0.21.8
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/README.md +1 -1
- package/dist/config/agent-disable.d.ts +26 -0
- package/dist/config/agent-disable.d.ts.map +1 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/schema/magic-context.d.ts +0 -6
- package/dist/config/schema/magic-context.d.ts.map +1 -1
- package/dist/features/magic-context/compartment-lease.d.ts +14 -0
- package/dist/features/magic-context/compartment-lease.d.ts.map +1 -0
- package/dist/features/magic-context/compartment-storage.d.ts +5 -1
- package/dist/features/magic-context/compartment-storage.d.ts.map +1 -1
- package/dist/features/magic-context/compression-depth-storage.d.ts +2 -1
- package/dist/features/magic-context/compression-depth-storage.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/runner.d.ts.map +1 -1
- package/dist/features/magic-context/migrations.d.ts.map +1 -1
- package/dist/features/magic-context/sidekick/agent.d.ts.map +1 -1
- package/dist/features/magic-context/storage-db.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-persisted.d.ts +5 -0
- package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-session.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta.d.ts +1 -1
- package/dist/features/magic-context/storage-meta.d.ts.map +1 -1
- package/dist/features/magic-context/storage-subagent-invocations.d.ts +52 -0
- package/dist/features/magic-context/storage-subagent-invocations.d.ts.map +1 -0
- package/dist/features/magic-context/storage.d.ts +3 -2
- package/dist/features/magic-context/storage.d.ts.map +1 -1
- package/dist/features/magic-context/subagent-token-capture.d.ts +33 -0
- package/dist/features/magic-context/subagent-token-capture.d.ts.map +1 -0
- package/dist/features/magic-context/user-memory/review-user-memories.d.ts.map +1 -1
- package/dist/features/magic-context/work-metrics.d.ts +13 -0
- package/dist/features/magic-context/work-metrics.d.ts.map +1 -0
- package/dist/hooks/magic-context/auto-search-runner.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-compressor.d.ts +4 -0
- package/dist/hooks/magic-context/compartment-runner-compressor.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-historian.d.ts +2 -0
- package/dist/hooks/magic-context/compartment-runner-historian.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-partial-recomp.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-recomp.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-types.d.ts +3 -0
- package/dist/hooks/magic-context/compartment-runner-types.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner.d.ts +5 -0
- package/dist/hooks/magic-context/compartment-runner.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-compartment-phase.d.ts +2 -2
- package/dist/hooks/magic-context/transform-compartment-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +1 -0
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform.d.ts +2 -0
- package/dist/hooks/magic-context/transform.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14944 -14084
- package/dist/plugin/conflict-warning-hook.d.ts +10 -0
- package/dist/plugin/conflict-warning-hook.d.ts.map +1 -1
- package/dist/plugin/dream-timer.d.ts.map +1 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
- package/dist/plugin/rpc-handlers.d.ts.map +1 -1
- package/dist/plugin/tool-registry.d.ts.map +1 -1
- package/dist/shared/announcement.d.ts +55 -0
- package/dist/shared/announcement.d.ts.map +1 -0
- package/dist/shared/format-threshold.d.ts +24 -0
- package/dist/shared/format-threshold.d.ts.map +1 -0
- package/dist/shared/rpc-types.d.ts +2 -0
- package/dist/shared/rpc-types.d.ts.map +1 -1
- package/dist/shared/subagent-runner.d.ts +5 -0
- package/dist/shared/subagent-runner.d.ts.map +1 -1
- package/dist/tui/data/context-db.d.ts +14 -0
- package/dist/tui/data/context-db.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/shared/announcement.test.ts +143 -0
- package/src/shared/announcement.ts +97 -0
- package/src/shared/format-threshold.ts +28 -0
- package/src/shared/rpc-types.ts +2 -0
- package/src/shared/subagent-runner.ts +14 -0
- package/src/tui/data/context-db.ts +45 -0
- package/src/tui/index.tsx +85 -29
- package/src/tui/slots/sidebar-content.tsx +51 -60
package/src/tui/index.tsx
CHANGED
|
@@ -6,7 +6,8 @@ import { createMemo } from "solid-js"
|
|
|
6
6
|
import type { TuiPlugin, TuiPluginApi, TuiThemeCurrent } from "@opencode-ai/plugin/tui"
|
|
7
7
|
import { createSidebarContentSlot } from "./slots/sidebar-content"
|
|
8
8
|
import packageJson from "../../package.json"
|
|
9
|
-
import { closeRpc, consumeTuiMessages, getCompartmentCount, initRpcClient, loadStatusDetail, requestRecomp, type StatusDetail } from "./data/context-db"
|
|
9
|
+
import { closeRpc, consumeTuiMessages, getAnnouncement, getCompartmentCount, initRpcClient, loadStatusDetail, markAnnounced, requestRecomp, type StatusDetail } from "./data/context-db"
|
|
10
|
+
import { formatThresholdPercent } from "../shared/format-threshold"
|
|
10
11
|
import { detectConflicts } from "../shared/conflict-detector"
|
|
11
12
|
import { fixConflicts } from "../shared/conflict-fixer"
|
|
12
13
|
import { readJsoncFile } from "../shared/jsonc-parser"
|
|
@@ -260,27 +261,12 @@ const StatusDialog = (props: { api: TuiPluginApi; s: StatusDetail }) => {
|
|
|
260
261
|
return { segs, total }
|
|
261
262
|
}
|
|
262
263
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
let sum = widths.reduce((a, b) => a + b, 0)
|
|
270
|
-
while (sum > barWidth) {
|
|
271
|
-
const maxIdx = widths.indexOf(Math.max(...widths))
|
|
272
|
-
if (widths[maxIdx] > 1) { widths[maxIdx]--; sum-- } else break
|
|
273
|
-
}
|
|
274
|
-
while (sum < barWidth) {
|
|
275
|
-
const maxIdx = widths.indexOf(Math.max(...widths))
|
|
276
|
-
widths[maxIdx]++; sum++
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
return segs.map((seg, i) => ({
|
|
280
|
-
chars: "█".repeat(widths[i] || 0),
|
|
281
|
-
color: seg.color,
|
|
282
|
-
}))
|
|
283
|
-
}
|
|
264
|
+
// The status-dialog breakdown bar uses flex layout (same approach as the
|
|
265
|
+
// sidebar breakdown). Each segment becomes a colored box with
|
|
266
|
+
// flexGrow=tokens and flexBasis=0, parent has width="100%", so opentui
|
|
267
|
+
// distributes the dialog's full width proportionally regardless of the
|
|
268
|
+
// dialog's actual rendered width.
|
|
269
|
+
const barSegments = () => breakdownSegments().segs.filter((seg) => seg.tokens > 0)
|
|
284
270
|
|
|
285
271
|
return (
|
|
286
272
|
<box flexDirection="column" width="100%" paddingLeft={2} paddingRight={2} paddingTop={1} paddingBottom={1}>
|
|
@@ -296,17 +282,24 @@ const StatusDialog = (props: { api: TuiPluginApi; s: StatusDetail }) => {
|
|
|
296
282
|
them how close they are to compaction triggering. */}
|
|
297
283
|
<box flexDirection="row" justifyContent="space-between" width="100%">
|
|
298
284
|
<text fg={s().usagePercentage >= 80 ? t().error : s().usagePercentage >= 65 ? t().warning : t().accent}>
|
|
299
|
-
<b>{s().usagePercentage.toFixed(1)}%</b> / {s().executeThreshold}%
|
|
285
|
+
<b>{s().usagePercentage.toFixed(1)}%</b> / {formatThresholdPercent(s().executeThreshold)}%
|
|
300
286
|
</text>
|
|
301
287
|
<text fg={s().usagePercentage >= 80 ? t().error : s().usagePercentage >= 65 ? t().warning : t().accent}>
|
|
302
288
|
{fmt(s().inputTokens)} / {contextLimit() > 0 ? fmt(contextLimit()) : "?"} tokens
|
|
303
289
|
</text>
|
|
304
290
|
</box>
|
|
305
291
|
|
|
306
|
-
{/* Segmented breakdown bar
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
292
|
+
{/* Segmented breakdown bar: flex row of colored boxes filling
|
|
293
|
+
the dialog width. See barSegments comment above. */}
|
|
294
|
+
<box width="100%" flexDirection="row" height={1}>
|
|
295
|
+
{barSegments().map((seg) => (
|
|
296
|
+
<box
|
|
297
|
+
key={seg.label}
|
|
298
|
+
flexGrow={Math.max(1, seg.tokens)}
|
|
299
|
+
flexBasis={0}
|
|
300
|
+
height={1}
|
|
301
|
+
backgroundColor={seg.color}
|
|
302
|
+
/>
|
|
310
303
|
))}
|
|
311
304
|
</box>
|
|
312
305
|
|
|
@@ -341,7 +334,7 @@ const StatusDialog = (props: { api: TuiPluginApi; s: StatusDetail }) => {
|
|
|
341
334
|
<R t={t()} l="Configured" v={s().cacheTtl} />
|
|
342
335
|
<R t={t()} l="Last response" v={s().lastResponseTime > 0 ? `${Math.round(elapsed() / 1000)}s ago` : "never"} />
|
|
343
336
|
<R t={t()} l="Remaining" v={s().cacheExpired ? "expired" : `${Math.round(s().cacheRemainingMs / 1000)}s`} fg={s().cacheExpired ? t().warning : t().textMuted} />
|
|
344
|
-
<R t={t()} l="Auto-execute" v={s().cacheExpired ? "yes (expired)" : `at TTL or ≥${s().executeThreshold}%`} fg={t().textMuted} />
|
|
337
|
+
<R t={t()} l="Auto-execute" v={s().cacheExpired ? "yes (expired)" : `at TTL or ≥${formatThresholdPercent(s().executeThreshold)}%`} fg={t().textMuted} />
|
|
345
338
|
<box marginTop={1}>
|
|
346
339
|
<text fg={t().text}><b>Memory</b></text>
|
|
347
340
|
</box>
|
|
@@ -351,7 +344,7 @@ const StatusDialog = (props: { api: TuiPluginApi; s: StatusDetail }) => {
|
|
|
351
344
|
{/* Right column */}
|
|
352
345
|
<box flexDirection="column" flexGrow={1} flexBasis={0}>
|
|
353
346
|
<text fg={t().text}><b>Rolling Nudges</b></text>
|
|
354
|
-
<R t={t()} l="Execute threshold" v={`${s().executeThreshold}%`} />
|
|
347
|
+
<R t={t()} l="Execute threshold" v={`${formatThresholdPercent(s().executeThreshold)}%`} />
|
|
355
348
|
<R t={t()} l="Nudge anchor" v={`${fmt(s().lastNudgeTokens)} tok`} />
|
|
356
349
|
<R t={t()} l="Interval" v={`${fmt(s().nudgeInterval)} tok`} fg={t().textMuted} />
|
|
357
350
|
<R t={t()} l="Next nudge after" v={`${fmt(s().nextNudgeAfter)} tok`} />
|
|
@@ -563,6 +556,63 @@ function registerCommandPaletteEntries(api: TuiPluginApi): void {
|
|
|
563
556
|
// via RPC.
|
|
564
557
|
}
|
|
565
558
|
|
|
559
|
+
/**
|
|
560
|
+
* Show the one-shot "What's new" dialog on TUI startup if the server tells us
|
|
561
|
+
* to. The server is the source of truth: it has the version + features
|
|
562
|
+
* constants AND owns the persistence file. We just render and report back.
|
|
563
|
+
*
|
|
564
|
+
* Failure-tolerant by design — if the server isn't ready or the RPC fails,
|
|
565
|
+
* we silently skip (the next TUI launch will retry).
|
|
566
|
+
*/
|
|
567
|
+
/**
|
|
568
|
+
* URLs render as plain text. Modern terminals (iTerm2, kitty, WezTerm, Ghostty,
|
|
569
|
+
* recent macOS Terminal) auto-detect URLs and let users Cmd-click; older
|
|
570
|
+
* terminals require manual copy. We tried opentui's `<a href>` JSX intrinsic
|
|
571
|
+
* for application-level OSC 8 clickability, but it's a span-like element that
|
|
572
|
+
* forced text out of opentui's word-wrap mode, causing bullets to bleed past
|
|
573
|
+
* the dialog border. Pure-string children of `<text>` wrap correctly, so the
|
|
574
|
+
* AFT-style DialogAlert + plain string is the right surface here.
|
|
575
|
+
*/
|
|
576
|
+
async function showStartupAnnouncement(api: TuiPluginApi): Promise<void> {
|
|
577
|
+
try {
|
|
578
|
+
const ann = await getAnnouncement()
|
|
579
|
+
if (!ann.show || !ann.version || !ann.features || ann.features.length === 0) return
|
|
580
|
+
|
|
581
|
+
const title = `Magic Context v${ann.version}`
|
|
582
|
+
const lines: string[] = [
|
|
583
|
+
"What's new:",
|
|
584
|
+
"",
|
|
585
|
+
...ann.features.map((line) => ` • ${line}`),
|
|
586
|
+
]
|
|
587
|
+
if (ann.footer && ann.footer.trim().length > 0) {
|
|
588
|
+
// Blank-line separator keeps the persistent footer (Discord invite,
|
|
589
|
+
// etc.) visually distinct from the version-specific bullets.
|
|
590
|
+
lines.push("", ann.footer)
|
|
591
|
+
}
|
|
592
|
+
const message = lines.join("\n")
|
|
593
|
+
|
|
594
|
+
api.ui.dialog.replace(
|
|
595
|
+
() => (
|
|
596
|
+
<api.ui.DialogAlert
|
|
597
|
+
title={title}
|
|
598
|
+
message={message}
|
|
599
|
+
onConfirm={() => {
|
|
600
|
+
void markAnnounced()
|
|
601
|
+
}}
|
|
602
|
+
/>
|
|
603
|
+
),
|
|
604
|
+
() => {
|
|
605
|
+
// User dismissed via Escape rather than confirming. Mark
|
|
606
|
+
// dismissed anyway — they saw the dialog, that's the contract.
|
|
607
|
+
void markAnnounced()
|
|
608
|
+
},
|
|
609
|
+
)
|
|
610
|
+
} catch {
|
|
611
|
+
// RPC not ready yet (port file missing or transient HTTP failure) —
|
|
612
|
+
// silently skip. The next TUI start re-checks.
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
566
616
|
const tui: TuiPlugin = async (api, _options, meta) => {
|
|
567
617
|
// Initialize RPC client for server communication
|
|
568
618
|
const directory = api.state.path.directory ?? ""
|
|
@@ -623,6 +673,12 @@ const tui: TuiPlugin = async (api, _options, meta) => {
|
|
|
623
673
|
return
|
|
624
674
|
}
|
|
625
675
|
|
|
676
|
+
// Show one-shot release announcement after conflict gate.
|
|
677
|
+
// Fire-and-forget: if the server isn't ready or RPC fails, the next TUI
|
|
678
|
+
// launch will retry. Dialog only appears once per ANNOUNCEMENT_VERSION
|
|
679
|
+
// (persisted via mark-announced RPC writing last_announced_version).
|
|
680
|
+
void showStartupAnnouncement(api)
|
|
681
|
+
|
|
626
682
|
// Note: if TUI plugin is loaded, tui.json already has our entry.
|
|
627
683
|
// But if the user added it manually and later removes it, or if they
|
|
628
684
|
// use setup/doctor which handles tui.json, this code is already running.
|
|
@@ -3,6 +3,7 @@ import { createEffect, createMemo, createSignal, on, onCleanup } from "solid-js"
|
|
|
3
3
|
import type { TuiSlotPlugin, TuiPluginApi, TuiThemeCurrent } from "@opencode-ai/plugin/tui"
|
|
4
4
|
import packageJson from "../../../package.json"
|
|
5
5
|
import { loadSidebarSnapshot, type SidebarSnapshot } from "../data/context-db"
|
|
6
|
+
import { formatThresholdPercent } from "../../shared/format-threshold"
|
|
6
7
|
|
|
7
8
|
const SINGLE_BORDER = { type: "single" } as any
|
|
8
9
|
const REFRESH_DEBOUNCE_MS = 150
|
|
@@ -47,8 +48,13 @@ const TokenBreakdown = (props: {
|
|
|
47
48
|
theme: TuiThemeCurrent
|
|
48
49
|
snapshot: SidebarSnapshot
|
|
49
50
|
}) => {
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
// The bar is rendered as a flex row of colored boxes, each with
|
|
52
|
+
// flexGrow=tokens and flexBasis=0. opentui distributes the parent
|
|
53
|
+
// container's full width proportionally, so the bar always fills the
|
|
54
|
+
// sidebar regardless of terminal size. No hardcoded width is needed —
|
|
55
|
+
// this fixes both the over-wide bar that wrapped onto a second line on
|
|
56
|
+
// narrow sidebars (issue #90) and the under-wide bar that left empty
|
|
57
|
+
// space on the right on wide sidebars.
|
|
52
58
|
const segments = createMemo<TokenSegment[]>(() => {
|
|
53
59
|
const s = props.snapshot
|
|
54
60
|
const total = s.inputTokens || 1
|
|
@@ -142,66 +148,33 @@ const TokenBreakdown = (props: {
|
|
|
142
148
|
|
|
143
149
|
const totalTokens = createMemo(() => props.snapshot.inputTokens || 1)
|
|
144
150
|
|
|
145
|
-
//
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
// Convert to character widths. Minimum 1 char ONLY when the segment
|
|
155
|
-
// has tokens > 0 — zero-token segments (e.g. Conversation when the
|
|
156
|
-
// calibrator rounded it to zero) must get width 0 so the bar stays
|
|
157
|
-
// proportional. The legend row still renders for zero-token segments
|
|
158
|
-
// to keep the row stable.
|
|
159
|
-
let widths = segs.map((seg, i) =>
|
|
160
|
-
seg.tokens > 0 ? Math.max(1, Math.round(proportions[i] * barWidth)) : 0,
|
|
161
|
-
)
|
|
162
|
-
|
|
163
|
-
// Adjust to exactly barWidth
|
|
164
|
-
const sum = widths.reduce((a, b) => a + b, 0)
|
|
165
|
-
if (sum > barWidth) {
|
|
166
|
-
// Shrink from the largest segments
|
|
167
|
-
let excess = sum - barWidth
|
|
168
|
-
while (excess > 0) {
|
|
169
|
-
const maxIdx = widths.indexOf(Math.max(...widths))
|
|
170
|
-
if (widths[maxIdx] > 1) {
|
|
171
|
-
widths[maxIdx]--
|
|
172
|
-
excess--
|
|
173
|
-
} else {
|
|
174
|
-
break
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
} else if (sum < barWidth) {
|
|
178
|
-
// Expand the largest segments
|
|
179
|
-
let deficit = barWidth - sum
|
|
180
|
-
while (deficit > 0) {
|
|
181
|
-
const maxIdx = widths.indexOf(Math.max(...widths))
|
|
182
|
-
widths[maxIdx]++
|
|
183
|
-
deficit--
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
return widths
|
|
188
|
-
})
|
|
189
|
-
|
|
190
|
-
const barSegments = createMemo(() => {
|
|
191
|
-
const segs = segments()
|
|
192
|
-
const widths = segmentWidths()
|
|
193
|
-
return segs.map((seg, i) => ({
|
|
194
|
-
chars: "█".repeat(widths[i] || 0),
|
|
195
|
-
color: seg.color,
|
|
196
|
-
}))
|
|
197
|
-
})
|
|
151
|
+
// Render-time segments for the bar. Zero-token segments are filtered out
|
|
152
|
+
// entirely (no flex weight, no rendered box) so they don't claim any
|
|
153
|
+
// width. Non-zero segments still get a Math.max(1, ...) floor on
|
|
154
|
+
// flexGrow so very small contributions remain visible as a thin sliver.
|
|
155
|
+
// The legend rows below show every segment (including zeros) for table
|
|
156
|
+
// stability — only the bar prunes them.
|
|
157
|
+
const barSegments = createMemo(() =>
|
|
158
|
+
segments().filter((seg) => seg.tokens > 0),
|
|
159
|
+
)
|
|
198
160
|
|
|
199
161
|
return (
|
|
200
162
|
<box width="100%" flexDirection="column">
|
|
201
|
-
{/* Segmented bar
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
163
|
+
{/* Segmented bar: a width="100%" flex row of colored boxes,
|
|
164
|
+
each with flexGrow proportional to its token count and
|
|
165
|
+
flexBasis=0. opentui distributes the parent's full width
|
|
166
|
+
proportionally, so the bar always fills the sidebar
|
|
167
|
+
regardless of terminal size. Height is fixed at 1 row;
|
|
168
|
+
backgroundColor renders the colored bar. */}
|
|
169
|
+
<box width="100%" flexDirection="row" height={1}>
|
|
170
|
+
{barSegments().map((seg) => (
|
|
171
|
+
<box
|
|
172
|
+
key={seg.key}
|
|
173
|
+
flexGrow={Math.max(1, seg.tokens)}
|
|
174
|
+
flexBasis={0}
|
|
175
|
+
height={1}
|
|
176
|
+
backgroundColor={seg.color}
|
|
177
|
+
/>
|
|
205
178
|
))}
|
|
206
179
|
</box>
|
|
207
180
|
|
|
@@ -370,7 +343,7 @@ const SidebarContent = (props: {
|
|
|
370
343
|
"47.5% / 65%" tells the user how close they
|
|
371
344
|
are to the next compaction trigger. */}
|
|
372
345
|
<text fg={contextSummaryColor()}>
|
|
373
|
-
<b>{s()!.usagePercentage.toFixed(1)}%</b> / {s()!.executeThreshold}%
|
|
346
|
+
<b>{s()!.usagePercentage.toFixed(1)}%</b> / {formatThresholdPercent(s()!.executeThreshold)}%
|
|
374
347
|
</text>
|
|
375
348
|
{/* Right: absolute token usage vs the model's
|
|
376
349
|
full context window (separate from the
|
|
@@ -468,6 +441,24 @@ const SidebarContent = (props: {
|
|
|
468
441
|
/>
|
|
469
442
|
</>
|
|
470
443
|
)}
|
|
444
|
+
|
|
445
|
+
{/* Stats — v0.21.8 ships a single "Total tokens" number while we
|
|
446
|
+
figure out how to present the new-work / reprocessed
|
|
447
|
+
categorization without confusing users. The underlying
|
|
448
|
+
snapshot fields (newWorkTokens, totalInputTokens) and the
|
|
449
|
+
session_meta columns are still populated; only the UI is
|
|
450
|
+
simplified for now. */}
|
|
451
|
+
{s()?.totalInputTokens != null && (
|
|
452
|
+
<>
|
|
453
|
+
<SectionHeader theme={props.theme} title="Stats" />
|
|
454
|
+
<StatRow
|
|
455
|
+
theme={props.theme}
|
|
456
|
+
label="Total tokens"
|
|
457
|
+
value={compactTokens(s()!.totalInputTokens ?? 0)}
|
|
458
|
+
dim
|
|
459
|
+
/>
|
|
460
|
+
</>
|
|
461
|
+
)}
|
|
471
462
|
</box>
|
|
472
463
|
)
|
|
473
464
|
}
|