@genesislcap/ai-assistant 14.420.0 → 14.421.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.
Files changed (105) hide show
  1. package/dist/ai-assistant.api.json +4061 -1416
  2. package/dist/ai-assistant.d.ts +594 -81
  3. package/dist/dts/channel/ai-activity-channel.d.ts +4 -22
  4. package/dist/dts/channel/ai-activity-channel.d.ts.map +1 -1
  5. package/dist/dts/components/ai-driver/ai-driver.d.ts +52 -0
  6. package/dist/dts/components/ai-driver/ai-driver.d.ts.map +1 -0
  7. package/dist/dts/components/ai-driver/index.d.ts +2 -0
  8. package/dist/dts/components/ai-driver/index.d.ts.map +1 -0
  9. package/dist/dts/components/chat-driver/chat-driver.d.ts +63 -8
  10. package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -1
  11. package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.d.ts +3 -3
  12. package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.d.ts.map +1 -1
  13. package/dist/dts/components/chat-markdown/chat-markdown.d.ts +1 -1
  14. package/dist/dts/components/chat-markdown/chat-markdown.d.ts.map +1 -1
  15. package/dist/dts/components/halo-overlay.d.ts +13 -1
  16. package/dist/dts/components/halo-overlay.d.ts.map +1 -1
  17. package/dist/dts/components/orchestrating-driver/index.d.ts +2 -0
  18. package/dist/dts/components/orchestrating-driver/index.d.ts.map +1 -0
  19. package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts +39 -0
  20. package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts.map +1 -0
  21. package/dist/dts/components/popout-manager/index.d.ts +2 -0
  22. package/dist/dts/components/popout-manager/index.d.ts.map +1 -0
  23. package/dist/dts/components/popout-manager/popout-manager.d.ts +72 -0
  24. package/dist/dts/components/popout-manager/popout-manager.d.ts.map +1 -0
  25. package/dist/dts/config/config.d.ts +43 -15
  26. package/dist/dts/config/config.d.ts.map +1 -1
  27. package/dist/dts/config/fallback-agents.d.ts +20 -0
  28. package/dist/dts/config/fallback-agents.d.ts.map +1 -0
  29. package/dist/dts/config/index.d.ts +1 -0
  30. package/dist/dts/config/index.d.ts.map +1 -1
  31. package/dist/dts/index.d.ts +6 -0
  32. package/dist/dts/index.d.ts.map +1 -1
  33. package/dist/dts/main/main.d.ts +122 -21
  34. package/dist/dts/main/main.d.ts.map +1 -1
  35. package/dist/dts/main/main.styles.d.ts.map +1 -1
  36. package/dist/dts/main/main.template.d.ts.map +1 -1
  37. package/dist/dts/main/main.types.d.ts +16 -0
  38. package/dist/dts/main/main.types.d.ts.map +1 -1
  39. package/dist/dts/state/ai-assistant-slice.d.ts +38 -0
  40. package/dist/dts/state/ai-assistant-slice.d.ts.map +1 -0
  41. package/dist/dts/state/driver-registry.d.ts +22 -0
  42. package/dist/dts/state/driver-registry.d.ts.map +1 -0
  43. package/dist/dts/state/session-store.d.ts +37 -0
  44. package/dist/dts/state/session-store.d.ts.map +1 -0
  45. package/dist/dts/suggestions/chat-suggestions.d.ts +7 -0
  46. package/dist/dts/suggestions/chat-suggestions.d.ts.map +1 -0
  47. package/dist/dts/types/ai-chat-widget.d.ts +3 -2
  48. package/dist/dts/types/ai-chat-widget.d.ts.map +1 -1
  49. package/dist/dts/utils/index.d.ts +1 -0
  50. package/dist/dts/utils/index.d.ts.map +1 -1
  51. package/dist/dts/utils/tool-fold.d.ts +133 -0
  52. package/dist/dts/utils/tool-fold.d.ts.map +1 -0
  53. package/dist/esm/components/ai-driver/ai-driver.js +1 -0
  54. package/dist/esm/components/ai-driver/index.js +1 -0
  55. package/dist/esm/components/chat-driver/chat-driver.js +499 -67
  56. package/dist/esm/components/chat-interaction-wrapper/chat-interaction-wrapper.js +2 -2
  57. package/dist/esm/components/chat-markdown/chat-markdown.js +1 -1
  58. package/dist/esm/components/halo-overlay.js +53 -7
  59. package/dist/esm/components/orchestrating-driver/index.js +1 -0
  60. package/dist/esm/components/orchestrating-driver/orchestrating-driver.js +247 -0
  61. package/dist/esm/components/popout-manager/index.js +1 -0
  62. package/dist/esm/components/popout-manager/popout-manager.js +126 -0
  63. package/dist/esm/config/fallback-agents.js +26 -0
  64. package/dist/esm/config/index.js +1 -0
  65. package/dist/esm/index.js +6 -0
  66. package/dist/esm/main/main.js +546 -112
  67. package/dist/esm/main/main.styles.js +200 -4
  68. package/dist/esm/main/main.template.js +163 -63
  69. package/dist/esm/state/ai-assistant-slice.js +54 -0
  70. package/dist/esm/state/driver-registry.js +46 -0
  71. package/dist/esm/state/session-store.js +39 -0
  72. package/dist/esm/suggestions/chat-suggestions.js +147 -0
  73. package/dist/esm/utils/index.js +1 -0
  74. package/dist/esm/utils/tool-fold.js +92 -0
  75. package/dist/tsconfig.tsbuildinfo +1 -1
  76. package/docs/migration-FUI-2495.md +339 -0
  77. package/docs/sub_agent.md +310 -0
  78. package/package.json +16 -15
  79. package/src/channel/ai-activity-channel.ts +4 -20
  80. package/src/components/ai-driver/ai-driver.ts +69 -0
  81. package/src/components/ai-driver/index.ts +1 -0
  82. package/src/components/chat-driver/chat-driver.ts +600 -73
  83. package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.ts +3 -3
  84. package/src/components/chat-markdown/chat-markdown.ts +1 -1
  85. package/src/components/halo-overlay.ts +45 -7
  86. package/src/components/orchestrating-driver/index.ts +1 -0
  87. package/src/components/orchestrating-driver/orchestrating-driver.ts +328 -0
  88. package/src/components/popout-manager/index.ts +1 -0
  89. package/src/components/popout-manager/popout-manager.ts +147 -0
  90. package/src/config/config.ts +45 -15
  91. package/src/config/fallback-agents.ts +29 -0
  92. package/src/config/index.ts +1 -0
  93. package/src/index.ts +6 -0
  94. package/src/main/main.styles.ts +200 -4
  95. package/src/main/main.template.ts +200 -80
  96. package/src/main/main.ts +567 -94
  97. package/src/main/main.types.ts +11 -0
  98. package/src/state/ai-assistant-slice.ts +80 -0
  99. package/src/state/driver-registry.ts +51 -0
  100. package/src/state/session-store.ts +56 -0
  101. package/src/suggestions/chat-suggestions.ts +158 -0
  102. package/src/types/ai-chat-widget.ts +4 -2
  103. package/src/utils/index.ts +1 -0
  104. package/src/utils/tool-fold.ts +181 -0
  105. package/docs/multi-agent-architecture.md +0 -198
@@ -0,0 +1,39 @@
1
+ import { createStore } from '@genesislcap/foundation-redux';
2
+ import { aiAssistantSlice, defaultSessionState, } from './ai-assistant-slice';
3
+ const sessionStores = new Map();
4
+ function makeStore(devTools) {
5
+ return createStore([aiAssistantSlice], { aiAssistant: defaultSessionState }, devTools !== undefined ? { devTools } : undefined);
6
+ }
7
+ /**
8
+ * Returns the session store for the given key, creating one if it does not
9
+ * already exist. Omit `devTools` (or pass `undefined`) to defer to `isDev()`.
10
+ * Pass `true` to force-enable or `false` to force-disable Redux DevTools.
11
+ */
12
+ export function getSessionStore(key, devTools) {
13
+ const existing = sessionStores.get(key);
14
+ if (existing)
15
+ return existing;
16
+ const created = makeStore(devTools);
17
+ sessionStores.set(key, created);
18
+ return created;
19
+ }
20
+ /**
21
+ * Returns true if a session store already exists for the given key.
22
+ */
23
+ export function hasSessionStore(key) {
24
+ return sessionStores.has(key);
25
+ }
26
+ /**
27
+ * Deletes the session store for the given key.
28
+ */
29
+ export function deleteSessionStore(key) {
30
+ sessionStores.delete(key);
31
+ }
32
+ /**
33
+ * Removes all session stores. Exposed for test isolation only — not part of the public API.
34
+ *
35
+ * @internal
36
+ */
37
+ export function clearAllSessionStores() {
38
+ sessionStores.clear();
39
+ }
@@ -0,0 +1,147 @@
1
+ import { __decorate } from "tslib";
2
+ import { customElement, observable, html, css, repeat, when } from '@genesislcap/web-core';
3
+ import { GenesisElement } from '@genesislcap/web-core';
4
+ const PILL_ANIMATION_STAGGER_MS = 70;
5
+ const template = html `
6
+ ${when((x) => x.state.status === 'loading', html `
7
+ <div class="suggestions-container">
8
+ ${repeat(() => Array.from({ length: 3 }), html `
9
+ <div class="suggestion-placeholder"></div>
10
+ `)}
11
+ </div>
12
+ `)}
13
+ ${when((x) => x.state.status === 'loaded', html `
14
+ <div class="suggestions-container">
15
+ ${repeat((x) => x.state.suggestions, html `
16
+ <button
17
+ class="suggestion-pill"
18
+ style="animation-delay: ${(_, c) => c.index * PILL_ANIMATION_STAGGER_MS}ms"
19
+ @click="${(suggestion, c) => c.parent.handleSuggestionClick(suggestion)}"
20
+ >
21
+ ${(suggestion) => suggestion}
22
+ </button>
23
+ `)}
24
+ </div>
25
+ `)}
26
+ `;
27
+ const styles = css `
28
+ :host {
29
+ display: block;
30
+ }
31
+
32
+ .suggestions-container {
33
+ display: flex;
34
+ flex-wrap: wrap;
35
+ gap: 6px;
36
+ padding: 10px calc(var(--design-unit) * 3px) 6px;
37
+ background-color: var(--neutral-layer-2);
38
+ animation: suggestions-appear 0.2s ease-out both;
39
+ }
40
+
41
+ @keyframes suggestions-appear {
42
+ from {
43
+ opacity: 0;
44
+ transform: translateY(6px);
45
+ }
46
+
47
+ to {
48
+ opacity: 1;
49
+ transform: translateY(0);
50
+ }
51
+ }
52
+
53
+ .suggestion-pill {
54
+ display: inline-flex;
55
+ align-items: center;
56
+ padding: 5px 14px;
57
+ background: color-mix(in srgb, var(--accent-fill-rest) 10%, var(--neutral-layer-3));
58
+ color: var(--neutral-foreground-rest);
59
+ border: 1px solid color-mix(in srgb, var(--accent-fill-rest) 25%, var(--neutral-stroke-rest));
60
+ border-radius: 20px;
61
+ font-family: var(--body-font);
62
+ font-size: 0.85em;
63
+ cursor: pointer;
64
+ transition:
65
+ background 0.15s ease,
66
+ border-color 0.15s ease,
67
+ transform 0.15s ease,
68
+ box-shadow 0.15s ease;
69
+ animation: pill-appear 0.3s ease-out both;
70
+ max-width: 100%;
71
+ white-space: normal;
72
+ text-align: left;
73
+ }
74
+
75
+ .suggestion-pill:hover {
76
+ background: color-mix(in srgb, var(--accent-fill-rest) 20%, var(--neutral-layer-3));
77
+ border-color: color-mix(in srgb, var(--accent-fill-rest) 50%, var(--neutral-stroke-rest));
78
+ transform: translateY(-1px);
79
+ box-shadow: 0 2px 8px color-mix(in srgb, var(--accent-fill-rest) 20%, transparent);
80
+ }
81
+
82
+ .suggestion-pill:active {
83
+ transform: translateY(0);
84
+ box-shadow: none;
85
+ }
86
+
87
+ @keyframes pill-appear {
88
+ from {
89
+ opacity: 0;
90
+ transform: scale(0.88) translateY(4px);
91
+ }
92
+
93
+ to {
94
+ opacity: 1;
95
+ transform: scale(1) translateY(0);
96
+ }
97
+ }
98
+
99
+ .suggestion-placeholder {
100
+ height: 30px;
101
+ width: 110px;
102
+ background: var(--neutral-layer-3);
103
+ border-radius: 20px;
104
+ animation: placeholder-pulse 1.6s ease-in-out infinite;
105
+ }
106
+
107
+ .suggestion-placeholder:nth-child(2) {
108
+ width: 150px;
109
+ animation-delay: 0.25s;
110
+ }
111
+
112
+ .suggestion-placeholder:nth-child(3) {
113
+ width: 130px;
114
+ animation-delay: 0.5s;
115
+ }
116
+
117
+ @keyframes placeholder-pulse {
118
+ 0%,
119
+ 100% {
120
+ opacity: 0.35;
121
+ }
122
+
123
+ 50% {
124
+ opacity: 0.65;
125
+ }
126
+ }
127
+ `;
128
+ let ChatSuggestions = class ChatSuggestions extends GenesisElement {
129
+ constructor() {
130
+ super(...arguments);
131
+ this.state = { status: 'idle' };
132
+ }
133
+ handleSuggestionClick(suggestion) {
134
+ this.$emit('suggestion-clicked', suggestion);
135
+ }
136
+ };
137
+ __decorate([
138
+ observable
139
+ ], ChatSuggestions.prototype, "state", void 0);
140
+ ChatSuggestions = __decorate([
141
+ customElement({
142
+ name: 'chat-suggestions',
143
+ template,
144
+ styles,
145
+ })
146
+ ], ChatSuggestions);
147
+ export { ChatSuggestions };
@@ -1 +1,2 @@
1
1
  export * from './logger';
2
+ export * from './tool-fold';
@@ -0,0 +1,92 @@
1
+ import { __awaiter } from "tslib";
2
+ /**
3
+ * Symbol used to attach ToolFold metadata to a facade handler function.
4
+ * The ChatDriver inspects this to detect fold facades at runtime.
5
+ *
6
+ * @internal
7
+ */
8
+ export const TOOL_FOLD_SYMBOL = Symbol('toolFold');
9
+ /**
10
+ * Creates a tool fold — a facade that hides a group of related tools behind a single
11
+ * named entry, revealing them progressively when the model invokes the facade.
12
+ *
13
+ * The `tools` and `handlers` parameters accept the exact same types as `AgentConfig.toolDefinitions`
14
+ * and `AgentConfig.toolHandlers`, so existing tool creation functions can be passed in unchanged.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const tradingTools = createToolFold({
19
+ * name: 'trading_tools',
20
+ * tools: createTradesToolDefinitions(),
21
+ * handlers: createTradesToolHandlers(connect),
22
+ * });
23
+ *
24
+ * const agent: AgentConfig = {
25
+ * name: 'Trades',
26
+ * systemPrompt: '...',
27
+ * toolDefinitions: [getCurrentDateDef, tradingTools.definition],
28
+ * toolHandlers: { get_current_date: handler, ...tradingTools.handler },
29
+ * };
30
+ * ```
31
+ *
32
+ * @example Nesting folds
33
+ * ```typescript
34
+ * const insertFold = createToolFold({
35
+ * name: 'trade_insertion',
36
+ * tools: [validateDef, insertDef],
37
+ * handlers: { validate_trade: vHandler, insert_trade: iHandler },
38
+ * });
39
+ *
40
+ * const tradingFold = createToolFold({
41
+ * name: 'trading_tools',
42
+ * tools: [searchDef, insertFold.definition],
43
+ * handlers: { search_trades: sHandler, ...insertFold.handler },
44
+ * });
45
+ * ```
46
+ *
47
+ * @beta
48
+ */
49
+ export function createToolFold(config) {
50
+ var _a, _b;
51
+ const description = (_a = config.description) !== null && _a !== void 0 ? _a : `Contains: ${config.tools.map((t) => t.name).join(', ')}. Invoke to access these tools.`;
52
+ const fold = {
53
+ name: config.name,
54
+ description,
55
+ usageNotes: config.usageNotes,
56
+ tools: config.tools,
57
+ handlers: config.handlers,
58
+ exclusive: (_b = config.exclusive) !== null && _b !== void 0 ? _b : true,
59
+ };
60
+ const definition = {
61
+ name: config.name,
62
+ description,
63
+ parameters: { type: 'object', properties: {} },
64
+ };
65
+ // The facade handler body is never actually called — the driver intercepts it
66
+ // via TOOL_FOLD_SYMBOL before dispatch. The body is a no-op fallback.
67
+ const facadeHandler = () => __awaiter(this, void 0, void 0, function* () { return `Fold: ${config.name}`; });
68
+ facadeHandler[TOOL_FOLD_SYMBOL] = fold;
69
+ return {
70
+ definition,
71
+ handler: {
72
+ [config.name]: facadeHandler,
73
+ },
74
+ };
75
+ }
76
+ /**
77
+ * Expands a flat list of tool definitions into a nested tree by resolving fold
78
+ * metadata from the corresponding handlers. Use this for debug output so the
79
+ * full tool hierarchy is visible rather than just the top-level facade names.
80
+ *
81
+ * @beta
82
+ */
83
+ export function expandToolTree(definitions, handlers) {
84
+ return definitions.map((def) => {
85
+ const handler = handlers[def.name];
86
+ const fold = handler ? handler[TOOL_FOLD_SYMBOL] : undefined;
87
+ if (fold) {
88
+ return Object.assign(Object.assign({}, def), { tools: expandToolTree(fold.tools, fold.handlers) });
89
+ }
90
+ return Object.assign({}, def);
91
+ });
92
+ }
@@ -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/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/config/config.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/styles/ai-colours.ts","../src/styles/index.ts","../src/styles/styles.ts","../src/tags/index.ts","../src/types/ai-chat-widget.ts","../src/utils/index.ts","../src/utils/logger.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/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/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/index.ts","../src/utils/logger.ts","../src/utils/tool-fold.ts"],"version":"5.9.2"}
@@ -0,0 +1,339 @@
1
+ # Migration Guide — FUI-2495 (AI Header & Orchestration)
2
+
3
+ This document lists every breaking change introduced by the `mw/FUI-2495-ai-header-and-orchestration` branch and shows exactly what to change in consumer code.
4
+
5
+ ---
6
+
7
+ ## 1. `ChatConfig` restructured into sub-namespaces
8
+
9
+ `ChatConfig` (from `@genesislcap/foundation-ai`) was a flat interface. It is now a three-field wrapper. The old fields have been split across `ui`, `agent`, and `suggestions`.
10
+
11
+ The old `ChatConfig` interface is renamed `ChatUiConfig` and is still exported for typing the `ui` field.
12
+
13
+ **Before**
14
+
15
+ ```ts
16
+ import type { ChatConfig } from '@genesislcap/foundation-ai';
17
+
18
+ const chatConfig: ChatConfig = {
19
+ showToolCalls: true,
20
+ showThinkingSteps: false,
21
+ allowDebugDownload: true,
22
+ acceptedFiles: '.txt,.md',
23
+ loadingDelay: 5,
24
+ animations: { enabledAnimations: ['halo'] },
25
+ maxToolIterations: 50,
26
+ };
27
+ ```
28
+
29
+ **After**
30
+
31
+ ```ts
32
+ import type { ChatConfig } from '@genesislcap/foundation-ai';
33
+
34
+ const chatConfig: ChatConfig = {
35
+ ui: {
36
+ showToolCalls: true,
37
+ showThinkingSteps: false,
38
+ allowDebugDownload: true,
39
+ acceptedFiles: '.txt,.md',
40
+ loadingDelay: 5,
41
+ animations: { enabledAnimations: ['halo'] },
42
+ },
43
+ agent: {
44
+ maxToolIterations: 50,
45
+ },
46
+ };
47
+ ```
48
+
49
+ **New fields available** (all optional):
50
+
51
+ | Namespace | Field | Description |
52
+ |---|---|---|
53
+ | `ui` | `showSplash` | Show `slot="splash"` content when no messages exist |
54
+ | `ui` | `showAgentSwitchIndicator` | Show a divider when the active specialist changes |
55
+ | `ui` | `showContextUsage` | Show context window usage in the settings panel |
56
+ | `agent` | `maxHandoffs` | Max agent-to-agent handoffs per user turn (default: 3) |
57
+ | `agent` | `classifierHistoryLength` | Recent messages passed to the classifier (default: 4) |
58
+ | `agent` | `classifierRetries` | Retries on classifier failure (default: 2) |
59
+ | `agent` | `maxFoldOperations` | Max fold open/close cycles before a guidance message is injected (default: 5) |
60
+ | `suggestions` | `behavior` | `'never'` \| `'initial'` \| `'always'` |
61
+ | `suggestions` | `count` | Number of suggestions to generate (default: 3) |
62
+ | `suggestions` | `prompt` | Custom prompt to steer suggestion generation |
63
+
64
+ ---
65
+
66
+ ## 2. `AgentConfig` is now a discriminated union
67
+
68
+ `AgentConfig` (from `@genesislcap/foundation-mf/ai-assistant`) was a single flat interface. It is now a union of `SpecialistAgentConfig` (requires `description`) and `FallbackAgentConfig` (requires `fallback: true`).
69
+
70
+ **Existing single-agent usage — no change required.** TypeScript will resolve the object as `SpecialistAgentConfig` automatically as long as `description` is present.
71
+
72
+ **Before (multi-agent)**
73
+
74
+ ```ts
75
+ import type { AgentConfig } from '@genesislcap/foundation-mf/ai-assistant';
76
+
77
+ const agents: AgentConfig[] = [
78
+ {
79
+ name: 'Trades Agent',
80
+ description: 'Handles trade queries and execution.',
81
+ systemPrompt: '...',
82
+ toolHandlers: { ... },
83
+ },
84
+ {
85
+ name: 'General Agent',
86
+ description: 'Handles everything else.',
87
+ systemPrompt: '...',
88
+ },
89
+ ];
90
+ ```
91
+
92
+ **After** — if you want a dedicated fallback agent, mark it explicitly. Specialist agents are unchanged.
93
+
94
+ ```ts
95
+ import type { AgentConfig } from '@genesislcap/foundation-mf/ai-assistant';
96
+
97
+ const agents: AgentConfig[] = [
98
+ {
99
+ name: 'Trades Agent',
100
+ description: 'Handles trade queries and execution.', // still required for specialists
101
+ systemPrompt: '...',
102
+ toolHandlers: { ... },
103
+ },
104
+ {
105
+ name: 'General Agent',
106
+ fallback: true, // replaces description for the catch-all agent
107
+ systemPrompt: '...',
108
+ },
109
+ ];
110
+ ```
111
+
112
+ **Rules:**
113
+ - A specialist must have `description` and must not have `fallback`.
114
+ - A fallback must have `fallback: true` and must not have `description`.
115
+ - Only one fallback is permitted per `agents` array.
116
+
117
+ ---
118
+
119
+ ## 3. `chat-popout` and `chat-popin` bus events no longer carry a state payload
120
+
121
+ `AiAssistantSerializedState` has been deleted. The `chat-popout` and `chat-popin` bus events now carry `undefined`. State is managed automatically by the Redux session store shared between the bubble and layout instances — no manual transfer is required.
122
+
123
+ **Before**
124
+
125
+ ```ts
126
+ import { agenticActivityBus } from '@genesislcap/foundation-mf/ai-assistant';
127
+ import type { AiAssistantSerializedState } from '@genesislcap/foundation-mf/ai-assistant';
128
+
129
+ agenticActivityBus.subscribe('chat-popin', ({ state }: { state: AiAssistantSerializedState }) => {
130
+ myAssistant.applyState(state);
131
+ });
132
+ ```
133
+
134
+ **After**
135
+
136
+ ```ts
137
+ import { agenticActivityBus } from '@genesislcap/foundation-mf/ai-assistant';
138
+
139
+ // No payload — state transfer is automatic.
140
+ agenticActivityBus.subscribe('chat-popin', () => {
141
+ // React to the event if needed, but state is already synced.
142
+ });
143
+ ```
144
+
145
+ Remove all imports of `AiAssistantSerializedState` — it no longer exists.
146
+
147
+ ---
148
+
149
+ ## 4. `applyState()` and `serializeState()` removed from `FoundationAiAssistant`
150
+
151
+ These methods were used to manually transfer state between the bubble and layout instances. The session store makes them redundant.
152
+
153
+ **Before**
154
+
155
+ ```ts
156
+ const state = bubbleAssistant.serializeState();
157
+ layoutAssistant.applyState(state);
158
+ ```
159
+
160
+ **After** — delete these call sites. The session store handles state continuity automatically when the component is connected/disconnected.
161
+
162
+ ---
163
+
164
+ ## 5. `ChatInteraction.resolved` changed from `boolean` to `InteractionResult<unknown>`
165
+
166
+ **Before**
167
+
168
+ ```ts
169
+ if (this.resolved === true) {
170
+ // show read-only state
171
+ }
172
+ ```
173
+
174
+ **After** — use a truthy check. The value itself is now the result object, which widgets can also read.
175
+
176
+ ```ts
177
+ if (this.resolved) {
178
+ // show read-only state
179
+ // optionally: use this.resolved.value for the result data
180
+ }
181
+ ```
182
+
183
+ If your interaction component explicitly typed this field:
184
+
185
+ ```ts
186
+ // Before
187
+ resolved?: boolean;
188
+
189
+ // After
190
+ import type { InteractionResult } from '@genesislcap/foundation-ai';
191
+ resolved?: InteractionResult<unknown>;
192
+ ```
193
+
194
+ ---
195
+
196
+ ## 6. `ChatToolCall` changed from an interface to a union type
197
+
198
+ `ChatToolCall` is now `ChatToolCallBase | ChatToolCallUnknown`. Code that was written against the original interface will still compile, but exhaustive handling should use the new type guard.
199
+
200
+ **Before**
201
+
202
+ ```ts
203
+ import type { ChatToolCall } from '@genesislcap/foundation-ai';
204
+
205
+ function handleToolCall(tc: ChatToolCall) {
206
+ console.log(tc.name, tc.args);
207
+ }
208
+ ```
209
+
210
+ **After** — use `isChatToolCallUnknown` before narrowing:
211
+
212
+ ```ts
213
+ import { isChatToolCallUnknown } from '@genesislcap/foundation-ai';
214
+ import type { ChatToolCall } from '@genesislcap/foundation-ai';
215
+
216
+ function handleToolCall(tc: ChatToolCall) {
217
+ if (isChatToolCallUnknown(tc)) {
218
+ console.warn('Unknown tool call', tc.name, 'Available:', tc.availableTools);
219
+ return;
220
+ }
221
+ console.log(tc.name, tc.args);
222
+ }
223
+ ```
224
+
225
+ New optional fields on all `ChatToolCall` objects (not sent to the AI provider):
226
+
227
+ - `foldEvent?: 'open' | 'close'` — set when this call is a fold lifecycle event
228
+ - `foldPath?: string[]` — breadcrumb path of active folds when the call was made
229
+
230
+ ---
231
+
232
+ ## 7. `ChatRole` has a new `'system-event'` value
233
+
234
+ If you have an exhaustive `switch` or conditional chain on `ChatRole`, add a case for `'system-event'`.
235
+
236
+ **Before**
237
+
238
+ ```ts
239
+ switch (message.role) {
240
+ case 'user': ...
241
+ case 'assistant': ...
242
+ case 'system': ...
243
+ case 'tool': ...
244
+ }
245
+ ```
246
+
247
+ **After**
248
+
249
+ ```ts
250
+ switch (message.role) {
251
+ case 'user': ...
252
+ case 'assistant': ...
253
+ case 'system': ...
254
+ case 'tool': ...
255
+ case 'system-event': ...
256
+ }
257
+ ```
258
+
259
+ ---
260
+
261
+ ## 8. `GeminiAIConfig.model` is now typed as `GeminiModelId`
262
+
263
+ The `model` field no longer accepts arbitrary strings. Only the values in `SUPPORTED_GEMINI_MODEL_IDS` are accepted at runtime and at compile time.
264
+
265
+ **Before**
266
+
267
+ ```ts
268
+ const config: GeminiAIConfig = {
269
+ providerType: 'gemini',
270
+ model: 'gemini-2.0-flash', // was accepted as string
271
+ };
272
+ ```
273
+
274
+ **After**
275
+
276
+ ```ts
277
+ import { SUPPORTED_GEMINI_MODEL_IDS } from '@genesislcap/foundation-ai';
278
+ import type { GeminiAIConfig } from '@genesislcap/foundation-ai';
279
+
280
+ const config: GeminiAIConfig = {
281
+ providerType: 'gemini',
282
+ model: 'gemini-2.5-flash', // must be a GeminiModelId
283
+ };
284
+ ```
285
+
286
+ Supported values: `'gemini-2.5-flash'` | `'gemini-2.5-flash-lite'`
287
+
288
+ If you were using `'gemini-2.0-flash'` or another deprecated model, migrate to `'gemini-2.5-flash'`.
289
+
290
+ ---
291
+
292
+ ## 9. `downloadHistory()` renamed to `getDebugLog()`
293
+
294
+ **Before**
295
+
296
+ ```ts
297
+ assistant.downloadHistory();
298
+ ```
299
+
300
+ **After**
301
+
302
+ ```ts
303
+ assistant.getDebugLog();
304
+ ```
305
+
306
+ ---
307
+
308
+ ## 10. `headerTitle` now defaults to `'Genesis Assistant'`
309
+
310
+ Previously `headerTitle` was `undefined` by default, so the header title area was empty unless explicitly set. It now defaults to `'Genesis Assistant'`.
311
+
312
+ If you were relying on the absence of a title (e.g. to hide a title element via CSS `:empty` or conditional rendering), set the attribute explicitly to an empty string or adjust your CSS.
313
+
314
+ **Before** — no attribute set, title area was empty.
315
+
316
+ **After** — set explicitly if you want a different value or no value:
317
+
318
+ ```html
319
+ <!-- Custom title -->
320
+ <foundation-ai-assistant header-title="My Assistant"></foundation-ai-assistant>
321
+
322
+ <!-- No title -->
323
+ <foundation-ai-assistant header-title=""></foundation-ai-assistant>
324
+ ```
325
+
326
+ ---
327
+
328
+ ## Quick reference checklist
329
+
330
+ - [ ] Move `ChatConfig` fields into `ui`, `agent`, and `suggestions` sub-objects
331
+ - [ ] Remove all imports of `AiAssistantSerializedState`
332
+ - [ ] Remove `chat-popin`/`chat-popout` state payload destructuring
333
+ - [ ] Remove calls to `applyState()` and `serializeState()`
334
+ - [ ] Update `ChatInteraction.resolved` checks from `=== true` to truthy
335
+ - [ ] Update `AgentConfig` arrays — add `fallback: true` to catch-all agents (remove `description` from them)
336
+ - [ ] Add `'system-event'` case to any exhaustive `ChatRole` switches
337
+ - [ ] Update `GeminiAIConfig.model` from `'gemini-2.0-flash'` to a supported `GeminiModelId`
338
+ - [ ] Rename `downloadHistory()` calls to `getDebugLog()`
339
+ - [ ] Audit `header-title` attribute usage if you relied on an empty default