@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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AggregateUsage } from '@genesislcap/foundation-ai';
|
|
2
2
|
import { html, ref, repeat, slotted, when, type ViewTemplate } from '@genesislcap/web-core';
|
|
3
3
|
import type { FoundationAiAssistant } from '../../main/main';
|
|
4
4
|
import { ANIMATION_DEFS, type AiAssistantAnimation } from '../../main/main.types';
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
type CostSessionModelEntry,
|
|
9
9
|
type CostSessionRecord,
|
|
10
10
|
} from '../../utils/cost-session-history';
|
|
11
|
-
import {
|
|
11
|
+
import { formatCompactTokenCountSafe } from '../../utils/sum-tokens';
|
|
12
12
|
|
|
13
13
|
const SESSION_COST_DECIMALS = 2;
|
|
14
14
|
|
|
@@ -114,18 +114,41 @@ const animationsPickerTemplate = (
|
|
|
114
114
|
</div>
|
|
115
115
|
`;
|
|
116
116
|
|
|
117
|
-
/**
|
|
117
|
+
/**
|
|
118
|
+
* Tab identifiers for the modal tab bar.
|
|
119
|
+
*
|
|
120
|
+
* The values are deliberately NOT renamed to match the visible labels ("Configuration" and
|
|
121
|
+
* "Usage"): they are the public API that hosts read and write via `settingsModalTab`, and are
|
|
122
|
+
* matched by the `settings-modal-tab-*` shadow parts. Renaming a label is cosmetic; renaming
|
|
123
|
+
* these would break consumers.
|
|
124
|
+
*/
|
|
118
125
|
export type SettingsModalTab = 'settings' | 'cost';
|
|
119
126
|
|
|
127
|
+
/**
|
|
128
|
+
* How a settings row places its control.
|
|
129
|
+
*
|
|
130
|
+
* `row` top-aligns it, which is what a switch wants — it then sits level with the title rather
|
|
131
|
+
* than floating beside a two-line description. `row-centered` is for a taller control such as a
|
|
132
|
+
* button, which top-aligned reads as hanging above the text it belongs to. `column` stacks the
|
|
133
|
+
* control under the text, for controls that need the full width.
|
|
134
|
+
*/
|
|
135
|
+
type SettingsRowLayout = 'row' | 'row-centered' | 'column';
|
|
136
|
+
|
|
137
|
+
const SETTINGS_ROW_LAYOUT_CLASS: Record<SettingsRowLayout, string> = {
|
|
138
|
+
row: '',
|
|
139
|
+
'row-centered': 'is-centered',
|
|
140
|
+
column: 'is-stacked',
|
|
141
|
+
};
|
|
142
|
+
|
|
120
143
|
/** Shared label + control row used for switches, buttons, and multiselects. */
|
|
121
144
|
const settingsToggleRow = (
|
|
122
145
|
title: string,
|
|
123
146
|
description: string,
|
|
124
147
|
part: string,
|
|
125
148
|
control: ViewTemplate<FoundationAiAssistant>,
|
|
126
|
-
layout:
|
|
149
|
+
layout: SettingsRowLayout = 'row',
|
|
127
150
|
) => html<FoundationAiAssistant>`
|
|
128
|
-
<div class="settings-toggle-row ${layout
|
|
151
|
+
<div class="settings-toggle-row ${SETTINGS_ROW_LAYOUT_CLASS[layout]}" part="${part}-row">
|
|
129
152
|
<div class="settings-toggle-text">
|
|
130
153
|
<div class="settings-toggle-title">${title}</div>
|
|
131
154
|
<div class="settings-toggle-desc">${description}</div>
|
|
@@ -265,7 +288,34 @@ const chatBotSettingsTemplate = (
|
|
|
265
288
|
|
|
266
289
|
const appSettingsTemplate = (
|
|
267
290
|
switchTag: string,
|
|
291
|
+
iconTag: string,
|
|
268
292
|
): ViewTemplate<FoundationAiAssistant> => html<FoundationAiAssistant>`
|
|
293
|
+
<!--
|
|
294
|
+
Slot FIRST, ahead of the provider toggles. The assistant renders no heading for this
|
|
295
|
+
section, so whatever the host slots in has to be able to title it — and a heading below the
|
|
296
|
+
rows it names would title nothing. Anything the host wants under the toggles it can render
|
|
297
|
+
below them within its own slotted block.
|
|
298
|
+
-->
|
|
299
|
+
<slot name="settings-app" ${slotted('settingsAppSlotted')}></slot>
|
|
300
|
+
<!--
|
|
301
|
+
Sub-heading for the toggle group, rendered only when the host asks for one — both the text
|
|
302
|
+
and the icon come from its provider. It exists so a host whose slotted content covers other
|
|
303
|
+
ground can still mark these rows off from it.
|
|
304
|
+
-->
|
|
305
|
+
${when(
|
|
306
|
+
(x) => !!x.appSettingsTogglesHeading,
|
|
307
|
+
html<FoundationAiAssistant>`
|
|
308
|
+
<h4 class="ai-settings-section-title settings-app-toggles-title" part="app-toggles-title">
|
|
309
|
+
${when(
|
|
310
|
+
(x) => !!x.appSettingsTogglesHeading?.icon,
|
|
311
|
+
html<FoundationAiAssistant>`
|
|
312
|
+
<${iconTag} name="${(x) => x.appSettingsTogglesHeading?.icon}"></${iconTag}>
|
|
313
|
+
`,
|
|
314
|
+
)}
|
|
315
|
+
${(x) => x.appSettingsTogglesHeading?.title}
|
|
316
|
+
</h4>
|
|
317
|
+
`,
|
|
318
|
+
)}
|
|
269
319
|
${repeat(
|
|
270
320
|
(x) => x.visibleAppSettingsToggles,
|
|
271
321
|
html<AssistantAppSettingsToggle, FoundationAiAssistant>`
|
|
@@ -298,9 +348,142 @@ const appSettingsTemplate = (
|
|
|
298
348
|
</div>
|
|
299
349
|
`,
|
|
300
350
|
)}
|
|
301
|
-
<slot name="settings-app" ${slotted('settingsAppSlotted')}></slot>
|
|
302
351
|
`;
|
|
303
352
|
|
|
353
|
+
/**
|
|
354
|
+
* One model chip. Shared by the live session and the history rows so the two can't drift — both
|
|
355
|
+
* already reduce to a distinct-model list, the live one via `uniqueProviderModels` and a row via
|
|
356
|
+
* `collectSessionModels`.
|
|
357
|
+
*/
|
|
358
|
+
const modelTagTemplate = html<CostSessionModelEntry>`
|
|
359
|
+
<span class="settings-model-tag vendor-${(entry) => entry.provider || 'generic'}">
|
|
360
|
+
${(entry) => entry.model}
|
|
361
|
+
</span>
|
|
362
|
+
`;
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* The four billing buckets for one `AggregateUsage`, wherever it comes from — the lifetime
|
|
366
|
+
* total, the live session, or a history row.
|
|
367
|
+
*
|
|
368
|
+
* Why four numbers instead of one: each bucket bills at a different rate, so a single token
|
|
369
|
+
* total can't explain a session that processed millions of tokens for pennies. Uncached input,
|
|
370
|
+
* cache read and cache write partition the prompt (they sum to it, never add on top), and
|
|
371
|
+
* output is what the model generated.
|
|
372
|
+
*
|
|
373
|
+
* Ordered input, output, cache read, cache write to match how Claude Code reports usage, so the
|
|
374
|
+
* two can be read side by side — comparing against it is what this breakdown exists for. Note
|
|
375
|
+
* this puts output between the three buckets that partition the prompt, so their relationship is
|
|
376
|
+
* carried by the tooltips rather than by adjacency.
|
|
377
|
+
*
|
|
378
|
+
* `partPrefix` keeps the shadow-part names caller-specific (`session-tokens-input`,
|
|
379
|
+
* `lifetime-tokens-input`, …) so a host can style one context without hitting the others.
|
|
380
|
+
*/
|
|
381
|
+
const tokenBreakdownTemplate = <T>(
|
|
382
|
+
usageOf: (x: T) => AggregateUsage,
|
|
383
|
+
partPrefix: string,
|
|
384
|
+
): ViewTemplate<T> => html<T>`
|
|
385
|
+
<div class="settings-token-breakdown" part="${partPrefix}-token-breakdown">
|
|
386
|
+
<!--
|
|
387
|
+
Titles the box. Nothing in it otherwise said "tokens": the four bucket names are the
|
|
388
|
+
token TYPES, not the unit, so the panel read as four unexplained numbers unless the
|
|
389
|
+
reader found the per-bucket tooltips.
|
|
390
|
+
-->
|
|
391
|
+
<div class="settings-token-breakdown-label">Tokens</div>
|
|
392
|
+
<div class="settings-token-buckets">
|
|
393
|
+
<div class="settings-token-bucket" part="${partPrefix}-tokens-input">
|
|
394
|
+
<span
|
|
395
|
+
class="settings-token-bucket-label"
|
|
396
|
+
title="Prompt tokens billed at the full uncached rate"
|
|
397
|
+
>
|
|
398
|
+
Input
|
|
399
|
+
</span>
|
|
400
|
+
<span
|
|
401
|
+
class="settings-token-bucket-value"
|
|
402
|
+
:innerHTML="${(x: T) => formatCompactTokenCountSafe(usageOf(x).uncachedInputTokens)}"
|
|
403
|
+
></span>
|
|
404
|
+
</div>
|
|
405
|
+
<div class="settings-token-bucket" part="${partPrefix}-tokens-output">
|
|
406
|
+
<span
|
|
407
|
+
class="settings-token-bucket-label"
|
|
408
|
+
title="Tokens the model generated, including reasoning tokens billed as output"
|
|
409
|
+
>
|
|
410
|
+
Output
|
|
411
|
+
</span>
|
|
412
|
+
<span
|
|
413
|
+
class="settings-token-bucket-value"
|
|
414
|
+
:innerHTML="${(x: T) => formatCompactTokenCountSafe(usageOf(x).outputTokens)}"
|
|
415
|
+
></span>
|
|
416
|
+
</div>
|
|
417
|
+
<div class="settings-token-bucket is-discounted" part="${partPrefix}-tokens-cache-read">
|
|
418
|
+
<span
|
|
419
|
+
class="settings-token-bucket-label"
|
|
420
|
+
title="Prompt tokens served from the provider cache — billed at a fraction of the uncached rate"
|
|
421
|
+
>
|
|
422
|
+
Cache read
|
|
423
|
+
</span>
|
|
424
|
+
<span
|
|
425
|
+
class="settings-token-bucket-value"
|
|
426
|
+
:innerHTML="${(x: T) => formatCompactTokenCountSafe(usageOf(x).cacheReadTokens)}"
|
|
427
|
+
></span>
|
|
428
|
+
</div>
|
|
429
|
+
<div class="settings-token-bucket" part="${partPrefix}-tokens-cache-write">
|
|
430
|
+
<span
|
|
431
|
+
class="settings-token-bucket-label"
|
|
432
|
+
title="Prompt tokens written to the provider cache — billed at a premium over the uncached rate"
|
|
433
|
+
>
|
|
434
|
+
Cache write
|
|
435
|
+
</span>
|
|
436
|
+
<span
|
|
437
|
+
class="settings-token-bucket-value"
|
|
438
|
+
:innerHTML="${(x: T) => formatCompactTokenCountSafe(usageOf(x).cacheWriteTokens)}"
|
|
439
|
+
></span>
|
|
440
|
+
</div>
|
|
441
|
+
</div>
|
|
442
|
+
</div>
|
|
443
|
+
`;
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Lifetime usage across every project in the history scope.
|
|
447
|
+
*
|
|
448
|
+
* Sits above the live session block because it is the wider figure. It used to live inside
|
|
449
|
+
* that block as a lone "Total build cost", where the live badge and project name directly
|
|
450
|
+
* above it implied the total belonged to the open project.
|
|
451
|
+
*/
|
|
452
|
+
const lifetimeUsageTemplate =
|
|
453
|
+
(): ViewTemplate<FoundationAiAssistant> => html<FoundationAiAssistant>`
|
|
454
|
+
${when(
|
|
455
|
+
(x) => x.settingsCostLifetimeVisible,
|
|
456
|
+
html<FoundationAiAssistant>`
|
|
457
|
+
<div class="settings-lifetime-usage" part="lifetime-usage">
|
|
458
|
+
<div class="settings-lifetime-usage-top">
|
|
459
|
+
<span class="settings-lifetime-label">Lifetime usage</span>
|
|
460
|
+
</div>
|
|
461
|
+
<div class="settings-usage-row">
|
|
462
|
+
<div
|
|
463
|
+
class="settings-build-stat"
|
|
464
|
+
part="lifetime-cost"
|
|
465
|
+
?hidden=${(x) => !x.resolvedCostHistory.showCost}
|
|
466
|
+
>
|
|
467
|
+
<div class="settings-build-stat-label">Total build cost</div>
|
|
468
|
+
<div class="settings-build-stat-value">
|
|
469
|
+
<span class="prefix">$</span>
|
|
470
|
+
<span class="value">
|
|
471
|
+
${(x) => x.cumulativeCostUsd.toFixed(SESSION_COST_DECIMALS)}
|
|
472
|
+
</span>
|
|
473
|
+
</div>
|
|
474
|
+
</div>
|
|
475
|
+
<div
|
|
476
|
+
class="settings-usage-row-tokens"
|
|
477
|
+
?hidden=${(x) => !x.resolvedCostHistory.showTokens}
|
|
478
|
+
>
|
|
479
|
+
${tokenBreakdownTemplate<FoundationAiAssistant>((x) => x.cumulativeUsage, 'lifetime')}
|
|
480
|
+
</div>
|
|
481
|
+
</div>
|
|
482
|
+
</div>
|
|
483
|
+
`,
|
|
484
|
+
)}
|
|
485
|
+
`;
|
|
486
|
+
|
|
304
487
|
const sessionCostSummaryTemplate =
|
|
305
488
|
(): ViewTemplate<FoundationAiAssistant> => html<FoundationAiAssistant>`
|
|
306
489
|
${when(
|
|
@@ -319,58 +502,38 @@ const sessionCostSummaryTemplate =
|
|
|
319
502
|
`,
|
|
320
503
|
)}
|
|
321
504
|
</div>
|
|
322
|
-
<div class="settings-
|
|
505
|
+
<div class="settings-usage-row">
|
|
323
506
|
<div
|
|
324
507
|
class="settings-build-stat"
|
|
325
508
|
part="session-cost"
|
|
326
509
|
?hidden=${(x) => !x.resolvedCostHistory.showCost}
|
|
327
510
|
>
|
|
328
|
-
|
|
511
|
+
<!--
|
|
512
|
+
Label states the scope rather than implying one. These figures are whatever
|
|
513
|
+
the open transcript proves, so they are the project's whole history exactly
|
|
514
|
+
when that transcript is restored on load, and only this page-load's when it
|
|
515
|
+
isn't. Same number, two meanings — so the caller can't be left guessing.
|
|
516
|
+
-->
|
|
517
|
+
<div class="settings-build-stat-label">
|
|
518
|
+
${(x) => (x.liveUsageIsProjectLifetime ? 'Project cost' : 'Session cost')}
|
|
519
|
+
</div>
|
|
329
520
|
<div class="settings-build-stat-value">
|
|
330
521
|
<span class="prefix">$</span>
|
|
331
522
|
<span class="value">${(x) => x.sessionCostUsd.toFixed(SESSION_COST_DECIMALS)}</span>
|
|
332
523
|
</div>
|
|
333
524
|
</div>
|
|
334
525
|
<div
|
|
335
|
-
class="settings-
|
|
336
|
-
part="session-tokens"
|
|
526
|
+
class="settings-usage-row-tokens"
|
|
337
527
|
?hidden=${(x) => !x.resolvedCostHistory.showTokens}
|
|
338
528
|
>
|
|
339
|
-
<
|
|
340
|
-
<div
|
|
341
|
-
class="settings-build-stat-value"
|
|
342
|
-
:innerHTML="${(x) => formatCompactTokenCountSafe(x.sessionTokensConsumed)}"
|
|
343
|
-
></div>
|
|
344
|
-
</div>
|
|
345
|
-
<div
|
|
346
|
-
class="settings-build-stat"
|
|
347
|
-
part="session-cumulative-cost"
|
|
348
|
-
?hidden=${(x) => !x.resolvedCostHistory.showCost}
|
|
349
|
-
>
|
|
350
|
-
<div class="settings-build-stat-label">Total build cost</div>
|
|
351
|
-
<div class="settings-build-stat-value">
|
|
352
|
-
<span class="prefix">$</span>
|
|
353
|
-
<span class="value">
|
|
354
|
-
${(x) => x.cumulativeCostUsd.toFixed(SESSION_COST_DECIMALS)}
|
|
355
|
-
</span>
|
|
356
|
-
</div>
|
|
529
|
+
${tokenBreakdownTemplate<FoundationAiAssistant>((x) => x.sessionUsage, 'session')}
|
|
357
530
|
</div>
|
|
358
531
|
</div>
|
|
359
532
|
${when(
|
|
360
|
-
(x) => x.chatConfig.ui?.showActiveModel !== false && x.
|
|
533
|
+
(x) => x.chatConfig.ui?.showActiveModel !== false && x.uniqueProviderModels.length > 0,
|
|
361
534
|
html<FoundationAiAssistant>`
|
|
362
535
|
<div class="settings-model-tags" part="session-model-tags">
|
|
363
|
-
${repeat(
|
|
364
|
-
(x) => x.providerStatuses,
|
|
365
|
-
html<AIProviderRegistryStatusEntry>`
|
|
366
|
-
<span
|
|
367
|
-
class="settings-model-tag vendor-${(entry) =>
|
|
368
|
-
entry.status?.provider || 'generic'}"
|
|
369
|
-
>
|
|
370
|
-
${(entry) => entry.status?.model ?? entry.name}
|
|
371
|
-
</span>
|
|
372
|
-
`,
|
|
373
|
-
)}
|
|
536
|
+
${repeat((x) => x.uniqueProviderModels, modelTagTemplate)}
|
|
374
537
|
</div>
|
|
375
538
|
`,
|
|
376
539
|
)}
|
|
@@ -384,7 +547,7 @@ const costSessionHistoryTemplate = (
|
|
|
384
547
|
buttonTag: string,
|
|
385
548
|
): ViewTemplate<FoundationAiAssistant> => html<FoundationAiAssistant>`
|
|
386
549
|
${repeat(
|
|
387
|
-
(x) => x.
|
|
550
|
+
(x) => x.sortedCostSessionHistory,
|
|
388
551
|
html<CostSessionRecord>`
|
|
389
552
|
<div class="settings-cost-history-row" part="cost-history-row">
|
|
390
553
|
<div class="settings-cost-history-row-content">
|
|
@@ -397,29 +560,46 @@ const costSessionHistoryTemplate = (
|
|
|
397
560
|
${(record) => record.title}
|
|
398
561
|
</div>
|
|
399
562
|
<div class="settings-cost-history-row-date">
|
|
400
|
-
${(record) => formatCostSessionDate(record.
|
|
563
|
+
${(record) => formatCostSessionDate(record.updatedAt)}
|
|
401
564
|
</div>
|
|
402
565
|
</div>
|
|
566
|
+
<!--
|
|
567
|
+
Cost and the delete control live together so they centre against each other.
|
|
568
|
+
The button used to sit outside the row body, held in place by a negative margin
|
|
569
|
+
tuned for when this column had a second line (a merged token total); that line
|
|
570
|
+
is gone, and a nudge measured against a removed sibling is exactly the kind that
|
|
571
|
+
rots silently.
|
|
572
|
+
-->
|
|
403
573
|
<div class="settings-cost-history-row-right">
|
|
404
574
|
<div class="settings-cost-history-row-metrics">
|
|
405
575
|
<div class="settings-cost-history-row-cost">
|
|
406
|
-
$${(record) => record.costUsd.toFixed(SESSION_COST_DECIMALS)}
|
|
407
|
-
</div>
|
|
408
|
-
<div class="settings-cost-history-row-tokens">
|
|
409
|
-
${(record) => formatCompactTokenCountPlain(record.tokensConsumed)} tokens
|
|
576
|
+
$${(record) => record.usage.costUsd.toFixed(SESSION_COST_DECIMALS)}
|
|
410
577
|
</div>
|
|
411
578
|
</div>
|
|
579
|
+
<${buttonTag}
|
|
580
|
+
class="settings-cost-history-delete"
|
|
581
|
+
part="cost-history-delete"
|
|
582
|
+
appearance="stealth"
|
|
583
|
+
aria-label="Remove from build history"
|
|
584
|
+
@click=${(record, c) => c.parent.showConfirmationDialog('Are you sure you want to remove this build from history?', c.parent.settingsModal, () => c.parent.removeCostSessionFromHistory(record.id))}
|
|
585
|
+
>
|
|
586
|
+
<${iconTag} name="trash"></${iconTag}>
|
|
587
|
+
</${buttonTag}>
|
|
412
588
|
</div>
|
|
413
589
|
</div>
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
590
|
+
</div>
|
|
591
|
+
<!--
|
|
592
|
+
The same four buckets as the live block, on its own line below the row rather than
|
|
593
|
+
beside the cost — the row's own width is spoken for by title, cost and delete.
|
|
594
|
+
Replaces a merged total that could not explain a huge token count at a small cost
|
|
595
|
+
without hovering for a tooltip.
|
|
596
|
+
-->
|
|
597
|
+
<div
|
|
598
|
+
class="settings-cost-history-row-content settings-cost-history-row-breakdown"
|
|
599
|
+
?hidden=${(record, c) =>
|
|
600
|
+
!(c.parent as FoundationAiAssistant).resolvedCostHistory.showTokens}
|
|
601
|
+
>
|
|
602
|
+
${tokenBreakdownTemplate<CostSessionRecord>((record) => record.usage, 'cost-history-row')}
|
|
423
603
|
</div>
|
|
424
604
|
${when(
|
|
425
605
|
(record) => record.models.length > 0,
|
|
@@ -427,14 +607,7 @@ const costSessionHistoryTemplate = (
|
|
|
427
607
|
<div
|
|
428
608
|
class="settings-cost-history-row-content settings-model-tags settings-cost-history-model-tags"
|
|
429
609
|
>
|
|
430
|
-
${repeat(
|
|
431
|
-
(record) => record.models,
|
|
432
|
-
html<CostSessionModelEntry>`
|
|
433
|
-
<span class="settings-model-tag vendor-${(model) => model.provider || 'generic'}">
|
|
434
|
-
${(model) => model.model}
|
|
435
|
-
</span>
|
|
436
|
-
`,
|
|
437
|
-
)}
|
|
610
|
+
${repeat((record) => record.models, modelTagTemplate)}
|
|
438
611
|
</div>
|
|
439
612
|
`,
|
|
440
613
|
)}
|
|
@@ -444,7 +617,7 @@ const costSessionHistoryTemplate = (
|
|
|
444
617
|
`;
|
|
445
618
|
|
|
446
619
|
/**
|
|
447
|
-
* Modal shell for assistant settings —
|
|
620
|
+
* Modal shell for assistant settings — Configuration and Usage tabs, section slots for
|
|
448
621
|
* app-specific UI, and built-in chat-bot toggles.
|
|
449
622
|
*/
|
|
450
623
|
export const SettingsModalTemplate = (
|
|
@@ -471,7 +644,7 @@ export const SettingsModalTemplate = (
|
|
|
471
644
|
part="settings-section-model"
|
|
472
645
|
?hidden=${(x) => !x.settingsModelSectionVisible}
|
|
473
646
|
>
|
|
474
|
-
<h3 class="settings-
|
|
647
|
+
<h3 class="ai-settings-section-title">
|
|
475
648
|
<${iconTag} name="microchip"></${iconTag}> AI Model Settings
|
|
476
649
|
</h3>
|
|
477
650
|
<div class="settings-modal-section-body">
|
|
@@ -479,18 +652,11 @@ export const SettingsModalTemplate = (
|
|
|
479
652
|
${platformContextUsageTemplate(progressTag)}
|
|
480
653
|
</div>
|
|
481
654
|
</section>
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
<h3 class="settings-modal-section-title">
|
|
488
|
-
<${iconTag} name="home"></${iconTag}> ${(x) => x.appSettingsSectionTitle}
|
|
489
|
-
</h3>
|
|
490
|
-
<div class="settings-modal-section-body">
|
|
491
|
-
${appSettingsTemplate(switchTag)}
|
|
492
|
-
</div>
|
|
493
|
-
</section>
|
|
655
|
+
<!--
|
|
656
|
+
Above the host section, deliberately. This is the assistant's own control, so it belongs
|
|
657
|
+
with the assistant's other sections rather than trailing the host's — where, now that the
|
|
658
|
+
host section carries no heading of its own, it read as one more of the host's settings.
|
|
659
|
+
-->
|
|
494
660
|
${when(
|
|
495
661
|
(x) => x.chatConfig.ui?.allowDebugDownload === true,
|
|
496
662
|
html<FoundationAiAssistant>`
|
|
@@ -520,13 +686,29 @@ export const SettingsModalTemplate = (
|
|
|
520
686
|
${(x) => (x.downloadingDebugLog ? 'Downloading…' : 'Download')}
|
|
521
687
|
</${buttonTag}>
|
|
522
688
|
`,
|
|
689
|
+
'row-centered',
|
|
523
690
|
)}
|
|
524
691
|
</div>
|
|
525
692
|
</section>
|
|
526
693
|
`,
|
|
527
694
|
)}
|
|
695
|
+
<!--
|
|
696
|
+
No heading here, by design. This section belongs to the host: only it knows what its
|
|
697
|
+
settings are called, so the assistant supplies neither text nor icon and the host titles
|
|
698
|
+
the section from the slot. See assistantSettingsSectionTitleStyles for the heading style
|
|
699
|
+
it can apply to match the sections above and below.
|
|
700
|
+
-->
|
|
701
|
+
<section
|
|
702
|
+
class="settings-modal-section"
|
|
703
|
+
part="settings-section-app"
|
|
704
|
+
?hidden=${(x) => !x.settingsAppSectionVisible}
|
|
705
|
+
>
|
|
706
|
+
<div class="settings-modal-section-body">
|
|
707
|
+
${appSettingsTemplate(switchTag, iconTag)}
|
|
708
|
+
</div>
|
|
709
|
+
</section>
|
|
528
710
|
<section class="settings-modal-section" part="settings-section-chat">
|
|
529
|
-
<h3 class="settings-
|
|
711
|
+
<h3 class="ai-settings-section-title">
|
|
530
712
|
<${iconTag} name="comment"></${iconTag}> AI Chat Bot Settings
|
|
531
713
|
</h3>
|
|
532
714
|
<p class="settings-modal-section-note">These options only control what you see in the chat window and do not control model behaviour.</p>
|
|
@@ -545,6 +727,7 @@ export const SettingsModalTemplate = (
|
|
|
545
727
|
part="cost-tab-panel"
|
|
546
728
|
>
|
|
547
729
|
<div class="settings-modal-body" part="settings-modal-body">
|
|
730
|
+
${lifetimeUsageTemplate()}
|
|
548
731
|
${sessionCostSummaryTemplate()}
|
|
549
732
|
<div
|
|
550
733
|
class="settings-cost-current"
|
|
@@ -595,7 +778,7 @@ export const SettingsModalTemplate = (
|
|
|
595
778
|
appearance="secondary"
|
|
596
779
|
class="settings-modal-tab"
|
|
597
780
|
part="settings-modal-tab-settings"
|
|
598
|
-
><${iconTag} name="
|
|
781
|
+
><${iconTag} name="sliders"></${iconTag}> Configuration</${tabTag}>${when(
|
|
599
782
|
(x) => x.settingsCostTabVisible,
|
|
600
783
|
html<FoundationAiAssistant>`<${tabTag}
|
|
601
784
|
slot="tab"
|
|
@@ -603,7 +786,7 @@ export const SettingsModalTemplate = (
|
|
|
603
786
|
appearance="secondary"
|
|
604
787
|
class="settings-modal-tab"
|
|
605
788
|
part="settings-modal-tab-cost"
|
|
606
|
-
><${iconTag} name="
|
|
789
|
+
><${iconTag} name="chart-simple"></${iconTag}> Usage</${tabTag}>`,
|
|
607
790
|
)}${settingsTabPanel}${when((x) => x.settingsCostTabVisible, costTabPanel)}</${tabsTag}>
|
|
608
791
|
</div>
|
|
609
792
|
<div slot="bottom" class="settings-modal-footer" part="settings-modal-footer">
|
|
@@ -612,7 +795,13 @@ export const SettingsModalTemplate = (
|
|
|
612
795
|
(x) => x.settingsModalTab === 'cost',
|
|
613
796
|
html<FoundationAiAssistant>`
|
|
614
797
|
<span class="settings-disclaimer">
|
|
615
|
-
|
|
798
|
+
${when(
|
|
799
|
+
(x) => !!x.resolvedCostHistory.summaryHint,
|
|
800
|
+
html<FoundationAiAssistant>`
|
|
801
|
+
${(x) => x.resolvedCostHistory.summaryHint}
|
|
802
|
+
`,
|
|
803
|
+
)}Costs
|
|
804
|
+
are estimated from token usage at list price.
|
|
616
805
|
</span>
|
|
617
806
|
`,
|
|
618
807
|
)}
|
package/src/index.ts
CHANGED
|
@@ -16,6 +16,7 @@ export * from './state/persistence';
|
|
|
16
16
|
export * from './provider/ai-provider-switcher';
|
|
17
17
|
export * from './provider/assistant-app-settings';
|
|
18
18
|
export * from './utils/tool-fold';
|
|
19
|
+
export { assistantSettingsSectionTitleStyles } from './styles/settings-section';
|
|
19
20
|
export type { TurnFailureReason } from './state/debug-event-log';
|
|
20
21
|
export type { TimelineMessage } from './utils/flatten-sub-agent-messages';
|
|
21
22
|
export type { CostSessionModelEntry, CostSessionRecord } from './utils/cost-session-history';
|