@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.
- package/dist/ai-assistant.api.json +4061 -1416
- package/dist/ai-assistant.d.ts +594 -81
- package/dist/dts/channel/ai-activity-channel.d.ts +4 -22
- package/dist/dts/channel/ai-activity-channel.d.ts.map +1 -1
- package/dist/dts/components/ai-driver/ai-driver.d.ts +52 -0
- package/dist/dts/components/ai-driver/ai-driver.d.ts.map +1 -0
- package/dist/dts/components/ai-driver/index.d.ts +2 -0
- package/dist/dts/components/ai-driver/index.d.ts.map +1 -0
- package/dist/dts/components/chat-driver/chat-driver.d.ts +63 -8
- package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -1
- package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.d.ts +3 -3
- package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.d.ts.map +1 -1
- package/dist/dts/components/chat-markdown/chat-markdown.d.ts +1 -1
- package/dist/dts/components/chat-markdown/chat-markdown.d.ts.map +1 -1
- package/dist/dts/components/halo-overlay.d.ts +13 -1
- package/dist/dts/components/halo-overlay.d.ts.map +1 -1
- package/dist/dts/components/orchestrating-driver/index.d.ts +2 -0
- package/dist/dts/components/orchestrating-driver/index.d.ts.map +1 -0
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts +39 -0
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts.map +1 -0
- package/dist/dts/components/popout-manager/index.d.ts +2 -0
- package/dist/dts/components/popout-manager/index.d.ts.map +1 -0
- package/dist/dts/components/popout-manager/popout-manager.d.ts +72 -0
- package/dist/dts/components/popout-manager/popout-manager.d.ts.map +1 -0
- package/dist/dts/config/config.d.ts +43 -15
- package/dist/dts/config/config.d.ts.map +1 -1
- package/dist/dts/config/fallback-agents.d.ts +20 -0
- package/dist/dts/config/fallback-agents.d.ts.map +1 -0
- package/dist/dts/config/index.d.ts +1 -0
- package/dist/dts/config/index.d.ts.map +1 -1
- package/dist/dts/index.d.ts +6 -0
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/dts/main/main.d.ts +122 -21
- 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/main/main.types.d.ts +16 -0
- package/dist/dts/main/main.types.d.ts.map +1 -1
- package/dist/dts/state/ai-assistant-slice.d.ts +38 -0
- package/dist/dts/state/ai-assistant-slice.d.ts.map +1 -0
- package/dist/dts/state/driver-registry.d.ts +22 -0
- package/dist/dts/state/driver-registry.d.ts.map +1 -0
- package/dist/dts/state/session-store.d.ts +37 -0
- package/dist/dts/state/session-store.d.ts.map +1 -0
- package/dist/dts/suggestions/chat-suggestions.d.ts +7 -0
- package/dist/dts/suggestions/chat-suggestions.d.ts.map +1 -0
- package/dist/dts/types/ai-chat-widget.d.ts +3 -2
- package/dist/dts/types/ai-chat-widget.d.ts.map +1 -1
- package/dist/dts/utils/index.d.ts +1 -0
- package/dist/dts/utils/index.d.ts.map +1 -1
- package/dist/dts/utils/tool-fold.d.ts +133 -0
- package/dist/dts/utils/tool-fold.d.ts.map +1 -0
- package/dist/esm/components/ai-driver/ai-driver.js +1 -0
- package/dist/esm/components/ai-driver/index.js +1 -0
- package/dist/esm/components/chat-driver/chat-driver.js +499 -67
- package/dist/esm/components/chat-interaction-wrapper/chat-interaction-wrapper.js +2 -2
- package/dist/esm/components/chat-markdown/chat-markdown.js +1 -1
- package/dist/esm/components/halo-overlay.js +53 -7
- package/dist/esm/components/orchestrating-driver/index.js +1 -0
- package/dist/esm/components/orchestrating-driver/orchestrating-driver.js +247 -0
- package/dist/esm/components/popout-manager/index.js +1 -0
- package/dist/esm/components/popout-manager/popout-manager.js +126 -0
- package/dist/esm/config/fallback-agents.js +26 -0
- package/dist/esm/config/index.js +1 -0
- package/dist/esm/index.js +6 -0
- package/dist/esm/main/main.js +546 -112
- package/dist/esm/main/main.styles.js +200 -4
- package/dist/esm/main/main.template.js +163 -63
- package/dist/esm/state/ai-assistant-slice.js +54 -0
- package/dist/esm/state/driver-registry.js +46 -0
- package/dist/esm/state/session-store.js +39 -0
- package/dist/esm/suggestions/chat-suggestions.js +147 -0
- package/dist/esm/utils/index.js +1 -0
- package/dist/esm/utils/tool-fold.js +92 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/docs/migration-FUI-2495.md +339 -0
- package/docs/sub_agent.md +310 -0
- package/package.json +16 -15
- package/src/channel/ai-activity-channel.ts +4 -20
- package/src/components/ai-driver/ai-driver.ts +69 -0
- package/src/components/ai-driver/index.ts +1 -0
- package/src/components/chat-driver/chat-driver.ts +600 -73
- package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.ts +3 -3
- package/src/components/chat-markdown/chat-markdown.ts +1 -1
- package/src/components/halo-overlay.ts +45 -7
- package/src/components/orchestrating-driver/index.ts +1 -0
- package/src/components/orchestrating-driver/orchestrating-driver.ts +328 -0
- package/src/components/popout-manager/index.ts +1 -0
- package/src/components/popout-manager/popout-manager.ts +147 -0
- package/src/config/config.ts +45 -15
- package/src/config/fallback-agents.ts +29 -0
- package/src/config/index.ts +1 -0
- package/src/index.ts +6 -0
- package/src/main/main.styles.ts +200 -4
- package/src/main/main.template.ts +200 -80
- package/src/main/main.ts +567 -94
- package/src/main/main.types.ts +11 -0
- package/src/state/ai-assistant-slice.ts +80 -0
- package/src/state/driver-registry.ts +51 -0
- package/src/state/session-store.ts +56 -0
- package/src/suggestions/chat-suggestions.ts +158 -0
- package/src/types/ai-chat-widget.ts +4 -2
- package/src/utils/index.ts +1 -0
- package/src/utils/tool-fold.ts +181 -0
- package/docs/multi-agent-architecture.md +0 -198
package/src/main/main.types.ts
CHANGED
|
@@ -14,6 +14,17 @@ export type AiAssistantState = 'idle' | 'loading' | 'error';
|
|
|
14
14
|
*/
|
|
15
15
|
export type PopoutMode = 'expand' | 'collapse';
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* State of the chat suggestions feature.
|
|
19
|
+
*
|
|
20
|
+
* @beta
|
|
21
|
+
*/
|
|
22
|
+
export type SuggestionsState =
|
|
23
|
+
| { status: 'idle' }
|
|
24
|
+
| { status: 'loading' }
|
|
25
|
+
| { status: 'loaded'; suggestions: string[] }
|
|
26
|
+
| { status: 'error'; message: string };
|
|
27
|
+
|
|
17
28
|
/**
|
|
18
29
|
* Metadata describing a UI animation in the assistant.
|
|
19
30
|
*
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { ChatMessage } from '@genesislcap/foundation-ai';
|
|
2
|
+
import { createSlice } from '@genesislcap/foundation-redux';
|
|
3
|
+
import type { PayloadAction } from '@genesislcap/foundation-redux';
|
|
4
|
+
import type { AgentConfig } from '../config/config';
|
|
5
|
+
import type { AiAssistantAnimation, AiAssistantState, SuggestionsState } from '../main/main.types';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Shape of a single AI assistant session's serializable state.
|
|
9
|
+
*
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
export interface AiAssistantSessionState {
|
|
13
|
+
messages: ChatMessage[];
|
|
14
|
+
state: AiAssistantState;
|
|
15
|
+
showToolCalls: boolean;
|
|
16
|
+
showThinkingSteps: boolean;
|
|
17
|
+
showAgentSwitchIndicator: boolean;
|
|
18
|
+
enabledAnimations: AiAssistantAnimation[];
|
|
19
|
+
suggestionsState: SuggestionsState;
|
|
20
|
+
contextTokens: number | undefined;
|
|
21
|
+
contextLimit: number | undefined;
|
|
22
|
+
activeAgent: Omit<AgentConfig, 'toolHandlers'> | undefined;
|
|
23
|
+
/** Draft text in the input box — preserved across pop-in/pop-out cycles. */
|
|
24
|
+
inputValue: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const defaultSessionState: AiAssistantSessionState = {
|
|
28
|
+
messages: [],
|
|
29
|
+
state: 'idle',
|
|
30
|
+
showToolCalls: false,
|
|
31
|
+
showThinkingSteps: false,
|
|
32
|
+
showAgentSwitchIndicator: false,
|
|
33
|
+
enabledAnimations: [],
|
|
34
|
+
suggestionsState: { status: 'idle' },
|
|
35
|
+
contextTokens: undefined,
|
|
36
|
+
contextLimit: undefined,
|
|
37
|
+
activeAgent: undefined,
|
|
38
|
+
inputValue: '',
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const aiAssistantSlice = createSlice({
|
|
42
|
+
name: 'aiAssistant',
|
|
43
|
+
initialState: defaultSessionState,
|
|
44
|
+
reducers: {
|
|
45
|
+
setMessages(state, action: PayloadAction<ChatMessage[]>) {
|
|
46
|
+
state.messages = action.payload;
|
|
47
|
+
},
|
|
48
|
+
setState(state, action: PayloadAction<AiAssistantState>) {
|
|
49
|
+
state.state = action.payload;
|
|
50
|
+
},
|
|
51
|
+
setShowToolCalls(state, action: PayloadAction<boolean>) {
|
|
52
|
+
state.showToolCalls = action.payload;
|
|
53
|
+
},
|
|
54
|
+
setShowThinkingSteps(state, action: PayloadAction<boolean>) {
|
|
55
|
+
state.showThinkingSteps = action.payload;
|
|
56
|
+
},
|
|
57
|
+
setShowAgentSwitchIndicator(state, action: PayloadAction<boolean>) {
|
|
58
|
+
state.showAgentSwitchIndicator = action.payload;
|
|
59
|
+
},
|
|
60
|
+
setEnabledAnimations(state, action: PayloadAction<AiAssistantAnimation[]>) {
|
|
61
|
+
state.enabledAnimations = action.payload;
|
|
62
|
+
},
|
|
63
|
+
setSuggestionsState(state, action: PayloadAction<SuggestionsState>) {
|
|
64
|
+
state.suggestionsState = action.payload;
|
|
65
|
+
},
|
|
66
|
+
setContextTokens(state, action: PayloadAction<number | undefined>) {
|
|
67
|
+
state.contextTokens = action.payload;
|
|
68
|
+
},
|
|
69
|
+
setContextLimit(state, action: PayloadAction<number | undefined>) {
|
|
70
|
+
state.contextLimit = action.payload;
|
|
71
|
+
},
|
|
72
|
+
setActiveAgent(state, action: PayloadAction<Omit<AgentConfig, 'toolHandlers'> | undefined>) {
|
|
73
|
+
state.activeAgent = action.payload;
|
|
74
|
+
},
|
|
75
|
+
setInputValue(state, action: PayloadAction<string>) {
|
|
76
|
+
state.inputValue = action.payload;
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
selectors: {},
|
|
80
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { AiDriver } from '../components/ai-driver/ai-driver';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Module-level registry of AI drivers keyed by session identity string.
|
|
5
|
+
*
|
|
6
|
+
* Driver lifetime is tied to the conversation session, not the DOM.
|
|
7
|
+
* Components look up or create drivers on connect and only unwire
|
|
8
|
+
* event listeners on disconnect — the driver is never destroyed by
|
|
9
|
+
* a lifecycle event.
|
|
10
|
+
*
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
const driverRegistry = new Map<string, AiDriver>();
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Returns the existing driver for the given key, or calls the factory
|
|
17
|
+
* to create and register a new one.
|
|
18
|
+
*/
|
|
19
|
+
export function getOrCreateDriver(key: string, factory: () => AiDriver): AiDriver {
|
|
20
|
+
const existing = driverRegistry.get(key);
|
|
21
|
+
if (existing) return existing;
|
|
22
|
+
const driver = factory();
|
|
23
|
+
driverRegistry.set(key, driver);
|
|
24
|
+
return driver;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Returns the existing driver for the given key, or `undefined`.
|
|
29
|
+
*/
|
|
30
|
+
export function getDriver(key: string): AiDriver | undefined {
|
|
31
|
+
return driverRegistry.get(key);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Removes and returns the driver for the given key.
|
|
36
|
+
* Used when agents change (driver must be rebuilt) or on explicit session teardown.
|
|
37
|
+
*/
|
|
38
|
+
export function deleteDriver(key: string): AiDriver | undefined {
|
|
39
|
+
const driver = driverRegistry.get(key);
|
|
40
|
+
driverRegistry.delete(key);
|
|
41
|
+
return driver;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Removes all entries. Exposed for test isolation only — not part of the public API.
|
|
46
|
+
*
|
|
47
|
+
* @internal
|
|
48
|
+
*/
|
|
49
|
+
export function clearDriverRegistry(): void {
|
|
50
|
+
driverRegistry.clear();
|
|
51
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { createStore } from '@genesislcap/foundation-redux';
|
|
2
|
+
import {
|
|
3
|
+
aiAssistantSlice,
|
|
4
|
+
defaultSessionState,
|
|
5
|
+
type AiAssistantSessionState,
|
|
6
|
+
} from './ai-assistant-slice';
|
|
7
|
+
|
|
8
|
+
type SessionStoreReturn = ReturnType<typeof makeStore>;
|
|
9
|
+
|
|
10
|
+
const sessionStores = new Map<string, SessionStoreReturn>();
|
|
11
|
+
|
|
12
|
+
function makeStore(devTools: boolean | undefined) {
|
|
13
|
+
return createStore(
|
|
14
|
+
[aiAssistantSlice],
|
|
15
|
+
{ aiAssistant: defaultSessionState },
|
|
16
|
+
devTools !== undefined ? { devTools } : undefined,
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Returns the session store for the given key, creating one if it does not
|
|
22
|
+
* already exist. Omit `devTools` (or pass `undefined`) to defer to `isDev()`.
|
|
23
|
+
* Pass `true` to force-enable or `false` to force-disable Redux DevTools.
|
|
24
|
+
*/
|
|
25
|
+
export function getSessionStore(key: string, devTools?: boolean): SessionStoreReturn {
|
|
26
|
+
const existing = sessionStores.get(key);
|
|
27
|
+
if (existing) return existing;
|
|
28
|
+
const created = makeStore(devTools);
|
|
29
|
+
sessionStores.set(key, created);
|
|
30
|
+
return created;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Returns true if a session store already exists for the given key.
|
|
35
|
+
*/
|
|
36
|
+
export function hasSessionStore(key: string): boolean {
|
|
37
|
+
return sessionStores.has(key);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Deletes the session store for the given key.
|
|
42
|
+
*/
|
|
43
|
+
export function deleteSessionStore(key: string): void {
|
|
44
|
+
sessionStores.delete(key);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Removes all session stores. Exposed for test isolation only — not part of the public API.
|
|
49
|
+
*
|
|
50
|
+
* @internal
|
|
51
|
+
*/
|
|
52
|
+
export function clearAllSessionStores(): void {
|
|
53
|
+
sessionStores.clear();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type { SessionStoreReturn, AiAssistantSessionState };
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { customElement, observable, html, css, repeat, when } from '@genesislcap/web-core';
|
|
2
|
+
import { GenesisElement } from '@genesislcap/web-core';
|
|
3
|
+
import type { SuggestionsState } from '../main/main.types';
|
|
4
|
+
|
|
5
|
+
type LoadedState = { status: 'loaded'; suggestions: string[] };
|
|
6
|
+
|
|
7
|
+
const PILL_ANIMATION_STAGGER_MS = 70;
|
|
8
|
+
|
|
9
|
+
const template = html<ChatSuggestions>`
|
|
10
|
+
${when(
|
|
11
|
+
(x) => x.state.status === 'loading',
|
|
12
|
+
html<ChatSuggestions>`
|
|
13
|
+
<div class="suggestions-container">
|
|
14
|
+
${repeat(
|
|
15
|
+
() => Array.from({ length: 3 }),
|
|
16
|
+
html`
|
|
17
|
+
<div class="suggestion-placeholder"></div>
|
|
18
|
+
`,
|
|
19
|
+
)}
|
|
20
|
+
</div>
|
|
21
|
+
`,
|
|
22
|
+
)}
|
|
23
|
+
${when(
|
|
24
|
+
(x) => x.state.status === 'loaded',
|
|
25
|
+
html<ChatSuggestions>`
|
|
26
|
+
<div class="suggestions-container">
|
|
27
|
+
${repeat(
|
|
28
|
+
(x) => (x.state as LoadedState).suggestions,
|
|
29
|
+
html<string, ChatSuggestions>`
|
|
30
|
+
<button
|
|
31
|
+
class="suggestion-pill"
|
|
32
|
+
style="animation-delay: ${(_, c) => c.index * PILL_ANIMATION_STAGGER_MS}ms"
|
|
33
|
+
@click="${(suggestion, c) => c.parent.handleSuggestionClick(suggestion)}"
|
|
34
|
+
>
|
|
35
|
+
${(suggestion) => suggestion}
|
|
36
|
+
</button>
|
|
37
|
+
`,
|
|
38
|
+
)}
|
|
39
|
+
</div>
|
|
40
|
+
`,
|
|
41
|
+
)}
|
|
42
|
+
`;
|
|
43
|
+
|
|
44
|
+
const styles = css`
|
|
45
|
+
:host {
|
|
46
|
+
display: block;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.suggestions-container {
|
|
50
|
+
display: flex;
|
|
51
|
+
flex-wrap: wrap;
|
|
52
|
+
gap: 6px;
|
|
53
|
+
padding: 10px calc(var(--design-unit) * 3px) 6px;
|
|
54
|
+
background-color: var(--neutral-layer-2);
|
|
55
|
+
animation: suggestions-appear 0.2s ease-out both;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@keyframes suggestions-appear {
|
|
59
|
+
from {
|
|
60
|
+
opacity: 0;
|
|
61
|
+
transform: translateY(6px);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
to {
|
|
65
|
+
opacity: 1;
|
|
66
|
+
transform: translateY(0);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.suggestion-pill {
|
|
71
|
+
display: inline-flex;
|
|
72
|
+
align-items: center;
|
|
73
|
+
padding: 5px 14px;
|
|
74
|
+
background: color-mix(in srgb, var(--accent-fill-rest) 10%, var(--neutral-layer-3));
|
|
75
|
+
color: var(--neutral-foreground-rest);
|
|
76
|
+
border: 1px solid color-mix(in srgb, var(--accent-fill-rest) 25%, var(--neutral-stroke-rest));
|
|
77
|
+
border-radius: 20px;
|
|
78
|
+
font-family: var(--body-font);
|
|
79
|
+
font-size: 0.85em;
|
|
80
|
+
cursor: pointer;
|
|
81
|
+
transition:
|
|
82
|
+
background 0.15s ease,
|
|
83
|
+
border-color 0.15s ease,
|
|
84
|
+
transform 0.15s ease,
|
|
85
|
+
box-shadow 0.15s ease;
|
|
86
|
+
animation: pill-appear 0.3s ease-out both;
|
|
87
|
+
max-width: 100%;
|
|
88
|
+
white-space: normal;
|
|
89
|
+
text-align: left;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.suggestion-pill:hover {
|
|
93
|
+
background: color-mix(in srgb, var(--accent-fill-rest) 20%, var(--neutral-layer-3));
|
|
94
|
+
border-color: color-mix(in srgb, var(--accent-fill-rest) 50%, var(--neutral-stroke-rest));
|
|
95
|
+
transform: translateY(-1px);
|
|
96
|
+
box-shadow: 0 2px 8px color-mix(in srgb, var(--accent-fill-rest) 20%, transparent);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.suggestion-pill:active {
|
|
100
|
+
transform: translateY(0);
|
|
101
|
+
box-shadow: none;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@keyframes pill-appear {
|
|
105
|
+
from {
|
|
106
|
+
opacity: 0;
|
|
107
|
+
transform: scale(0.88) translateY(4px);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
to {
|
|
111
|
+
opacity: 1;
|
|
112
|
+
transform: scale(1) translateY(0);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.suggestion-placeholder {
|
|
117
|
+
height: 30px;
|
|
118
|
+
width: 110px;
|
|
119
|
+
background: var(--neutral-layer-3);
|
|
120
|
+
border-radius: 20px;
|
|
121
|
+
animation: placeholder-pulse 1.6s ease-in-out infinite;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.suggestion-placeholder:nth-child(2) {
|
|
125
|
+
width: 150px;
|
|
126
|
+
animation-delay: 0.25s;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.suggestion-placeholder:nth-child(3) {
|
|
130
|
+
width: 130px;
|
|
131
|
+
animation-delay: 0.5s;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
@keyframes placeholder-pulse {
|
|
135
|
+
0%,
|
|
136
|
+
100% {
|
|
137
|
+
opacity: 0.35;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
50% {
|
|
141
|
+
opacity: 0.65;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
`;
|
|
145
|
+
|
|
146
|
+
@customElement({
|
|
147
|
+
name: 'chat-suggestions',
|
|
148
|
+
template,
|
|
149
|
+
styles,
|
|
150
|
+
})
|
|
151
|
+
export class ChatSuggestions extends GenesisElement {
|
|
152
|
+
@observable
|
|
153
|
+
state: SuggestionsState = { status: 'idle' };
|
|
154
|
+
|
|
155
|
+
handleSuggestionClick(suggestion: string): void {
|
|
156
|
+
this.$emit('suggestion-clicked', suggestion);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { InteractionResult } from '@genesislcap/foundation-ai';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Interface that AI inline interaction components can implement.
|
|
3
5
|
* The `AiChatInteractionWrapper` always sets `data` and `resolved` on the rendered element.
|
|
@@ -6,6 +8,6 @@
|
|
|
6
8
|
*/
|
|
7
9
|
export interface AiChatWidget {
|
|
8
10
|
data: unknown;
|
|
9
|
-
/**
|
|
10
|
-
resolved?:
|
|
11
|
+
/** The result when the interaction has been resolved. Truthy = resolved; value contains status/payload for display. */
|
|
12
|
+
resolved?: InteractionResult<unknown>;
|
|
11
13
|
}
|
package/src/utils/index.ts
CHANGED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import type { ChatToolDefinition, ChatToolHandlers } from '@genesislcap/foundation-ai';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Symbol used to attach ToolFold metadata to a facade handler function.
|
|
5
|
+
* The ChatDriver inspects this to detect fold facades at runtime.
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
export const TOOL_FOLD_SYMBOL = Symbol('toolFold');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Internal metadata for a tool fold. Attached to the facade handler via TOOL_FOLD_SYMBOL.
|
|
13
|
+
*
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
export interface ToolFold {
|
|
17
|
+
/** Facade tool name. */
|
|
18
|
+
name: string;
|
|
19
|
+
/** Description shown to the model. */
|
|
20
|
+
description: string;
|
|
21
|
+
/** Optional guidance injected in the fold-open response. */
|
|
22
|
+
usageNotes?: string;
|
|
23
|
+
/** Tool definitions inside this fold. */
|
|
24
|
+
tools: ChatToolDefinition[];
|
|
25
|
+
/** Handlers for the inner tools (keyed by tool name). */
|
|
26
|
+
handlers: ChatToolHandlers;
|
|
27
|
+
/**
|
|
28
|
+
* When true (default), all other tools are removed when this fold opens —
|
|
29
|
+
* the model can only see this fold's tools until it closes.
|
|
30
|
+
* When false, sibling tools remain alongside the inner tools.
|
|
31
|
+
*/
|
|
32
|
+
exclusive: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The value returned by `createToolFold`.
|
|
37
|
+
*
|
|
38
|
+
* @beta
|
|
39
|
+
*/
|
|
40
|
+
export interface ToolFoldResult {
|
|
41
|
+
/**
|
|
42
|
+
* The facade tool definition. Add this to your agent's `toolDefinitions`.
|
|
43
|
+
* It is a regular `ChatToolDefinition` with an empty parameter schema.
|
|
44
|
+
*/
|
|
45
|
+
definition: ChatToolDefinition;
|
|
46
|
+
/**
|
|
47
|
+
* The facade handler — a single-entry map keyed by the fold name.
|
|
48
|
+
* Spread this into your agent's `toolHandlers`.
|
|
49
|
+
* Inner tool handlers are NOT included here — they are encapsulated inside
|
|
50
|
+
* the fold metadata and installed by the driver only when the fold opens.
|
|
51
|
+
*/
|
|
52
|
+
handler: ChatToolHandlers;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Creates a tool fold — a facade that hides a group of related tools behind a single
|
|
57
|
+
* named entry, revealing them progressively when the model invokes the facade.
|
|
58
|
+
*
|
|
59
|
+
* The `tools` and `handlers` parameters accept the exact same types as `AgentConfig.toolDefinitions`
|
|
60
|
+
* and `AgentConfig.toolHandlers`, so existing tool creation functions can be passed in unchanged.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* const tradingTools = createToolFold({
|
|
65
|
+
* name: 'trading_tools',
|
|
66
|
+
* tools: createTradesToolDefinitions(),
|
|
67
|
+
* handlers: createTradesToolHandlers(connect),
|
|
68
|
+
* });
|
|
69
|
+
*
|
|
70
|
+
* const agent: AgentConfig = {
|
|
71
|
+
* name: 'Trades',
|
|
72
|
+
* systemPrompt: '...',
|
|
73
|
+
* toolDefinitions: [getCurrentDateDef, tradingTools.definition],
|
|
74
|
+
* toolHandlers: { get_current_date: handler, ...tradingTools.handler },
|
|
75
|
+
* };
|
|
76
|
+
* ```
|
|
77
|
+
*
|
|
78
|
+
* @example Nesting folds
|
|
79
|
+
* ```typescript
|
|
80
|
+
* const insertFold = createToolFold({
|
|
81
|
+
* name: 'trade_insertion',
|
|
82
|
+
* tools: [validateDef, insertDef],
|
|
83
|
+
* handlers: { validate_trade: vHandler, insert_trade: iHandler },
|
|
84
|
+
* });
|
|
85
|
+
*
|
|
86
|
+
* const tradingFold = createToolFold({
|
|
87
|
+
* name: 'trading_tools',
|
|
88
|
+
* tools: [searchDef, insertFold.definition],
|
|
89
|
+
* handlers: { search_trades: sHandler, ...insertFold.handler },
|
|
90
|
+
* });
|
|
91
|
+
* ```
|
|
92
|
+
*
|
|
93
|
+
* @beta
|
|
94
|
+
*/
|
|
95
|
+
export function createToolFold(config: {
|
|
96
|
+
/** Facade tool name — used as the tool name the model calls to open this fold. */
|
|
97
|
+
name: string;
|
|
98
|
+
/**
|
|
99
|
+
* Description shown to the model for the facade tool.
|
|
100
|
+
* If omitted, auto-generated from the inner tool names:
|
|
101
|
+
* "Contains: tool_a, tool_b. Invoke to access these tools."
|
|
102
|
+
*/
|
|
103
|
+
description?: string;
|
|
104
|
+
/**
|
|
105
|
+
* Optional usage guidance injected into the fold-open response message.
|
|
106
|
+
* Not included in the facade description — only visible once the fold is open.
|
|
107
|
+
*/
|
|
108
|
+
usageNotes?: string;
|
|
109
|
+
/** Inner tool definitions. Same type as `AgentConfig.toolDefinitions`. */
|
|
110
|
+
tools: ChatToolDefinition[];
|
|
111
|
+
/** Inner tool handlers. Same type as `AgentConfig.toolHandlers`. */
|
|
112
|
+
handlers: ChatToolHandlers;
|
|
113
|
+
/**
|
|
114
|
+
* When true (default), all other tools are removed when this fold opens.
|
|
115
|
+
* When false, sibling tools remain available alongside the inner tools.
|
|
116
|
+
*/
|
|
117
|
+
exclusive?: boolean;
|
|
118
|
+
}): ToolFoldResult {
|
|
119
|
+
const description =
|
|
120
|
+
config.description ??
|
|
121
|
+
`Contains: ${config.tools.map((t) => t.name).join(', ')}. Invoke to access these tools.`;
|
|
122
|
+
|
|
123
|
+
const fold: ToolFold = {
|
|
124
|
+
name: config.name,
|
|
125
|
+
description,
|
|
126
|
+
usageNotes: config.usageNotes,
|
|
127
|
+
tools: config.tools,
|
|
128
|
+
handlers: config.handlers,
|
|
129
|
+
exclusive: config.exclusive ?? true,
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const definition: ChatToolDefinition = {
|
|
133
|
+
name: config.name,
|
|
134
|
+
description,
|
|
135
|
+
parameters: { type: 'object', properties: {} },
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// The facade handler body is never actually called — the driver intercepts it
|
|
139
|
+
// via TOOL_FOLD_SYMBOL before dispatch. The body is a no-op fallback.
|
|
140
|
+
const facadeHandler = async (): Promise<string> => `Fold: ${config.name}`;
|
|
141
|
+
(facadeHandler as any)[TOOL_FOLD_SYMBOL] = fold;
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
definition,
|
|
145
|
+
handler: {
|
|
146
|
+
[config.name]: facadeHandler,
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* A tool entry in the expanded debug tree.
|
|
153
|
+
* Folds are represented as nodes with a `tools` array; regular tools have none.
|
|
154
|
+
*
|
|
155
|
+
* @beta
|
|
156
|
+
*/
|
|
157
|
+
export interface ToolTreeNode extends ChatToolDefinition {
|
|
158
|
+
/** Present when this tool is a fold — contains its inner tools recursively expanded. */
|
|
159
|
+
tools?: ToolTreeNode[];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Expands a flat list of tool definitions into a nested tree by resolving fold
|
|
164
|
+
* metadata from the corresponding handlers. Use this for debug output so the
|
|
165
|
+
* full tool hierarchy is visible rather than just the top-level facade names.
|
|
166
|
+
*
|
|
167
|
+
* @beta
|
|
168
|
+
*/
|
|
169
|
+
export function expandToolTree(
|
|
170
|
+
definitions: ChatToolDefinition[],
|
|
171
|
+
handlers: ChatToolHandlers,
|
|
172
|
+
): ToolTreeNode[] {
|
|
173
|
+
return definitions.map((def) => {
|
|
174
|
+
const handler = handlers[def.name];
|
|
175
|
+
const fold: ToolFold | undefined = handler ? (handler as any)[TOOL_FOLD_SYMBOL] : undefined;
|
|
176
|
+
if (fold) {
|
|
177
|
+
return { ...def, tools: expandToolTree(fold.tools, fold.handlers) };
|
|
178
|
+
}
|
|
179
|
+
return { ...def };
|
|
180
|
+
});
|
|
181
|
+
}
|