@genesislcap/ai-assistant 14.419.2 → 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
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import { __awaiter } from "tslib";
|
|
2
2
|
import { MalformedFunctionCallError } from '@genesislcap/foundation-ai';
|
|
3
3
|
import { logger } from '../../utils/logger';
|
|
4
|
+
import { TOOL_FOLD_SYMBOL } from '../../utils/tool-fold';
|
|
4
5
|
const DEFAULT_MAX_TOOL_ITERATIONS = 50;
|
|
6
|
+
const DEFAULT_MAX_FOLD_OPERATIONS = 5;
|
|
7
|
+
const DEFAULT_MAX_UNKNOWN_TOOL_CALLS = 5;
|
|
5
8
|
const MAX_MALFORMED_RETRIES = 2;
|
|
9
|
+
const SUGGESTIONS_HISTORY_WINDOW = 8;
|
|
10
|
+
/** Name reserved for the cross-agent handoff tool — injected by OrchestratingDriver. */
|
|
11
|
+
export const REQUEST_CONTINUATION_TOOL = 'request_continuation';
|
|
12
|
+
/** Paired in history for each `request_continuation` so tool_calls stay balanced for the provider. */
|
|
13
|
+
const HANDOFF_TOOL_RESULT_PLACEHOLDER = 'Handoff to another specialist — routing continues on the next turn.';
|
|
6
14
|
/**
|
|
7
15
|
* Plain TS class that drives a multi-turn chat conversation, including the tool-call loop.
|
|
8
16
|
* Owned by `FoundationAiAssistant` — created in `connectedCallback`, torn down in `disconnectedCallback`.
|
|
@@ -14,24 +22,133 @@ const MAX_MALFORMED_RETRIES = 2;
|
|
|
14
22
|
* @beta
|
|
15
23
|
*/
|
|
16
24
|
export class ChatDriver extends EventTarget {
|
|
17
|
-
constructor(aiProvider, toolHandlers = {}, toolDefinitions = [], systemPrompt, primerHistory, maxToolIterations = DEFAULT_MAX_TOOL_ITERATIONS) {
|
|
25
|
+
constructor(aiProvider, toolHandlers = {}, toolDefinitions = [], systemPrompt, primerHistory, maxToolIterations = DEFAULT_MAX_TOOL_ITERATIONS, maxFoldOperations = DEFAULT_MAX_FOLD_OPERATIONS) {
|
|
18
26
|
super();
|
|
19
27
|
this.aiProvider = aiProvider;
|
|
20
|
-
this.toolHandlers = toolHandlers;
|
|
21
|
-
this.toolDefinitions = toolDefinitions;
|
|
22
|
-
this.systemPrompt = systemPrompt;
|
|
23
|
-
this.primerHistory = primerHistory;
|
|
24
28
|
this.maxToolIterations = maxToolIterations;
|
|
25
29
|
this.history = [];
|
|
26
30
|
this.busy = false;
|
|
27
31
|
this.pendingInteractions = new Map();
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
/** Stack of fold frames — grows when a fold opens, shrinks when it closes. */
|
|
33
|
+
this.foldStack = [];
|
|
34
|
+
/** Consecutive fold open/close ops without a real tool call. Reset on real tool execution. */
|
|
35
|
+
this.consecutiveFoldOps = 0;
|
|
36
|
+
/** Consecutive unknown-tool calls without a real tool call. Reset on real tool execution. */
|
|
37
|
+
this.consecutiveUnknownToolCalls = 0;
|
|
38
|
+
this.toolHandlers = toolHandlers;
|
|
39
|
+
this.toolDefinitions = toolDefinitions;
|
|
40
|
+
this.systemPrompt = systemPrompt;
|
|
41
|
+
this.primerHistory = primerHistory;
|
|
42
|
+
this.maxFoldOperations = maxFoldOperations;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Swap in a new agent's configuration. Called by OrchestratingDriver before
|
|
46
|
+
* each specialist turn so the shared driver runs with the right tools and prompt.
|
|
47
|
+
*/
|
|
48
|
+
applyAgent(config) {
|
|
49
|
+
var _a, _b;
|
|
50
|
+
this.systemPrompt = config.systemPrompt;
|
|
51
|
+
this.toolDefinitions = (_a = config.toolDefinitions) !== null && _a !== void 0 ? _a : [];
|
|
52
|
+
this.toolHandlers = (_b = config.toolHandlers) !== null && _b !== void 0 ? _b : {};
|
|
53
|
+
this.primerHistory = config.primerHistory;
|
|
54
|
+
this.activeAgentName = config.name;
|
|
55
|
+
// Reset fold state when agent changes — each specialist starts fresh
|
|
56
|
+
this.foldStack = [];
|
|
57
|
+
this.consecutiveFoldOps = 0;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Optional transform applied to conversation history immediately before each LLM request.
|
|
61
|
+
* Cleared when `undefined`. Does not alter stored history.
|
|
62
|
+
*/
|
|
63
|
+
setProviderHistoryTransform(transform) {
|
|
64
|
+
this.providerHistoryTransform = transform;
|
|
31
65
|
}
|
|
32
66
|
getHistory() {
|
|
33
67
|
return this.history;
|
|
34
68
|
}
|
|
69
|
+
getRawHistory() {
|
|
70
|
+
return this.history;
|
|
71
|
+
}
|
|
72
|
+
/** Returns the current fold stack names for debugging. */
|
|
73
|
+
getActiveFoldNames() {
|
|
74
|
+
return this.foldStack.map((f) => f.foldName);
|
|
75
|
+
}
|
|
76
|
+
getSuggestions(history, prompt, count, allAgentInfo) {
|
|
77
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
78
|
+
if (!this.aiProvider.prompt) {
|
|
79
|
+
logger.warn('ChatDriver: AIProvider does not implement prompt()');
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
let agentContext = '';
|
|
83
|
+
let toolContext = '';
|
|
84
|
+
if (allAgentInfo === null || allAgentInfo === void 0 ? void 0 : allAgentInfo.length) {
|
|
85
|
+
const agentDescriptions = allAgentInfo
|
|
86
|
+
.map((agent) => {
|
|
87
|
+
const tools = agent.tools.map((t) => t.name).join(', ');
|
|
88
|
+
return `- ${agent.name} (${agent.description}): ${tools ? `(Tools: ${tools})` : 'No tools'}`;
|
|
89
|
+
})
|
|
90
|
+
.join('\n');
|
|
91
|
+
agentContext = `The assistant has the following capabilities:\n${agentDescriptions}`;
|
|
92
|
+
const allToolNames = allAgentInfo
|
|
93
|
+
.flatMap((agent) => agent.tools.map((t) => t.name))
|
|
94
|
+
.filter((value, index, self) => self.indexOf(value) === index)
|
|
95
|
+
.join(', ');
|
|
96
|
+
toolContext = allToolNames
|
|
97
|
+
? `You have access to the following tools across all agents: ${allToolNames}.`
|
|
98
|
+
: '';
|
|
99
|
+
}
|
|
100
|
+
else if (this.activeAgentName) {
|
|
101
|
+
const toolNames = this.toolDefinitions.map((tool) => tool.name).join(', ');
|
|
102
|
+
agentContext = `You are currently acting as the "${this.activeAgentName}" agent.`;
|
|
103
|
+
toolContext = toolNames ? `You have access to the following tools: ${toolNames}.` : '';
|
|
104
|
+
}
|
|
105
|
+
let systemPrompt;
|
|
106
|
+
if (history.length === 0) {
|
|
107
|
+
systemPrompt = `You are generating short, simple starter prompts to show a user what an AI assistant can do. Generate exactly ${count} brief suggestions phrased as the user would write them. Keep them short and generic — do not invent specific names, IDs, or data (e.g. prefer "Search for a trade" over "Find all trades with Client A").`;
|
|
108
|
+
if (agentContext || toolContext) {
|
|
109
|
+
systemPrompt += " Base suggestions only on the agent's actual capabilities.";
|
|
110
|
+
if (agentContext)
|
|
111
|
+
systemPrompt += ` ${agentContext}`;
|
|
112
|
+
if (toolContext)
|
|
113
|
+
systemPrompt += ` ${toolContext}`;
|
|
114
|
+
systemPrompt += ' Do not suggest anything outside these capabilities.';
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
systemPrompt = `You are generating suggested follow-up prompts that a user could send to an AI assistant. Generate exactly ${count} suggestions phrased as the user would write them — first-person requests or questions directed at the agent. The first ${Math.max(0, count - 1)} should be natural follow-ups to the last turn of conversation. Do not invent specific names, IDs, or data values that do not appear in the conversation history.`;
|
|
119
|
+
if (agentContext || toolContext) {
|
|
120
|
+
systemPrompt += ' Suggestions must only cover what the agent is capable of.';
|
|
121
|
+
if (agentContext)
|
|
122
|
+
systemPrompt += ` ${agentContext}`;
|
|
123
|
+
if (toolContext)
|
|
124
|
+
systemPrompt += ` ${toolContext}`;
|
|
125
|
+
systemPrompt += ' Do not suggest anything outside these capabilities.';
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (prompt) {
|
|
129
|
+
systemPrompt += ` Additional guidance: "${prompt}"`;
|
|
130
|
+
}
|
|
131
|
+
systemPrompt +=
|
|
132
|
+
' Output only the suggestions — one per line, no numbering, no bullets, no commentary, no preamble. Do not describe or explain the suggestions.';
|
|
133
|
+
const conversationContext = history
|
|
134
|
+
.filter((m) => {
|
|
135
|
+
var _a, _b;
|
|
136
|
+
return (m.role === 'user' || m.role === 'assistant') &&
|
|
137
|
+
!((_a = m.toolCalls) === null || _a === void 0 ? void 0 : _a.length) &&
|
|
138
|
+
!m.thinking &&
|
|
139
|
+
!!((_b = m.content) === null || _b === void 0 ? void 0 : _b.trim());
|
|
140
|
+
})
|
|
141
|
+
.slice(-SUGGESTIONS_HISTORY_WINDOW)
|
|
142
|
+
.map((m) => `${m.role === 'user' ? 'User' : 'Assistant'}: ${m.content}`)
|
|
143
|
+
.join('\n');
|
|
144
|
+
const message = conversationContext || 'The conversation has not started yet.';
|
|
145
|
+
const text = yield this.aiProvider.prompt(message, { systemPrompt });
|
|
146
|
+
return text
|
|
147
|
+
.split('\n')
|
|
148
|
+
.map((s) => s.trim())
|
|
149
|
+
.filter(Boolean);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
35
152
|
isBusy() {
|
|
36
153
|
return this.busy;
|
|
37
154
|
}
|
|
@@ -62,11 +179,9 @@ export class ChatDriver extends EventTarget {
|
|
|
62
179
|
resolveInteraction(interactionId, result) {
|
|
63
180
|
const interaction = this.pendingInteractions.get(interactionId);
|
|
64
181
|
if (interaction) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (msg === null || msg === void 0 ? void 0 : msg.interaction) {
|
|
69
|
-
msg.interaction.resolved = true;
|
|
182
|
+
const idx = this.history.findIndex((m) => { var _a; return ((_a = m.interaction) === null || _a === void 0 ? void 0 : _a.interactionId) === interactionId; });
|
|
183
|
+
if (idx !== -1) {
|
|
184
|
+
this.history[idx] = Object.assign(Object.assign({}, this.history[idx]), { interaction: Object.assign(Object.assign({}, this.history[idx].interaction), { resolved: result }) });
|
|
70
185
|
this.dispatchEvent(new CustomEvent('history-updated', {
|
|
71
186
|
detail: this.history,
|
|
72
187
|
}));
|
|
@@ -84,57 +199,224 @@ export class ChatDriver extends EventTarget {
|
|
|
84
199
|
*/
|
|
85
200
|
loadHistory(messages) {
|
|
86
201
|
this.history = [...messages];
|
|
202
|
+
this.dispatchEvent(new CustomEvent('history-updated', {
|
|
203
|
+
detail: this.history,
|
|
204
|
+
}));
|
|
87
205
|
}
|
|
88
206
|
sendMessage(userInput, attachments) {
|
|
89
207
|
return __awaiter(this, void 0, void 0, function* () {
|
|
90
208
|
if (this.busy || (!userInput.trim() && !(attachments === null || attachments === void 0 ? void 0 : attachments.length)))
|
|
91
|
-
return;
|
|
209
|
+
return { reason: 'done' };
|
|
92
210
|
if (!this.aiProvider.chat) {
|
|
93
211
|
logger.warn('ChatDriver: AIProvider does not implement chat()');
|
|
94
|
-
return;
|
|
212
|
+
return { reason: 'done' };
|
|
95
213
|
}
|
|
96
214
|
this.busy = true;
|
|
97
215
|
this.appendToHistory({ role: 'user', content: userInput, attachments });
|
|
98
216
|
try {
|
|
99
|
-
yield this.runToolLoop(userInput, attachments);
|
|
217
|
+
return yield this.runToolLoop(userInput, attachments);
|
|
218
|
+
}
|
|
219
|
+
catch (e) {
|
|
220
|
+
logger.error('ChatDriver error:', e);
|
|
221
|
+
this.appendToHistory({ role: 'assistant', content: 'Sorry, something went wrong.' });
|
|
222
|
+
return { reason: 'done' };
|
|
223
|
+
}
|
|
224
|
+
finally {
|
|
225
|
+
this.busy = false;
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Continue the tool loop from current history without appending a new user message.
|
|
231
|
+
* Used by OrchestratingDriver after an agent handoff.
|
|
232
|
+
*/
|
|
233
|
+
continueFromHistory(transientPrimer) {
|
|
234
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
235
|
+
if (this.busy)
|
|
236
|
+
return { reason: 'done' };
|
|
237
|
+
if (!this.aiProvider.chat) {
|
|
238
|
+
logger.warn('ChatDriver: AIProvider does not implement chat()');
|
|
239
|
+
return { reason: 'done' };
|
|
240
|
+
}
|
|
241
|
+
this.busy = true;
|
|
242
|
+
try {
|
|
243
|
+
return yield this.runToolLoop('', undefined, transientPrimer);
|
|
100
244
|
}
|
|
101
245
|
catch (e) {
|
|
102
246
|
logger.error('ChatDriver error:', e);
|
|
103
247
|
this.appendToHistory({ role: 'assistant', content: 'Sorry, something went wrong.' });
|
|
248
|
+
return { reason: 'done' };
|
|
104
249
|
}
|
|
105
250
|
finally {
|
|
106
251
|
this.busy = false;
|
|
107
252
|
}
|
|
108
253
|
});
|
|
109
254
|
}
|
|
110
|
-
|
|
255
|
+
// ---------------------------------------------------------------------------
|
|
256
|
+
// Fold mechanics
|
|
257
|
+
// ---------------------------------------------------------------------------
|
|
258
|
+
/** Extract ToolFold metadata from a handler, or undefined if it isn't a fold facade. */
|
|
259
|
+
getFold(toolName) {
|
|
260
|
+
const handler = this.toolHandlers[toolName];
|
|
261
|
+
return handler ? handler[TOOL_FOLD_SYMBOL] : undefined;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Search all currently registered fold facades (and their nested folds recursively)
|
|
265
|
+
* to find which fold contains a given tool name. Returns the immediate parent fold name.
|
|
266
|
+
*/
|
|
267
|
+
findFoldContaining(toolName, handlers) {
|
|
268
|
+
const source = handlers !== null && handlers !== void 0 ? handlers : this.toolHandlers;
|
|
269
|
+
for (const [, handler] of Object.entries(source)) {
|
|
270
|
+
const fold = handler[TOOL_FOLD_SYMBOL];
|
|
271
|
+
if (!fold)
|
|
272
|
+
continue;
|
|
273
|
+
// Direct inner tool match
|
|
274
|
+
if (fold.handlers[toolName])
|
|
275
|
+
return fold.name;
|
|
276
|
+
// Recurse into nested folds
|
|
277
|
+
const nested = this.findFoldContaining(toolName, fold.handlers);
|
|
278
|
+
if (nested)
|
|
279
|
+
return fold.name;
|
|
280
|
+
}
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Install the fold's inner tool set, replacing (exclusive) or extending (non-exclusive)
|
|
285
|
+
* the current tool set. Also injects the close tool. Does NOT touch the fold stack.
|
|
286
|
+
*/
|
|
287
|
+
applyFoldToolSet(fold, foldName) {
|
|
288
|
+
const closeToolName = `close_${foldName}`;
|
|
289
|
+
const newDefs = [];
|
|
290
|
+
const newHandlers = {};
|
|
291
|
+
if (!fold.exclusive) {
|
|
292
|
+
// Non-exclusive: keep existing tools minus the facade we just opened
|
|
293
|
+
for (const def of this.toolDefinitions) {
|
|
294
|
+
if (def.name !== foldName)
|
|
295
|
+
newDefs.push(def);
|
|
296
|
+
}
|
|
297
|
+
for (const [name, handler] of Object.entries(this.toolHandlers)) {
|
|
298
|
+
if (name !== foldName)
|
|
299
|
+
newHandlers[name] = handler;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
// Install inner tools from fold metadata
|
|
303
|
+
newDefs.push(...fold.tools);
|
|
304
|
+
Object.assign(newHandlers, fold.handlers);
|
|
305
|
+
// Inject the close tool
|
|
306
|
+
newDefs.push({
|
|
307
|
+
name: closeToolName,
|
|
308
|
+
description: `Close the ${foldName} fold and return to the previous set of tools.`,
|
|
309
|
+
parameters: { type: 'object', properties: {} },
|
|
310
|
+
});
|
|
311
|
+
newHandlers[closeToolName] = () => __awaiter(this, void 0, void 0, function* () { return this.closeFold(); });
|
|
312
|
+
this.toolDefinitions = newDefs;
|
|
313
|
+
this.toolHandlers = newHandlers;
|
|
314
|
+
}
|
|
315
|
+
/** Open a fold: push a stack frame, swap the tool set, return the response message. */
|
|
316
|
+
openFold(foldName, fold, args) {
|
|
317
|
+
// Shortcut dispatch: model passed inner tool args directly, e.g.
|
|
318
|
+
// trading_tools({ search_trades: { side: "BUY" } })
|
|
319
|
+
for (const key of Object.keys(args)) {
|
|
320
|
+
const innerHandler = fold.handlers[key];
|
|
321
|
+
if (innerHandler) {
|
|
322
|
+
logger.debug(`ChatDriver: fold shortcut dispatch "${foldName}" → "${key}"`);
|
|
323
|
+
// Open the fold first so the tool set is correct for subsequent calls
|
|
324
|
+
this.pushFoldFrame(foldName);
|
|
325
|
+
this.applyFoldToolSet(fold, foldName);
|
|
326
|
+
this.consecutiveFoldOps = 0; // shortcut dispatch counts as real work
|
|
327
|
+
const innerArgs = typeof args[key] === 'object' && args[key] !== null
|
|
328
|
+
? args[key]
|
|
329
|
+
: {};
|
|
330
|
+
return innerHandler(innerArgs, {
|
|
331
|
+
requestInteraction: (c, d) => this.requestInteraction(c, d),
|
|
332
|
+
}).then((r) => (typeof r === 'string' ? r : JSON.stringify(r)));
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
// Normal two-step open
|
|
336
|
+
this.pushFoldFrame(foldName);
|
|
337
|
+
this.applyFoldToolSet(fold, foldName);
|
|
338
|
+
const innerToolNames = fold.tools.map((t) => t.name);
|
|
339
|
+
const closeToolName = `close_${foldName}`;
|
|
340
|
+
let message = `Fold opened: ${foldName}. Tools now available: ${[...innerToolNames, closeToolName].join(', ')}.`;
|
|
341
|
+
if (fold.usageNotes)
|
|
342
|
+
message += ` Notes: ${fold.usageNotes}`;
|
|
343
|
+
message += ` Call ${closeToolName} when done to return to the previous tools.`;
|
|
344
|
+
return Promise.resolve(message);
|
|
345
|
+
}
|
|
346
|
+
pushFoldFrame(foldName) {
|
|
347
|
+
this.foldStack.push({
|
|
348
|
+
foldName,
|
|
349
|
+
previousDefinitions: [...this.toolDefinitions],
|
|
350
|
+
previousHandlers: Object.assign({}, this.toolHandlers),
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
/** Close the top fold: pop the stack frame, restore the previous tool set. */
|
|
354
|
+
closeFold() {
|
|
355
|
+
const frame = this.foldStack.pop();
|
|
356
|
+
if (!frame)
|
|
357
|
+
return 'No fold is currently open.';
|
|
358
|
+
this.toolDefinitions = frame.previousDefinitions;
|
|
359
|
+
this.toolHandlers = frame.previousHandlers;
|
|
360
|
+
const toolNames = this.toolDefinitions.map((t) => t.name);
|
|
361
|
+
return `Fold closed: ${frame.foldName}. Tools now available: ${toolNames.join(', ')}.`;
|
|
362
|
+
}
|
|
363
|
+
/** Build the fold-awareness suffix appended to the system prompt each LLM call. */
|
|
364
|
+
buildFoldSystemPromptSuffix() {
|
|
365
|
+
// Collect fold facades from the current handler map
|
|
366
|
+
const activeFolds = [];
|
|
367
|
+
for (const handler of Object.values(this.toolHandlers)) {
|
|
368
|
+
const fold = handler[TOOL_FOLD_SYMBOL];
|
|
369
|
+
if (fold)
|
|
370
|
+
activeFolds.push(fold);
|
|
371
|
+
}
|
|
372
|
+
if (activeFolds.length === 0 && this.foldStack.length === 0)
|
|
373
|
+
return '';
|
|
374
|
+
const parts = ['\n\n--- Tool Folds ---'];
|
|
375
|
+
parts.push('Some tools are grouped into folds. You may see tool calls in the conversation history for tools that are not currently available — they are inside a fold. To access them, invoke the fold tool first.');
|
|
376
|
+
if (this.foldStack.length > 0) {
|
|
377
|
+
const current = this.foldStack[this.foldStack.length - 1];
|
|
378
|
+
parts.push(`You are currently inside the "${current.foldName}" fold. Call close_${current.foldName} when you are done with these tools.`);
|
|
379
|
+
}
|
|
380
|
+
for (const fold of activeFolds) {
|
|
381
|
+
parts.push(`• ${fold.name}: ${fold.description}`);
|
|
382
|
+
}
|
|
383
|
+
return parts.join('\n');
|
|
384
|
+
}
|
|
385
|
+
// ---------------------------------------------------------------------------
|
|
386
|
+
// Tool loop
|
|
387
|
+
// ---------------------------------------------------------------------------
|
|
388
|
+
runToolLoop(userInput, attachments, transientPrimer) {
|
|
111
389
|
return __awaiter(this, void 0, void 0, function* () {
|
|
112
390
|
var _a, _b, _c, _d, _e, _f;
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
};
|
|
117
|
-
// History has the user message at the end — pass everything before it as history,
|
|
118
|
-
// and the user input as the userMessage argument.
|
|
391
|
+
if (!this.systemPrompt) {
|
|
392
|
+
logger.warn('ChatDriver: no systemPrompt set. The assistant will have no instructions — provide a systemPrompt via agents config or the foundation-ai-assistant property.');
|
|
393
|
+
}
|
|
119
394
|
let currentInput = userInput;
|
|
120
395
|
let currentAttachments = attachments;
|
|
121
396
|
let iterations = 0;
|
|
122
397
|
let malformedAttempts = 0;
|
|
398
|
+
const startIteration = currentInput ? 1 : 0;
|
|
123
399
|
while (iterations < this.maxToolIterations) {
|
|
124
400
|
iterations += 1;
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
const
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
401
|
+
const foldSuffix = this.buildFoldSystemPromptSuffix();
|
|
402
|
+
const baseSystemPrompt = this.systemPrompt
|
|
403
|
+
? `${this.systemPrompt}${foldSuffix}`
|
|
404
|
+
: foldSuffix || undefined;
|
|
405
|
+
const primer = [...((_a = this.primerHistory) !== null && _a !== void 0 ? _a : []), ...(transientPrimer !== null && transientPrimer !== void 0 ? transientPrimer : [])];
|
|
406
|
+
const baseHistory = iterations === startIteration ? this.history.slice(0, -1) : this.history;
|
|
407
|
+
const historyForProvider = this.providerHistoryTransform
|
|
408
|
+
? this.providerHistoryTransform([...baseHistory])
|
|
409
|
+
: baseHistory;
|
|
410
|
+
const historyForCall = [...primer, ...historyForProvider];
|
|
134
411
|
const systemPrompt = malformedAttempts > 0
|
|
135
|
-
? `${
|
|
136
|
-
:
|
|
137
|
-
const options =
|
|
412
|
+
? `${baseSystemPrompt !== null && baseSystemPrompt !== void 0 ? baseSystemPrompt : ''}\n\nIMPORTANT: Use only the structured function-call API to invoke tools. Do not write Python code or use Python-style syntax to call tools.`
|
|
413
|
+
: baseSystemPrompt;
|
|
414
|
+
const options = {
|
|
415
|
+
systemPrompt,
|
|
416
|
+
// Strip fold-only properties (foldEvent, foldPath) before sending to provider
|
|
417
|
+
tools: this.toolDefinitions.length ? this.toolDefinitions : undefined,
|
|
418
|
+
attachments: currentAttachments,
|
|
419
|
+
};
|
|
138
420
|
let response;
|
|
139
421
|
try {
|
|
140
422
|
// eslint-disable-next-line no-await-in-loop
|
|
@@ -145,7 +427,7 @@ export class ChatDriver extends EventTarget {
|
|
|
145
427
|
malformedAttempts += 1;
|
|
146
428
|
if (malformedAttempts < MAX_MALFORMED_RETRIES) {
|
|
147
429
|
logger.warn(`ChatDriver: MALFORMED_FUNCTION_CALL, retrying (${malformedAttempts}/${MAX_MALFORMED_RETRIES})`);
|
|
148
|
-
iterations -= 1;
|
|
430
|
+
iterations -= 1;
|
|
149
431
|
continue;
|
|
150
432
|
}
|
|
151
433
|
logger.error('ChatDriver: MALFORMED_FUNCTION_CALL, max retries reached');
|
|
@@ -153,65 +435,215 @@ export class ChatDriver extends EventTarget {
|
|
|
153
435
|
role: 'assistant',
|
|
154
436
|
content: "I'm sorry, I wasn't able to complete that request. Please try rephrasing or breaking it into smaller steps.",
|
|
155
437
|
});
|
|
156
|
-
return;
|
|
438
|
+
return { reason: 'done' };
|
|
157
439
|
}
|
|
158
440
|
throw e;
|
|
159
441
|
}
|
|
160
|
-
currentAttachments = undefined;
|
|
161
|
-
const isThinkingStep = response.content && ((
|
|
162
|
-
const isEmptyResponse = !((
|
|
442
|
+
currentAttachments = undefined;
|
|
443
|
+
const isThinkingStep = response.content && ((_b = response.toolCalls) === null || _b === void 0 ? void 0 : _b.length);
|
|
444
|
+
const isEmptyResponse = !((_c = response.content) === null || _c === void 0 ? void 0 : _c.trim()) && !((_d = response.toolCalls) === null || _d === void 0 ? void 0 : _d.length);
|
|
163
445
|
if (isEmptyResponse) {
|
|
164
|
-
|
|
446
|
+
malformedAttempts += 1;
|
|
447
|
+
if (malformedAttempts < MAX_MALFORMED_RETRIES) {
|
|
448
|
+
logger.warn(`ChatDriver: empty model response, retrying (${malformedAttempts}/${MAX_MALFORMED_RETRIES})`);
|
|
449
|
+
iterations -= 1;
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
logger.error('ChatDriver: empty model response after all retries');
|
|
453
|
+
this.appendToHistory({
|
|
454
|
+
role: 'assistant',
|
|
455
|
+
content: 'Remote agent returned no response.',
|
|
456
|
+
});
|
|
457
|
+
return { reason: 'done' };
|
|
165
458
|
}
|
|
166
459
|
else if (isThinkingStep) {
|
|
167
|
-
// Separate thinking message and tool call message
|
|
168
460
|
this.appendToHistory(Object.assign(Object.assign({}, response), { toolCalls: undefined, thinking: true }));
|
|
169
461
|
this.appendToHistory(Object.assign(Object.assign({}, response), { content: '' }));
|
|
170
462
|
}
|
|
171
463
|
else {
|
|
172
464
|
this.appendToHistory(response);
|
|
173
465
|
}
|
|
174
|
-
if (!((
|
|
175
|
-
// Terminal text response — done
|
|
466
|
+
if (!((_e = response.toolCalls) === null || _e === void 0 ? void 0 : _e.length)) {
|
|
176
467
|
break;
|
|
177
468
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
469
|
+
const [toolCalls, systemCalls] = response.toolCalls.reduce((acc, tc) => {
|
|
470
|
+
if (tc.name === REQUEST_CONTINUATION_TOOL)
|
|
471
|
+
acc[1].push(tc);
|
|
472
|
+
else
|
|
473
|
+
acc[0].push(tc);
|
|
474
|
+
return acc;
|
|
475
|
+
}, [[], []]);
|
|
476
|
+
const executedById = new Map();
|
|
477
|
+
const unknownToolIds = new Set();
|
|
478
|
+
let anyRealToolExecuted = false;
|
|
479
|
+
let hitUnknownToolLimit = false;
|
|
480
|
+
if (toolCalls.length > 0) {
|
|
481
|
+
// eslint-disable-next-line no-await-in-loop
|
|
482
|
+
yield Promise.all(toolCalls.map((tc) => __awaiter(this, void 0, void 0, function* () {
|
|
483
|
+
// Check for fold facade
|
|
484
|
+
const fold = this.getFold(tc.name);
|
|
485
|
+
if (fold) {
|
|
486
|
+
this.consecutiveFoldOps += 1;
|
|
487
|
+
if (this.consecutiveFoldOps > this.maxFoldOperations) {
|
|
488
|
+
logger.warn(`ChatDriver: fold operation limit (${this.maxFoldOperations}) reached — injecting guidance`);
|
|
489
|
+
executedById.set(tc.id, {
|
|
490
|
+
toolCallId: tc.id,
|
|
491
|
+
content: `You have opened and closed folds ${this.consecutiveFoldOps} times without calling any tools. Please call a specific tool to make progress, or respond to the user.`,
|
|
492
|
+
});
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
const content = yield this.openFold(tc.name, fold, tc.args);
|
|
496
|
+
executedById.set(tc.id, { toolCallId: tc.id, content });
|
|
497
|
+
// Fold open/close does NOT count as a real iteration — decrement to compensate
|
|
498
|
+
iterations -= 1;
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
// Check for close-fold tool
|
|
502
|
+
if (tc.name.startsWith('close_') && this.foldStack.length > 0) {
|
|
503
|
+
const topFoldName = this.foldStack[this.foldStack.length - 1].foldName;
|
|
504
|
+
if (tc.name === `close_${topFoldName}`) {
|
|
505
|
+
this.consecutiveFoldOps += 1;
|
|
506
|
+
if (this.consecutiveFoldOps > this.maxFoldOperations) {
|
|
507
|
+
executedById.set(tc.id, {
|
|
508
|
+
toolCallId: tc.id,
|
|
509
|
+
content: `You have opened and closed folds ${this.consecutiveFoldOps} times without calling any tools. Please call a specific tool to make progress, or respond to the user.`,
|
|
510
|
+
});
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
const content = this.closeFold();
|
|
514
|
+
executedById.set(tc.id, { toolCallId: tc.id, content });
|
|
515
|
+
iterations -= 1;
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
// Regular tool — check if it's inside a fold and guide the model
|
|
520
|
+
const handler = this.toolHandlers[tc.name];
|
|
521
|
+
if (!handler) {
|
|
522
|
+
const containingFold = this.findFoldContaining(tc.name);
|
|
523
|
+
if (containingFold) {
|
|
524
|
+
logger.debug(`ChatDriver: model called folded tool "${tc.name}" — guiding to open "${containingFold}"`);
|
|
525
|
+
executedById.set(tc.id, {
|
|
526
|
+
toolCallId: tc.id,
|
|
527
|
+
content: `"${tc.name}" is not directly available. It is inside the "${containingFold}" fold. Call ${containingFold} first to access it.`,
|
|
528
|
+
});
|
|
529
|
+
// Guidance does not count as a real iteration or fold op
|
|
530
|
+
iterations -= 1;
|
|
531
|
+
}
|
|
532
|
+
else {
|
|
533
|
+
this.consecutiveUnknownToolCalls += 1;
|
|
534
|
+
logger.warn(`ChatDriver: no handler registered for tool "${tc.name}" (${this.consecutiveUnknownToolCalls}/${DEFAULT_MAX_UNKNOWN_TOOL_CALLS}). Available tools: ${Object.keys(this.toolHandlers).join(', ') || '(none)'}`);
|
|
535
|
+
executedById.set(tc.id, { toolCallId: tc.id, content: `Unknown tool: ${tc.name}` });
|
|
536
|
+
unknownToolIds.add(tc.id);
|
|
537
|
+
if (this.consecutiveUnknownToolCalls >= DEFAULT_MAX_UNKNOWN_TOOL_CALLS) {
|
|
538
|
+
hitUnknownToolLimit = true;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
// Real tool execution
|
|
544
|
+
try {
|
|
545
|
+
const result = yield handler(tc.args, {
|
|
546
|
+
requestInteraction: (componentName, data) => this.requestInteraction(componentName, data),
|
|
547
|
+
});
|
|
548
|
+
const content = typeof result === 'string' ? result : JSON.stringify(result);
|
|
549
|
+
executedById.set(tc.id, { toolCallId: tc.id, content });
|
|
550
|
+
anyRealToolExecuted = true;
|
|
551
|
+
}
|
|
552
|
+
catch (e) {
|
|
553
|
+
logger.error(`ChatDriver tool "${tc.name}" failed:`, e);
|
|
554
|
+
executedById.set(tc.id, {
|
|
555
|
+
toolCallId: tc.id,
|
|
556
|
+
content: `Tool error: ${e.message}`,
|
|
557
|
+
});
|
|
558
|
+
anyRealToolExecuted = true; // treat errors as real work for fold op counting
|
|
559
|
+
}
|
|
560
|
+
})));
|
|
561
|
+
}
|
|
562
|
+
// Reset counters whenever a real tool executes
|
|
563
|
+
if (anyRealToolExecuted) {
|
|
564
|
+
this.consecutiveFoldOps = 0;
|
|
565
|
+
this.consecutiveUnknownToolCalls = 0;
|
|
566
|
+
}
|
|
567
|
+
// Tag tool calls with fold UI metadata before appending results
|
|
568
|
+
const foldPath = this.foldStack.map((f) => f.foldName);
|
|
569
|
+
for (const tc of response.toolCalls) {
|
|
570
|
+
if (tc.name === REQUEST_CONTINUATION_TOOL) {
|
|
571
|
+
this.appendToHistory({
|
|
572
|
+
role: 'tool',
|
|
573
|
+
content: '',
|
|
574
|
+
toolResult: { toolCallId: tc.id, content: HANDOFF_TOOL_RESULT_PLACEHOLDER },
|
|
189
575
|
});
|
|
190
|
-
const content = typeof result === 'string' ? result : JSON.stringify(result);
|
|
191
|
-
return { toolCallId: tc.id, content };
|
|
192
576
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
577
|
+
else {
|
|
578
|
+
const r = executedById.get(tc.id);
|
|
579
|
+
if (r) {
|
|
580
|
+
this.appendToHistory({ role: 'tool', content: '', toolResult: r });
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
// Back-patch foldEvent and foldPath onto the tool call message we just appended.
|
|
585
|
+
// The response was appended before execution — find it and annotate.
|
|
586
|
+
let tcMsgIdx = -1;
|
|
587
|
+
for (let i = this.history.length - 1; i >= 0; i -= 1) {
|
|
588
|
+
if (this.history[i].role === 'assistant' && ((_f = this.history[i].toolCalls) === null || _f === void 0 ? void 0 : _f.length)) {
|
|
589
|
+
tcMsgIdx = i;
|
|
590
|
+
break;
|
|
196
591
|
}
|
|
197
|
-
}
|
|
198
|
-
|
|
592
|
+
}
|
|
593
|
+
if (tcMsgIdx !== -1) {
|
|
594
|
+
const tcMsg = this.history[tcMsgIdx];
|
|
595
|
+
const availableToolNames = Object.keys(this.toolHandlers);
|
|
596
|
+
const annotatedCalls = tcMsg.toolCalls.map((tc) => {
|
|
597
|
+
var _a, _b, _c, _d;
|
|
598
|
+
const isFoldOpen = !!this.getFold(tc.name) ||
|
|
599
|
+
(
|
|
600
|
+
// Was a fold facade at time of the call (now the tool set has changed)
|
|
601
|
+
// — detect by checking if the result message indicated a fold open
|
|
602
|
+
(_b = (_a = executedById.get(tc.id)) === null || _a === void 0 ? void 0 : _a.content) === null || _b === void 0 ? void 0 : _b.startsWith('Fold opened:'));
|
|
603
|
+
const isFoldClose = (_d = (_c = executedById.get(tc.id)) === null || _c === void 0 ? void 0 : _c.content) === null || _d === void 0 ? void 0 : _d.startsWith('Fold closed:');
|
|
604
|
+
const isUnknown = unknownToolIds.has(tc.id);
|
|
605
|
+
return Object.assign(Object.assign({}, tc), { foldEvent: isFoldOpen
|
|
606
|
+
? 'open'
|
|
607
|
+
: isFoldClose
|
|
608
|
+
? 'close'
|
|
609
|
+
: undefined,
|
|
610
|
+
// Use the fold path that was active at the START of this iteration (before any opens/closes)
|
|
611
|
+
foldPath: !isFoldOpen && !isFoldClose && foldPath.length > 0 ? foldPath : undefined, unknown: isUnknown || undefined, availableTools: isUnknown ? availableToolNames : undefined });
|
|
612
|
+
});
|
|
613
|
+
this.history[tcMsgIdx] = Object.assign(Object.assign({}, tcMsg), { toolCalls: annotatedCalls });
|
|
614
|
+
this.dispatchEvent(new CustomEvent('history-updated', {
|
|
615
|
+
detail: this.history,
|
|
616
|
+
}));
|
|
617
|
+
}
|
|
618
|
+
if (hitUnknownToolLimit) {
|
|
619
|
+
logger.error(`ChatDriver: unknown-tool limit (${DEFAULT_MAX_UNKNOWN_TOOL_CALLS}) reached — stopping`);
|
|
199
620
|
this.appendToHistory({
|
|
200
|
-
role: '
|
|
201
|
-
content: '',
|
|
202
|
-
toolResult: result,
|
|
621
|
+
role: 'assistant',
|
|
622
|
+
content: "I'm sorry, I repeatedly tried to use tools that don't exist. Please check your agent configuration or try rephrasing your request.",
|
|
203
623
|
});
|
|
624
|
+
return { reason: 'done' };
|
|
625
|
+
}
|
|
626
|
+
const firstContinuation = systemCalls[0];
|
|
627
|
+
if (firstContinuation) {
|
|
628
|
+
const { summary, remaining_task: remainingTask } = firstContinuation.args;
|
|
629
|
+
return { reason: 'agent-handoff', summary, remainingTask };
|
|
204
630
|
}
|
|
205
|
-
// Next iteration sends an empty string — the tool results are in history
|
|
206
631
|
currentInput = '';
|
|
207
632
|
}
|
|
208
633
|
if (iterations >= this.maxToolIterations) {
|
|
209
634
|
logger.warn('ChatDriver: reached max tool iterations, stopping');
|
|
635
|
+
this.appendToHistory({
|
|
636
|
+
role: 'assistant',
|
|
637
|
+
content: "I've reached my limit for this response. You can ask me to continue and I'll pick up where I left off.",
|
|
638
|
+
});
|
|
210
639
|
}
|
|
640
|
+
return { reason: 'done' };
|
|
211
641
|
});
|
|
212
642
|
}
|
|
213
643
|
appendToHistory(message) {
|
|
214
|
-
|
|
644
|
+
const tagged = this.activeAgentName
|
|
645
|
+
? Object.assign(Object.assign({}, message), { agentName: this.activeAgentName }) : message;
|
|
646
|
+
this.history = [...this.history, tagged];
|
|
215
647
|
this.dispatchEvent(new CustomEvent('history-updated', {
|
|
216
648
|
detail: this.history,
|
|
217
649
|
}));
|