@genesislcap/ai-assistant 14.444.1 → 14.445.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/dist/ai-assistant.api.json +312 -41
- package/dist/ai-assistant.d.ts +105 -8
- package/dist/dts/components/ai-driver/ai-driver.d.ts +7 -0
- package/dist/dts/components/ai-driver/ai-driver.d.ts.map +1 -1
- package/dist/dts/components/chat-driver/chat-driver.d.ts +37 -3
- package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -1
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts +5 -3
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts.map +1 -1
- package/dist/dts/config/config.d.ts +31 -0
- package/dist/dts/config/config.d.ts.map +1 -1
- package/dist/dts/config/define-stateful-agent.d.ts +9 -0
- package/dist/dts/config/define-stateful-agent.d.ts.map +1 -1
- package/dist/dts/config/validate-providers.d.ts +25 -0
- package/dist/dts/config/validate-providers.d.ts.map +1 -0
- package/dist/dts/config/validate-providers.test.d.ts +2 -0
- package/dist/dts/config/validate-providers.test.d.ts.map +1 -0
- package/dist/dts/main/main.d.ts +17 -5
- package/dist/dts/main/main.d.ts.map +1 -1
- package/dist/dts/main/main.styles.d.ts.map +1 -1
- package/dist/dts/main/main.template.d.ts.map +1 -1
- package/dist/dts/state/ai-assistant-slice.d.ts +14 -1
- package/dist/dts/state/ai-assistant-slice.d.ts.map +1 -1
- package/dist/dts/state/session-store.d.ts +2 -0
- package/dist/dts/state/session-store.d.ts.map +1 -1
- package/dist/dts/utils/sum-costs.d.ts +13 -0
- package/dist/dts/utils/sum-costs.d.ts.map +1 -0
- package/dist/dts/utils/sum-costs.test.d.ts +2 -0
- package/dist/dts/utils/sum-costs.test.d.ts.map +1 -0
- package/dist/esm/components/chat-driver/chat-driver.js +93 -15
- package/dist/esm/components/orchestrating-driver/orchestrating-driver.js +23 -4
- package/dist/esm/config/define-stateful-agent.js +12 -0
- package/dist/esm/config/validate-providers.js +47 -0
- package/dist/esm/config/validate-providers.test.js +100 -0
- package/dist/esm/main/main.js +76 -21
- package/dist/esm/main/main.styles.js +52 -0
- package/dist/esm/main/main.template.js +36 -1
- package/dist/esm/state/ai-assistant-slice.js +8 -0
- package/dist/esm/utils/sum-costs.js +23 -0
- package/dist/esm/utils/sum-costs.test.js +88 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/docs/migration-GENC-1262.md +219 -0
- package/package.json +16 -16
- package/src/components/ai-driver/ai-driver.ts +8 -0
- package/src/components/chat-driver/chat-driver.ts +107 -14
- package/src/components/orchestrating-driver/orchestrating-driver.ts +29 -4
- package/src/config/config.ts +32 -0
- package/src/config/define-stateful-agent.ts +28 -0
- package/src/config/validate-providers.test.ts +148 -0
- package/src/config/validate-providers.ts +58 -0
- package/src/main/main.styles.ts +52 -0
- package/src/main/main.template.ts +50 -2
- package/src/main/main.ts +69 -14
- package/src/state/ai-assistant-slice.ts +24 -1
- package/src/utils/sum-costs.test.ts +108 -0
- package/src/utils/sum-costs.ts +22 -0
package/dist/esm/main/main.js
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
// =============================================================================
|
|
23
23
|
var FoundationAiAssistant_1;
|
|
24
24
|
import { __awaiter, __decorate, __rest } from "tslib";
|
|
25
|
-
import {
|
|
25
|
+
import { AIProviderRegistry } from '@genesislcap/foundation-ai';
|
|
26
26
|
import { avoidTreeShaking } from '@genesislcap/foundation-utils';
|
|
27
27
|
import { customElement, html, GenesisElement, observable, volatile, attr, } from '@genesislcap/web-core';
|
|
28
28
|
import { agenticActivityBus } from '../channel/ai-activity-bus';
|
|
@@ -40,6 +40,7 @@ import { AI_COLOUR_AMBER, AI_COLOUR_CYAN, AI_COLOUR_PINK, AI_COLOUR_VIOLET, } fr
|
|
|
40
40
|
import { ChatSuggestions } from '../suggestions/chat-suggestions';
|
|
41
41
|
import { AnimatedPanelToggle } from '../utils/animated-panel-toggle';
|
|
42
42
|
import { logger } from '../utils/logger';
|
|
43
|
+
import { sumCosts } from '../utils/sum-costs';
|
|
43
44
|
import { expandToolTree } from '../utils/tool-fold';
|
|
44
45
|
import { styles } from './main.styles';
|
|
45
46
|
import { FoundationAiAssistantTemplate } from './main.template';
|
|
@@ -67,13 +68,13 @@ avoidTreeShaking(AiChatMarkdown, AiChatInteractionWrapper, AiHaloOverlay, AiChat
|
|
|
67
68
|
* - `toolHandlers` (functions),
|
|
68
69
|
* - `onActivate` / `onDeactivate` (lifecycle hooks, functions),
|
|
69
70
|
* - `getDebugSnapshot` (function),
|
|
70
|
-
* - function-form `systemPrompt` / `toolDefinitions` / `displayName`
|
|
71
|
-
* to `undefined` in the snapshot — the live config on the driver
|
|
72
|
-
* the source of truth; the slice only stores a serializable projection).
|
|
71
|
+
* - function-form `systemPrompt` / `toolDefinitions` / `displayName` / `provider`
|
|
72
|
+
* (downgraded to `undefined` in the snapshot — the live config on the driver
|
|
73
|
+
* is still the source of truth; the slice only stores a serializable projection).
|
|
73
74
|
*/
|
|
74
75
|
function stripHandlers(agent) {
|
|
75
|
-
const { toolHandlers: _h, onActivate: _on, onDeactivate: _off, getDebugSnapshot: _g, subAgents, systemPrompt, toolDefinitions, displayName } = agent, rest = __rest(agent, ["toolHandlers", "onActivate", "onDeactivate", "getDebugSnapshot", "subAgents", "systemPrompt", "toolDefinitions", "displayName"]);
|
|
76
|
-
const stripped = Object.assign(Object.assign({}, rest), { systemPrompt: typeof systemPrompt === 'function' ? undefined : systemPrompt, toolDefinitions: typeof toolDefinitions === 'function' ? undefined : toolDefinitions, displayName: typeof displayName === 'function' ? undefined : displayName });
|
|
76
|
+
const { toolHandlers: _h, onActivate: _on, onDeactivate: _off, getDebugSnapshot: _g, subAgents, systemPrompt, toolDefinitions, displayName, provider } = agent, rest = __rest(agent, ["toolHandlers", "onActivate", "onDeactivate", "getDebugSnapshot", "subAgents", "systemPrompt", "toolDefinitions", "displayName", "provider"]);
|
|
77
|
+
const stripped = Object.assign(Object.assign({}, rest), { systemPrompt: typeof systemPrompt === 'function' ? undefined : systemPrompt, toolDefinitions: typeof toolDefinitions === 'function' ? undefined : toolDefinitions, displayName: typeof displayName === 'function' ? undefined : displayName, provider: typeof provider === 'function' ? undefined : provider });
|
|
77
78
|
return (subAgents === null || subAgents === void 0 ? void 0 : subAgents.length)
|
|
78
79
|
? Object.assign(Object.assign({}, stripped), { subAgents: subAgents.map(stripHandlers) }) : stripped;
|
|
79
80
|
}
|
|
@@ -81,8 +82,11 @@ function stripHandlers(agent) {
|
|
|
81
82
|
* Foundation AI Assistant component.
|
|
82
83
|
*
|
|
83
84
|
* @remarks
|
|
84
|
-
*
|
|
85
|
-
*
|
|
85
|
+
* Register one or more AI providers via `registerAIProviders` from
|
|
86
|
+
* `@genesislcap/foundation-ai`; this element resolves the `AIProviderRegistry`
|
|
87
|
+
* through the DI container. Pass agent configuration via the `agents` property —
|
|
88
|
+
* each agent can override which registered provider it uses via `provider:`.
|
|
89
|
+
* The component creates a `ChatDriver` (single agent) or `OrchestratingDriver`
|
|
86
90
|
* (multiple agents) to manage the conversation loop.
|
|
87
91
|
*
|
|
88
92
|
* Popout/collapse coordination uses `agenticActivityBus` topics `chat-popout` and `chat-popin` — not DOM `CustomEvent`s on this element.
|
|
@@ -422,6 +426,24 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
422
426
|
var _a;
|
|
423
427
|
(_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.actions.aiAssistant.setActiveModel(value);
|
|
424
428
|
}
|
|
429
|
+
/** Name of the AI provider used on the most recent turn. */
|
|
430
|
+
get activeProviderName() {
|
|
431
|
+
var _a;
|
|
432
|
+
return (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.store.aiAssistant.activeProviderName;
|
|
433
|
+
}
|
|
434
|
+
set activeProviderName(value) {
|
|
435
|
+
var _a;
|
|
436
|
+
(_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.actions.aiAssistant.setActiveProviderName(value);
|
|
437
|
+
}
|
|
438
|
+
/** Per-provider status snapshot used by the settings panel. */
|
|
439
|
+
get providerStatuses() {
|
|
440
|
+
var _a, _b;
|
|
441
|
+
return (_b = (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.store.aiAssistant.providerStatuses) !== null && _b !== void 0 ? _b : [];
|
|
442
|
+
}
|
|
443
|
+
set providerStatuses(value) {
|
|
444
|
+
var _a;
|
|
445
|
+
(_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.actions.aiAssistant.setProviderStatuses(value);
|
|
446
|
+
}
|
|
425
447
|
get inputValue() {
|
|
426
448
|
var _a, _b;
|
|
427
449
|
return (_b = (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.store.aiAssistant.inputValue) !== null && _b !== void 0 ? _b : '';
|
|
@@ -627,7 +649,7 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
627
649
|
// this via `agent-changed` once applyAgent fires on first sendMessage.
|
|
628
650
|
this.activeAgent = agents[0];
|
|
629
651
|
}
|
|
630
|
-
return new OrchestratingDriver(this.
|
|
652
|
+
return new OrchestratingDriver(this.providerRegistry, agents, {
|
|
631
653
|
sessionKey: (_b = this.getStateKey()) !== null && _b !== void 0 ? _b : '',
|
|
632
654
|
maxHandoffs: agent.maxHandoffs,
|
|
633
655
|
classifierHistoryLength: agent.classifierHistoryLength,
|
|
@@ -637,7 +659,7 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
637
659
|
maxTurnSnapshots: agent.maxTurnSnapshots,
|
|
638
660
|
});
|
|
639
661
|
}
|
|
640
|
-
return new ChatDriver(this.
|
|
662
|
+
return new ChatDriver(this.providerRegistry, {}, [], undefined, undefined, agent.maxToolIterations, agent.maxFoldOperations, agent.maxTurnSnapshots);
|
|
641
663
|
}
|
|
642
664
|
/**
|
|
643
665
|
* Attaches event listeners to the current driver. Stores a cleanup function
|
|
@@ -672,13 +694,22 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
672
694
|
});
|
|
673
695
|
};
|
|
674
696
|
const onSubAgentStop = (e) => {
|
|
675
|
-
var _a;
|
|
697
|
+
var _a, _b;
|
|
676
698
|
this.liveSubAgentTrace = [];
|
|
677
699
|
this.liveSubAgentName = null;
|
|
678
700
|
const { invocationId } = e.detail;
|
|
679
701
|
if (invocationId) {
|
|
680
702
|
(_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.actions.aiAssistant.removeInputOverride({ id: invocationId });
|
|
681
703
|
}
|
|
704
|
+
// Sub-agents may have flipped the displayed provider while running;
|
|
705
|
+
// restore the parent agent's provider so the cog reflects the
|
|
706
|
+
// conversational driver again. Status (model / context-limit) is
|
|
707
|
+
// refreshed alongside so they don't disagree.
|
|
708
|
+
const restored = (_b = this.driver) === null || _b === void 0 ? void 0 : _b.getActiveProviderName();
|
|
709
|
+
if (restored && restored !== this.activeProviderName) {
|
|
710
|
+
this.activeProviderName = restored;
|
|
711
|
+
void this.resolveContextLimit();
|
|
712
|
+
}
|
|
682
713
|
};
|
|
683
714
|
const onInteractionStart = (e) => {
|
|
684
715
|
var _a;
|
|
@@ -697,11 +728,19 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
697
728
|
(_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.actions.aiAssistant.removeInputOverride({ id: interactionId });
|
|
698
729
|
}
|
|
699
730
|
};
|
|
731
|
+
const onProviderChanged = (e) => {
|
|
732
|
+
const { name } = e.detail;
|
|
733
|
+
this.activeProviderName = name;
|
|
734
|
+
// Status (model id / context limit) belongs to the active provider — refresh
|
|
735
|
+
// the displayed values so the header reflects the provider that just took over.
|
|
736
|
+
void this.resolveContextLimit();
|
|
737
|
+
};
|
|
700
738
|
driver.addEventListener('sub-agent-history-updated', onSubAgentHistoryUpdated);
|
|
701
739
|
driver.addEventListener('sub-agent-start', onSubAgentStart);
|
|
702
740
|
driver.addEventListener('sub-agent-stop', onSubAgentStop);
|
|
703
741
|
driver.addEventListener('interaction-start', onInteractionStart);
|
|
704
742
|
driver.addEventListener('interaction-stop', onInteractionStop);
|
|
743
|
+
driver.addEventListener('provider-changed', onProviderChanged);
|
|
705
744
|
const cleanups = [
|
|
706
745
|
() => driver.removeEventListener('history-updated', onHistoryUpdated),
|
|
707
746
|
() => driver.removeEventListener('sub-agent-history-updated', onSubAgentHistoryUpdated),
|
|
@@ -709,6 +748,7 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
709
748
|
() => driver.removeEventListener('sub-agent-stop', onSubAgentStop),
|
|
710
749
|
() => driver.removeEventListener('interaction-start', onInteractionStart),
|
|
711
750
|
() => driver.removeEventListener('interaction-stop', onInteractionStop),
|
|
751
|
+
() => driver.removeEventListener('provider-changed', onProviderChanged),
|
|
712
752
|
];
|
|
713
753
|
if (driver instanceof OrchestratingDriver) {
|
|
714
754
|
// Restore the user pin and flow-owner lock from the session store onto
|
|
@@ -837,6 +877,7 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
837
877
|
}
|
|
838
878
|
this.fetchSuggestions();
|
|
839
879
|
void this.resolveContextLimit();
|
|
880
|
+
void this.loadProviderStatuses();
|
|
840
881
|
if (this.messagesEl) {
|
|
841
882
|
this._scrollListener = () => {
|
|
842
883
|
this._userScrolledAway =
|
|
@@ -885,17 +926,33 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
885
926
|
}
|
|
886
927
|
resolveContextLimit() {
|
|
887
928
|
return __awaiter(this, void 0, void 0, function* () {
|
|
888
|
-
var _a
|
|
929
|
+
var _a;
|
|
889
930
|
try {
|
|
890
|
-
|
|
931
|
+
// Resolve status for the currently active provider when a driver is up
|
|
932
|
+
// (so per-agent provider swaps update the displayed model/limit); fall
|
|
933
|
+
// back to the registry default at startup before any turn has run.
|
|
934
|
+
const activeName = (_a = this.driver) === null || _a === void 0 ? void 0 : _a.getActiveProviderName();
|
|
935
|
+
const status = activeName
|
|
936
|
+
? yield this.providerRegistry.getStatus(activeName)
|
|
937
|
+
: yield this.providerRegistry.getStatus();
|
|
891
938
|
this.contextLimit = status === null || status === void 0 ? void 0 : status.contextLimit;
|
|
892
939
|
this.activeModel = status === null || status === void 0 ? void 0 : status.model;
|
|
893
940
|
}
|
|
894
|
-
catch (
|
|
941
|
+
catch (_b) {
|
|
895
942
|
// Non-fatal — context limit / model display simply won't show
|
|
896
943
|
}
|
|
897
944
|
});
|
|
898
945
|
}
|
|
946
|
+
loadProviderStatuses() {
|
|
947
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
948
|
+
try {
|
|
949
|
+
this.providerStatuses = yield this.providerRegistry.listStatuses();
|
|
950
|
+
}
|
|
951
|
+
catch (_a) {
|
|
952
|
+
// Non-fatal — settings panel just won't list providers
|
|
953
|
+
}
|
|
954
|
+
});
|
|
955
|
+
}
|
|
899
956
|
chatConfigChanged() {
|
|
900
957
|
this.syncShowingSplash();
|
|
901
958
|
}
|
|
@@ -945,11 +1002,9 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
945
1002
|
// Recompute aggregated session cost from per-message `cost` fields. Recomputing
|
|
946
1003
|
// (rather than incrementing on append) keeps the total correct under any
|
|
947
1004
|
// mutation of the message list — including future clear-chat / re-render flows.
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
runningCost += m.cost;
|
|
952
|
-
}
|
|
1005
|
+
// `sumCosts` recurses into `toolCall.subAgentTrace` so cost incurred by
|
|
1006
|
+
// sub-agents (potentially on different providers) is included.
|
|
1007
|
+
const runningCost = sumCosts(this.messages);
|
|
953
1008
|
if (runningCost !== this.sessionCostUsd) {
|
|
954
1009
|
this.sessionCostUsd = runningCost;
|
|
955
1010
|
}
|
|
@@ -1493,8 +1548,8 @@ FoundationAiAssistant.DEFAULT_LOADING_DELAY_S = 5;
|
|
|
1493
1548
|
FoundationAiAssistant.DEFAULT_SUGGESTION_COUNT = 3;
|
|
1494
1549
|
FoundationAiAssistant.MS_PER_SECOND = 1000;
|
|
1495
1550
|
__decorate([
|
|
1496
|
-
|
|
1497
|
-
], FoundationAiAssistant.prototype, "
|
|
1551
|
+
AIProviderRegistry
|
|
1552
|
+
], FoundationAiAssistant.prototype, "providerRegistry", void 0);
|
|
1498
1553
|
__decorate([
|
|
1499
1554
|
observable
|
|
1500
1555
|
], FoundationAiAssistant.prototype, "designSystemPrefix", void 0);
|
|
@@ -251,6 +251,58 @@ export const styles = css `
|
|
|
251
251
|
font-family: var(--monospace-font, ui-monospace, monospace);
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
+
.provider-list {
|
|
255
|
+
animation: settings-slide-in 0.2s ease-out;
|
|
256
|
+
padding: calc(var(--design-unit) * 1.5px) calc(var(--design-unit) * 3px);
|
|
257
|
+
border-bottom: 1px solid var(--neutral-stroke-rest);
|
|
258
|
+
background-color: var(--neutral-layer-2);
|
|
259
|
+
display: flex;
|
|
260
|
+
flex-direction: column;
|
|
261
|
+
gap: calc(var(--design-unit) * 1px);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.provider-list-label {
|
|
265
|
+
font-size: 0.85em;
|
|
266
|
+
font-weight: 600;
|
|
267
|
+
color: var(--neutral-foreground-rest);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.provider-list-row {
|
|
271
|
+
display: flex;
|
|
272
|
+
align-items: baseline;
|
|
273
|
+
gap: calc(var(--design-unit) * 1.5px);
|
|
274
|
+
font-size: 0.8em;
|
|
275
|
+
color: var(--neutral-foreground-hint, var(--neutral-foreground-rest));
|
|
276
|
+
opacity: 80%;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.provider-list-row.is-current {
|
|
280
|
+
opacity: 100%;
|
|
281
|
+
color: var(--neutral-foreground-rest);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.provider-list-name {
|
|
285
|
+
font-weight: 600;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.provider-list-model {
|
|
289
|
+
font-family: var(--monospace-font, ui-monospace, monospace);
|
|
290
|
+
flex: 1;
|
|
291
|
+
overflow: hidden;
|
|
292
|
+
text-overflow: ellipsis;
|
|
293
|
+
white-space: nowrap;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.provider-list-badge {
|
|
297
|
+
font-size: 0.7em;
|
|
298
|
+
text-transform: uppercase;
|
|
299
|
+
letter-spacing: 0.05em;
|
|
300
|
+
padding: 0 calc(var(--design-unit) * 0.75px);
|
|
301
|
+
border-radius: calc(var(--design-unit) * 0.5px);
|
|
302
|
+
background-color: var(--neutral-layer-3);
|
|
303
|
+
color: var(--neutral-foreground-rest);
|
|
304
|
+
}
|
|
305
|
+
|
|
254
306
|
.splash-wrapper {
|
|
255
307
|
position: absolute;
|
|
256
308
|
inset: 0;
|
|
@@ -376,12 +376,47 @@ ${(tc) => { var _a; return (((_a = tc.foldPath) === null || _a === void 0 ? void
|
|
|
376
376
|
></${progressTag}>
|
|
377
377
|
</div>
|
|
378
378
|
`)}
|
|
379
|
-
${when((x) => {
|
|
379
|
+
${when((x) => {
|
|
380
|
+
var _a;
|
|
381
|
+
return x.settingsOpen &&
|
|
382
|
+
((_a = x.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.showActiveModel) !== false &&
|
|
383
|
+
x.providerStatuses.length <= 1 &&
|
|
384
|
+
x.activeModel != null;
|
|
385
|
+
}, html `
|
|
380
386
|
<div class="session-stat" part="active-model">
|
|
381
387
|
<span class="session-stat-label">Model</span>
|
|
382
388
|
<span class="session-stat-value">${(x) => x.activeModel}</span>
|
|
383
389
|
</div>
|
|
384
390
|
`)}
|
|
391
|
+
${when((x) => {
|
|
392
|
+
var _a;
|
|
393
|
+
return x.settingsOpen &&
|
|
394
|
+
((_a = x.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.showActiveModel) !== false &&
|
|
395
|
+
x.providerStatuses.length > 1;
|
|
396
|
+
}, html `
|
|
397
|
+
<div class="provider-list" part="provider-list">
|
|
398
|
+
<div class="provider-list-label">Providers</div>
|
|
399
|
+
${repeat((x) => x.providerStatuses, html `
|
|
400
|
+
<div
|
|
401
|
+
class="provider-list-row ${(entry, c) => entry.name === c.parent.activeProviderName ? 'is-current' : ''}"
|
|
402
|
+
part="provider-list-row"
|
|
403
|
+
>
|
|
404
|
+
<span class="provider-list-name">${(entry) => entry.name}</span>
|
|
405
|
+
<span class="provider-list-model">${(entry) => { var _a, _b; return (_b = (_a = entry.status) === null || _a === void 0 ? void 0 : _a.model) !== null && _b !== void 0 ? _b : '—'; }}</span>
|
|
406
|
+
${when((entry) => entry.isDefault, html `
|
|
407
|
+
<span class="provider-list-badge" part="provider-list-default-badge">
|
|
408
|
+
default
|
|
409
|
+
</span>
|
|
410
|
+
`)}
|
|
411
|
+
${when((entry, c) => entry.name === c.parent.activeProviderName, html `
|
|
412
|
+
<span class="provider-list-badge" part="provider-list-current-badge">
|
|
413
|
+
current
|
|
414
|
+
</span>
|
|
415
|
+
`)}
|
|
416
|
+
</div>
|
|
417
|
+
`)}
|
|
418
|
+
</div>
|
|
419
|
+
`)}
|
|
385
420
|
${when((x) => { var _a; return x.settingsOpen && ((_a = x.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.showSessionCost) !== false && x.sessionCostUsd > 0; }, html `
|
|
386
421
|
<div class="session-stat" part="session-cost">
|
|
387
422
|
<span class="session-stat-label">Session cost</span>
|
|
@@ -11,6 +11,8 @@ export const defaultSessionState = {
|
|
|
11
11
|
contextLimit: undefined,
|
|
12
12
|
sessionCostUsd: 0,
|
|
13
13
|
activeModel: undefined,
|
|
14
|
+
activeProviderName: undefined,
|
|
15
|
+
providerStatuses: [],
|
|
14
16
|
activeAgent: undefined,
|
|
15
17
|
pinnedAgentName: null,
|
|
16
18
|
flowOwnerAgentName: null,
|
|
@@ -57,6 +59,12 @@ export const aiAssistantSlice = createSlice({
|
|
|
57
59
|
setActiveModel(state, action) {
|
|
58
60
|
state.activeModel = action.payload;
|
|
59
61
|
},
|
|
62
|
+
setActiveProviderName(state, action) {
|
|
63
|
+
state.activeProviderName = action.payload;
|
|
64
|
+
},
|
|
65
|
+
setProviderStatuses(state, action) {
|
|
66
|
+
state.providerStatuses = action.payload;
|
|
67
|
+
},
|
|
60
68
|
setActiveAgent(state, action) {
|
|
61
69
|
state.activeAgent = action.payload;
|
|
62
70
|
},
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sum the `cost` field across a message list, recursing into each tool call's
|
|
3
|
+
* `subAgentTrace` so LLM calls made by sub-agents (potentially on a different
|
|
4
|
+
* provider with different rates) contribute to the total.
|
|
5
|
+
*
|
|
6
|
+
* Returns 0 when no messages carry a cost — providers that don't report usage
|
|
7
|
+
* metadata (e.g. Chrome built-in) are skipped silently.
|
|
8
|
+
*
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
export function sumCosts(messages) {
|
|
12
|
+
var _a;
|
|
13
|
+
let total = 0;
|
|
14
|
+
for (const m of messages) {
|
|
15
|
+
if (m.cost != null)
|
|
16
|
+
total += m.cost;
|
|
17
|
+
for (const tc of (_a = m.toolCalls) !== null && _a !== void 0 ? _a : []) {
|
|
18
|
+
if (tc.subAgentTrace)
|
|
19
|
+
total += sumCosts(tc.subAgentTrace);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return total;
|
|
23
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { assert, createLogicSuite } from '@genesislcap/foundation-testing';
|
|
2
|
+
import { sumCosts } from './sum-costs';
|
|
3
|
+
const userMsg = (overrides = {}) => (Object.assign({ role: 'user', content: '' }, overrides));
|
|
4
|
+
const assistantMsg = (overrides = {}) => (Object.assign({ role: 'assistant', content: '' }, overrides));
|
|
5
|
+
const suite = createLogicSuite('sumCosts');
|
|
6
|
+
suite('returns 0 for an empty message list', () => {
|
|
7
|
+
assert.is(sumCosts([]), 0);
|
|
8
|
+
});
|
|
9
|
+
suite('returns 0 when no message carries cost (e.g. all from a no-usage provider)', () => {
|
|
10
|
+
assert.is(sumCosts([userMsg(), assistantMsg({ content: 'hi' })]), 0);
|
|
11
|
+
});
|
|
12
|
+
suite('sums top-level message costs', () => {
|
|
13
|
+
// Use integer-cents arithmetic to avoid float-equality flake; the production
|
|
14
|
+
// path only ever adds floats here, so any rounding lives in display, not sum.
|
|
15
|
+
const cost = sumCosts([
|
|
16
|
+
assistantMsg({ cost: 0.5 }),
|
|
17
|
+
assistantMsg({ cost: 1.5 }),
|
|
18
|
+
userMsg(), // no cost — ignored
|
|
19
|
+
]);
|
|
20
|
+
assert.is(cost, 2);
|
|
21
|
+
});
|
|
22
|
+
suite('includes sub-agent trace costs attached to a tool call', () => {
|
|
23
|
+
const parentToolCallMsg = assistantMsg({
|
|
24
|
+
cost: 0.1,
|
|
25
|
+
toolCalls: [
|
|
26
|
+
{
|
|
27
|
+
id: 'tc1',
|
|
28
|
+
name: 'spawn_sub',
|
|
29
|
+
args: {},
|
|
30
|
+
subAgentTrace: [assistantMsg({ cost: 0.5 }), assistantMsg({ cost: 0.4 })],
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
});
|
|
34
|
+
// 0.1 (parent) + 0.5 + 0.4 (sub) = 1.0
|
|
35
|
+
assert.is(sumCosts([parentToolCallMsg]), 1);
|
|
36
|
+
});
|
|
37
|
+
suite('recurses into nested sub-agent traces', () => {
|
|
38
|
+
const innerTrace = [assistantMsg({ cost: 0.25 })];
|
|
39
|
+
const middleTrace = [
|
|
40
|
+
assistantMsg({
|
|
41
|
+
cost: 0.5,
|
|
42
|
+
toolCalls: [{ id: 'inner', name: 'spawn_inner', args: {}, subAgentTrace: innerTrace }],
|
|
43
|
+
}),
|
|
44
|
+
];
|
|
45
|
+
const top = assistantMsg({
|
|
46
|
+
cost: 1,
|
|
47
|
+
toolCalls: [{ id: 'middle', name: 'spawn_middle', args: {}, subAgentTrace: middleTrace }],
|
|
48
|
+
});
|
|
49
|
+
// 1 + 0.5 + 0.25 = 1.75
|
|
50
|
+
assert.is(sumCosts([top]), 1.75);
|
|
51
|
+
});
|
|
52
|
+
suite('handles a mix of tool calls with and without subAgentTrace', () => {
|
|
53
|
+
const cost = sumCosts([
|
|
54
|
+
assistantMsg({
|
|
55
|
+
cost: 1,
|
|
56
|
+
toolCalls: [
|
|
57
|
+
{ id: 'a', name: 'plain_tool', args: {} }, // no trace — skipped
|
|
58
|
+
{
|
|
59
|
+
id: 'b',
|
|
60
|
+
name: 'sub_tool',
|
|
61
|
+
args: {},
|
|
62
|
+
subAgentTrace: [assistantMsg({ cost: 2 })],
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
}),
|
|
66
|
+
]);
|
|
67
|
+
assert.is(cost, 3);
|
|
68
|
+
});
|
|
69
|
+
suite('ignores missing cost fields inside an otherwise-populated trace', () => {
|
|
70
|
+
const cost = sumCosts([
|
|
71
|
+
assistantMsg({
|
|
72
|
+
toolCalls: [
|
|
73
|
+
{
|
|
74
|
+
id: 'tc',
|
|
75
|
+
name: 'sub',
|
|
76
|
+
args: {},
|
|
77
|
+
subAgentTrace: [
|
|
78
|
+
userMsg(), // role=user, no cost
|
|
79
|
+
assistantMsg({ content: 'partial', cost: 0.3 }),
|
|
80
|
+
assistantMsg({ content: 'unbilled' }), // no cost field
|
|
81
|
+
],
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
}),
|
|
85
|
+
]);
|
|
86
|
+
assert.is(cost, 0.3);
|
|
87
|
+
});
|
|
88
|
+
suite.run();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../src/index.ts","../src/channel/ai-activity-bus.ts","../src/channel/ai-activity-channel.ts","../src/components/halo-overlay.ts","../src/components/activity-halo/activity-halo.ts","../src/components/agent-picker/agent-picker.constants.ts","../src/components/agent-picker/agent-picker.styles.ts","../src/components/agent-picker/agent-picker.template.ts","../src/components/agent-picker/agent-picker.ts","../src/components/agent-picker/index.ts","../src/components/ai-driver/ai-driver.ts","../src/components/ai-driver/index.ts","../src/components/chat-bubble/chat-bubble.styles.ts","../src/components/chat-bubble/chat-bubble.template.ts","../src/components/chat-bubble/chat-bubble.ts","../src/components/chat-bubble/index.ts","../src/components/chat-driver/chat-driver.ts","../src/components/chat-driver/index.ts","../src/components/chat-interaction-wrapper/chat-interaction-wrapper.styles.ts","../src/components/chat-interaction-wrapper/chat-interaction-wrapper.template.ts","../src/components/chat-interaction-wrapper/chat-interaction-wrapper.ts","../src/components/chat-interaction-wrapper/index.ts","../src/components/chat-markdown/chat-markdown.ts","../src/components/chat-markdown/index.ts","../src/components/orchestrating-driver/index.ts","../src/components/orchestrating-driver/orchestrating-driver.ts","../src/components/popout-manager/index.ts","../src/components/popout-manager/popout-manager.ts","../src/config/config.ts","../src/config/define-stateful-agent.ts","../src/config/fallback-agents.ts","../src/config/index.ts","../src/main/index.ts","../src/main/main.styles.ts","../src/main/main.template.ts","../src/main/main.ts","../src/main/main.types.ts","../src/state/ai-assistant-slice.ts","../src/state/driver-registry.ts","../src/state/session-store.ts","../src/styles/ai-colours.ts","../src/styles/index.ts","../src/styles/styles.ts","../src/suggestions/chat-suggestions.ts","../src/tags/index.ts","../src/types/ai-chat-widget.ts","../src/utils/animated-panel-toggle.ts","../src/utils/history-transform.ts","../src/utils/index.ts","../src/utils/logger.ts","../src/utils/tool-fold.ts"],"version":"5.9.2"}
|
|
1
|
+
{"root":["../src/index.ts","../src/channel/ai-activity-bus.ts","../src/channel/ai-activity-channel.ts","../src/components/halo-overlay.ts","../src/components/activity-halo/activity-halo.ts","../src/components/agent-picker/agent-picker.constants.ts","../src/components/agent-picker/agent-picker.styles.ts","../src/components/agent-picker/agent-picker.template.ts","../src/components/agent-picker/agent-picker.ts","../src/components/agent-picker/index.ts","../src/components/ai-driver/ai-driver.ts","../src/components/ai-driver/index.ts","../src/components/chat-bubble/chat-bubble.styles.ts","../src/components/chat-bubble/chat-bubble.template.ts","../src/components/chat-bubble/chat-bubble.ts","../src/components/chat-bubble/index.ts","../src/components/chat-driver/chat-driver.ts","../src/components/chat-driver/index.ts","../src/components/chat-interaction-wrapper/chat-interaction-wrapper.styles.ts","../src/components/chat-interaction-wrapper/chat-interaction-wrapper.template.ts","../src/components/chat-interaction-wrapper/chat-interaction-wrapper.ts","../src/components/chat-interaction-wrapper/index.ts","../src/components/chat-markdown/chat-markdown.ts","../src/components/chat-markdown/index.ts","../src/components/orchestrating-driver/index.ts","../src/components/orchestrating-driver/orchestrating-driver.ts","../src/components/popout-manager/index.ts","../src/components/popout-manager/popout-manager.ts","../src/config/config.ts","../src/config/define-stateful-agent.ts","../src/config/fallback-agents.ts","../src/config/index.ts","../src/config/validate-providers.test.ts","../src/config/validate-providers.ts","../src/main/index.ts","../src/main/main.styles.ts","../src/main/main.template.ts","../src/main/main.ts","../src/main/main.types.ts","../src/state/ai-assistant-slice.ts","../src/state/driver-registry.ts","../src/state/session-store.ts","../src/styles/ai-colours.ts","../src/styles/index.ts","../src/styles/styles.ts","../src/suggestions/chat-suggestions.ts","../src/tags/index.ts","../src/types/ai-chat-widget.ts","../src/utils/animated-panel-toggle.ts","../src/utils/history-transform.ts","../src/utils/index.ts","../src/utils/logger.ts","../src/utils/sum-costs.test.ts","../src/utils/sum-costs.ts","../src/utils/tool-fold.ts"],"version":"5.9.2"}
|