@genesislcap/ai-assistant 14.420.0 → 14.421.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
@@ -11,6 +11,7 @@ export const styles = css `
11
11
  }
12
12
 
13
13
  .chat-wrapper {
14
+ container-type: inline-size;
14
15
  position: relative;
15
16
  display: flex;
16
17
  flex-direction: column;
@@ -56,22 +57,189 @@ export const styles = css `
56
57
  font-size: 1.2em;
57
58
  }
58
59
 
60
+ @keyframes settings-slide-in {
61
+ from {
62
+ opacity: 0%;
63
+ transform: translateY(-6px);
64
+ }
65
+
66
+ to {
67
+ opacity: 100%;
68
+ transform: translateY(0);
69
+ }
70
+ }
71
+
72
+ @keyframes settings-slide-out {
73
+ from {
74
+ opacity: 100%;
75
+ transform: translateY(0);
76
+ }
77
+
78
+ to {
79
+ opacity: 0%;
80
+ transform: translateY(-6px);
81
+ }
82
+ }
83
+
59
84
  .settings-panel {
60
- display: flex;
61
- flex-wrap: wrap;
62
- align-items: center;
63
- gap: calc(var(--design-unit) * 4px);
85
+ animation: settings-slide-in 0.2s ease-out;
86
+ display: grid;
87
+ grid-template-columns: 1fr;
88
+ gap: calc(var(--design-unit) * 2px) calc(var(--design-unit) * 4px);
64
89
  padding: calc(var(--design-unit) * 2px) calc(var(--design-unit) * 3px);
65
90
  border-bottom: 1px solid var(--neutral-stroke-rest);
66
91
  background-color: var(--neutral-layer-2);
67
92
  }
68
93
 
94
+ .settings-panel.closing {
95
+ animation: settings-slide-out 0.2s ease-in forwards;
96
+ }
97
+
98
+ rapid-multiselect::part(root) {
99
+ min-width: 80px;
100
+ width: 300%;
101
+ }
102
+
103
+ rapid-multiselect::part(control),
104
+ .settings-panel > [part='download-button'] {
105
+ width: fit-content;
106
+ }
107
+
108
+ .settings-panel > [part='toggle-tool-calls'] {
109
+ grid-column: 1;
110
+ grid-row: 1;
111
+ }
112
+
113
+ .settings-panel > [part='toggle-thinking'] {
114
+ grid-column: 2;
115
+ grid-row: 1;
116
+ }
117
+
118
+ .settings-panel > [part='toggle-agent-switch'] {
119
+ grid-column: 1;
120
+ grid-row: 2;
121
+ }
122
+
123
+ .settings-panel > [part='download-button'] {
124
+ grid-column: 2;
125
+ grid-row: 2;
126
+ }
127
+
69
128
  .settings-animations {
70
129
  display: flex;
71
130
  align-items: center;
72
131
  gap: calc(var(--design-unit) * 2px);
73
132
  }
74
133
 
134
+ .settings-panel > .settings-animations {
135
+ grid-column: 1/2;
136
+ grid-row: 3;
137
+ }
138
+
139
+ @container (min-width: 400px) {
140
+ .settings-panel {
141
+ grid-template-columns: auto 1fr;
142
+ align-items: start;
143
+ }
144
+
145
+ .settings-panel > [part='toggle-tool-calls'] {
146
+ grid-column: 1;
147
+ grid-row: 1;
148
+ }
149
+
150
+ .settings-panel > [part='toggle-thinking'] {
151
+ grid-column: 1;
152
+ grid-row: 2;
153
+ }
154
+
155
+ .settings-panel > [part='toggle-agent-switch'] {
156
+ grid-column: 1;
157
+ grid-row: 3;
158
+ }
159
+
160
+ .settings-panel > [part='download-button'],
161
+ .settings-panel > .settings-animations {
162
+ grid-column: 2;
163
+ justify-self: end;
164
+ }
165
+
166
+ .settings-panel > [part='download-button'] {
167
+ grid-row: 1;
168
+ }
169
+
170
+ .settings-panel > .settings-animations {
171
+ grid-row: 2;
172
+ }
173
+ }
174
+
175
+ @container (min-width: 750px) {
176
+ .settings-panel {
177
+ display: flex;
178
+ flex-wrap: nowrap;
179
+ align-items: center;
180
+ gap: calc(var(--design-unit) * 4px);
181
+ }
182
+
183
+ .settings-panel > [part='download-button'],
184
+ .settings-panel > .settings-animations {
185
+ justify-self: unset;
186
+ margin-left: auto;
187
+ }
188
+
189
+ .settings-panel > [part='download-button'] ~ .settings-animations {
190
+ margin-left: 0;
191
+ }
192
+ }
193
+
194
+ .context-usage {
195
+ animation: settings-slide-in 0.2s ease-out;
196
+ padding: calc(var(--design-unit) * 2px) calc(var(--design-unit) * 3px);
197
+ border-bottom: 1px solid var(--neutral-stroke-rest);
198
+ background-color: var(--neutral-layer-2);
199
+ display: flex;
200
+ flex-direction: column;
201
+ gap: calc(var(--design-unit) * 1px);
202
+ }
203
+
204
+ .context-usage-header {
205
+ display: flex;
206
+ justify-content: space-between;
207
+ align-items: baseline;
208
+ gap: calc(var(--design-unit) * 2px);
209
+ }
210
+
211
+ .context-usage-label {
212
+ font-size: 0.85em;
213
+ font-weight: 600;
214
+ color: var(--neutral-foreground-rest);
215
+ white-space: nowrap;
216
+ }
217
+
218
+ .context-usage-tokens {
219
+ font-size: 0.8em;
220
+ color: var(--neutral-foreground-hint, var(--neutral-foreground-rest));
221
+ opacity: 80%;
222
+ }
223
+
224
+ .context-usage rapid-progress,
225
+ .context-usage [part='context-progress'] {
226
+ width: 100%;
227
+ }
228
+
229
+ .splash-wrapper {
230
+ position: absolute;
231
+ inset: 0;
232
+ z-index: 10;
233
+ overflow: hidden;
234
+ pointer-events: none;
235
+ }
236
+
237
+ ::slotted([slot='splash']) {
238
+ display: block;
239
+ width: 100%;
240
+ height: 100%;
241
+ }
242
+
75
243
  .messages {
76
244
  flex: 1;
77
245
  overflow-y: auto;
@@ -117,6 +285,28 @@ export const styles = css `
117
285
  }
118
286
  }
119
287
 
288
+ .agent-switch-indicator {
289
+ display: flex;
290
+ align-items: center;
291
+ gap: 8px;
292
+ margin: 8px 0;
293
+ color: var(--neutral-foreground-hint, var(--neutral-foreground-rest));
294
+ opacity: 60%;
295
+ font-size: 0.75em;
296
+ }
297
+
298
+ .agent-switch-indicator::before,
299
+ .agent-switch-indicator::after {
300
+ content: '';
301
+ flex: 1;
302
+ height: 1px;
303
+ background: currentColor;
304
+ }
305
+
306
+ .agent-switch-label {
307
+ white-space: nowrap;
308
+ }
309
+
120
310
  .message-row {
121
311
  display: flex;
122
312
  align-items: flex-start;
@@ -249,6 +439,12 @@ export const styles = css `
249
439
  font-size: 0.95em;
250
440
  }
251
441
 
442
+ .payload.unknown-tool {
443
+ color: var(--error-color, #f87171);
444
+ border-left: 2px solid color-mix(in srgb, var(--error-color, #f87171) 40%, transparent);
445
+ padding-left: 8px;
446
+ }
447
+
252
448
  .input-row {
253
449
  display: flex;
254
450
  gap: calc(var(--design-unit) * 2px);
@@ -1,8 +1,22 @@
1
- import { html, repeat, when } from '@genesislcap/web-core';
1
+ import { isChatToolCallUnknown } from '@genesislcap/foundation-ai';
2
+ import { html, ref, repeat, when } from '@genesislcap/web-core';
2
3
  import { ANIMATION_DEFS } from './main.types';
4
+ function unknownToolPayload(tc) {
5
+ if (!isChatToolCallUnknown(tc))
6
+ return '';
7
+ const lines = [`${tc.name} — tool does not exist`];
8
+ if (tc.availableTools.length > 0) {
9
+ lines.push('Available tools:');
10
+ tc.availableTools.forEach((t) => lines.push(` • ${t}`));
11
+ }
12
+ return lines.join('\n');
13
+ }
3
14
  const animationItemRenderer = (option) => html `
4
15
  <span part="option-label" title="${() => option.tooltip}">${() => option.label}</span>
5
16
  `;
17
+ const HALO_SPEED_DEFAULT = 1.5;
18
+ const HALO_SPEED_ORCHESTRATING = 0.4;
19
+ const HALO_BORDER_SIZE_DEFAULT = 3;
6
20
  const animationOptions = Object.entries(ANIMATION_DEFS).map(([value, def]) => ({
7
21
  value,
8
22
  label: def.label,
@@ -28,6 +42,8 @@ const genesisIconTemplate = html `
28
42
  */
29
43
  const messageType = (m) => {
30
44
  switch (true) {
45
+ case m.role === 'system-event':
46
+ return 'agent-switch';
31
47
  case m.role === 'user':
32
48
  return 'user';
33
49
  case !!m.thinking:
@@ -54,32 +70,34 @@ export const FoundationAiAssistantTemplate = (designSystemPrefix) => {
54
70
  const multiselectTag = `${designSystemPrefix}-multiselect`;
55
71
  const textareaTag = `${designSystemPrefix}-text-area`;
56
72
  const iconTag = `${designSystemPrefix}-icon`;
73
+ const progressTag = `${designSystemPrefix}-progress`;
57
74
  return html `
58
75
  <div class="chat-wrapper" part="chat-wrapper">
59
- ${when((x) => !!x.chatConfig.acceptedFiles, html `
76
+ ${when((x) => { var _a; return !!((_a = x.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.acceptedFiles); }, html `
60
77
  <input
61
78
  class="file-input"
62
79
  type="file"
63
80
  multiple
64
- accept=${(x) => x.chatConfig.acceptedFiles}
81
+ accept=${(x) => { var _a; return (_a = x.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.acceptedFiles; }}
65
82
  @change=${(x, c) => x.handleFileSelect(c.event)}
66
83
  />
67
84
  `)}
68
- ${when((x) => !!x.popoutMode ||
69
- x.chatConfig.showToolCalls != null ||
70
- x.chatConfig.showThinkingSteps != null ||
71
- x.chatConfig.allowDebugDownload === true ||
72
- x.chatConfig.animations != null, html `
85
+ ${when((x) => {
86
+ var _a, _b, _c, _d, _e;
87
+ return !!x.popoutMode ||
88
+ ((_a = x.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.showToolCalls) != null ||
89
+ ((_b = x.chatConfig.ui) === null || _b === void 0 ? void 0 : _b.showThinkingSteps) != null ||
90
+ ((_c = x.chatConfig.ui) === null || _c === void 0 ? void 0 : _c.showAgentSwitchIndicator) != null ||
91
+ ((_d = x.chatConfig.ui) === null || _d === void 0 ? void 0 : _d.allowDebugDownload) === true ||
92
+ ((_e = x.chatConfig.ui) === null || _e === void 0 ? void 0 : _e.animations) != null;
93
+ }, html `
73
94
  <div
74
- class="chat-header ${(x) => (x.popoutMode === 'expand' ? 'draggable' : '')}"
95
+ class="chat-header ${(x) => (x.popoutMode !== 'collapse' ? 'draggable' : '')}"
75
96
  part="chat-header"
76
97
  @mousedown="${(x, c) => x.onChatHeaderMouseDown(c.event)}"
77
98
  >
78
- ${when((x) => { var _a, _b, _c; return !!((_a = x.headerTitle) !== null && _a !== void 0 ? _a : (_c = (_b = x.agents) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.name); }, html `
79
- <!-- TODO: multi-agent orchestration — bind to active agent name rather than always agents[0] -->
80
- <span class="chat-title" part="chat-title">
81
- ${(x) => { var _a, _b, _c; return (_a = x.headerTitle) !== null && _a !== void 0 ? _a : (_c = (_b = x.agents) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.name; }}
82
- </span>
99
+ ${when((x) => !!x.headerTitle, html `
100
+ <span class="chat-title" part="chat-title">${(x) => x.headerTitle}</span>
83
101
  `)}
84
102
  <div class="header-actions" part="header-actions">
85
103
  <slot name="header-actions-extra"></slot>
@@ -93,10 +111,14 @@ export const FoundationAiAssistantTemplate = (designSystemPrefix) => {
93
111
  @click=${(x) => x.handlePopout()}
94
112
  ><${iconTag} name="${(x) => (x.popoutMode === 'expand' ? 'external-link-alt' : 'compress-alt')}"></${iconTag}></${buttonTag}>
95
113
  `)}
96
- ${when((x) => x.chatConfig.showToolCalls != null ||
97
- x.chatConfig.showThinkingSteps != null ||
98
- x.chatConfig.allowDebugDownload === true ||
99
- x.chatConfig.animations != null, html `
114
+ ${when((x) => {
115
+ var _a, _b, _c, _d, _e;
116
+ return ((_a = x.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.showToolCalls) != null ||
117
+ ((_b = x.chatConfig.ui) === null || _b === void 0 ? void 0 : _b.showThinkingSteps) != null ||
118
+ ((_c = x.chatConfig.ui) === null || _c === void 0 ? void 0 : _c.showAgentSwitchIndicator) != null ||
119
+ ((_d = x.chatConfig.ui) === null || _d === void 0 ? void 0 : _d.allowDebugDownload) === true ||
120
+ ((_e = x.chatConfig.ui) === null || _e === void 0 ? void 0 : _e.animations) != null;
121
+ }, html `
100
122
  <${buttonTag}
101
123
  class="settings-button"
102
124
  part="settings-button"
@@ -109,28 +131,35 @@ export const FoundationAiAssistantTemplate = (designSystemPrefix) => {
109
131
  </div>
110
132
  ${when((x) => x.settingsOpen, html `
111
133
  <div class="settings-panel" part="settings-panel">
112
- ${when((x) => x.chatConfig.showToolCalls != null, html `
134
+ ${when((x) => { var _a; return ((_a = x.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.showToolCalls) != null; }, html `
113
135
  <${switchTag}
114
136
  part="toggle-tool-calls"
115
137
  :checked=${(x) => x.showToolCalls}
116
138
  @change=${(x) => x.toggleShowToolCalls()}
117
139
  >Tool calls</${switchTag}>
118
140
  `)}
119
- ${when((x) => x.chatConfig.showThinkingSteps != null, html `
141
+ ${when((x) => { var _a; return ((_a = x.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.showThinkingSteps) != null; }, html `
120
142
  <${switchTag}
121
143
  part="toggle-thinking"
122
144
  :checked=${(x) => x.showThinkingSteps}
123
145
  @change=${(x) => x.toggleShowThinkingSteps()}
124
146
  >Thinking</${switchTag}>
125
147
  `)}
126
- ${when((x) => x.chatConfig.allowDebugDownload === true, html `
148
+ ${when((x) => { var _a; return ((_a = x.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.showAgentSwitchIndicator) != null; }, html `
149
+ <${switchTag}
150
+ part="toggle-agent-switch"
151
+ :checked=${(x) => x.showAgentSwitchIndicator}
152
+ @change=${(x) => x.toggleShowAgentSwitchIndicator()}
153
+ >Agents</${switchTag}>
154
+ `)}
155
+ ${when((x) => { var _a; return ((_a = x.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.allowDebugDownload) === true; }, html `
127
156
  <${buttonTag}
128
157
  part="download-button"
129
158
  appearance="stealth"
130
- @click=${(x) => x.downloadHistory()}
131
- >Download debug state</${buttonTag}>
159
+ @click=${(x) => x.downloadDebugLog()}
160
+ >Download agent log</${buttonTag}>
132
161
  `)}
133
- ${when((x) => { var _a; return ((_a = x.chatConfig.animations) === null || _a === void 0 ? void 0 : _a.userConfigurable) === true; }, html `
162
+ ${when((x) => { var _a, _b; return ((_b = (_a = x.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.animations) === null || _b === void 0 ? void 0 : _b.userConfigurable) === true; }, html `
134
163
  <div class="settings-animations">
135
164
  <span class="settings-label">Animations</span>
136
165
  <${multiselectTag}
@@ -147,49 +176,110 @@ export const FoundationAiAssistantTemplate = (designSystemPrefix) => {
147
176
  </div>
148
177
  `)}
149
178
  `)}
179
+ ${when((x) => {
180
+ var _a;
181
+ return x.settingsOpen &&
182
+ ((_a = x.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.showContextUsage) !== false &&
183
+ x.contextTokens != null &&
184
+ x.contextLimit != null;
185
+ }, html `
186
+ <div class="context-usage" part="context-usage">
187
+ <div class="context-usage-header">
188
+ <span class="context-usage-label">Context window</span>
189
+ <span class="context-usage-tokens">
190
+ ${(x) => x.contextTokens.toLocaleString()} /
191
+ ${(x) => x.contextLimit.toLocaleString()} tokens
192
+ (${(x) => Math.round((x.contextTokens / x.contextLimit) * 100)}%)
193
+ </span>
194
+ </div>
195
+ <${progressTag}
196
+ part="context-progress"
197
+ value="${(x) => Math.min(100, Math.round((x.contextTokens / x.contextLimit) * 100))}"
198
+ ></${progressTag}>
199
+ </div>
200
+ `)}
150
201
 
151
- <div class="messages" part="messages">
202
+ <div class="splash-wrapper" part="splash">
203
+ <slot name="splash"></slot>
204
+ </div>
205
+
206
+ <div class="messages" part="messages" ${ref('messagesEl')}>
152
207
 
153
208
  ${repeat((x) => x.visibleMessages, html `
154
- <div class="message-row ${(m) => messageType(m)}">
155
- ${when((m) => m.role !== 'user', html `
156
- <div class="avatar ${(m) => messageType(m)}">
157
- ${when((m, c) => !!c.parent.imageSrc, html `
158
- <img
159
- src="${(m, c) => c.parent.imageSrc}"
160
- alt="Assistant"
161
- class="avatar-img"
162
- />
163
- `)}
164
- ${when((m, c) => !c.parent.imageSrc, genesisIconTemplate)}
165
- </div>
166
- `)}
167
- <div class="message ${(m) => messageType(m)}">
168
- <div class="sender">${(m) => senderLabel[messageType(m)]}</div>
169
- <div class="content">
170
- ${when((m) => m.content, html `
171
- <ai-chat-markdown :content="${(m) => m.content}"></ai-chat-markdown>
209
+ ${when((m) => m.role === 'system-event', html `
210
+ <div class="agent-switch-indicator" part="agent-switch-indicator">
211
+ <span class="agent-switch-label">${(m) => m.content}</span>
212
+ </div>
213
+ `)}
214
+ ${when((m) => m.role !== 'system-event', html `
215
+ <div class="message-row ${(m) => messageType(m)}">
216
+ ${when((m) => m.role !== 'user', html `
217
+ <div class="avatar ${(m) => messageType(m)}">
218
+ ${when((m, c) => !!c.parent.imageSrc, html `
219
+ <img
220
+ src="${(m, c) => c.parent.imageSrc}"
221
+ alt="Assistant"
222
+ class="avatar-img"
223
+ />
224
+ `)}
225
+ ${when((m, c) => !c.parent.imageSrc, genesisIconTemplate)}
226
+ </div>
172
227
  `)}
173
- ${when((m) => m.toolCalls, html `
174
- ${repeat((m) => { var _a; return (_a = m.toolCalls) !== null && _a !== void 0 ? _a : []; }, html `
175
- <pre class="payload"><strong>${(tc) => tc.name}</strong>(${(tc) => JSON.stringify(tc.args, null, 2)})</pre>
228
+ <div class="message ${(m) => messageType(m)}">
229
+ <div class="sender">
230
+ ${(m, c) => messageType(m) === 'ai-function' &&
231
+ m.agentName &&
232
+ c.parent.showAgentSwitchIndicator
233
+ ? `Tool Call · ${m.agentName}`
234
+ : senderLabel[messageType(m)]}
235
+ </div>
236
+ <div class="content">
237
+ ${when((m) => m.content, html `
238
+ <ai-chat-markdown :content="${(m) => m.content}"></ai-chat-markdown>
176
239
  `)}
177
- `)}
178
- ${when((m) => m.interaction, html `
179
- <ai-chat-interaction-wrapper
180
- :componentName=${(m) => m.interaction.componentName}
181
- :data=${(m) => m.interaction.data}
182
- :interactionId=${(m) => m.interaction.interactionId}
183
- :resolved=${(m) => !!m.interaction.resolved}
184
- @interaction-completed=${(m, c) => c.parent.handleInteractionCompleted(c.event)}
185
- ></ai-chat-interaction-wrapper>
186
- `)}
240
+ ${when((m) => m.toolCalls, html `
241
+ ${repeat((m) => { var _a; return (_a = m.toolCalls) !== null && _a !== void 0 ? _a : []; }, html `
242
+ ${when((tc) => tc.foldEvent === 'open', html `
243
+ <pre class="payload fold-event fold-open">
244
+ &#9658; ${(tc) => tc.name}</pre
245
+ >
246
+ `)}
247
+ ${when((tc) => tc.foldEvent === 'close', html `
248
+ <pre class="payload fold-event fold-close">
249
+ &#9668; ${(tc) => tc.name}</pre
250
+ >
251
+ `)}
252
+ ${when((tc) => !tc.foldEvent && !isChatToolCallUnknown(tc), html `
253
+ <pre class="payload">
254
+ ${(tc) => { var _a; return (((_a = tc.foldPath) === null || _a === void 0 ? void 0 : _a.length) ? `${tc.foldPath.join(' › ')} › ` : ''); }}<strong>${(tc) => tc.name}</strong>(${(tc) => JSON.stringify(tc.args, null, 2)})</pre
255
+ >
256
+ `)}
257
+ ${when(isChatToolCallUnknown, html `
258
+ <pre class="payload unknown-tool">
259
+ ${(tc) => unknownToolPayload(tc)}</pre
260
+ >
261
+ `)}
262
+ `)}
263
+ `)}
264
+ ${when((m) => m.interaction, html `
265
+ <ai-chat-interaction-wrapper
266
+ :componentName=${(m) => m.interaction.componentName}
267
+ :data=${(m) => m.interaction.data}
268
+ :interactionId=${(m) => m.interaction.interactionId}
269
+ :resolved=${(m) => m.interaction.resolved}
270
+ @interaction-completed=${(m, c) => c.parent.handleInteractionCompleted(c.event)}
271
+ ></ai-chat-interaction-wrapper>
272
+ `)}
273
+ </div>
274
+ </div>
187
275
  </div>
188
- </div>
189
- </div>
276
+ `)}
190
277
  `)}
191
- ${when((x) => x.showLoadingIndicator &&
192
- (x.chatConfig.animations == null || x.enabledAnimations.includes('loading')), html `
278
+ ${when((x) => {
279
+ var _a;
280
+ return x.showLoadingIndicator &&
281
+ (((_a = x.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.animations) == null || x.enabledAnimations.includes('loading'));
282
+ }, html `
193
283
  <div class="message-row ai" part="thinking">
194
284
  <div class="avatar">
195
285
  ${when((x) => !!x.imageSrc, html `
@@ -236,13 +326,20 @@ export const FoundationAiAssistantTemplate = (designSystemPrefix) => {
236
326
  </div>
237
327
  `)}
238
328
 
329
+ ${when((x) => { var _a; return ((_a = x.chatConfig.suggestions) === null || _a === void 0 ? void 0 : _a.behavior) !== 'never'; }, html `
330
+ <chat-suggestions
331
+ :state="${(x) => x.suggestionsState}"
332
+ @suggestion-clicked="${(x, c) => x.handleSuggestionClick(c.event.detail)}"
333
+ ></chat-suggestions>
334
+ `)}
335
+
239
336
  <div class="input-row" part="input-row">
240
- ${when((x) => !!x.chatConfig.acceptedFiles, html `
337
+ ${when((x) => { var _a; return !!((_a = x.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.acceptedFiles); }, html `
241
338
  <${buttonTag}
242
339
  class="attach-button"
243
340
  part="attach-button"
244
341
  appearance="stealth"
245
- title=${(x) => `Attach file (${x.chatConfig.acceptedFiles})`}
342
+ title=${(x) => { var _a; return `Attach file (${(_a = x.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.acceptedFiles})`; }}
246
343
  ?disabled=${(x) => x.state === 'loading'}
247
344
  @click=${(x) => x.triggerFileInput()}
248
345
  ><${iconTag} name="paperclip"></${iconTag}></${buttonTag}>
@@ -273,7 +370,10 @@ export const FoundationAiAssistantTemplate = (designSystemPrefix) => {
273
370
  </div>
274
371
  <ai-halo-overlay
275
372
  part="halo-overlay"
276
- ?active=${(x) => x.showHalo && x.enabledAnimations.includes('halo')}
373
+ ?active=${(x) => x.showHalo !== 'no' && x.enabledAnimations.includes('halo')}
374
+ :speed="${(x) => (x.showHalo === 'orchestrating' ? HALO_SPEED_ORCHESTRATING : HALO_SPEED_DEFAULT)}"
375
+ direction="${(x) => (x.showHalo === 'orchestrating' ? 'ccw' : 'cw')}"
376
+ :borderSize="${(x) => (x.showHalo === 'orchestrating' ? 2 : HALO_BORDER_SIZE_DEFAULT)}"
277
377
  ></ai-halo-overlay>
278
378
  </div>
279
379
  `;
@@ -0,0 +1,54 @@
1
+ import { createSlice } from '@genesislcap/foundation-redux';
2
+ export const defaultSessionState = {
3
+ messages: [],
4
+ state: 'idle',
5
+ showToolCalls: false,
6
+ showThinkingSteps: false,
7
+ showAgentSwitchIndicator: false,
8
+ enabledAnimations: [],
9
+ suggestionsState: { status: 'idle' },
10
+ contextTokens: undefined,
11
+ contextLimit: undefined,
12
+ activeAgent: undefined,
13
+ inputValue: '',
14
+ };
15
+ export const aiAssistantSlice = createSlice({
16
+ name: 'aiAssistant',
17
+ initialState: defaultSessionState,
18
+ reducers: {
19
+ setMessages(state, action) {
20
+ state.messages = action.payload;
21
+ },
22
+ setState(state, action) {
23
+ state.state = action.payload;
24
+ },
25
+ setShowToolCalls(state, action) {
26
+ state.showToolCalls = action.payload;
27
+ },
28
+ setShowThinkingSteps(state, action) {
29
+ state.showThinkingSteps = action.payload;
30
+ },
31
+ setShowAgentSwitchIndicator(state, action) {
32
+ state.showAgentSwitchIndicator = action.payload;
33
+ },
34
+ setEnabledAnimations(state, action) {
35
+ state.enabledAnimations = action.payload;
36
+ },
37
+ setSuggestionsState(state, action) {
38
+ state.suggestionsState = action.payload;
39
+ },
40
+ setContextTokens(state, action) {
41
+ state.contextTokens = action.payload;
42
+ },
43
+ setContextLimit(state, action) {
44
+ state.contextLimit = action.payload;
45
+ },
46
+ setActiveAgent(state, action) {
47
+ state.activeAgent = action.payload;
48
+ },
49
+ setInputValue(state, action) {
50
+ state.inputValue = action.payload;
51
+ },
52
+ },
53
+ selectors: {},
54
+ });
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Module-level registry of AI drivers keyed by session identity string.
3
+ *
4
+ * Driver lifetime is tied to the conversation session, not the DOM.
5
+ * Components look up or create drivers on connect and only unwire
6
+ * event listeners on disconnect — the driver is never destroyed by
7
+ * a lifecycle event.
8
+ *
9
+ * @internal
10
+ */
11
+ const driverRegistry = new Map();
12
+ /**
13
+ * Returns the existing driver for the given key, or calls the factory
14
+ * to create and register a new one.
15
+ */
16
+ export function getOrCreateDriver(key, factory) {
17
+ const existing = driverRegistry.get(key);
18
+ if (existing)
19
+ return existing;
20
+ const driver = factory();
21
+ driverRegistry.set(key, driver);
22
+ return driver;
23
+ }
24
+ /**
25
+ * Returns the existing driver for the given key, or `undefined`.
26
+ */
27
+ export function getDriver(key) {
28
+ return driverRegistry.get(key);
29
+ }
30
+ /**
31
+ * Removes and returns the driver for the given key.
32
+ * Used when agents change (driver must be rebuilt) or on explicit session teardown.
33
+ */
34
+ export function deleteDriver(key) {
35
+ const driver = driverRegistry.get(key);
36
+ driverRegistry.delete(key);
37
+ return driver;
38
+ }
39
+ /**
40
+ * Removes all entries. Exposed for test isolation only — not part of the public API.
41
+ *
42
+ * @internal
43
+ */
44
+ export function clearDriverRegistry() {
45
+ driverRegistry.clear();
46
+ }