@genesislcap/ai-assistant 15.19.0 → 15.19.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/package.json +17 -18
- package/api-extractor.json +0 -11
- package/dist/dts/components/chat-driver/chat-driver.compact.test.d.ts +0 -2
- package/dist/dts/components/chat-driver/chat-driver.compact.test.d.ts.map +0 -1
- package/dist/dts/components/chat-driver/chat-driver.invocation-scope.test.d.ts +0 -2
- package/dist/dts/components/chat-driver/chat-driver.invocation-scope.test.d.ts.map +0 -1
- package/dist/dts/components/chat-driver/chat-driver.test.d.ts +0 -2
- package/dist/dts/components/chat-driver/chat-driver.test.d.ts.map +0 -1
- package/dist/dts/components/chat-driver/chat-driver.thinking-policy.test.d.ts +0 -2
- package/dist/dts/components/chat-driver/chat-driver.thinking-policy.test.d.ts.map +0 -1
- package/dist/dts/components/chat-driver/chat-driver.trace-capture.test.d.ts +0 -2
- package/dist/dts/components/chat-driver/chat-driver.trace-capture.test.d.ts.map +0 -1
- package/dist/dts/components/chat-driver/chat-driver.turn-usage.test.d.ts +0 -2
- package/dist/dts/components/chat-driver/chat-driver.turn-usage.test.d.ts.map +0 -1
- package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.test.d.ts +0 -2
- package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.test.d.ts.map +0 -1
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.budget.test.d.ts +0 -2
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.budget.test.d.ts.map +0 -1
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.cost.test.d.ts +0 -2
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.cost.test.d.ts.map +0 -1
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.pin.test.d.ts +0 -2
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.pin.test.d.ts.map +0 -1
- package/dist/dts/components/settings-modal/settings-modal.styles.test.d.ts +0 -2
- package/dist/dts/components/settings-modal/settings-modal.styles.test.d.ts.map +0 -1
- package/dist/dts/components/settings-modal/settings-modal.template.test.d.ts +0 -2
- package/dist/dts/components/settings-modal/settings-modal.template.test.d.ts.map +0 -1
- package/dist/dts/config/define-stateful-agent.test.d.ts +0 -2
- package/dist/dts/config/define-stateful-agent.test.d.ts.map +0 -1
- package/dist/dts/config/validate-providers.test.d.ts +0 -2
- package/dist/dts/config/validate-providers.test.d.ts.map +0 -1
- package/dist/dts/main/blocked-state.test.d.ts +0 -2
- package/dist/dts/main/blocked-state.test.d.ts.map +0 -1
- package/dist/dts/main/budget-meter.test.d.ts +0 -2
- package/dist/dts/main/budget-meter.test.d.ts.map +0 -1
- package/dist/dts/main/cost-session-banking.test.d.ts +0 -2
- package/dist/dts/main/cost-session-banking.test.d.ts.map +0 -1
- package/dist/dts/main/file-attachments.test.d.ts +0 -2
- package/dist/dts/main/file-attachments.test.d.ts.map +0 -1
- package/dist/dts/main/interaction-cost.test.d.ts +0 -2
- package/dist/dts/main/interaction-cost.test.d.ts.map +0 -1
- package/dist/dts/main/main.styles.test.d.ts +0 -2
- package/dist/dts/main/main.styles.test.d.ts.map +0 -1
- package/dist/dts/main/persistence-broken-sources.test.d.ts +0 -2
- package/dist/dts/main/persistence-broken-sources.test.d.ts.map +0 -1
- package/dist/dts/main/popout-interaction-gate.test.d.ts +0 -2
- package/dist/dts/main/popout-interaction-gate.test.d.ts.map +0 -1
- package/dist/dts/state/ai-assistant-slice.test.d.ts +0 -2
- package/dist/dts/state/ai-assistant-slice.test.d.ts.map +0 -1
- package/dist/dts/state/debug-event-log.test.d.ts +0 -2
- package/dist/dts/state/debug-event-log.test.d.ts.map +0 -1
- package/dist/dts/state/driver-registry.test.d.ts +0 -2
- package/dist/dts/state/driver-registry.test.d.ts.map +0 -1
- package/dist/dts/state/interaction-context.test.d.ts +0 -2
- package/dist/dts/state/interaction-context.test.d.ts.map +0 -1
- package/dist/dts/state/persistence/diagnostics-cursors.test.d.ts +0 -2
- package/dist/dts/state/persistence/diagnostics-cursors.test.d.ts.map +0 -1
- package/dist/dts/state/persistence/diagnostics.test.d.ts +0 -2
- package/dist/dts/state/persistence/diagnostics.test.d.ts.map +0 -1
- package/dist/dts/state/persistence/session-persistence-provider.test.d.ts +0 -2
- package/dist/dts/state/persistence/session-persistence-provider.test.d.ts.map +0 -1
- package/dist/dts/state/persistence/session-persistence.integration.test.d.ts +0 -2
- package/dist/dts/state/persistence/session-persistence.integration.test.d.ts.map +0 -1
- package/dist/dts/state/persistence/session-persister.test.d.ts +0 -2
- package/dist/dts/state/persistence/session-persister.test.d.ts.map +0 -1
- package/dist/dts/state/persistence/session-snapshot.test.d.ts +0 -2
- package/dist/dts/state/persistence/session-snapshot.test.d.ts.map +0 -1
- package/dist/dts/state/persistence/stateful-restore.e2e.test.d.ts +0 -2
- package/dist/dts/state/persistence/stateful-restore.e2e.test.d.ts.map +0 -1
- package/dist/dts/utils/animation-exclusivity.test.d.ts +0 -2
- package/dist/dts/utils/animation-exclusivity.test.d.ts.map +0 -1
- package/dist/dts/utils/collect-session-models.test.d.ts +0 -2
- package/dist/dts/utils/collect-session-models.test.d.ts.map +0 -1
- package/dist/dts/utils/condense-history.test.d.ts +0 -2
- package/dist/dts/utils/condense-history.test.d.ts.map +0 -1
- package/dist/dts/utils/cost-session-history.test.d.ts +0 -2
- package/dist/dts/utils/cost-session-history.test.d.ts.map +0 -1
- package/dist/dts/utils/derive-cost-session-title.test.d.ts +0 -2
- package/dist/dts/utils/derive-cost-session-title.test.d.ts.map +0 -1
- package/dist/dts/utils/flatten-sub-agent-messages.test.d.ts +0 -2
- package/dist/dts/utils/flatten-sub-agent-messages.test.d.ts.map +0 -1
- package/dist/dts/utils/history-transform.test.d.ts +0 -2
- package/dist/dts/utils/history-transform.test.d.ts.map +0 -1
- package/dist/dts/utils/message-partition.test.d.ts +0 -2
- package/dist/dts/utils/message-partition.test.d.ts.map +0 -1
- package/dist/dts/utils/resolve-cost-history-config.test.d.ts +0 -2
- package/dist/dts/utils/resolve-cost-history-config.test.d.ts.map +0 -1
- package/dist/dts/utils/resolve-preference-baseline.test.d.ts +0 -2
- package/dist/dts/utils/resolve-preference-baseline.test.d.ts.map +0 -1
- package/dist/dts/utils/strip-agent-handlers.test.d.ts +0 -2
- package/dist/dts/utils/strip-agent-handlers.test.d.ts.map +0 -1
- package/dist/dts/utils/sum-costs.test.d.ts +0 -2
- package/dist/dts/utils/sum-costs.test.d.ts.map +0 -1
- package/dist/dts/utils/sum-tokens.test.d.ts +0 -2
- package/dist/dts/utils/sum-tokens.test.d.ts.map +0 -1
- package/dist/dts/utils/sum-usage.test.d.ts +0 -2
- package/dist/dts/utils/sum-usage.test.d.ts.map +0 -1
- package/dist/dts/utils/usage-rows.test.d.ts +0 -2
- package/dist/dts/utils/usage-rows.test.d.ts.map +0 -1
- package/dist/esm/components/chat-driver/chat-driver.compact.test.js +0 -93
- package/dist/esm/components/chat-driver/chat-driver.invocation-scope.test.js +0 -238
- package/dist/esm/components/chat-driver/chat-driver.test.js +0 -2770
- package/dist/esm/components/chat-driver/chat-driver.thinking-policy.test.js +0 -137
- package/dist/esm/components/chat-driver/chat-driver.trace-capture.test.js +0 -200
- package/dist/esm/components/chat-driver/chat-driver.turn-usage.test.js +0 -268
- package/dist/esm/components/chat-interaction-wrapper/chat-interaction-wrapper.test.js +0 -104
- package/dist/esm/components/orchestrating-driver/orchestrating-driver.budget.test.js +0 -312
- package/dist/esm/components/orchestrating-driver/orchestrating-driver.cost.test.js +0 -76
- package/dist/esm/components/orchestrating-driver/orchestrating-driver.pin.test.js +0 -61
- package/dist/esm/components/settings-modal/settings-modal.styles.test.js +0 -80
- package/dist/esm/components/settings-modal/settings-modal.template.test.js +0 -91
- package/dist/esm/config/define-stateful-agent.test.js +0 -53
- package/dist/esm/config/validate-providers.test.js +0 -100
- package/dist/esm/main/blocked-state.test.js +0 -1065
- package/dist/esm/main/budget-meter.test.js +0 -359
- package/dist/esm/main/cost-session-banking.test.js +0 -538
- package/dist/esm/main/file-attachments.test.js +0 -164
- package/dist/esm/main/interaction-cost.test.js +0 -107
- package/dist/esm/main/main.styles.test.js +0 -161
- package/dist/esm/main/persistence-broken-sources.test.js +0 -180
- package/dist/esm/main/popout-interaction-gate.test.js +0 -54
- package/dist/esm/state/ai-assistant-slice.test.js +0 -229
- package/dist/esm/state/debug-event-log.test.js +0 -117
- package/dist/esm/state/driver-registry.test.js +0 -55
- package/dist/esm/state/interaction-context.test.js +0 -78
- package/dist/esm/state/persistence/diagnostics-cursors.test.js +0 -30
- package/dist/esm/state/persistence/diagnostics.test.js +0 -280
- package/dist/esm/state/persistence/session-persistence-provider.test.js +0 -110
- package/dist/esm/state/persistence/session-persistence.integration.test.js +0 -113
- package/dist/esm/state/persistence/session-persister.test.js +0 -613
- package/dist/esm/state/persistence/session-snapshot.test.js +0 -256
- package/dist/esm/state/persistence/stateful-restore.e2e.test.js +0 -158
- package/dist/esm/utils/animation-exclusivity.test.js +0 -52
- package/dist/esm/utils/collect-session-models.test.js +0 -53
- package/dist/esm/utils/condense-history.test.js +0 -547
- package/dist/esm/utils/cost-session-history.test.js +0 -174
- package/dist/esm/utils/derive-cost-session-title.test.js +0 -37
- package/dist/esm/utils/flatten-sub-agent-messages.test.js +0 -139
- package/dist/esm/utils/history-transform.test.js +0 -158
- package/dist/esm/utils/message-partition.test.js +0 -90
- package/dist/esm/utils/resolve-cost-history-config.test.js +0 -32
- package/dist/esm/utils/resolve-preference-baseline.test.js +0 -69
- package/dist/esm/utils/strip-agent-handlers.test.js +0 -81
- package/dist/esm/utils/sum-costs.test.js +0 -114
- package/dist/esm/utils/sum-tokens.test.js +0 -51
- package/dist/esm/utils/sum-usage.test.js +0 -164
- package/dist/esm/utils/usage-rows.test.js +0 -189
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/license.txt +0 -46
- package/sandbox/tsconfig.json +0 -8
- package/src/channel/ai-activity-bus.ts +0 -209
- package/src/channel/ai-activity-channel.ts +0 -146
- package/src/chat-driver-node.ts +0 -159
- package/src/components/activity-halo/activity-halo.ts +0 -106
- package/src/components/agent-picker/agent-picker.constants.ts +0 -2
- package/src/components/agent-picker/agent-picker.styles.ts +0 -24
- package/src/components/agent-picker/agent-picker.template.ts +0 -54
- package/src/components/agent-picker/agent-picker.ts +0 -99
- package/src/components/agent-picker/index.ts +0 -1
- package/src/components/ai-driver/ai-driver.ts +0 -166
- package/src/components/ai-driver/index.ts +0 -1
- package/src/components/chat-bubble/chat-bubble.styles.ts +0 -194
- package/src/components/chat-bubble/chat-bubble.template.ts +0 -67
- package/src/components/chat-bubble/chat-bubble.ts +0 -436
- package/src/components/chat-bubble/index.ts +0 -1
- package/src/components/chat-driver/align-event-globals.ts +0 -23
- package/src/components/chat-driver/chat-driver.compact.test.ts +0 -131
- package/src/components/chat-driver/chat-driver.invocation-scope.test.ts +0 -285
- package/src/components/chat-driver/chat-driver.test.ts +0 -3679
- package/src/components/chat-driver/chat-driver.thinking-policy.test.ts +0 -185
- package/src/components/chat-driver/chat-driver.trace-capture.test.ts +0 -251
- package/src/components/chat-driver/chat-driver.ts +0 -3925
- package/src/components/chat-driver/chat-driver.turn-usage.test.ts +0 -362
- package/src/components/chat-driver/index.ts +0 -1
- package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.styles.ts +0 -7
- package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.template.ts +0 -6
- package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.test.ts +0 -144
- package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.ts +0 -243
- package/src/components/chat-interaction-wrapper/index.ts +0 -3
- package/src/components/chat-markdown/chat-markdown.ts +0 -89
- package/src/components/chat-markdown/index.ts +0 -1
- package/src/components/flowing-waves-indicator.ts +0 -286
- package/src/components/halo-overlay.ts +0 -184
- package/src/components/orchestrating-driver/index.ts +0 -1
- package/src/components/orchestrating-driver/orchestrating-driver.budget.test.ts +0 -438
- package/src/components/orchestrating-driver/orchestrating-driver.cost.test.ts +0 -107
- package/src/components/orchestrating-driver/orchestrating-driver.pin.test.ts +0 -96
- package/src/components/orchestrating-driver/orchestrating-driver.ts +0 -813
- package/src/components/plasma-orb-indicator.ts +0 -281
- package/src/components/popout-manager/index.ts +0 -1
- package/src/components/popout-manager/popout-manager.ts +0 -212
- package/src/components/settings-modal/index.ts +0 -2
- package/src/components/settings-modal/settings-modal.styles.test.ts +0 -94
- package/src/components/settings-modal/settings-modal.styles.ts +0 -853
- package/src/components/settings-modal/settings-modal.template.test.ts +0 -124
- package/src/components/settings-modal/settings-modal.template.ts +0 -943
- package/src/components/waves-indicator.ts +0 -241
- package/src/config/config.ts +0 -608
- package/src/config/define-stateful-agent.test.ts +0 -64
- package/src/config/define-stateful-agent.ts +0 -642
- package/src/config/fallback-agents.ts +0 -29
- package/src/config/index.ts +0 -2
- package/src/config/validate-providers.test.ts +0 -148
- package/src/config/validate-providers.ts +0 -58
- package/src/index.ts +0 -29
- package/src/main/blocked-state.test.ts +0 -1444
- package/src/main/budget-meter.test.ts +0 -477
- package/src/main/cost-session-banking.test.ts +0 -679
- package/src/main/file-attachments.test.ts +0 -215
- package/src/main/index.ts +0 -3
- package/src/main/interaction-cost.test.ts +0 -140
- package/src/main/main.styles.test.ts +0 -216
- package/src/main/main.styles.ts +0 -995
- package/src/main/main.template.ts +0 -1119
- package/src/main/main.ts +0 -5432
- package/src/main/main.types.ts +0 -225
- package/src/main/persistence-broken-sources.test.ts +0 -219
- package/src/main/popout-interaction-gate.test.ts +0 -87
- package/src/provider/ai-provider-switcher.ts +0 -40
- package/src/provider/assistant-app-settings.ts +0 -84
- package/src/state/ai-assistant-slice.test.ts +0 -336
- package/src/state/ai-assistant-slice.ts +0 -604
- package/src/state/debug-event-log.test.ts +0 -154
- package/src/state/debug-event-log.ts +0 -358
- package/src/state/driver-registry.test.ts +0 -70
- package/src/state/driver-registry.ts +0 -79
- package/src/state/interaction-context.test.ts +0 -90
- package/src/state/interaction-context.ts +0 -101
- package/src/state/persistence/build-timeline-entries.ts +0 -66
- package/src/state/persistence/diagnostics-cursors.test.ts +0 -53
- package/src/state/persistence/diagnostics-cursors.ts +0 -80
- package/src/state/persistence/diagnostics.test.ts +0 -346
- package/src/state/persistence/diagnostics.ts +0 -220
- package/src/state/persistence/index.ts +0 -10
- package/src/state/persistence/persister-registry.ts +0 -55
- package/src/state/persistence/session-persistence-provider.test.ts +0 -136
- package/src/state/persistence/session-persistence-provider.ts +0 -226
- package/src/state/persistence/session-persistence.integration.test.ts +0 -141
- package/src/state/persistence/session-persister.test.ts +0 -787
- package/src/state/persistence/session-persister.ts +0 -917
- package/src/state/persistence/session-snapshot.test.ts +0 -352
- package/src/state/persistence/session-snapshot.ts +0 -344
- package/src/state/persistence/stateful-restore.e2e.test.ts +0 -229
- package/src/state/session-store.ts +0 -56
- package/src/styles/ai-colours.ts +0 -30
- package/src/styles/menu.ts +0 -187
- package/src/styles/settings-section.ts +0 -40
- package/src/suggestions/chat-suggestions.ts +0 -186
- package/src/tags/index.ts +0 -1
- package/src/types/ai-chat-widget.ts +0 -67
- package/src/types/interaction-context.ts +0 -82
- package/src/utils/animated-panel-toggle.ts +0 -62
- package/src/utils/animation-exclusivity.test.ts +0 -72
- package/src/utils/animation-exclusivity.ts +0 -40
- package/src/utils/banked-usage-baselines.ts +0 -90
- package/src/utils/collect-session-models.test.ts +0 -82
- package/src/utils/collect-session-models.ts +0 -65
- package/src/utils/condense-history.test.ts +0 -746
- package/src/utils/condense-history.ts +0 -448
- package/src/utils/cost-session-history.test.ts +0 -226
- package/src/utils/cost-session-history.ts +0 -218
- package/src/utils/derive-cost-session-title.test.ts +0 -63
- package/src/utils/derive-cost-session-title.ts +0 -76
- package/src/utils/flatten-sub-agent-messages.test.ts +0 -163
- package/src/utils/flatten-sub-agent-messages.ts +0 -88
- package/src/utils/format-usd.ts +0 -26
- package/src/utils/history-transform.test.ts +0 -228
- package/src/utils/history-transform.ts +0 -163
- package/src/utils/index.ts +0 -3
- package/src/utils/logger.ts +0 -3
- package/src/utils/message-partition.test.ts +0 -129
- package/src/utils/message-partition.ts +0 -115
- package/src/utils/resolve-cost-history-config.test.ts +0 -38
- package/src/utils/resolve-cost-history-config.ts +0 -38
- package/src/utils/resolve-preference-baseline.test.ts +0 -84
- package/src/utils/resolve-preference-baseline.ts +0 -80
- package/src/utils/strip-agent-handlers.test.ts +0 -99
- package/src/utils/strip-agent-handlers.ts +0 -53
- package/src/utils/sum-costs.test.ts +0 -136
- package/src/utils/sum-costs.ts +0 -18
- package/src/utils/sum-tokens.test.ts +0 -93
- package/src/utils/sum-tokens.ts +0 -68
- package/src/utils/sum-usage.test.ts +0 -191
- package/src/utils/sum-usage.ts +0 -208
- package/src/utils/tool-fold.ts +0 -181
- package/src/utils/usage-rows.test.ts +0 -237
- package/src/utils/usage-rows.ts +0 -187
- package/src/utils/with-timeout.ts +0 -24
- package/tsconfig.json +0 -9
|
@@ -1,604 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
AggregateUsage,
|
|
3
|
-
AIProviderRegistryStatusEntry,
|
|
4
|
-
AIProviderType,
|
|
5
|
-
ChatInputDuringExecutionMode,
|
|
6
|
-
ChatMessage,
|
|
7
|
-
} from '@genesislcap/foundation-ai';
|
|
8
|
-
import type { PayloadAction } from '@genesislcap/foundation-redux';
|
|
9
|
-
import { createSlice } from '@genesislcap/foundation-redux';
|
|
10
|
-
import type { AgentConfig } from '../config/config';
|
|
11
|
-
import type { AiAssistantAnimation, AiAssistantState, SuggestionsState } from '../main/main.types';
|
|
12
|
-
import { emptyUsage, sumUsage } from '../utils/sum-usage';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* A single in-flight per-call chat-input override pushed by a `requestSubAgent`
|
|
16
|
-
* or `requestInteraction` call. Tracked as an array (not a counter) so the
|
|
17
|
-
* slice can survive pop-in/pop-out and so a listener that connects
|
|
18
|
-
* mid-execution can compute the effective mode without having seen the start
|
|
19
|
-
* event.
|
|
20
|
-
*
|
|
21
|
-
* @internal
|
|
22
|
-
*/
|
|
23
|
-
export interface InputOverride {
|
|
24
|
-
/** Unique per-invocation id, paired with the start/stop events. */
|
|
25
|
-
id: string;
|
|
26
|
-
/** The mode requested for this invocation. */
|
|
27
|
-
mode: ChatInputDuringExecutionMode;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* One vendor's AI-spend figures, as fed by the host from the budget endpoint
|
|
32
|
-
* (GENC-1464 — the settings-modal budget meter).
|
|
33
|
-
*
|
|
34
|
-
* Dollars, not a percentage: the meter's text shows the real figures (spend CAN
|
|
35
|
-
* exceed the cap — the last allowed call may overshoot it), and only the bar
|
|
36
|
-
* clamps. Storing a pre-computed percentage would destroy the overshoot.
|
|
37
|
-
*
|
|
38
|
-
* @beta
|
|
39
|
-
*/
|
|
40
|
-
export interface VendorBudgetFigures {
|
|
41
|
-
/** The vendor's budget cap in USD. Finite and `>= 0`. */
|
|
42
|
-
budgetUsd: number;
|
|
43
|
-
/** Metered spend against that cap in USD. Finite and `>= 0`; may exceed the cap. */
|
|
44
|
-
spentUsd: number;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Shape of a single AI assistant session's serializable state.
|
|
49
|
-
*
|
|
50
|
-
* @internal
|
|
51
|
-
*/
|
|
52
|
-
export interface AiAssistantSessionState {
|
|
53
|
-
messages: ChatMessage[];
|
|
54
|
-
state: AiAssistantState;
|
|
55
|
-
showToolCalls: boolean;
|
|
56
|
-
showThinkingSteps: boolean;
|
|
57
|
-
showNarration: boolean;
|
|
58
|
-
showAgentSwitchIndicator: boolean;
|
|
59
|
-
enabledAnimations: AiAssistantAnimation[];
|
|
60
|
-
suggestionsState: SuggestionsState;
|
|
61
|
-
contextTokens: number | undefined;
|
|
62
|
-
contextLimit: number | undefined;
|
|
63
|
-
/**
|
|
64
|
-
* Cost and per-bucket token totals across every chat turn in this session,
|
|
65
|
-
* including sub-agent turns and spend banked by a compaction.
|
|
66
|
-
*
|
|
67
|
-
* One field rather than a cost scalar plus a token scalar: they are derived from
|
|
68
|
-
* the same walk of the transcript, so keeping them together makes it impossible
|
|
69
|
-
* for the two to disagree about which turns they counted.
|
|
70
|
-
*/
|
|
71
|
-
sessionUsage: AggregateUsage;
|
|
72
|
-
/** Active model id (e.g. `claude-sonnet-4-6`), resolved on connect. */
|
|
73
|
-
activeModel: string | undefined;
|
|
74
|
-
/**
|
|
75
|
-
* Name of the AI provider used on the most recent turn — drives "current"
|
|
76
|
-
* marker in the settings panel. `undefined` until the first turn runs.
|
|
77
|
-
*/
|
|
78
|
-
activeProviderName: string | undefined;
|
|
79
|
-
/**
|
|
80
|
-
* Snapshot of every registered provider's status, in registration order.
|
|
81
|
-
* Populated on connect (or on settings-open if not yet loaded) and reused
|
|
82
|
-
* for the lifetime of the session — provider registration is static.
|
|
83
|
-
*
|
|
84
|
-
* Describes the registry rather than the conversation, so it survives
|
|
85
|
-
* {@link resetSession} ("Clear" / "New chat"); see the reasoning there. Only
|
|
86
|
-
* a registry change (via the observable-registry subscription) rewrites it.
|
|
87
|
-
*/
|
|
88
|
-
providerStatuses: AIProviderRegistryStatusEntry[];
|
|
89
|
-
activeAgent: Omit<AgentConfig, 'toolHandlers'> | undefined;
|
|
90
|
-
/**
|
|
91
|
-
* Name of the agent the user has pinned via the picker. `null` means the
|
|
92
|
-
* orchestrator chooses each turn (Auto). Survives lifecycle events but
|
|
93
|
-
* resets on page refresh. Only changed by explicit user picker action (or
|
|
94
|
-
* the host's `setAgent` API) — never by the orchestrator's auto-pin on
|
|
95
|
-
* stateful activation. See {@link flowOwnerAgentName} for that.
|
|
96
|
-
*/
|
|
97
|
-
pinnedAgentName: string | null;
|
|
98
|
-
/**
|
|
99
|
-
* Name of the agent that currently owns a guided flow — set by the
|
|
100
|
-
* orchestrator when a stateful agent's `onActivate` fires, cleared by
|
|
101
|
-
* `releaseAgent` or session teardown. While non-null, routing precedence
|
|
102
|
-
* goes to this agent and the picker is locked. Independent of
|
|
103
|
-
* {@link pinnedAgentName}: a flow can wrap up and release without
|
|
104
|
-
* disturbing the user's sticky picker choice.
|
|
105
|
-
*/
|
|
106
|
-
flowOwnerAgentName: string | null;
|
|
107
|
-
/**
|
|
108
|
-
* The user prompt that started the current guided flow (GENC-1351 §5.9) —
|
|
109
|
-
* captured when a flow-owner locks in, cleared when it releases. Persisted only
|
|
110
|
-
* into a *non-resumable tombstone* (not the normal snapshot), where it pre-fills
|
|
111
|
-
* the composer on return so the user can re-run the task. Slice-backed (like
|
|
112
|
-
* {@link flowOwnerAgentName}) so both element instances agree and it survives
|
|
113
|
-
* pop-in/out.
|
|
114
|
-
*/
|
|
115
|
-
flowActivationPrompt: string | null;
|
|
116
|
-
/**
|
|
117
|
-
* Whether the agent picker slide-out panel is currently open. Lives in the
|
|
118
|
-
* slice so the bubble-dialog and popout-panel instances stay in sync as the
|
|
119
|
-
* user pops in and out.
|
|
120
|
-
*/
|
|
121
|
-
agentPickerOpen: boolean;
|
|
122
|
-
/**
|
|
123
|
-
* Whether the lifecycle (session) menu slide-out panel is open. Mirrors
|
|
124
|
-
* `agentPickerOpen` — kept in the slice so panel state survives pop-in/out.
|
|
125
|
-
*/
|
|
126
|
-
sessionMenuOpen: boolean;
|
|
127
|
-
/**
|
|
128
|
-
* Whether a manual compaction's async summary is in flight (GENC-1351 §5.7).
|
|
129
|
-
* In the slice (not element-local) so the "Compacting…" transcript view and
|
|
130
|
-
* input-disable stay in sync across pop-in/out, like the panel flags above.
|
|
131
|
-
*/
|
|
132
|
-
compacting: boolean;
|
|
133
|
-
/**
|
|
134
|
-
* Whether a page-reload restore is in flight — `provider.load()` hasn't
|
|
135
|
-
* resolved yet (GENC-1351 §6). In the slice (like `compacting`) so the
|
|
136
|
-
* "Restoring…" transcript view + input-disable survive pop-in/out. Invisible
|
|
137
|
-
* for synchronous providers (resolves before paint); matters for server-backed
|
|
138
|
-
* ones (S3 latency), where the disable also **prevents a mid-load send from
|
|
139
|
-
* being clobbered** when the restored history loads.
|
|
140
|
-
*/
|
|
141
|
-
restoring: boolean;
|
|
142
|
-
/**
|
|
143
|
-
* Whether the assistant is **blocked by a backend condition** and cannot send
|
|
144
|
-
* at all — today, an exhausted AI-spend budget (GENC-1464). Unlike `compacting`
|
|
145
|
-
* and `restoring`, which are short-lived in-flight flags that clear
|
|
146
|
-
* themselves, this is a **latched** state: nothing the user or the element
|
|
147
|
-
* does clears it, because nothing they can do fixes it. It stays set until the
|
|
148
|
-
* host writes `blocked = false` (typically after a fresh pre-flight shows
|
|
149
|
-
* budget available again).
|
|
150
|
-
*
|
|
151
|
-
* Also unlike those two, it does **not** replace the transcript — the user
|
|
152
|
-
* keeps their conversation and gets a banner above a disabled composer.
|
|
153
|
-
*
|
|
154
|
-
* In the slice so the banner + input-disable stay in sync across pop-in/out.
|
|
155
|
-
* Preserved across `resetSession` ("Clear" / "New chat"): starting a new chat
|
|
156
|
-
* does not refill the budget.
|
|
157
|
-
*
|
|
158
|
-
* **Vendor-agnostic.** Under per-vendor budgets this means "blocked whatever
|
|
159
|
-
* vendor you use" — a host-set block, or a wall that could not be attributed
|
|
160
|
-
* to a vendor. A wall that WAS attributed lands in
|
|
161
|
-
* {@link AiAssistantSessionState.blockedVendors} instead, and the element
|
|
162
|
-
* derives its public `blocked` from the two together.
|
|
163
|
-
*/
|
|
164
|
-
blocked: boolean;
|
|
165
|
-
/**
|
|
166
|
-
* Host- or driver-supplied explanation shown in the blocked banner. `null`
|
|
167
|
-
* falls back to the element's default copy. Meaningless while
|
|
168
|
-
* {@link AiAssistantSessionState.blocked} is false.
|
|
169
|
-
*/
|
|
170
|
-
blockedReason: string | null;
|
|
171
|
-
/**
|
|
172
|
-
* Vendors walled by the AI-spend budget (GENC-1464 per-vendor), in the order they
|
|
173
|
-
* were walled.
|
|
174
|
-
*
|
|
175
|
-
* An **array**, not a `Set` and not one `Record<AIProviderType, {...}>`:
|
|
176
|
-
* a `Set` is neither plain JSON for the redux store nor structured-cloneable
|
|
177
|
-
* across the tab channel; the order is what the multi-vendor banner copy
|
|
178
|
-
* ("Anthropic and Gemini") reads; and a combined record would conflate "not
|
|
179
|
-
* walled" with "walled, no reason".
|
|
180
|
-
*
|
|
181
|
-
* Empty for every host that never adopts the per-vendor API — which is what
|
|
182
|
-
* makes the element's derived `blocked` byte-identical to the old stored one.
|
|
183
|
-
*/
|
|
184
|
-
blockedVendors: AIProviderType[];
|
|
185
|
-
/**
|
|
186
|
-
* The subset of {@link AiAssistantSessionState.blockedVendors} walled by the
|
|
187
|
-
* `otherVendorAvailable: false` SWEEP rather than by a refusal of their own.
|
|
188
|
-
* Provenance, not a second wall list: for locking, `blockedVendors` remains
|
|
189
|
-
* the whole truth. The distinction exists for the banner's unknown-reachability
|
|
190
|
-
* fallback — a refusal is a fact about a vendor the user just used and is
|
|
191
|
-
* nameable anywhere; a sweep entry is a headroom fact about the PROXY, and
|
|
192
|
-
* naming it before the statuses reveal whether the host even ships that vendor
|
|
193
|
-
* put a vendor in front of the user that may not be theirs.
|
|
194
|
-
*/
|
|
195
|
-
sweptVendors: AIProviderType[];
|
|
196
|
-
/**
|
|
197
|
-
* Per-vendor banner copy, keyed by a member of
|
|
198
|
-
* {@link AiAssistantSessionState.blockedVendors}. A missing entry falls back to
|
|
199
|
-
* copy composed from the vendor's display name.
|
|
200
|
-
*/
|
|
201
|
-
blockedVendorReasons: Partial<Record<AIProviderType, string>>;
|
|
202
|
-
/**
|
|
203
|
-
* Per-vendor AI-spend figures for the settings-modal budget meter (GENC-1464),
|
|
204
|
-
* fed by the host via the element's `setVendorBudget`. A vendor with no entry
|
|
205
|
-
* renders no meter row — which is also how "unlimited" is expressed: the host
|
|
206
|
-
* simply never feeds a vendor the server marks unlimited.
|
|
207
|
-
*
|
|
208
|
-
* NOT persisted to the session snapshot, deliberately. The server is
|
|
209
|
-
* authoritative for spend and the host re-feeds on boot (and after each turn),
|
|
210
|
-
* so a persisted copy could only ever show stale dollars across a reload —
|
|
211
|
-
* wrong money is worse than a beat of no meter. The snapshot assembler
|
|
212
|
-
* (`session-snapshot.ts`) builds from an explicit field list, so absence is
|
|
213
|
-
* structural, not an accident of serialization.
|
|
214
|
-
*/
|
|
215
|
-
vendorBudgets: Partial<Record<AIProviderType, VendorBudgetFigures>>;
|
|
216
|
-
/** Draft text in the input box — preserved across pop-in/pop-out cycles. */
|
|
217
|
-
inputValue: string;
|
|
218
|
-
/** Live trace from a currently-executing sub-agent. Cleared on sub-agent-stop. */
|
|
219
|
-
liveSubAgentTrace: ChatMessage[];
|
|
220
|
-
/** Name of the currently-executing sub-agent, or null when idle. */
|
|
221
|
-
liveSubAgentName: string | null;
|
|
222
|
-
/**
|
|
223
|
-
* In-flight per-call chat-input overrides pushed by `requestSubAgent` or
|
|
224
|
-
* `requestInteraction` calls. The most restrictive entry (`'hidden'` >
|
|
225
|
-
* `'disabled'`) wins; an empty list means the agent-level
|
|
226
|
-
* `chatInputDuringExecution` applies.
|
|
227
|
-
*/
|
|
228
|
-
inputOverrides: InputOverride[];
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* The subset of session state restored verbatim from a persisted session
|
|
233
|
-
* (page-reload restore). Agent *identity* (`activeAgent`) and per-agent machine
|
|
234
|
-
* snapshots are applied separately by the host — resolve the name against the
|
|
235
|
-
* live agents array, then hand the snapshot to the agent's `init({ restored })`.
|
|
236
|
-
*
|
|
237
|
-
* @internal
|
|
238
|
-
*/
|
|
239
|
-
export interface LoadSessionPayload {
|
|
240
|
-
messages: ChatMessage[];
|
|
241
|
-
pinnedAgentName: string | null;
|
|
242
|
-
flowOwnerAgentName: string | null;
|
|
243
|
-
contextTokens: number | undefined;
|
|
244
|
-
contextLimit: number | undefined;
|
|
245
|
-
activeModel: string | undefined;
|
|
246
|
-
activeProviderName: string | undefined;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Build a fresh default session state. A factory (not just a shared const) so
|
|
251
|
-
* each call returns independent collections — `resetSession` can hand back a
|
|
252
|
-
* clean slate without aliasing the initial state's arrays.
|
|
253
|
-
*/
|
|
254
|
-
export function createDefaultSessionState(): AiAssistantSessionState {
|
|
255
|
-
return {
|
|
256
|
-
messages: [],
|
|
257
|
-
state: 'idle',
|
|
258
|
-
showToolCalls: false,
|
|
259
|
-
showThinkingSteps: false,
|
|
260
|
-
showNarration: false,
|
|
261
|
-
showAgentSwitchIndicator: false,
|
|
262
|
-
enabledAnimations: [],
|
|
263
|
-
suggestionsState: { status: 'idle' },
|
|
264
|
-
contextTokens: undefined,
|
|
265
|
-
contextLimit: undefined,
|
|
266
|
-
sessionUsage: emptyUsage(),
|
|
267
|
-
activeModel: undefined,
|
|
268
|
-
activeProviderName: undefined,
|
|
269
|
-
providerStatuses: [],
|
|
270
|
-
activeAgent: undefined,
|
|
271
|
-
pinnedAgentName: null,
|
|
272
|
-
flowOwnerAgentName: null,
|
|
273
|
-
flowActivationPrompt: null,
|
|
274
|
-
agentPickerOpen: false,
|
|
275
|
-
sessionMenuOpen: false,
|
|
276
|
-
compacting: false,
|
|
277
|
-
restoring: false,
|
|
278
|
-
blocked: false,
|
|
279
|
-
blockedReason: null,
|
|
280
|
-
blockedVendors: [],
|
|
281
|
-
sweptVendors: [],
|
|
282
|
-
blockedVendorReasons: {},
|
|
283
|
-
vendorBudgets: {},
|
|
284
|
-
inputValue: '',
|
|
285
|
-
liveSubAgentTrace: [],
|
|
286
|
-
liveSubAgentName: null,
|
|
287
|
-
inputOverrides: [],
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
export const defaultSessionState: AiAssistantSessionState = createDefaultSessionState();
|
|
292
|
-
|
|
293
|
-
export const aiAssistantSlice = createSlice({
|
|
294
|
-
name: 'aiAssistant',
|
|
295
|
-
initialState: defaultSessionState,
|
|
296
|
-
reducers: {
|
|
297
|
-
setMessages(state, action: PayloadAction<ChatMessage[]>) {
|
|
298
|
-
state.messages = action.payload;
|
|
299
|
-
},
|
|
300
|
-
setState(state, action: PayloadAction<AiAssistantState>) {
|
|
301
|
-
state.state = action.payload;
|
|
302
|
-
},
|
|
303
|
-
setShowToolCalls(state, action: PayloadAction<boolean>) {
|
|
304
|
-
state.showToolCalls = action.payload;
|
|
305
|
-
},
|
|
306
|
-
setShowThinkingSteps(state, action: PayloadAction<boolean>) {
|
|
307
|
-
state.showThinkingSteps = action.payload;
|
|
308
|
-
},
|
|
309
|
-
setShowNarration(state, action: PayloadAction<boolean>) {
|
|
310
|
-
state.showNarration = action.payload;
|
|
311
|
-
},
|
|
312
|
-
setShowAgentSwitchIndicator(state, action: PayloadAction<boolean>) {
|
|
313
|
-
state.showAgentSwitchIndicator = action.payload;
|
|
314
|
-
},
|
|
315
|
-
setEnabledAnimations(state, action: PayloadAction<AiAssistantAnimation[]>) {
|
|
316
|
-
state.enabledAnimations = action.payload;
|
|
317
|
-
},
|
|
318
|
-
setSuggestionsState(state, action: PayloadAction<SuggestionsState>) {
|
|
319
|
-
state.suggestionsState = action.payload;
|
|
320
|
-
},
|
|
321
|
-
setContextTokens(state, action: PayloadAction<number | undefined>) {
|
|
322
|
-
state.contextTokens = action.payload;
|
|
323
|
-
},
|
|
324
|
-
setContextLimit(state, action: PayloadAction<number | undefined>) {
|
|
325
|
-
state.contextLimit = action.payload;
|
|
326
|
-
},
|
|
327
|
-
setSessionUsage(state, action: PayloadAction<AggregateUsage>) {
|
|
328
|
-
state.sessionUsage = action.payload;
|
|
329
|
-
},
|
|
330
|
-
setActiveModel(state, action: PayloadAction<string | undefined>) {
|
|
331
|
-
state.activeModel = action.payload;
|
|
332
|
-
},
|
|
333
|
-
setActiveProviderName(state, action: PayloadAction<string | undefined>) {
|
|
334
|
-
state.activeProviderName = action.payload;
|
|
335
|
-
},
|
|
336
|
-
setProviderStatuses(state, action: PayloadAction<AIProviderRegistryStatusEntry[]>) {
|
|
337
|
-
state.providerStatuses = action.payload;
|
|
338
|
-
},
|
|
339
|
-
setActiveAgent(state, action: PayloadAction<Omit<AgentConfig, 'toolHandlers'> | undefined>) {
|
|
340
|
-
state.activeAgent = action.payload;
|
|
341
|
-
},
|
|
342
|
-
setPinnedAgentName(state, action: PayloadAction<string | null>) {
|
|
343
|
-
state.pinnedAgentName = action.payload;
|
|
344
|
-
},
|
|
345
|
-
setFlowOwnerAgentName(state, action: PayloadAction<string | null>) {
|
|
346
|
-
state.flowOwnerAgentName = action.payload;
|
|
347
|
-
},
|
|
348
|
-
setFlowActivationPrompt(state, action: PayloadAction<string | null>) {
|
|
349
|
-
state.flowActivationPrompt = action.payload;
|
|
350
|
-
},
|
|
351
|
-
setAgentPickerOpen(state, action: PayloadAction<boolean>) {
|
|
352
|
-
state.agentPickerOpen = action.payload;
|
|
353
|
-
},
|
|
354
|
-
setSessionMenuOpen(state, action: PayloadAction<boolean>) {
|
|
355
|
-
state.sessionMenuOpen = action.payload;
|
|
356
|
-
},
|
|
357
|
-
setCompacting(state, action: PayloadAction<boolean>) {
|
|
358
|
-
state.compacting = action.payload;
|
|
359
|
-
},
|
|
360
|
-
setRestoring(state, action: PayloadAction<boolean>) {
|
|
361
|
-
state.restoring = action.payload;
|
|
362
|
-
},
|
|
363
|
-
/**
|
|
364
|
-
* Latch (or release) the backend-blocked state.
|
|
365
|
-
*
|
|
366
|
-
* `reason` is only meaningful while blocking, and it distinguishes three
|
|
367
|
-
* cases rather than two:
|
|
368
|
-
*
|
|
369
|
-
* - **omitted** — a re-latch that leaves the existing explanation intact.
|
|
370
|
-
* Load-bearing: the block has two writers (the host, and the driver's
|
|
371
|
-
* automatic latch), and the driver's write lands AFTER a bus-subscribed
|
|
372
|
-
* host has already set its own detailed copy. Treating "no reason" as
|
|
373
|
-
* "clear the reason" made that sequence replace the host's figures with
|
|
374
|
-
* generic copy every time, deterministically.
|
|
375
|
-
* - **a string** — replaces the explanation.
|
|
376
|
-
* - **`null`** — explicitly clears the explanation while staying blocked;
|
|
377
|
-
* the banner falls back to the element's default copy.
|
|
378
|
-
*
|
|
379
|
-
* Unblocking always clears it, so a stale explanation can never outlive the
|
|
380
|
-
* condition it described.
|
|
381
|
-
*
|
|
382
|
-
* Unblocking **also clears every per-vendor latch**, and that is the single
|
|
383
|
-
* rule that keeps per-vendor budgets non-breaking. A host asserting "the wall
|
|
384
|
-
* is gone" (the documented pre-flight, migration-GENC-1464 Option B) must not be
|
|
385
|
-
* silently overruled by a driver latch it cannot see; without this, the same
|
|
386
|
-
* `setBlocked(false)` that used to unblock would leave the composer locked.
|
|
387
|
-
*/
|
|
388
|
-
setBlocked(state, action: PayloadAction<{ blocked: boolean; reason?: string | null }>) {
|
|
389
|
-
state.blocked = action.payload.blocked;
|
|
390
|
-
if (!action.payload.blocked) {
|
|
391
|
-
state.blockedReason = null;
|
|
392
|
-
state.blockedVendors = [];
|
|
393
|
-
state.sweptVendors = [];
|
|
394
|
-
state.blockedVendorReasons = {};
|
|
395
|
-
} else if (action.payload.reason !== undefined) {
|
|
396
|
-
state.blockedReason = action.payload.reason;
|
|
397
|
-
}
|
|
398
|
-
},
|
|
399
|
-
/**
|
|
400
|
-
* Write the banner explanation without touching the flag.
|
|
401
|
-
*
|
|
402
|
-
* Separate from {@link setBlocked} rather than expressed as
|
|
403
|
-
* `setBlocked({ blocked: current, reason })`, because the element's public
|
|
404
|
-
* `blocked` is now DERIVED: re-latching with the derived value would pass
|
|
405
|
-
* `false` for a partially-walled session and so clear every per-vendor latch
|
|
406
|
-
* as a side effect of setting a string.
|
|
407
|
-
*/
|
|
408
|
-
setBlockedReason(state, action: PayloadAction<string | null>) {
|
|
409
|
-
state.blockedReason = action.payload;
|
|
410
|
-
},
|
|
411
|
-
/**
|
|
412
|
-
* Wall (or release) a single vendor's budget.
|
|
413
|
-
*
|
|
414
|
-
* The `reason` trichotomy mirrors {@link setBlocked}'s exactly, and for the
|
|
415
|
-
* same reason: the same two-writer race (the driver's latch landing after a
|
|
416
|
-
* bus-subscribed host has written its own copy) applies per vendor.
|
|
417
|
-
*
|
|
418
|
-
* Adding an already-walled vendor is idempotent — the array is a set with an
|
|
419
|
-
* order, so the vendor keeps the position it was first walled at.
|
|
420
|
-
*/
|
|
421
|
-
setVendorBlocked(
|
|
422
|
-
state,
|
|
423
|
-
action: PayloadAction<{
|
|
424
|
-
vendor: AIProviderType;
|
|
425
|
-
blocked: boolean;
|
|
426
|
-
reason?: string | null;
|
|
427
|
-
/**
|
|
428
|
-
* Marks the wall as SWEEP-derived (see `sweptVendors`). Only the
|
|
429
|
-
* element's `otherVendorAvailable: false` sweep passes `true`; a host
|
|
430
|
-
* call or a refusal latch leaves it unset, which also UPGRADES a
|
|
431
|
-
* previously swept vendor — a refusal (or an explicit host statement)
|
|
432
|
-
* is nameable evidence, and first-hand evidence beats the verdict.
|
|
433
|
-
*/
|
|
434
|
-
swept?: boolean;
|
|
435
|
-
}>,
|
|
436
|
-
) {
|
|
437
|
-
const { vendor, blocked, reason, swept } = action.payload;
|
|
438
|
-
const index = state.blockedVendors.indexOf(vendor);
|
|
439
|
-
const sweptIndex = state.sweptVendors.indexOf(vendor);
|
|
440
|
-
if (blocked) {
|
|
441
|
-
if (index < 0) state.blockedVendors.push(vendor);
|
|
442
|
-
if (swept) {
|
|
443
|
-
// Provenance is FIRST-writer-wins: marking swept only records a wall
|
|
444
|
-
// this call itself created. The sweep site already skips walled
|
|
445
|
-
// vendors, so this guard is belt-and-braces against a future caller
|
|
446
|
-
// downgrading a refused wall to "just the proxy's word".
|
|
447
|
-
if (index < 0 && sweptIndex < 0) state.sweptVendors.push(vendor);
|
|
448
|
-
} else if (sweptIndex >= 0) {
|
|
449
|
-
state.sweptVendors.splice(sweptIndex, 1);
|
|
450
|
-
}
|
|
451
|
-
if (reason === null) {
|
|
452
|
-
delete state.blockedVendorReasons[vendor];
|
|
453
|
-
} else if (reason !== undefined) {
|
|
454
|
-
state.blockedVendorReasons[vendor] = reason;
|
|
455
|
-
}
|
|
456
|
-
} else {
|
|
457
|
-
if (index >= 0) state.blockedVendors.splice(index, 1);
|
|
458
|
-
if (sweptIndex >= 0) state.sweptVendors.splice(sweptIndex, 1);
|
|
459
|
-
delete state.blockedVendorReasons[vendor];
|
|
460
|
-
}
|
|
461
|
-
},
|
|
462
|
-
/**
|
|
463
|
-
* Store (or clear, with `null`) one vendor's budget figures for the meter.
|
|
464
|
-
*
|
|
465
|
-
* Validation lives at the element boundary (`setVendorBudget` on the
|
|
466
|
-
* element), not here — the reducer is the storage layer and trusts its one
|
|
467
|
-
* caller, exactly as `setVendorBlocked` above trusts its.
|
|
468
|
-
*
|
|
469
|
-
* Idempotent by VALUE, not just by effect: re-feeding the figures a vendor
|
|
470
|
-
* already holds leaves the stored object untouched. The documented feed
|
|
471
|
-
* re-runs after every turn, so without this every turn would publish a new
|
|
472
|
-
* store reference for the same dollars and re-render a meter that has not
|
|
473
|
-
* changed.
|
|
474
|
-
*/
|
|
475
|
-
setVendorBudget(
|
|
476
|
-
state,
|
|
477
|
-
action: PayloadAction<{ vendor: AIProviderType; figures: VendorBudgetFigures | null }>,
|
|
478
|
-
) {
|
|
479
|
-
const { vendor, figures } = action.payload;
|
|
480
|
-
if (figures === null) {
|
|
481
|
-
delete state.vendorBudgets[vendor];
|
|
482
|
-
return;
|
|
483
|
-
}
|
|
484
|
-
const existing = state.vendorBudgets[vendor];
|
|
485
|
-
if (
|
|
486
|
-
existing &&
|
|
487
|
-
existing.budgetUsd === figures.budgetUsd &&
|
|
488
|
-
existing.spentUsd === figures.spentUsd
|
|
489
|
-
) {
|
|
490
|
-
return;
|
|
491
|
-
}
|
|
492
|
-
// A fresh object rather than the caller's: the store may freeze what it
|
|
493
|
-
// holds, and adopting the host's object would freeze state the host still
|
|
494
|
-
// owns (and let later host mutations bypass the store).
|
|
495
|
-
state.vendorBudgets[vendor] = { budgetUsd: figures.budgetUsd, spentUsd: figures.spentUsd };
|
|
496
|
-
},
|
|
497
|
-
setInputValue(state, action: PayloadAction<string>) {
|
|
498
|
-
state.inputValue = action.payload;
|
|
499
|
-
},
|
|
500
|
-
setLiveSubAgentTrace(state, action: PayloadAction<ChatMessage[]>) {
|
|
501
|
-
state.liveSubAgentTrace = action.payload;
|
|
502
|
-
},
|
|
503
|
-
setLiveSubAgentName(state, action: PayloadAction<string | null>) {
|
|
504
|
-
state.liveSubAgentName = action.payload;
|
|
505
|
-
},
|
|
506
|
-
addInputOverride(state, action: PayloadAction<InputOverride>) {
|
|
507
|
-
state.inputOverrides.push(action.payload);
|
|
508
|
-
},
|
|
509
|
-
removeInputOverride(state, action: PayloadAction<{ id: string }>) {
|
|
510
|
-
state.inputOverrides = state.inputOverrides.filter((o) => o.id !== action.payload.id);
|
|
511
|
-
},
|
|
512
|
-
/**
|
|
513
|
-
* Bulk-hydrate the restorable fields of a persisted session (page-reload
|
|
514
|
-
* restore) and set `state` back to `idle`. Agent identity and the per-agent
|
|
515
|
-
* machine snapshot are applied separately by the host — see
|
|
516
|
-
* `LoadSessionPayload`.
|
|
517
|
-
*/
|
|
518
|
-
loadSession(state, action: PayloadAction<LoadSessionPayload>) {
|
|
519
|
-
const p = action.payload;
|
|
520
|
-
state.messages = p.messages;
|
|
521
|
-
state.pinnedAgentName = p.pinnedAgentName;
|
|
522
|
-
state.flowOwnerAgentName = p.flowOwnerAgentName;
|
|
523
|
-
// Derive the totals from the restored transcript rather than trusting a stored
|
|
524
|
-
// counter. Restore dispatches this action directly, bypassing the host's
|
|
525
|
-
// `messages` setter — the thing that normally recomputes — so a stored total
|
|
526
|
-
// would sit stale (historically at 0) until the next turn arrived. The
|
|
527
|
-
// transcript is the source of truth: per-message usage survives the snapshot,
|
|
528
|
-
// and a compacted summary carries the usage of the turns it replaced.
|
|
529
|
-
state.sessionUsage = sumUsage(p.messages);
|
|
530
|
-
state.contextTokens = p.contextTokens;
|
|
531
|
-
state.contextLimit = p.contextLimit;
|
|
532
|
-
state.activeModel = p.activeModel;
|
|
533
|
-
state.activeProviderName = p.activeProviderName;
|
|
534
|
-
state.state = 'idle';
|
|
535
|
-
},
|
|
536
|
-
/**
|
|
537
|
-
* Wipe the session back to a fresh default — the "Clear" / "New chat" action.
|
|
538
|
-
*
|
|
539
|
-
* Three categories survive, for three different reasons — keep them distinct:
|
|
540
|
-
*
|
|
541
|
-
* 1. The user's **display preferences** (tool-call / thinking / narration /
|
|
542
|
-
* agent-switch visibility + animations). They are settings, not
|
|
543
|
-
* conversation state, and are persisted independently of the snapshot
|
|
544
|
-
* (GENC-1351). Without this they'd flip to their all-off defaults in the
|
|
545
|
-
* live store on Clear, since `clearSession` re-applies no baseline and the
|
|
546
|
-
* idempotent setters suppress a re-apply.
|
|
547
|
-
* 2. The **backend block** (`blocked` / `blockedReason`, and the per-vendor
|
|
548
|
-
* latches beside them). Not a preference at
|
|
549
|
-
* all — a standing backend condition that Clear cannot change. It is here
|
|
550
|
-
* because starting a new chat does not refill the AI budget, so clearing
|
|
551
|
-
* it would hand the user a working-looking composer that fails on the very
|
|
552
|
-
* next send.
|
|
553
|
-
* 3. The **provider statuses**. They describe the REGISTRY, not the
|
|
554
|
-
* conversation — provider registration is static for the store's lifetime
|
|
555
|
-
* and this snapshot is refreshed from the registry, never from a turn. They
|
|
556
|
-
* are session state only because that is where the element's store lives.
|
|
557
|
-
*
|
|
558
|
-
* Dropping them on Clear is not cosmetic: the element derives `blocked`
|
|
559
|
-
* from "every REACHABLE vendor is walled", and an empty reachable set fails
|
|
560
|
-
* safe to "blocked". So wiping the statuses while keeping the per-vendor
|
|
561
|
-
* latches (category 2) escalated one walled vendor out of two into a fully
|
|
562
|
-
* locked composer, and nothing reloads them — `loadProviderStatuses()` runs
|
|
563
|
-
* only on connect/session-switch and from the observable-registry
|
|
564
|
-
* subscription, so a host with a static registry stayed locked until a
|
|
565
|
-
* remount. Preserving is the fix rather than re-loading after reset,
|
|
566
|
-
* because the reload is async: it would still flash a fully locked composer
|
|
567
|
-
* for a turn of the event loop, and would break a host whose registry is
|
|
568
|
-
* immutable (nothing to re-listen to) or whose `listStatuses()` rejects.
|
|
569
|
-
*
|
|
570
|
-
* Stated separately so a future contributor deciding what belongs in this
|
|
571
|
-
* list does not "tidy" the block out on the grounds that it is not a display
|
|
572
|
-
* preference.
|
|
573
|
-
*/
|
|
574
|
-
resetSession(state) {
|
|
575
|
-
// `satisfies` gives this literal excess-property checking and per-field type
|
|
576
|
-
// checking, neither of which `Object.assign` provides — its source parameter
|
|
577
|
-
// is structurally permissive, so a typo (`blockedResaon`) would compile
|
|
578
|
-
// clean, silently stop preserving the real key, AND add a junk key to the
|
|
579
|
-
// draft that then leaks into serialised state.
|
|
580
|
-
const preservedAcrossReset = {
|
|
581
|
-
showToolCalls: state.showToolCalls,
|
|
582
|
-
showThinkingSteps: state.showThinkingSteps,
|
|
583
|
-
showNarration: state.showNarration,
|
|
584
|
-
showAgentSwitchIndicator: state.showAgentSwitchIndicator,
|
|
585
|
-
enabledAnimations: state.enabledAnimations,
|
|
586
|
-
providerStatuses: state.providerStatuses,
|
|
587
|
-
blocked: state.blocked,
|
|
588
|
-
blockedReason: state.blockedReason,
|
|
589
|
-
blockedVendors: state.blockedVendors,
|
|
590
|
-
sweptVendors: state.sweptVendors,
|
|
591
|
-
blockedVendorReasons: state.blockedVendorReasons,
|
|
592
|
-
// Category 2, beside the blocked latch: the budget figures are a fact
|
|
593
|
-
// about the USER'S DEPLOYMENT (what the server meters), not about this
|
|
594
|
-
// conversation — "Clear" refills no budget, so wiping them would blank
|
|
595
|
-
// the meter until the host's next re-feed while the dollars are
|
|
596
|
-
// unchanged. Opposite side from `contextTokens`, which IS conversation
|
|
597
|
-
// state (a new chat genuinely has an empty context window).
|
|
598
|
-
vendorBudgets: state.vendorBudgets,
|
|
599
|
-
} satisfies Partial<AiAssistantSessionState>;
|
|
600
|
-
Object.assign(state, createDefaultSessionState(), preservedAcrossReset);
|
|
601
|
-
},
|
|
602
|
-
},
|
|
603
|
-
selectors: {},
|
|
604
|
-
});
|