@genesislcap/ai-assistant 15.4.1 → 15.6.0
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/dist/ai-assistant.api.json +571 -82
- package/dist/ai-assistant.d.ts +336 -36
- package/dist/chat-driver.cjs +104 -26
- package/dist/chat-driver.cjs.map +3 -3
- package/dist/chat-driver.mjs +104 -26
- package/dist/chat-driver.mjs.map +3 -3
- package/dist/custom-elements.json +314 -36
- package/dist/dts/components/chat-driver/chat-driver.d.ts +12 -0
- package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -1
- package/dist/dts/components/settings-modal/settings-modal.styles.d.ts.map +1 -1
- package/dist/dts/components/settings-modal/settings-modal.template.d.ts +9 -2
- package/dist/dts/components/settings-modal/settings-modal.template.d.ts.map +1 -1
- package/dist/dts/index.d.ts +1 -0
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/dts/main/cost-session-banking.test.d.ts +2 -0
- package/dist/dts/main/cost-session-banking.test.d.ts.map +1 -0
- package/dist/dts/main/main.d.ts +184 -24
- package/dist/dts/main/main.d.ts.map +1 -1
- package/dist/dts/provider/assistant-app-settings.d.ts +30 -5
- package/dist/dts/provider/assistant-app-settings.d.ts.map +1 -1
- package/dist/dts/state/ai-assistant-slice.d.ts +11 -8
- package/dist/dts/state/ai-assistant-slice.d.ts.map +1 -1
- package/dist/dts/state/persistence/session-persistence-provider.d.ts +23 -0
- package/dist/dts/state/persistence/session-persistence-provider.d.ts.map +1 -1
- package/dist/dts/state/persistence/session-snapshot.d.ts.map +1 -1
- package/dist/dts/state/session-store.d.ts +1 -2
- package/dist/dts/state/session-store.d.ts.map +1 -1
- package/dist/dts/styles/settings-section.d.ts +29 -0
- package/dist/dts/styles/settings-section.d.ts.map +1 -0
- package/dist/dts/utils/condense-history.d.ts +14 -0
- package/dist/dts/utils/condense-history.d.ts.map +1 -1
- package/dist/dts/utils/cost-session-history.d.ts +103 -12
- package/dist/dts/utils/cost-session-history.d.ts.map +1 -1
- package/dist/dts/utils/resolve-cost-history-config.d.ts +9 -3
- package/dist/dts/utils/resolve-cost-history-config.d.ts.map +1 -1
- package/dist/dts/utils/sum-costs.d.ts.map +1 -1
- package/dist/dts/utils/sum-tokens.d.ts +8 -8
- package/dist/dts/utils/sum-tokens.d.ts.map +1 -1
- package/dist/dts/utils/sum-usage.d.ts +59 -0
- package/dist/dts/utils/sum-usage.d.ts.map +1 -0
- package/dist/dts/utils/sum-usage.test.d.ts +2 -0
- package/dist/dts/utils/sum-usage.test.d.ts.map +1 -0
- package/dist/esm/components/chat-driver/chat-driver.js +9 -1
- package/dist/esm/components/settings-modal/settings-modal.styles.js +237 -18
- package/dist/esm/components/settings-modal/settings-modal.template.js +229 -73
- package/dist/esm/index.js +1 -0
- package/dist/esm/main/cost-session-banking.test.js +308 -0
- package/dist/esm/main/main.js +424 -71
- package/dist/esm/state/ai-assistant-slice.js +11 -8
- package/dist/esm/state/ai-assistant-slice.test.js +12 -5
- package/dist/esm/state/debug-event-log.js +2 -2
- package/dist/esm/state/persistence/session-persistence.integration.test.js +5 -1
- package/dist/esm/state/persistence/session-persister.js +2 -2
- package/dist/esm/state/persistence/session-persister.test.js +10 -1
- package/dist/esm/state/persistence/session-snapshot.js +6 -2
- package/dist/esm/state/persistence/session-snapshot.test.js +4 -1
- package/dist/esm/state/persistence/stateful-restore.e2e.test.js +10 -1
- package/dist/esm/styles/settings-section.js +39 -0
- package/dist/esm/utils/condense-history.js +18 -5
- package/dist/esm/utils/condense-history.test.js +67 -0
- package/dist/esm/utils/cost-session-history.js +92 -15
- package/dist/esm/utils/cost-session-history.test.js +155 -13
- package/dist/esm/utils/resolve-cost-history-config.js +2 -1
- package/dist/esm/utils/sum-costs.js +2 -13
- package/dist/esm/utils/sum-tokens.js +10 -27
- package/dist/esm/utils/sum-tokens.test.js +1 -5
- package/dist/esm/utils/sum-usage.js +123 -0
- package/dist/esm/utils/sum-usage.test.js +120 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +17 -17
- package/src/components/chat-driver/chat-driver.ts +21 -0
- package/src/components/settings-modal/settings-modal.styles.ts +237 -18
- package/src/components/settings-modal/settings-modal.template.ts +270 -81
- package/src/index.ts +1 -0
- package/src/main/cost-session-banking.test.ts +407 -0
- package/src/main/main.ts +433 -68
- package/src/provider/assistant-app-settings.ts +31 -5
- package/src/state/ai-assistant-slice.test.ts +12 -5
- package/src/state/ai-assistant-slice.ts +21 -13
- package/src/state/debug-event-log.ts +2 -2
- package/src/state/persistence/session-persistence-provider.ts +24 -0
- package/src/state/persistence/session-persistence.integration.test.ts +8 -1
- package/src/state/persistence/session-persister.test.ts +10 -1
- package/src/state/persistence/session-persister.ts +2 -2
- package/src/state/persistence/session-snapshot.test.ts +4 -1
- package/src/state/persistence/session-snapshot.ts +5 -1
- package/src/state/persistence/stateful-restore.e2e.test.ts +9 -1
- package/src/styles/settings-section.ts +40 -0
- package/src/utils/condense-history.test.ts +103 -0
- package/src/utils/condense-history.ts +33 -3
- package/src/utils/cost-session-history.test.ts +187 -16
- package/src/utils/cost-session-history.ts +142 -23
- package/src/utils/resolve-cost-history-config.ts +10 -3
- package/src/utils/sum-costs.ts +2 -9
- package/src/utils/sum-tokens.test.ts +1 -11
- package/src/utils/sum-tokens.ts +10 -26
- package/src/utils/sum-usage.test.ts +140 -0
- package/src/utils/sum-usage.ts +130 -0
|
@@ -65,6 +65,7 @@ import {
|
|
|
65
65
|
normalizeForProvider,
|
|
66
66
|
} from '../../utils/history-transform';
|
|
67
67
|
import { logger } from '../../utils/logger';
|
|
68
|
+
import { sumUsage } from '../../utils/sum-usage';
|
|
68
69
|
import { TOOL_FOLD_SYMBOL, type ToolFold } from '../../utils/tool-fold';
|
|
69
70
|
import type { AiDriver, AllAgentSummary } from '../ai-driver/ai-driver';
|
|
70
71
|
|
|
@@ -217,6 +218,16 @@ export interface ChatDriverConfig {
|
|
|
217
218
|
maxToolIterations?: number;
|
|
218
219
|
/** Hard cap on fold operations. Default `5`. */
|
|
219
220
|
maxFoldOperations?: number;
|
|
221
|
+
/**
|
|
222
|
+
* Collapse `condenseWhen` payloads in batches of this many model-calls rather than as
|
|
223
|
+
* soon as each trigger fires. A positive integer; decimals are floored and anything
|
|
224
|
+
* below `1` becomes `1`. Default `1` — collapse immediately, the historical behaviour.
|
|
225
|
+
*
|
|
226
|
+
* Condensation rewrites history in place, so collapsing continuously breaks the
|
|
227
|
+
* provider's prompt cache on nearly every call of a re-read loop. Raising this trades
|
|
228
|
+
* up to that many calls' worth of extra context for one cache break per batch.
|
|
229
|
+
*/
|
|
230
|
+
condenseBatchCalls?: number;
|
|
220
231
|
/** Ring-buffer size for per-turn snapshots. Default `400`. */
|
|
221
232
|
maxTurnSnapshots?: number;
|
|
222
233
|
/** Session identity used to file meta events onto the shared debug-log timeline. */
|
|
@@ -565,6 +576,8 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
565
576
|
|
|
566
577
|
/** Hard cap on tool-loop iterations. */
|
|
567
578
|
private readonly maxToolIterations: number;
|
|
579
|
+
/** Model-calls per condensation batch; `1` collapses as soon as a trigger fires. */
|
|
580
|
+
private readonly condenseBatchCalls: number;
|
|
568
581
|
/** Session identity used to file meta events onto the shared debug-log timeline. */
|
|
569
582
|
private readonly sessionKey: string;
|
|
570
583
|
/** Injected activity bus; defaults to a no-op off-browser (Node/tests/headless). */
|
|
@@ -582,11 +595,13 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
582
595
|
primerHistory,
|
|
583
596
|
maxToolIterations = DEFAULT_MAX_TOOL_ITERATIONS,
|
|
584
597
|
maxFoldOperations = DEFAULT_MAX_FOLD_OPERATIONS,
|
|
598
|
+
condenseBatchCalls = 1,
|
|
585
599
|
maxTurnSnapshots = DEFAULT_MAX_TURN_SNAPSHOTS,
|
|
586
600
|
sessionKey = '',
|
|
587
601
|
activityBus = NOOP_ACTIVITY_BUS,
|
|
588
602
|
} = config;
|
|
589
603
|
this.maxToolIterations = maxToolIterations;
|
|
604
|
+
this.condenseBatchCalls = condenseBatchCalls;
|
|
590
605
|
this.sessionKey = sessionKey;
|
|
591
606
|
this.activityBus = activityBus;
|
|
592
607
|
if (typeof toolHandlers === 'function') {
|
|
@@ -1299,6 +1314,11 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
1299
1314
|
tokensBefore,
|
|
1300
1315
|
model: status?.model,
|
|
1301
1316
|
createdAt,
|
|
1317
|
+
// Bank what the replaced turns spent. `toCompact` is dropped from history
|
|
1318
|
+
// below, taking its per-message cost/token fields with it — so a host total
|
|
1319
|
+
// re-derived from the transcript would otherwise fall by exactly this much
|
|
1320
|
+
// on every compaction. Summarising a turn does not refund what it cost.
|
|
1321
|
+
rolledUpUsage: sumUsage(toCompact),
|
|
1302
1322
|
},
|
|
1303
1323
|
};
|
|
1304
1324
|
|
|
@@ -2329,6 +2349,7 @@ export class ChatDriver extends EventTarget implements AiDriver {
|
|
|
2329
2349
|
// A call's phase has ended once the app declared a later phase epoch
|
|
2330
2350
|
// current via `endPhase()`.
|
|
2331
2351
|
phaseEnded: (phaseEpoch) => phaseEpoch < this.currentPhaseEpoch,
|
|
2352
|
+
batchCalls: this.condenseBatchCalls,
|
|
2332
2353
|
},
|
|
2333
2354
|
(detail) => recordMetaEvent(this.sessionKey, 'context.condensed', detail),
|
|
2334
2355
|
);
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { css } from '@genesislcap/web-core';
|
|
2
|
+
import { assistantSettingsSectionTitleStyles } from '../../styles/settings-section';
|
|
2
3
|
|
|
3
4
|
/** Styles for the assistant settings modal (tabs, sections, toggles, cost summary). */
|
|
4
5
|
export const settingsModalStyles = css`
|
|
6
|
+
/*
|
|
7
|
+
* Section headings come from the shared fragment the package also exports, so a host titling
|
|
8
|
+
* its own slotted section applies the same rule rather than a copy of it.
|
|
9
|
+
*/
|
|
10
|
+
${assistantSettingsSectionTitleStyles}
|
|
11
|
+
|
|
5
12
|
*,
|
|
6
13
|
*::before,
|
|
7
14
|
*::after {
|
|
@@ -116,16 +123,13 @@ export const settingsModalStyles = css`
|
|
|
116
123
|
border-bottom: none;
|
|
117
124
|
}
|
|
118
125
|
|
|
119
|
-
|
|
126
|
+
/*
|
|
127
|
+
* Only the spacing. Everything else about this heading comes from the exported fragment
|
|
128
|
+
* above, which the assistant and its hosts share; margin is deliberately not in there,
|
|
129
|
+
* since the gap below a heading depends on the layout around it.
|
|
130
|
+
*/
|
|
131
|
+
.ai-settings-section-title {
|
|
120
132
|
margin: 0 0 calc(var(--design-unit) * 3px);
|
|
121
|
-
font-size: 11px;
|
|
122
|
-
font-weight: 600;
|
|
123
|
-
letter-spacing: 0.08em;
|
|
124
|
-
text-transform: uppercase;
|
|
125
|
-
color: var(--neutral-foreground-hint);
|
|
126
|
-
display: flex;
|
|
127
|
-
align-items: center;
|
|
128
|
-
gap: calc(var(--design-unit) * 2px);
|
|
129
133
|
}
|
|
130
134
|
|
|
131
135
|
.settings-modal-section-note {
|
|
@@ -159,6 +163,15 @@ export const settingsModalStyles = css`
|
|
|
159
163
|
gap: calc(var(--design-unit) * 2px);
|
|
160
164
|
}
|
|
161
165
|
|
|
166
|
+
/*
|
|
167
|
+
* For rows whose control is a button rather than a switch. The base row top-aligns, which suits
|
|
168
|
+
* a switch (it lands level with the title), but a button is tall enough that top-aligning leaves
|
|
169
|
+
* it above the title-plus-description block it acts on.
|
|
170
|
+
*/
|
|
171
|
+
.settings-toggle-row.is-centered {
|
|
172
|
+
align-items: center;
|
|
173
|
+
}
|
|
174
|
+
|
|
162
175
|
.settings-animation-list {
|
|
163
176
|
display: flex;
|
|
164
177
|
flex-direction: column;
|
|
@@ -236,6 +249,18 @@ export const settingsModalStyles = css`
|
|
|
236
249
|
max-width: none;
|
|
237
250
|
}
|
|
238
251
|
|
|
252
|
+
/*
|
|
253
|
+
* Sub-heading over the provider toggle group. Unlike the section headings it sits INSIDE
|
|
254
|
+
* .settings-modal-section-body, so the parent's 4-unit flex gap already separates it from the
|
|
255
|
+
* first toggle — and flex does not collapse margins, so the shared rule's 3-unit margin-bottom
|
|
256
|
+
* would add to that gap rather than replace it, leaving the label adrift from the rows it
|
|
257
|
+
* names. Zero the margin and pull back 2 of the 4 gap units, expressed in the same token so it
|
|
258
|
+
* tracks if that gap changes.
|
|
259
|
+
*/
|
|
260
|
+
.settings-app-toggles-title {
|
|
261
|
+
margin: 0 0 calc(var(--design-unit) * -2px);
|
|
262
|
+
}
|
|
263
|
+
|
|
239
264
|
.settings-app-toggle-block {
|
|
240
265
|
display: flex;
|
|
241
266
|
flex-direction: column;
|
|
@@ -270,7 +295,8 @@ export const settingsModalStyles = css`
|
|
|
270
295
|
color: var(--neutral-foreground-rest);
|
|
271
296
|
}
|
|
272
297
|
|
|
273
|
-
.settings-current-build
|
|
298
|
+
.settings-current-build,
|
|
299
|
+
.settings-lifetime-usage {
|
|
274
300
|
background: var(--neutral-layer-2);
|
|
275
301
|
border: 1px solid var(--neutral-stroke-rest);
|
|
276
302
|
border-radius: calc(var(--control-corner-radius) * 1.5px);
|
|
@@ -278,6 +304,28 @@ export const settingsModalStyles = css`
|
|
|
278
304
|
margin: calc(var(--design-unit) * 3px) 0;
|
|
279
305
|
}
|
|
280
306
|
|
|
307
|
+
/*
|
|
308
|
+
* Deliberately quieter than the live block: no pulsing badge and no accent, so the
|
|
309
|
+
* eye still lands on what is being built now. It leads only because it is the wider
|
|
310
|
+
* figure, not because it is the more urgent one.
|
|
311
|
+
*/
|
|
312
|
+
.settings-lifetime-usage-top {
|
|
313
|
+
display: flex;
|
|
314
|
+
align-items: center;
|
|
315
|
+
flex-wrap: wrap;
|
|
316
|
+
gap: calc(var(--design-unit) * 2px);
|
|
317
|
+
margin-bottom: calc(var(--design-unit) * 3px);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.settings-lifetime-label {
|
|
321
|
+
font-size: 10px;
|
|
322
|
+
line-height: 1;
|
|
323
|
+
font-weight: 700;
|
|
324
|
+
text-transform: uppercase;
|
|
325
|
+
letter-spacing: 0.06em;
|
|
326
|
+
color: var(--neutral-foreground-hint);
|
|
327
|
+
}
|
|
328
|
+
|
|
281
329
|
.settings-current-build-top {
|
|
282
330
|
display: flex;
|
|
283
331
|
align-items: center;
|
|
@@ -286,20 +334,44 @@ export const settingsModalStyles = css`
|
|
|
286
334
|
margin-bottom: calc(var(--design-unit) * 3px);
|
|
287
335
|
}
|
|
288
336
|
|
|
337
|
+
/*
|
|
338
|
+
* Optically centring these 10px caps against the 14px project name beside them. Flex offers
|
|
339
|
+
* only two alignments and neither lands it, because a box centre and a text baseline are
|
|
340
|
+
* different lines:
|
|
341
|
+
* - align-items: center sits the badge baseline about 1.2px high
|
|
342
|
+
* - align-items: baseline matches baselines, but the caps then read as bottom-aligned
|
|
343
|
+
* against the name's ascenders, so the badge looks low
|
|
344
|
+
* The eye wants the midpoint, so: centre the boxes, then translate down by the ~1px of
|
|
345
|
+
* overshoot. This is a nudge, but it corrects a fixed relationship between two font sizes
|
|
346
|
+
* rather than some neighbouring element's layout, so unlike a margin tuned to a sibling it
|
|
347
|
+
* cannot rot when that sibling changes. In em, so it tracks the badge text.
|
|
348
|
+
*
|
|
349
|
+
* display: block, not inline-flex: a flex container has no text baseline of its own and
|
|
350
|
+
* synthesizes one from its first child, which here is the dot, an empty span. As a block box
|
|
351
|
+
* the badge takes the baseline of its first line, i.e. the text.
|
|
352
|
+
*/
|
|
289
353
|
.settings-live-badge {
|
|
290
|
-
display:
|
|
291
|
-
align-items: center;
|
|
292
|
-
gap: calc(var(--design-unit) * 1px);
|
|
354
|
+
display: block;
|
|
293
355
|
font-size: 10px;
|
|
356
|
+
line-height: 1;
|
|
294
357
|
font-weight: 700;
|
|
295
358
|
text-transform: uppercase;
|
|
296
359
|
letter-spacing: 0.06em;
|
|
297
360
|
color: var(--accent-fill-rest);
|
|
361
|
+
white-space: nowrap;
|
|
362
|
+
transform: translateY(0.1em);
|
|
298
363
|
}
|
|
299
364
|
|
|
300
365
|
.settings-live-dot {
|
|
366
|
+
display: inline-block;
|
|
301
367
|
width: 7px;
|
|
302
368
|
height: 7px;
|
|
369
|
+
|
|
370
|
+
/* Replaces the flex gap the badge no longer has. */
|
|
371
|
+
margin-right: calc(var(--design-unit) * 1px);
|
|
372
|
+
|
|
373
|
+
/* Centres the dot on the text beside it without disturbing that text's baseline. */
|
|
374
|
+
vertical-align: middle;
|
|
303
375
|
border-radius: 50%;
|
|
304
376
|
background: var(--accent-fill-rest);
|
|
305
377
|
animation: settings-live-pulse 1.6s ease-out infinite;
|
|
@@ -321,18 +393,30 @@ export const settingsModalStyles = css`
|
|
|
321
393
|
|
|
322
394
|
.settings-current-build-name {
|
|
323
395
|
font-size: 14px;
|
|
396
|
+
line-height: 1;
|
|
324
397
|
font-weight: 600;
|
|
325
398
|
}
|
|
326
399
|
|
|
327
|
-
|
|
400
|
+
/*
|
|
401
|
+
* Cost and the token buckets side by side: one number in its own box, the four buckets
|
|
402
|
+
* sharing the box next to it. Stretch, so the two are the same height however tall the 22px
|
|
403
|
+
* cost value makes the left one.
|
|
404
|
+
*/
|
|
405
|
+
.settings-usage-row {
|
|
328
406
|
display: flex;
|
|
329
407
|
flex-wrap: wrap;
|
|
408
|
+
align-items: stretch;
|
|
330
409
|
gap: calc(var(--design-unit) * 3px);
|
|
331
410
|
margin-bottom: calc(var(--design-unit) * 3px);
|
|
332
411
|
}
|
|
333
412
|
|
|
413
|
+
.settings-usage-row-tokens {
|
|
414
|
+
display: flex;
|
|
415
|
+
flex: 1 1 auto;
|
|
416
|
+
min-width: 0;
|
|
417
|
+
}
|
|
418
|
+
|
|
334
419
|
.settings-build-stat {
|
|
335
|
-
flex: 1;
|
|
336
420
|
min-width: 0;
|
|
337
421
|
background: var(--neutral-layer-1);
|
|
338
422
|
border: 1px solid var(--neutral-stroke-rest);
|
|
@@ -360,6 +444,105 @@ export const settingsModalStyles = css`
|
|
|
360
444
|
color: var(--accent-fill-rest);
|
|
361
445
|
}
|
|
362
446
|
|
|
447
|
+
/*
|
|
448
|
+
* Per-bucket token split, beneath the headline cost. Small paired label/value cells
|
|
449
|
+
* rather than four more stat cards: they are the detail behind the cost above, so they
|
|
450
|
+
* have to read as subordinate to it rather than compete with it. The four buckets ARE
|
|
451
|
+
* the token figure now — there is no merged total anywhere for them to break down.
|
|
452
|
+
*/
|
|
453
|
+
.settings-token-breakdown {
|
|
454
|
+
display: flex;
|
|
455
|
+
flex-direction: column;
|
|
456
|
+
|
|
457
|
+
/*
|
|
458
|
+
* Tight against the buckets below. One design unit rather than two, because each bucket
|
|
459
|
+
* separates its own label from its value by only 2px — at two units the title sat four
|
|
460
|
+
* times further from the bucket labels than they sit from their own numbers, which read
|
|
461
|
+
* as a gap between unrelated blocks instead of a heading over its content.
|
|
462
|
+
*/
|
|
463
|
+
gap: calc(var(--design-unit) * 1px);
|
|
464
|
+
margin-bottom: calc(var(--design-unit) * 3px);
|
|
465
|
+
padding: calc(var(--design-unit) * 2px) calc(var(--design-unit) * 3px);
|
|
466
|
+
background: var(--neutral-layer-1);
|
|
467
|
+
border: 1px solid var(--neutral-stroke-rest);
|
|
468
|
+
border-radius: calc(var(--control-corner-radius) * 1px);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/*
|
|
472
|
+
* Titles the box, because nothing else in it named the unit. Same styling and position as
|
|
473
|
+
* the cost box's label, so two boxes sitting side by side are titled the same way; the
|
|
474
|
+
* hierarchy against the bucket labels comes from position, not a second type size.
|
|
475
|
+
*/
|
|
476
|
+
.settings-token-breakdown-label {
|
|
477
|
+
font-size: 10px;
|
|
478
|
+
line-height: 1;
|
|
479
|
+
font-weight: 600;
|
|
480
|
+
text-transform: uppercase;
|
|
481
|
+
letter-spacing: 0.06em;
|
|
482
|
+
color: var(--neutral-foreground-hint);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
.settings-token-buckets {
|
|
486
|
+
display: flex;
|
|
487
|
+
flex-wrap: wrap;
|
|
488
|
+
gap: calc(var(--design-unit) * 4px);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
.settings-token-bucket {
|
|
492
|
+
display: flex;
|
|
493
|
+
min-width: 0;
|
|
494
|
+
flex-direction: column;
|
|
495
|
+
gap: 2px;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
.settings-token-bucket-label {
|
|
499
|
+
font-size: 10px;
|
|
500
|
+
font-weight: 600;
|
|
501
|
+
text-transform: uppercase;
|
|
502
|
+
letter-spacing: 0.06em;
|
|
503
|
+
color: var(--neutral-foreground-hint);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
.settings-token-bucket-value {
|
|
507
|
+
font-size: 13px;
|
|
508
|
+
font-weight: 600;
|
|
509
|
+
color: var(--neutral-foreground-rest);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/*
|
|
513
|
+
* Cache reads bill at a fraction of the uncached input rate, and in an agentic
|
|
514
|
+
* session they are usually the largest bucket by a wide margin. Muted so the biggest
|
|
515
|
+
* number on the row doesn't read as the dominant cost driver when it is the cheapest.
|
|
516
|
+
*/
|
|
517
|
+
.settings-token-bucket.is-discounted .settings-token-bucket-value {
|
|
518
|
+
font-weight: 500;
|
|
519
|
+
color: var(--neutral-foreground-hint);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/*
|
|
523
|
+
* Usage-row overrides. Placed after the base rules they refine rather than up beside
|
|
524
|
+
* .settings-usage-row itself, because stylelint's no-descending-specificity rejects a
|
|
525
|
+
* compound selector appearing before the plain one it overrides.
|
|
526
|
+
*/
|
|
527
|
+
|
|
528
|
+
/* Sized by its content; the buckets take the rest, since four cells need the width. */
|
|
529
|
+
.settings-usage-row > .settings-build-stat {
|
|
530
|
+
flex: 0 1 auto;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
.settings-usage-row .settings-token-breakdown {
|
|
534
|
+
flex: 1 1 auto;
|
|
535
|
+
|
|
536
|
+
/* The row owns the spacing now; this centres the column against the taller cost box. */
|
|
537
|
+
justify-content: center;
|
|
538
|
+
margin-bottom: 0;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/* Equal columns, so the four buckets line up as a grid rather than bunching left. */
|
|
542
|
+
.settings-usage-row .settings-token-bucket {
|
|
543
|
+
flex: 1 1 0;
|
|
544
|
+
}
|
|
545
|
+
|
|
363
546
|
.settings-model-tags {
|
|
364
547
|
display: flex;
|
|
365
548
|
flex-wrap: wrap;
|
|
@@ -432,6 +615,32 @@ export const settingsModalStyles = css`
|
|
|
432
615
|
gap: calc(var(--design-unit) * 2px);
|
|
433
616
|
}
|
|
434
617
|
|
|
618
|
+
/*
|
|
619
|
+
* Both of these carry a ?hidden binding (showTokens), and both declare display: flex — which
|
|
620
|
+
* silently beat the UA [hidden] { display: none } rule, because author-origin declarations
|
|
621
|
+
* outrank user-agent origin whatever the specificity. Without this, showTokens: false still
|
|
622
|
+
* rendered the full breakdown in the lifetime block, the live block and every history row.
|
|
623
|
+
* Sibling elements that hide correctly do so only by declaring no display at all. Same
|
|
624
|
+
* treatment as .settings-modal-tab-panel[hidden] near the top of this file.
|
|
625
|
+
*/
|
|
626
|
+
.settings-usage-row-tokens[hidden],
|
|
627
|
+
.settings-cost-history-row-content[hidden] {
|
|
628
|
+
display: none !important;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/*
|
|
632
|
+
* The buckets inside a history row drop the panel treatment they carry in the summary
|
|
633
|
+
* blocks — the row is already a bordered card, and a second box inside it reads as a
|
|
634
|
+
* nested panel. The adjacent-sibling rule above supplies the separator instead.
|
|
635
|
+
*/
|
|
636
|
+
.settings-cost-history-row-breakdown .settings-token-breakdown {
|
|
637
|
+
flex: 1 1 auto;
|
|
638
|
+
margin-bottom: 0;
|
|
639
|
+
padding: 0;
|
|
640
|
+
background: none;
|
|
641
|
+
border: none;
|
|
642
|
+
}
|
|
643
|
+
|
|
435
644
|
.settings-cost-history-row-content + .settings-cost-history-row-content {
|
|
436
645
|
border-top: 1px solid var(--neutral-stroke-rest);
|
|
437
646
|
margin-top: calc(var(--design-unit) * 2px);
|
|
@@ -446,8 +655,20 @@ export const settingsModalStyles = css`
|
|
|
446
655
|
justify-content: space-between;
|
|
447
656
|
}
|
|
448
657
|
|
|
658
|
+
/*
|
|
659
|
+
* Holds the cost and the delete button as one centred pair. The button used to be a sibling
|
|
660
|
+
* of the whole row body, kept in place by a -2px nudge tuned for when the metrics column had
|
|
661
|
+
* two lines (cost plus a merged token total). Dropping that second line left the nudge
|
|
662
|
+
* compensating for nothing. Centring the two against each other needs no magic number and
|
|
663
|
+
* cannot drift again if the metrics content changes.
|
|
664
|
+
*
|
|
665
|
+
* NB this rule previously set align-items with no display:flex, so it had no effect at all.
|
|
666
|
+
*/
|
|
449
667
|
.settings-cost-history-row-right {
|
|
450
|
-
|
|
668
|
+
display: flex;
|
|
669
|
+
align-items: center;
|
|
670
|
+
gap: calc(var(--design-unit) * 1px);
|
|
671
|
+
flex: 0 0 auto;
|
|
451
672
|
}
|
|
452
673
|
|
|
453
674
|
.settings-cost-history-row-cost,
|
|
@@ -468,7 +689,6 @@ export const settingsModalStyles = css`
|
|
|
468
689
|
white-space: nowrap;
|
|
469
690
|
}
|
|
470
691
|
|
|
471
|
-
.settings-cost-history-row-tokens,
|
|
472
692
|
.settings-cost-history-row-date {
|
|
473
693
|
font-size: 12px;
|
|
474
694
|
color: var(--neutral-foreground-hint);
|
|
@@ -476,7 +696,6 @@ export const settingsModalStyles = css`
|
|
|
476
696
|
|
|
477
697
|
.settings-cost-history-delete {
|
|
478
698
|
flex: 0 0 auto;
|
|
479
|
-
margin-top: -2px;
|
|
480
699
|
color: var(--neutral-foreground-hint);
|
|
481
700
|
}
|
|
482
701
|
|