@genesislcap/ai-assistant 14.479.0 → 14.480.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-assistant.api.json +546 -2
- package/dist/ai-assistant.d.ts +73 -1
- package/dist/dts/components/ai-driver/ai-driver.d.ts +8 -0
- package/dist/dts/components/ai-driver/ai-driver.d.ts.map +1 -1
- package/dist/dts/components/chat-driver/chat-driver.d.ts +32 -0
- package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -1
- package/dist/dts/components/chat-driver/chat-driver.test.d.ts.map +1 -1
- package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.d.ts +7 -0
- package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.d.ts.map +1 -1
- package/dist/dts/components/flowing-waves-indicator.d.ts +1 -1
- package/dist/dts/components/flowing-waves-indicator.d.ts.map +1 -1
- package/dist/dts/components/halo-overlay.d.ts +1 -0
- package/dist/dts/components/halo-overlay.d.ts.map +1 -1
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts +4 -0
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts.map +1 -1
- package/dist/dts/components/settings-modal/settings-modal.styles.d.ts.map +1 -1
- package/dist/dts/components/settings-modal/settings-modal.template.d.ts.map +1 -1
- package/dist/dts/components/waves-indicator.d.ts +1 -1
- package/dist/dts/components/waves-indicator.d.ts.map +1 -1
- package/dist/dts/main/main.d.ts +29 -1
- package/dist/dts/main/main.d.ts.map +1 -1
- package/dist/dts/main/main.styles.d.ts.map +1 -1
- package/dist/dts/main/main.template.d.ts.map +1 -1
- package/dist/dts/state/ai-assistant-slice.d.ts +2 -0
- package/dist/dts/state/ai-assistant-slice.d.ts.map +1 -1
- package/dist/dts/state/session-store.d.ts +1 -0
- package/dist/dts/state/session-store.d.ts.map +1 -1
- package/dist/dts/utils/message-partition.d.ts +1 -0
- package/dist/dts/utils/message-partition.d.ts.map +1 -1
- package/dist/esm/components/chat-driver/chat-driver.js +88 -8
- package/dist/esm/components/chat-driver/chat-driver.test.js +104 -0
- package/dist/esm/components/chat-interaction-wrapper/chat-interaction-wrapper.js +48 -6
- package/dist/esm/components/chat-interaction-wrapper/chat-interaction-wrapper.test.js +62 -0
- package/dist/esm/components/flowing-waves-indicator.js +22 -11
- package/dist/esm/components/halo-overlay.js +30 -6
- package/dist/esm/components/orchestrating-driver/orchestrating-driver.js +14 -0
- package/dist/esm/components/settings-modal/settings-modal.styles.js +7 -0
- package/dist/esm/components/settings-modal/settings-modal.template.js +18 -8
- package/dist/esm/components/waves-indicator.js +22 -8
- package/dist/esm/main/main.js +73 -6
- package/dist/esm/main/main.styles.js +6 -0
- package/dist/esm/main/main.template.js +21 -2
- package/dist/esm/state/ai-assistant-slice.js +4 -0
- package/dist/esm/utils/message-partition.js +10 -1
- package/dist/esm/utils/message-partition.test.js +2 -0
- package/package.json +17 -17
- package/src/components/ai-driver/ai-driver.ts +17 -0
- package/src/components/chat-driver/chat-driver.test.ts +143 -0
- package/src/components/chat-driver/chat-driver.ts +105 -4
- package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.test.ts +81 -0
- package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.ts +51 -5
- package/src/components/flowing-waves-indicator.ts +26 -6
- package/src/components/halo-overlay.ts +31 -6
- package/src/components/orchestrating-driver/orchestrating-driver.ts +18 -0
- package/src/components/settings-modal/settings-modal.styles.ts +7 -0
- package/src/components/settings-modal/settings-modal.template.ts +36 -16
- package/src/components/waves-indicator.ts +25 -5
- package/src/main/main.styles.ts +6 -0
- package/src/main/main.template.ts +25 -2
- package/src/main/main.ts +65 -1
- package/src/state/ai-assistant-slice.ts +5 -0
- package/src/utils/message-partition.test.ts +2 -0
- package/src/utils/message-partition.ts +11 -1
|
@@ -89,6 +89,12 @@ const messageType = (m: ChatMessage): string => {
|
|
|
89
89
|
// outcome; render it identically to a real user message.
|
|
90
90
|
case m.role === 'synthetic-user':
|
|
91
91
|
return 'user';
|
|
92
|
+
// Reasoning (chain-of-thought summary) and narration (interstitial prose the model emits
|
|
93
|
+
// alongside a tool call) both render as the "Thinking" bubble — visually set apart from a real
|
|
94
|
+
// final agent reply, even though they stay separate categories (own log entry + own toggle:
|
|
95
|
+
// `showThinkingSteps` vs `showNarration`). `thinking` is the legacy flag for pre-split sessions.
|
|
96
|
+
case m.category === 'reasoning':
|
|
97
|
+
case m.category === 'narration':
|
|
92
98
|
case !!m.thinking:
|
|
93
99
|
return 'ai-thinking';
|
|
94
100
|
case !!m.toolCalls:
|
|
@@ -108,6 +114,14 @@ const senderLabel: Record<string, string> = {
|
|
|
108
114
|
ai: 'Assistant',
|
|
109
115
|
};
|
|
110
116
|
|
|
117
|
+
/**
|
|
118
|
+
* Sender label for a message. Reasoning and narration share the "Thinking" bubble, so the label is
|
|
119
|
+
* the only thing that tells them apart: narration reads "Working" (the action it narrates alongside
|
|
120
|
+
* its tool call), reasoning stays "Thinking". Everything else uses the {@link messageType} map.
|
|
121
|
+
*/
|
|
122
|
+
const senderLabelFor = (m: ChatMessage): string =>
|
|
123
|
+
m.category === 'narration' ? 'Working' : senderLabel[messageType(m)];
|
|
124
|
+
|
|
111
125
|
/**
|
|
112
126
|
* Resolves how the host frames an interaction widget, defaulting to `'label'`
|
|
113
127
|
* (the historical "Assistant" label, no bubble). Callers guard on
|
|
@@ -293,7 +307,10 @@ ${(tc) => (tc.foldPath?.length ? `${tc.foldPath.join(' › ')} › ` : '')}<stro
|
|
|
293
307
|
userAvatarTemplate,
|
|
294
308
|
)}${when((m) => messageType(m) !== 'user', assistantAvatarTemplate)}
|
|
295
309
|
</div>
|
|
296
|
-
<div
|
|
310
|
+
<div
|
|
311
|
+
class="message ${(m) => messageType(m)} ${(m) =>
|
|
312
|
+
classNames(['narration', m.category === 'narration'])}"
|
|
313
|
+
>
|
|
297
314
|
${when(
|
|
298
315
|
// A 'bare' interaction owns its full presentation — suppress the
|
|
299
316
|
// host sender label. Every other message keeps it.
|
|
@@ -305,7 +322,7 @@ ${(tc) => (tc.foldPath?.length ? `${tc.foldPath.join(' › ')} › ` : '')}<stro
|
|
|
305
322
|
m.agentName &&
|
|
306
323
|
(c.parent as FoundationAiAssistant).showAgentSwitchIndicator
|
|
307
324
|
? `Tool Call · ${m.agentLabel ?? m.agentName}`
|
|
308
|
-
:
|
|
325
|
+
: senderLabelFor(m)}
|
|
309
326
|
</div>
|
|
310
327
|
`,
|
|
311
328
|
)}
|
|
@@ -326,6 +343,12 @@ ${(tc) => (tc.foldPath?.length ? `${tc.foldPath.join(' › ')} › ` : '')}<stro
|
|
|
326
343
|
(m) => m.interaction,
|
|
327
344
|
html<ChatMessage, FoundationAiAssistant>`
|
|
328
345
|
<ai-chat-interaction-wrapper
|
|
346
|
+
${
|
|
347
|
+
/* GENC-1410: bind ownerHostId BEFORE componentName/interactionId — bindings
|
|
348
|
+
apply in template order and both trigger renderComponent, so the ownership
|
|
349
|
+
gate prop must be set by the time the first render runs. */ ''
|
|
350
|
+
}
|
|
351
|
+
:ownerHostId=${(m) => m.interaction!.ownerHostId ?? ''}
|
|
329
352
|
:componentName=${(m) => m.interaction!.componentName}
|
|
330
353
|
:data=${(m) => m.interaction!.data}
|
|
331
354
|
:interactionId=${(m) => m.interaction!.interactionId}
|
package/src/main/main.ts
CHANGED
|
@@ -409,6 +409,14 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
409
409
|
this._sessionRef?.actions.aiAssistant.setShowThinkingSteps(value);
|
|
410
410
|
}
|
|
411
411
|
|
|
412
|
+
/** Current user-facing toggle state for narration (interstitial prose) visibility. */
|
|
413
|
+
get showNarration(): boolean {
|
|
414
|
+
return this._sessionRef?.store.aiAssistant.showNarration ?? false;
|
|
415
|
+
}
|
|
416
|
+
set showNarration(value: boolean) {
|
|
417
|
+
this._sessionRef?.actions.aiAssistant.setShowNarration(value);
|
|
418
|
+
}
|
|
419
|
+
|
|
412
420
|
/** Current user-facing toggle state for agent switch indicator visibility. */
|
|
413
421
|
get showAgentSwitchIndicator(): boolean {
|
|
414
422
|
return this._sessionRef?.store.aiAssistant.showAgentSwitchIndicator ?? false;
|
|
@@ -786,8 +794,18 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
786
794
|
* to avoid stealing focus from elsewhere on the page.
|
|
787
795
|
*/
|
|
788
796
|
@observable isEngaged: boolean = true;
|
|
797
|
+
/**
|
|
798
|
+
* GENC-1410 (Error 3): stable id for this assistant view. Reported to the shared driver so it
|
|
799
|
+
* can elect a single owner to render (and run) each interaction's widget when more than one view
|
|
800
|
+
* (e.g. a bubble and a docked panel) is wired to the same driver — so it fires once, not per view.
|
|
801
|
+
*/
|
|
802
|
+
private readonly hostId = crypto.randomUUID();
|
|
789
803
|
private _handleGlobalInteraction = (e: Event) => {
|
|
790
804
|
const next = e.composedPath().includes(this);
|
|
805
|
+
// GENC-1410: sticky "last active host" — whenever the user interacts inside THIS view, claim
|
|
806
|
+
// ownership on the shared driver. Unlike `isEngaged` this does NOT clear when the user later
|
|
807
|
+
// clicks elsewhere in the layout; it only moves when they use a *different* assistant view.
|
|
808
|
+
if (next) this.driver?.setActiveHost?.(this.hostId);
|
|
791
809
|
if (next === this.isEngaged) return;
|
|
792
810
|
this.isEngaged = next;
|
|
793
811
|
};
|
|
@@ -837,7 +855,7 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
837
855
|
}
|
|
838
856
|
}
|
|
839
857
|
|
|
840
|
-
/** True when there is a pending (unresolved) interaction
|
|
858
|
+
/** True when there is a pending (unresolved) interaction. */
|
|
841
859
|
get hasActivePendingInteraction(): boolean {
|
|
842
860
|
if (!this.busy) return false;
|
|
843
861
|
const last = this.messages[this.messages.length - 1];
|
|
@@ -892,6 +910,7 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
892
910
|
return filterVisibleMessages(listed, {
|
|
893
911
|
showToolCalls: this.showToolCalls,
|
|
894
912
|
showThinkingSteps: this.showThinkingSteps,
|
|
913
|
+
showNarration: this.showNarration,
|
|
895
914
|
showAgentSwitchIndicator,
|
|
896
915
|
});
|
|
897
916
|
}
|
|
@@ -1082,6 +1101,10 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
1082
1101
|
// Idempotent — unwire previous listeners on this component before re-wiring.
|
|
1083
1102
|
this.unwireDriver();
|
|
1084
1103
|
|
|
1104
|
+
// GENC-1410 (Error 3): announce this view to the (shared) driver so it can elect a single
|
|
1105
|
+
// owner for side-effecting interactions when more than one view is wired to it.
|
|
1106
|
+
driver.registerHost?.(this.hostId);
|
|
1107
|
+
|
|
1085
1108
|
const onHistoryUpdated = (e: Event) => {
|
|
1086
1109
|
this.messages = [...(e as CustomEvent<ChatMessage[]>).detail];
|
|
1087
1110
|
};
|
|
@@ -1166,6 +1189,7 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
1166
1189
|
driver.addEventListener('provider-changed', onProviderChanged);
|
|
1167
1190
|
|
|
1168
1191
|
const cleanups: (() => void)[] = [
|
|
1192
|
+
() => driver.unregisterHost?.(this.hostId),
|
|
1169
1193
|
() => driver.removeEventListener('history-updated', onHistoryUpdated),
|
|
1170
1194
|
() => driver.removeEventListener('sub-agent-history-updated', onSubAgentHistoryUpdated),
|
|
1171
1195
|
() => driver.removeEventListener('sub-agent-start', onSubAgentStart),
|
|
@@ -1257,6 +1281,7 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
1257
1281
|
const ui = this.chatConfig.ui ?? {};
|
|
1258
1282
|
this.showToolCalls = ui.showToolCalls === true;
|
|
1259
1283
|
this.showThinkingSteps = ui.showThinkingSteps === true;
|
|
1284
|
+
this.showNarration = ui.showNarration === true;
|
|
1260
1285
|
this.showAgentSwitchIndicator = ui.showAgentSwitchIndicator === true;
|
|
1261
1286
|
this.enabledAnimations = resolveExclusiveLoadingStyle(
|
|
1262
1287
|
(ui.animations?.enabled as AiAssistantAnimation[]) ??
|
|
@@ -1288,6 +1313,11 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
1288
1313
|
this.logMeta('driver.wired', { reason: 'popin' });
|
|
1289
1314
|
this.wireDriver();
|
|
1290
1315
|
this._finalizeCostSessionOnPageHide = true;
|
|
1316
|
+
// GENC-1410: popin makes THIS (bubble) view the visible one. Take interaction ownership on
|
|
1317
|
+
// the shared driver so it elects this view to render widgets — not the now-hidden docked
|
|
1318
|
+
// panel. Ownership transfers as part of the dock lifecycle, deterministically, rather than
|
|
1319
|
+
// being inferred from wherever the user last happened to click.
|
|
1320
|
+
this.driver?.setActiveHost?.(this.hostId);
|
|
1291
1321
|
});
|
|
1292
1322
|
this.unsubBus = () => {
|
|
1293
1323
|
unsubPopout();
|
|
@@ -1295,6 +1325,11 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
1295
1325
|
};
|
|
1296
1326
|
} else if (this.popoutMode === 'collapse') {
|
|
1297
1327
|
this._finalizeCostSessionOnPageHide = true;
|
|
1328
|
+
// GENC-1410: a collapse-mode view exists only while docked, and it connects (running this
|
|
1329
|
+
// setup) as part of `onDock` — the moment it becomes the visible view. Take interaction
|
|
1330
|
+
// ownership now so the shared driver renders widgets here, not in the hidden bubble. This is
|
|
1331
|
+
// the dock counterpart to the bubble's ownership claim on `chat-popin` above.
|
|
1332
|
+
this.driver?.setActiveHost?.(this.hostId);
|
|
1298
1333
|
const unsubPopout = agenticActivityBus.subscribe('chat-popout', () => {
|
|
1299
1334
|
this._finalizeCostSessionOnPageHide = true;
|
|
1300
1335
|
});
|
|
@@ -1595,6 +1630,30 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
1595
1630
|
}
|
|
1596
1631
|
|
|
1597
1632
|
/** Called when the user clicks the popout button. */
|
|
1633
|
+
/**
|
|
1634
|
+
* GENC-1410 (Error 3): claim the right to run a side-effecting interaction's server work for
|
|
1635
|
+
* THIS view, via the shared driver's first-writer-wins latch. Returns `true` when this view may
|
|
1636
|
+
* run it (first/owning claimant, or a re-render of the same view), `false` when another view
|
|
1637
|
+
* already holds it. Fail-open when the driver has no latch. Called by `ai-chat-interaction-wrapper`.
|
|
1638
|
+
*/
|
|
1639
|
+
claimSideEffect(interactionId: string): boolean {
|
|
1640
|
+
return this.driver?.claimSideEffect?.(interactionId, this.hostId) ?? true;
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
/**
|
|
1644
|
+
* GENC-1410 (Error 3): mark THIS view as the interaction owner on the shared driver, so it (not a
|
|
1645
|
+
* sibling view sharing the same driver) is elected to render side-effecting widgets. The bubble
|
|
1646
|
+
* and docked panel normally claim this automatically on the pop-in / dock lifecycle, but that
|
|
1647
|
+
* lifecycle only runs once `popout-mode` is active. A host that establishes a view outside that
|
|
1648
|
+
* lifecycle — e.g. a page that restores a docked assistant from a persisted layout before the
|
|
1649
|
+
* pop-out affordance is enabled — calls this to seize ownership deterministically rather than
|
|
1650
|
+
* leaving it to the last-interaction / first-registered fallback. Idempotent; safe to call on
|
|
1651
|
+
* every layout (re)establish. No-op if the driver was never wired.
|
|
1652
|
+
*/
|
|
1653
|
+
claimInteractionOwnership(): void {
|
|
1654
|
+
this.driver?.setActiveHost?.(this.hostId);
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1598
1657
|
handlePopout(): void {
|
|
1599
1658
|
if (this.popoutMode === 'expand') {
|
|
1600
1659
|
this.logMeta('assistant.popout');
|
|
@@ -1787,6 +1846,7 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
1787
1846
|
!!this.headerTitle ||
|
|
1788
1847
|
ui?.showToolCalls != null ||
|
|
1789
1848
|
ui?.showThinkingSteps != null ||
|
|
1849
|
+
ui?.showNarration != null ||
|
|
1790
1850
|
ui?.showAgentSwitchIndicator != null ||
|
|
1791
1851
|
ui?.allowDebugDownload === true ||
|
|
1792
1852
|
ui?.animations != null ||
|
|
@@ -1985,6 +2045,10 @@ export class FoundationAiAssistant extends GenesisElement {
|
|
|
1985
2045
|
this.showThinkingSteps = !this.showThinkingSteps;
|
|
1986
2046
|
}
|
|
1987
2047
|
|
|
2048
|
+
toggleShowNarration() {
|
|
2049
|
+
this.showNarration = !this.showNarration;
|
|
2050
|
+
}
|
|
2051
|
+
|
|
1988
2052
|
toggleShowAgentSwitchIndicator() {
|
|
1989
2053
|
this.showAgentSwitchIndicator = !this.showAgentSwitchIndicator;
|
|
1990
2054
|
}
|
|
@@ -34,6 +34,7 @@ export interface AiAssistantSessionState {
|
|
|
34
34
|
state: AiAssistantState;
|
|
35
35
|
showToolCalls: boolean;
|
|
36
36
|
showThinkingSteps: boolean;
|
|
37
|
+
showNarration: boolean;
|
|
37
38
|
showAgentSwitchIndicator: boolean;
|
|
38
39
|
enabledAnimations: AiAssistantAnimation[];
|
|
39
40
|
suggestionsState: SuggestionsState;
|
|
@@ -100,6 +101,7 @@ export const defaultSessionState: AiAssistantSessionState = {
|
|
|
100
101
|
state: 'idle',
|
|
101
102
|
showToolCalls: false,
|
|
102
103
|
showThinkingSteps: false,
|
|
104
|
+
showNarration: false,
|
|
103
105
|
showAgentSwitchIndicator: false,
|
|
104
106
|
enabledAnimations: [],
|
|
105
107
|
suggestionsState: { status: 'idle' },
|
|
@@ -136,6 +138,9 @@ export const aiAssistantSlice = createSlice({
|
|
|
136
138
|
setShowThinkingSteps(state, action: PayloadAction<boolean>) {
|
|
137
139
|
state.showThinkingSteps = action.payload;
|
|
138
140
|
},
|
|
141
|
+
setShowNarration(state, action: PayloadAction<boolean>) {
|
|
142
|
+
state.showNarration = action.payload;
|
|
143
|
+
},
|
|
139
144
|
setShowAgentSwitchIndicator(state, action: PayloadAction<boolean>) {
|
|
140
145
|
state.showAgentSwitchIndicator = action.payload;
|
|
141
146
|
},
|
|
@@ -22,11 +22,13 @@ const interaction = (interactionId: string, resolved = false): ChatInteraction =
|
|
|
22
22
|
const ALL_ON: MessageFilterState = {
|
|
23
23
|
showToolCalls: true,
|
|
24
24
|
showThinkingSteps: true,
|
|
25
|
+
showNarration: true,
|
|
25
26
|
showAgentSwitchIndicator: true,
|
|
26
27
|
};
|
|
27
28
|
const ALL_OFF: MessageFilterState = {
|
|
28
29
|
showToolCalls: false,
|
|
29
30
|
showThinkingSteps: false,
|
|
31
|
+
showNarration: false,
|
|
30
32
|
showAgentSwitchIndicator: false,
|
|
31
33
|
};
|
|
32
34
|
|
|
@@ -9,6 +9,7 @@ import type { ChatMessage } from '@genesislcap/foundation-ai';
|
|
|
9
9
|
export interface MessageFilterState {
|
|
10
10
|
showToolCalls: boolean;
|
|
11
11
|
showThinkingSteps: boolean;
|
|
12
|
+
showNarration: boolean;
|
|
12
13
|
showAgentSwitchIndicator: boolean;
|
|
13
14
|
}
|
|
14
15
|
|
|
@@ -52,7 +53,16 @@ export function filterVisibleMessages(
|
|
|
52
53
|
if (m.role === 'tool') {
|
|
53
54
|
return false;
|
|
54
55
|
}
|
|
55
|
-
//
|
|
56
|
+
// Reasoning (chain-of-thought summary) follows the thinking toggle.
|
|
57
|
+
if (m.category === 'reasoning') {
|
|
58
|
+
return toggles.showThinkingSteps;
|
|
59
|
+
}
|
|
60
|
+
// Narration (interstitial prose alongside a tool call) follows its own toggle.
|
|
61
|
+
if (m.category === 'narration') {
|
|
62
|
+
return toggles.showNarration;
|
|
63
|
+
}
|
|
64
|
+
// Legacy: sessions persisted before the reasoning/narration split tagged reasoning-ish
|
|
65
|
+
// content with `thinking`; keep honouring the flag so their history still hides it.
|
|
56
66
|
if (m.thinking && !toggles.showThinkingSteps) {
|
|
57
67
|
return false;
|
|
58
68
|
}
|