zerochat 7.3.0__py3-none-any.whl
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.
- zerochat-7.3.0.dist-info/METADATA +70 -0
- zerochat-7.3.0.dist-info/RECORD +130 -0
- zerochat-7.3.0.dist-info/WHEEL +5 -0
- zerochat-7.3.0.dist-info/entry_points.txt +2 -0
- zerochat-7.3.0.dist-info/licenses/LICENSE +34 -0
- zerochat-7.3.0.dist-info/top_level.txt +2 -0
- zerochat.py +2004 -0
- zerochat_runtime/__init__.py +1 -0
- zerochat_runtime/assets/css/base.css +199 -0
- zerochat_runtime/assets/css/components/composer.css +1258 -0
- zerochat_runtime/assets/css/components/debug.css +777 -0
- zerochat_runtime/assets/css/components/header.css +97 -0
- zerochat_runtime/assets/css/components/icons.css +39 -0
- zerochat_runtime/assets/css/components/markdown.css +685 -0
- zerochat_runtime/assets/css/components/messages.css +473 -0
- zerochat_runtime/assets/css/components/modals.css +1319 -0
- zerochat_runtime/assets/css/components/sidebar.css +622 -0
- zerochat_runtime/assets/css/components/tools.css +1223 -0
- zerochat_runtime/assets/css/layout.css +171 -0
- zerochat_runtime/assets/css/print.css +120 -0
- zerochat_runtime/assets/css/styles.css +18 -0
- zerochat_runtime/assets/css/theme-overrides.css +555 -0
- zerochat_runtime/assets/css/tokens.css +165 -0
- zerochat_runtime/assets/help/architecture.html +247 -0
- zerochat_runtime/assets/help/composer.html +134 -0
- zerochat_runtime/assets/help/conversations.html +105 -0
- zerochat_runtime/assets/help/debug.html +108 -0
- zerochat_runtime/assets/help/en/architecture.html +247 -0
- zerochat_runtime/assets/help/en/composer.html +134 -0
- zerochat_runtime/assets/help/en/conversations.html +104 -0
- zerochat_runtime/assets/help/en/debug.html +105 -0
- zerochat_runtime/assets/help/en/gemini-free.html +39 -0
- zerochat_runtime/assets/help/en/index.html +191 -0
- zerochat_runtime/assets/help/en/learning.html +71 -0
- zerochat_runtime/assets/help/en/mcp.html +143 -0
- zerochat_runtime/assets/help/en/openrouter-free.html +39 -0
- zerochat_runtime/assets/help/en/profiles.html +173 -0
- zerochat_runtime/assets/help/en/rag.html +130 -0
- zerochat_runtime/assets/help/en/reasoning-telemetry.html +108 -0
- zerochat_runtime/assets/help/en/tools-agent.html +133 -0
- zerochat_runtime/assets/help/en/webllm.html +355 -0
- zerochat_runtime/assets/help/gemini-free.html +39 -0
- zerochat_runtime/assets/help/help.css +604 -0
- zerochat_runtime/assets/help/help.js +51 -0
- zerochat_runtime/assets/help/index.html +194 -0
- zerochat_runtime/assets/help/learning.html +71 -0
- zerochat_runtime/assets/help/mcp.html +143 -0
- zerochat_runtime/assets/help/openrouter-free.html +39 -0
- zerochat_runtime/assets/help/profiles.html +174 -0
- zerochat_runtime/assets/help/rag.html +130 -0
- zerochat_runtime/assets/help/reasoning-telemetry.html +111 -0
- zerochat_runtime/assets/help/tools-agent.html +133 -0
- zerochat_runtime/assets/help/webllm.html +359 -0
- zerochat_runtime/assets/js/agent-core.js +1553 -0
- zerochat_runtime/assets/js/api.js +844 -0
- zerochat_runtime/assets/js/app.js +2558 -0
- zerochat_runtime/assets/js/attachments.js +220 -0
- zerochat_runtime/assets/js/charts.js +338 -0
- zerochat_runtime/assets/js/chat-engine.js +566 -0
- zerochat_runtime/assets/js/config-store.js +207 -0
- zerochat_runtime/assets/js/context-manager.js +584 -0
- zerochat_runtime/assets/js/conversation-service.js +484 -0
- zerochat_runtime/assets/js/cookies.js +688 -0
- zerochat_runtime/assets/js/data-reset-service.js +136 -0
- zerochat_runtime/assets/js/debug.js +508 -0
- zerochat_runtime/assets/js/defaults.js +16 -0
- zerochat_runtime/assets/js/export.js +179 -0
- zerochat_runtime/assets/js/file-parser.js +2088 -0
- zerochat_runtime/assets/js/generation-controller.js +417 -0
- zerochat_runtime/assets/js/i18n.js +1483 -0
- zerochat_runtime/assets/js/icons.js +156 -0
- zerochat_runtime/assets/js/ingestionEngine.js +436 -0
- zerochat_runtime/assets/js/markdown.js +588 -0
- zerochat_runtime/assets/js/mcp.js +1271 -0
- zerochat_runtime/assets/js/message-turns.js +62 -0
- zerochat_runtime/assets/js/profile-backup.js +118 -0
- zerochat_runtime/assets/js/profile-export-bundle.js +481 -0
- zerochat_runtime/assets/js/profile-repository.js +213 -0
- zerochat_runtime/assets/js/providers-webllm.js +486 -0
- zerochat_runtime/assets/js/providers.js +1560 -0
- zerochat_runtime/assets/js/rag-index.js +295 -0
- zerochat_runtime/assets/js/rag-service.js +530 -0
- zerochat_runtime/assets/js/rag-ui.js +997 -0
- zerochat_runtime/assets/js/ragStorage.js +676 -0
- zerochat_runtime/assets/js/sandbox.js +449 -0
- zerochat_runtime/assets/js/state.js +704 -0
- zerochat_runtime/assets/js/storage-db.js +144 -0
- zerochat_runtime/assets/js/tool-cards.js +330 -0
- zerochat_runtime/assets/js/tool-security.js +653 -0
- zerochat_runtime/assets/js/tools/README.md +50 -0
- zerochat_runtime/assets/js/tools/builtin/agent-checkpoint.tool.js +212 -0
- zerochat_runtime/assets/js/tools/builtin/download-pdf.tool.js +111 -0
- zerochat_runtime/assets/js/tools/builtin/execute-javascript.tool.js +219 -0
- zerochat_runtime/assets/js/tools/builtin/fetch-web-page.tool.js +112 -0
- zerochat_runtime/assets/js/tools/builtin/list-documents.tool.js +117 -0
- zerochat_runtime/assets/js/tools/builtin/read-knowledge-chunk.tool.js +129 -0
- zerochat_runtime/assets/js/tools/builtin/read-knowledge-image.tool.js +95 -0
- zerochat_runtime/assets/js/tools/builtin/render-chart.tool.js +90 -0
- zerochat_runtime/assets/js/tools/builtin/search-knowledge-base.tool.js +124 -0
- zerochat_runtime/assets/js/tools/builtin/search-web.tool.js +125 -0
- zerochat_runtime/assets/js/tools/tool-manifest.js +53 -0
- zerochat_runtime/assets/js/tools/tool-runtime.js +77 -0
- zerochat_runtime/assets/js/ui-composer.js +319 -0
- zerochat_runtime/assets/js/ui-conversation.js +655 -0
- zerochat_runtime/assets/js/ui-dialogs.js +132 -0
- zerochat_runtime/assets/js/ui-generation-status.js +119 -0
- zerochat_runtime/assets/js/ui-inspector.js +804 -0
- zerochat_runtime/assets/js/ui-mcp.js +604 -0
- zerochat_runtime/assets/js/ui-profiles.js +824 -0
- zerochat_runtime/assets/js/ui-reasoning.js +146 -0
- zerochat_runtime/assets/js/ui-settings.js +825 -0
- zerochat_runtime/assets/js/ui-shell.js +215 -0
- zerochat_runtime/assets/js/ui-sidebar.js +429 -0
- zerochat_runtime/assets/js/ui-telemetry.js +404 -0
- zerochat_runtime/assets/js/ui-transfer.js +262 -0
- zerochat_runtime/assets/js/utils.js +216 -0
- zerochat_runtime/assets/js/vendor/orama.browser.js +11 -0
- zerochat_runtime/assets/js/web-browser.js +569 -0
- zerochat_runtime/assets/js/web-search.js +519 -0
- zerochat_runtime/assets/manifest.webmanifest +20 -0
- zerochat_runtime/assets/services/dummy_mcp/dummy_mcp_server.py +47 -0
- zerochat_runtime/assets/services/dummy_mcp/service.json +22 -0
- zerochat_runtime/assets/services/lsp/installer.json +9 -0
- zerochat_runtime/assets/services/lsp/service.json +22 -0
- zerochat_runtime/assets/services/memory/installer.json +9 -0
- zerochat_runtime/assets/services/memory/service.json +24 -0
- zerochat_runtime/assets/services/playwright/installer.json +10 -0
- zerochat_runtime/assets/services/playwright/service.json +39 -0
- zerochat_runtime/assets/sw.js +164 -0
- zerochat_runtime/assets/zerochat.html +671 -0
|
@@ -0,0 +1,1553 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Módulo de Núcleo Agéntico y Sistema Modular de Herramientas (ChatAgentCore) para ZeroChat.
|
|
3
|
+
*
|
|
4
|
+
* Arquitectura:
|
|
5
|
+
* Model -> AgentCore -> ToolRegistry -> Tool -> ToolExecutor
|
|
6
|
+
*
|
|
7
|
+
* Preparado para MCP (Model Context Protocol) y herramientas extensibles.
|
|
8
|
+
* Compatible con entornos file://, http:// y Node.js.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
(function (root, factory) {
|
|
12
|
+
if (typeof exports === 'object' && typeof module !== 'undefined') {
|
|
13
|
+
module.exports = factory();
|
|
14
|
+
} else {
|
|
15
|
+
root.ChatAgentCore = factory();
|
|
16
|
+
}
|
|
17
|
+
})(typeof self !== 'undefined' ? self : this, function () {
|
|
18
|
+
'use strict';
|
|
19
|
+
|
|
20
|
+
// Resolutores perezosos de dependencias del entorno
|
|
21
|
+
function getBuiltinToolModule(globalName, modulePath) {
|
|
22
|
+
if (typeof window !== 'undefined' && window[globalName]) {
|
|
23
|
+
return window[globalName];
|
|
24
|
+
}
|
|
25
|
+
if (typeof require !== 'undefined') {
|
|
26
|
+
try { return require(modulePath); } catch (e) {}
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function getBuiltinToolManifest() {
|
|
32
|
+
if (typeof window !== 'undefined' && window.ChatToolManifest) return window.ChatToolManifest.builtin;
|
|
33
|
+
if (typeof require !== 'undefined') {
|
|
34
|
+
try { return require('./tools/tool-manifest.js').builtin; } catch (e) {}
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function createBuiltinTool(toolId, globalName, modulePath) {
|
|
40
|
+
const builtinManifest = getBuiltinToolManifest();
|
|
41
|
+
const toolModule = builtinManifest?.get(toolId) || getBuiltinToolModule(globalName, modulePath);
|
|
42
|
+
if (!toolModule || typeof toolModule.createTool !== 'function') {
|
|
43
|
+
throw new Error(`No se pudo cargar el módulo de la herramienta ${toolId}.`);
|
|
44
|
+
}
|
|
45
|
+
return toolModule.createTool(Tool);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function getAPI() {
|
|
49
|
+
if (typeof window !== 'undefined' && window.ChatAPI) return window.ChatAPI;
|
|
50
|
+
if (typeof require !== 'undefined') {
|
|
51
|
+
try { return require('./api.js'); } catch (e) {}
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function getContextManager() {
|
|
57
|
+
if (typeof window !== 'undefined' && window.ChatContextManager) return window.ChatContextManager;
|
|
58
|
+
if (typeof require !== 'undefined') {
|
|
59
|
+
try { return require('./context-manager.js'); } catch (e) {}
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function getToolRuntime() {
|
|
65
|
+
if (typeof window !== 'undefined' && window.ChatToolRuntime) return window.ChatToolRuntime;
|
|
66
|
+
if (typeof require !== 'undefined') {
|
|
67
|
+
try { return require('./tools/tool-runtime.js'); } catch (e) {}
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function getToolSecurity() {
|
|
73
|
+
if (typeof window !== 'undefined' && window.ChatToolSecurity) return window.ChatToolSecurity;
|
|
74
|
+
if (typeof require !== 'undefined') {
|
|
75
|
+
try { return require('./tool-security.js'); } catch (e) {}
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function getToolCards() {
|
|
81
|
+
if (typeof window !== 'undefined' && window.ChatToolCards) return window.ChatToolCards;
|
|
82
|
+
if (typeof require !== 'undefined') {
|
|
83
|
+
try { return require('./tool-cards.js'); } catch (e) {}
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function getI18n() {
|
|
89
|
+
if (typeof window !== 'undefined' && window.ChatI18n) return window.ChatI18n;
|
|
90
|
+
if (typeof require !== 'undefined') {
|
|
91
|
+
try { return require('./i18n.js'); } catch (e) {}
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Versión del contrato público que deben implementar las herramientas.
|
|
98
|
+
*
|
|
99
|
+
* Una herramienta puede exponerse como una instancia de Tool (adaptador de
|
|
100
|
+
* compatibilidad) o como un módulo que implemente los mismos métodos. Las
|
|
101
|
+
* futuras herramientas autocontenidas usarán este contrato directamente.
|
|
102
|
+
*/
|
|
103
|
+
const TOOL_CONTRACT_VERSION = 1;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Resultado normalizado de una ejecución. Mantiene el resultado nativo en
|
|
107
|
+
* `data` para no alterar el comportamiento de las herramientas existentes.
|
|
108
|
+
*/
|
|
109
|
+
class ToolOutcome {
|
|
110
|
+
constructor(options = {}) {
|
|
111
|
+
this.ok = options.ok !== false;
|
|
112
|
+
this.data = options.data === undefined ? null : options.data;
|
|
113
|
+
this.error = options.error || null;
|
|
114
|
+
this.meta = options.meta || {};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
static fromExecution(result, meta = {}) {
|
|
118
|
+
const ok = result?.success !== false;
|
|
119
|
+
return new ToolOutcome({
|
|
120
|
+
ok,
|
|
121
|
+
data: result,
|
|
122
|
+
error: result?.error || null,
|
|
123
|
+
meta
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
static fromError(error, meta = {}) {
|
|
128
|
+
return new ToolOutcome({
|
|
129
|
+
ok: false,
|
|
130
|
+
data: null,
|
|
131
|
+
error: error?.message || String(error || 'Error de ejecución de herramienta.'),
|
|
132
|
+
meta
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Valida el contrato mínimo de una herramienta antes de registrarla.
|
|
139
|
+
* La validación no impone todavía una vista específica: durante la migración
|
|
140
|
+
* las herramientas existentes pueden usar el renderer genérico.
|
|
141
|
+
*/
|
|
142
|
+
function validateToolContract(tool) {
|
|
143
|
+
const errors = [];
|
|
144
|
+
if (!tool || typeof tool !== 'object') {
|
|
145
|
+
return { valid: false, errors: ['La herramienta debe ser un objeto.'] };
|
|
146
|
+
}
|
|
147
|
+
if (!tool.id || typeof tool.id !== 'string') errors.push('Falta un id de herramienta válido.');
|
|
148
|
+
if (!tool.name || typeof tool.name !== 'string') errors.push('Falta un nombre de función válido.');
|
|
149
|
+
if (!tool.description || typeof tool.description !== 'string') errors.push('Falta una descripción válida.');
|
|
150
|
+
if (!tool.parameters || tool.parameters.type !== 'object') errors.push('El esquema parameters debe ser un objeto JSON Schema.');
|
|
151
|
+
if (typeof tool.getDefinition !== 'function') errors.push('Falta getDefinition().');
|
|
152
|
+
if (typeof tool.execute !== 'function') errors.push('Falta execute(args, context).');
|
|
153
|
+
if (typeof tool.isAvailable !== 'function') errors.push('Falta isAvailable(context).');
|
|
154
|
+
if (typeof tool.serializeResultForModel !== 'function') errors.push('Falta serializeResultForModel(args, result).');
|
|
155
|
+
if (typeof tool.formatMarkdownResult !== 'function') errors.push('Falta formatMarkdownResult(args, result).');
|
|
156
|
+
if (typeof tool.formatDispatchMarkdown !== 'function') errors.push('Falta formatDispatchMarkdown(args, result).');
|
|
157
|
+
if (!tool.settings || typeof tool.settings !== 'object') errors.push('Falta descriptor settings.');
|
|
158
|
+
|
|
159
|
+
return { valid: errors.length === 0, errors };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Representa una herramienta individual ejecutable y adapta la API histórica
|
|
164
|
+
* al contrato ToolModule v1.
|
|
165
|
+
*/
|
|
166
|
+
class Tool {
|
|
167
|
+
constructor(options = {}) {
|
|
168
|
+
const definition = options.definition || {};
|
|
169
|
+
this.contractVersion = options.contractVersion || TOOL_CONTRACT_VERSION;
|
|
170
|
+
this.id = options.id || definition.id || definition.name || options.name || '';
|
|
171
|
+
this.name = definition.name || options.name || '';
|
|
172
|
+
// Las tools históricas podían omitir descripción; el adaptador conserva
|
|
173
|
+
// su registro y les proporciona una descripción mínima válida.
|
|
174
|
+
this.description = definition.description || options.description || this.name;
|
|
175
|
+
this.parameters = definition.parameters || options.parameters || { type: 'object', properties: {} };
|
|
176
|
+
this.aliases = Array.isArray(options.aliases) ? options.aliases : [];
|
|
177
|
+
this.category = options.category || 'general';
|
|
178
|
+
this.metadata = options.metadata || {};
|
|
179
|
+
const settings = options.settings || {};
|
|
180
|
+
this.settings = {
|
|
181
|
+
titleKey: settings.titleKey || `agent_${this.name}_title`,
|
|
182
|
+
titleFallback: settings.titleFallback || options.metadata?.label || this.name,
|
|
183
|
+
descKey: settings.descKey || `agent_${this.name}_desc`,
|
|
184
|
+
descFallback: settings.descFallback || this.description,
|
|
185
|
+
icon: settings.icon || options.metadata?.icon || '⚙️',
|
|
186
|
+
defaultEnabled: settings.defaultEnabled !== false,
|
|
187
|
+
showInSettings: settings.showInSettings !== false
|
|
188
|
+
};
|
|
189
|
+
this.executeHandler = options.execute || null;
|
|
190
|
+
this.result = options.result || {};
|
|
191
|
+
this.formatter = options.formatter || null;
|
|
192
|
+
this.promptGuide = options.promptGuide || options.getSystemPromptGuide || null;
|
|
193
|
+
this.isAvailable = typeof options.isAvailable === 'function' ? options.isAvailable : (() => true);
|
|
194
|
+
this.displayMode = options.displayMode || options.view?.displayMode || options.metadata?.displayMode || options.definition?.displayMode || 'collapsed';
|
|
195
|
+
this.view = options.view ? { displayMode: this.displayMode, ...options.view } : { id: this.name, displayMode: this.displayMode };
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Devuelve la definición en formato estándar Function Calling (OpenAI, Claude, Gemini).
|
|
200
|
+
*/
|
|
201
|
+
getDefinition() {
|
|
202
|
+
return {
|
|
203
|
+
type: 'function',
|
|
204
|
+
function: {
|
|
205
|
+
name: this.name,
|
|
206
|
+
description: this.description,
|
|
207
|
+
parameters: this.parameters
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Devuelve la línea descriptiva de la herramienta para el System Prompt (guía de texto plano).
|
|
214
|
+
*/
|
|
215
|
+
getSystemPromptGuide(lang = 'es') {
|
|
216
|
+
if (typeof this.promptGuide === 'function') {
|
|
217
|
+
return this.promptGuide(lang);
|
|
218
|
+
}
|
|
219
|
+
return `- \`${this.name}\`: ${this.description}`;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Ejecuta la herramienta asíncronamente.
|
|
224
|
+
*/
|
|
225
|
+
async execute(args, context = {}) {
|
|
226
|
+
if (typeof this.executeHandler === 'function') {
|
|
227
|
+
return this.executeHandler(args, context);
|
|
228
|
+
}
|
|
229
|
+
throw new Error(`La herramienta ${this.name} no tiene handler de ejecución implementado.`);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Serializa una salida para el mensaje con rol `tool` que consume el LLM.
|
|
234
|
+
*/
|
|
235
|
+
serializeResultForModel(args, result, outcome) {
|
|
236
|
+
if (typeof this.result.toModel === 'function') {
|
|
237
|
+
return this.result.toModel(args, result, outcome);
|
|
238
|
+
}
|
|
239
|
+
return typeof result === 'object' ? JSON.stringify(result) : String(result ?? '');
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Formatea el resultado en Markdown para su inserción en el historial o exportación.
|
|
244
|
+
*/
|
|
245
|
+
formatMarkdownResult(args, result, outcome) {
|
|
246
|
+
if (typeof this.formatter === 'function') {
|
|
247
|
+
return this.formatter(args, result, outcome);
|
|
248
|
+
}
|
|
249
|
+
const output = typeof result === 'object' ? JSON.stringify(result, null, 2) : String(result);
|
|
250
|
+
return `> ⚙️ **${this.name}**\n> \`\`\`json\n> ${output.split('\n').join('\n> ')}\n> \`\`\``;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Formato resumido que se añade al despacho y a la exportación de chat.
|
|
255
|
+
* Puede diferir del formato detallado conservado por la API histórica.
|
|
256
|
+
*/
|
|
257
|
+
formatDispatchMarkdown(args, result, outcome) {
|
|
258
|
+
if (typeof this.result.toMarkdown === 'function') {
|
|
259
|
+
return this.result.toMarkdown(args, result, outcome);
|
|
260
|
+
}
|
|
261
|
+
return this.formatMarkdownResult(args, result, outcome);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Clase base para proveedores de herramientas (ToolProvider).
|
|
267
|
+
*/
|
|
268
|
+
class BaseToolProvider {
|
|
269
|
+
constructor(options = {}) {
|
|
270
|
+
this.id = options.id || 'base';
|
|
271
|
+
this.name = options.name || 'Base Tool Provider';
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Devuelve el array de instancias Tool provistas por este proveedor.
|
|
276
|
+
*/
|
|
277
|
+
getTools() {
|
|
278
|
+
return [];
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Proveedor de herramientas nativas de ZeroChat (BuiltinToolProvider).
|
|
284
|
+
* Provee las herramientas nativas registradas en módulos autocontenidos.
|
|
285
|
+
*/
|
|
286
|
+
class BuiltinToolProvider extends BaseToolProvider {
|
|
287
|
+
constructor() {
|
|
288
|
+
super({ id: 'builtin', name: 'ZeroChat Built-in Tools' });
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
getTools() {
|
|
292
|
+
const tools = [];
|
|
293
|
+
|
|
294
|
+
// 1. Herramienta execute_javascript (módulo autocontenido)
|
|
295
|
+
tools.push(createBuiltinTool('execute_javascript', 'ChatBuiltinExecuteJavascriptTool', './tools/builtin/execute-javascript.tool.js'));
|
|
296
|
+
|
|
297
|
+
// 2-4. Herramientas web autocontenidas
|
|
298
|
+
tools.push(createBuiltinTool('search_web', 'ChatBuiltinSearchWebTool', './tools/builtin/search-web.tool.js'));
|
|
299
|
+
tools.push(createBuiltinTool('fetch_web_page', 'ChatBuiltinFetchWebPageTool', './tools/builtin/fetch-web-page.tool.js'));
|
|
300
|
+
tools.push(createBuiltinTool('download_pdf', 'ChatBuiltinDownloadPdfTool', './tools/builtin/download-pdf.tool.js'));
|
|
301
|
+
|
|
302
|
+
// 5-8. Gráficos y RAG como módulos autocontenidos.
|
|
303
|
+
tools.push(createBuiltinTool('render_chart', 'ChatBuiltinRenderChartTool', './tools/builtin/render-chart.tool.js'));
|
|
304
|
+
tools.push(createBuiltinTool('list_documents', 'ChatBuiltinListDocumentsTool', './tools/builtin/list-documents.tool.js'));
|
|
305
|
+
tools.push(createBuiltinTool('search_knowledge_base', 'ChatBuiltinSearchKnowledgeBaseTool', './tools/builtin/search-knowledge-base.tool.js'));
|
|
306
|
+
tools.push(createBuiltinTool('read_knowledge_chunk', 'ChatBuiltinReadKnowledgeChunkTool', './tools/builtin/read-knowledge-chunk.tool.js'));
|
|
307
|
+
tools.push(createBuiltinTool('read_knowledge_image', 'ChatBuiltinReadKnowledgeImageTool', './tools/builtin/read-knowledge-image.tool.js'));
|
|
308
|
+
|
|
309
|
+
// 9. Punto de control agéntico multipropósito
|
|
310
|
+
tools.push(createBuiltinTool('agent_checkpoint', 'ChatBuiltinAgentCheckpointTool', './tools/builtin/agent-checkpoint.tool.js'));
|
|
311
|
+
|
|
312
|
+
return tools;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Registro central de herramientas y proveedores (ToolRegistry).
|
|
318
|
+
*/
|
|
319
|
+
class ToolRegistry {
|
|
320
|
+
constructor() {
|
|
321
|
+
this.tools = new Map();
|
|
322
|
+
this.aliasMap = new Map();
|
|
323
|
+
this.providers = new Map();
|
|
324
|
+
|
|
325
|
+
// Registrar proveedor de herramientas nativas por defecto
|
|
326
|
+
this.registerProvider(new BuiltinToolProvider());
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Registra un proveedor de herramientas.
|
|
331
|
+
*/
|
|
332
|
+
registerProvider(provider) {
|
|
333
|
+
if (!provider || !provider.id) return;
|
|
334
|
+
this.providers.set(provider.id, provider);
|
|
335
|
+
const tools = provider.getTools();
|
|
336
|
+
if (Array.isArray(tools)) {
|
|
337
|
+
tools.forEach(tool => this.registerTool(tool));
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Registra una herramienta individual.
|
|
343
|
+
*/
|
|
344
|
+
registerTool(tool) {
|
|
345
|
+
const validation = validateToolContract(tool);
|
|
346
|
+
if (!validation.valid) {
|
|
347
|
+
throw new Error(`Contrato de herramienta inválido: ${validation.errors.join(' ')}`);
|
|
348
|
+
}
|
|
349
|
+
const canonicalName = tool.name.trim().toLowerCase();
|
|
350
|
+
this.tools.set(canonicalName, tool);
|
|
351
|
+
|
|
352
|
+
if (tool.category === 'mcp') return tool;
|
|
353
|
+
|
|
354
|
+
// Mapear nombre canónico normalizado sin guiones bajos
|
|
355
|
+
this.aliasMap.set(canonicalName.replace(/_/g, ''), canonicalName);
|
|
356
|
+
|
|
357
|
+
// Mapear alias declarados
|
|
358
|
+
if (Array.isArray(tool.aliases)) {
|
|
359
|
+
tool.aliases.forEach(alias => {
|
|
360
|
+
const cleanAlias = String(alias).trim().toLowerCase();
|
|
361
|
+
this.aliasMap.set(cleanAlias, canonicalName);
|
|
362
|
+
this.aliasMap.set(cleanAlias.replace(/_/g, ''), canonicalName);
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
return tool;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Obtiene una herramienta por su nombre canónico o alias.
|
|
370
|
+
*/
|
|
371
|
+
getTool(rawName) {
|
|
372
|
+
if (!rawName) return null;
|
|
373
|
+
const clean = String(rawName).trim().toLowerCase();
|
|
374
|
+
if (this.tools.has(clean)) {
|
|
375
|
+
return this.tools.get(clean);
|
|
376
|
+
}
|
|
377
|
+
const canonical = this.aliasMap.get(clean) || this.aliasMap.get(clean.replace(/_/g, ''));
|
|
378
|
+
if (canonical && this.tools.has(canonical) && this.tools.get(canonical).category !== 'mcp') {
|
|
379
|
+
return this.tools.get(canonical);
|
|
380
|
+
}
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Comprueba si una herramienta está registrada.
|
|
386
|
+
*/
|
|
387
|
+
hasTool(name) {
|
|
388
|
+
return !!this.getTool(name);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Desregistra un proveedor y todas las herramientas que expone.
|
|
393
|
+
*/
|
|
394
|
+
unregisterProvider(providerId) {
|
|
395
|
+
if (!providerId || !this.providers.has(providerId)) return;
|
|
396
|
+
const provider = this.providers.get(providerId);
|
|
397
|
+
this.providers.delete(providerId);
|
|
398
|
+
const tools = typeof provider.getTools === 'function' ? provider.getTools() : [];
|
|
399
|
+
if (Array.isArray(tools)) {
|
|
400
|
+
tools.forEach(tool => {
|
|
401
|
+
if (tool && tool.name) {
|
|
402
|
+
this.unregisterTool(tool.name);
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Desregistra una herramienta individual y sus alias.
|
|
410
|
+
*/
|
|
411
|
+
unregisterTool(name) {
|
|
412
|
+
if (!name) return;
|
|
413
|
+
const clean = String(name).trim().toLowerCase();
|
|
414
|
+
const tool = this.tools.get(clean);
|
|
415
|
+
if (tool) {
|
|
416
|
+
this.tools.delete(clean);
|
|
417
|
+
this.aliasMap.delete(clean.replace(/_/g, ''));
|
|
418
|
+
if (Array.isArray(tool.aliases)) {
|
|
419
|
+
tool.aliases.forEach(alias => {
|
|
420
|
+
const cleanAlias = String(alias).trim().toLowerCase();
|
|
421
|
+
this.aliasMap.delete(cleanAlias);
|
|
422
|
+
this.aliasMap.delete(cleanAlias.replace(/_/g, ''));
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Devuelve las herramientas visibles para la UI de Configuración.
|
|
430
|
+
* @param {string} [filterCategory=null] - Opcional: filtrar por categoría ('builtin', 'mcp', etc.)
|
|
431
|
+
*/
|
|
432
|
+
listToolsForUI(filterCategory = null) {
|
|
433
|
+
const list = [];
|
|
434
|
+
for (const tool of this.tools.values()) {
|
|
435
|
+
if (tool.settings && tool.settings.showInSettings !== false) {
|
|
436
|
+
if (filterCategory && tool.category !== filterCategory) {
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
list.push({
|
|
440
|
+
id: tool.id || tool.name,
|
|
441
|
+
name: tool.name,
|
|
442
|
+
category: tool.category,
|
|
443
|
+
icon: tool.settings.icon,
|
|
444
|
+
titleKey: tool.settings.titleKey,
|
|
445
|
+
titleFallback: tool.settings.titleFallback,
|
|
446
|
+
descKey: tool.settings.descKey,
|
|
447
|
+
descFallback: tool.settings.descFallback,
|
|
448
|
+
defaultEnabled: tool.settings.defaultEnabled !== false
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
return list;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Resuelve las definiciones Function Calling de las herramientas activas según la configuración.
|
|
457
|
+
*/
|
|
458
|
+
getActiveDefinitions(appConfig = {}) {
|
|
459
|
+
const defs = [];
|
|
460
|
+
const enabledTools = appConfig.enabledTools || {};
|
|
461
|
+
|
|
462
|
+
for (const [name, tool] of this.tools.entries()) {
|
|
463
|
+
// 1. Comprobar si la herramienta está disponible por su contexto (ej. RAG requiere rama activa)
|
|
464
|
+
if (typeof tool.isAvailable === 'function' && !tool.isAvailable(appConfig)) {
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// 2. Si la herramienta se configura en la UI, verificar si está activada
|
|
469
|
+
if (tool.settings && tool.settings.showInSettings !== false) {
|
|
470
|
+
const toolId = tool.id || tool.name;
|
|
471
|
+
const isEnabled = enabledTools[toolId] !== undefined
|
|
472
|
+
? enabledTools[toolId] !== false
|
|
473
|
+
: (enabledTools[tool.name] !== undefined ? enabledTools[tool.name] !== false : tool.settings.defaultEnabled !== false);
|
|
474
|
+
|
|
475
|
+
if (!isEnabled) {
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
defs.push(tool.getDefinition());
|
|
481
|
+
}
|
|
482
|
+
return defs;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Alias compatible con versiones anteriores de getDefinitions.
|
|
487
|
+
*/
|
|
488
|
+
getDefinitions(filterOptions = {}) {
|
|
489
|
+
return this.getActiveDefinitions(filterOptions);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* Genera la guía textual de herramientas para el System Prompt.
|
|
494
|
+
*/
|
|
495
|
+
getActivePromptGuide(appConfig = {}) {
|
|
496
|
+
const enabledTools = appConfig.enabledTools || {};
|
|
497
|
+
const guides = [];
|
|
498
|
+
|
|
499
|
+
for (const tool of this.tools.values()) {
|
|
500
|
+
if (typeof tool.isAvailable === 'function' && !tool.isAvailable(appConfig)) {
|
|
501
|
+
continue;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
if (tool.settings && tool.settings.showInSettings !== false) {
|
|
505
|
+
const toolId = tool.id || tool.name;
|
|
506
|
+
const isEnabled = enabledTools[toolId] !== undefined
|
|
507
|
+
? enabledTools[toolId] !== false
|
|
508
|
+
: (enabledTools[tool.name] !== undefined ? enabledTools[tool.name] !== false : tool.settings.defaultEnabled !== false);
|
|
509
|
+
|
|
510
|
+
if (!isEnabled) {
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if (typeof tool.getSystemPromptGuide === 'function') {
|
|
516
|
+
const guideStr = tool.getSystemPromptGuide('en');
|
|
517
|
+
if (guideStr && typeof guideStr === 'string') {
|
|
518
|
+
guides.push(guideStr);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
if (guides.length === 0) return '';
|
|
524
|
+
|
|
525
|
+
return `\n\n[AVAILABLE TOOLS AND FUNCTIONS]:\nYou can use the following tools when needed to answer accurately:\n${guides.join('\n')}\n*Workflow instruction:* After using tools, answer the user's question directly, clearly, and concisely. Use findings only as evidence, citing sources briefly or via inline links. Avoid lengthy or redundant summaries of consulted sources and do not show raw tool output.`;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Lista todas las herramientas registradas con sus metadatos.
|
|
530
|
+
*/
|
|
531
|
+
listTools() {
|
|
532
|
+
return Array.from(this.tools.values());
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Motor de ejecución seguro para llamadas a herramientas (ToolExecutor).
|
|
538
|
+
*/
|
|
539
|
+
class ToolExecutor {
|
|
540
|
+
constructor(registry) {
|
|
541
|
+
this.registry = registry || new ToolRegistry();
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Parsea tolerante y seguramente los argumentos de una llamada a herramienta.
|
|
546
|
+
*/
|
|
547
|
+
parseArguments(rawArgs) {
|
|
548
|
+
if (!rawArgs) return {};
|
|
549
|
+
if (typeof rawArgs === 'object' && rawArgs !== null) return rawArgs;
|
|
550
|
+
|
|
551
|
+
const str = String(rawArgs).trim();
|
|
552
|
+
try {
|
|
553
|
+
return JSON.parse(str);
|
|
554
|
+
} catch (e) {
|
|
555
|
+
// Fallback tolerante para formato clave: valor o texto plano
|
|
556
|
+
const urlMatch = str.match(/(?:url|link|href)\s*[:=]\s*["']?([^"'\s,}]+)/i);
|
|
557
|
+
const queryMatch = str.match(/(?:query|q|search)\s*[:=]\s*["']?([^"'\r\n,}]+)/i);
|
|
558
|
+
const codeMatch = str.match(/(?:code|js|javascript)\s*[:=]\s*["']?([^"'\r\n]+)/i);
|
|
559
|
+
|
|
560
|
+
if (urlMatch) return { url: urlMatch[1] };
|
|
561
|
+
if (queryMatch) return { query: queryMatch[1].trim() };
|
|
562
|
+
if (codeMatch) return { code: codeMatch[1].trim().replace(/["']$/, '') };
|
|
563
|
+
|
|
564
|
+
return { input: str };
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Aplica la política de ejecución antes de invocar una herramienta. La UI
|
|
570
|
+
* puede aportar `requestToolAuthorization`, pero no puede omitir este
|
|
571
|
+
* punto de control.
|
|
572
|
+
* @private
|
|
573
|
+
*/
|
|
574
|
+
async _authorizeToolCall(tool, args, toolCall, context) {
|
|
575
|
+
const ToolSecurity = getToolSecurity();
|
|
576
|
+
const I18n = getI18n();
|
|
577
|
+
const t = (key, fallback) => (I18n?.t ? I18n.t(key) : fallback);
|
|
578
|
+
const authEval = ToolSecurity?.manager?.evaluateAuthorization
|
|
579
|
+
? ToolSecurity.manager.evaluateAuthorization(tool, args, context)
|
|
580
|
+
: { status: 'allow', requiresApproval: false };
|
|
581
|
+
|
|
582
|
+
if (authEval.status === 'deny') {
|
|
583
|
+
return { allowed: false, error: t('tool_security_policy_blocked', 'Herramienta bloqueada por política de seguridad.') };
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
if (!authEval.requiresApproval) {
|
|
587
|
+
return { allowed: true };
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
if (typeof context.requestToolAuthorization !== 'function') {
|
|
591
|
+
return { allowed: false, error: 'La herramienta requiere autorización explícita del usuario.' };
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
const decision = await context.requestToolAuthorization(toolCall, {
|
|
595
|
+
args,
|
|
596
|
+
serverName: authEval.serverName,
|
|
597
|
+
toolName: tool.name,
|
|
598
|
+
signal: context.signal
|
|
599
|
+
});
|
|
600
|
+
const decisionType = (typeof decision === 'object' && decision !== null) ? decision.decision : decision;
|
|
601
|
+
|
|
602
|
+
if (context.signal?.aborted) return { allowed: false, error: 'Tool execution cancelled.' };
|
|
603
|
+
if (ToolSecurity?.manager?.evaluateAuthorization(tool, args, context).status === 'deny') {
|
|
604
|
+
return { allowed: false, error: t('tool_security_policy_blocked', 'Tool blocked by security policy.') };
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
if (decisionType !== 'allow_once' && decisionType !== 'allow_always') {
|
|
608
|
+
return { allowed: false, error: t('tool_auth_denied_msg', 'Ejecución denegada por el usuario.') };
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
if (decisionType === 'allow_always') {
|
|
612
|
+
ToolSecurity.manager.setToolPolicy(authEval.toolId || tool.name, 'allow', {
|
|
613
|
+
serverName: authEval.serverName,
|
|
614
|
+
originalName: authEval.originalName,
|
|
615
|
+
constraints: (typeof decision === 'object' && decision !== null) ? (decision.constraints || null) : null
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
return { allowed: true };
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* Ejecuta una llamada a herramienta con control de tiempo, métricas y manejo de errores.
|
|
624
|
+
*/
|
|
625
|
+
async executeToolCall(toolCall, context = {}) {
|
|
626
|
+
const rawName = toolCall?.function?.name || '';
|
|
627
|
+
const tool = this.registry.getTool(rawName);
|
|
628
|
+
const parsedArgs = this.parseArguments(toolCall?.function?.arguments);
|
|
629
|
+
|
|
630
|
+
if (!tool) {
|
|
631
|
+
const error = `Herramienta '${rawName}' no encontrada en el registro.`;
|
|
632
|
+
return {
|
|
633
|
+
success: false,
|
|
634
|
+
toolName: rawName,
|
|
635
|
+
error,
|
|
636
|
+
executionTimeMs: 0,
|
|
637
|
+
result: null,
|
|
638
|
+
outcome: ToolOutcome.fromError(error, {
|
|
639
|
+
toolId: null,
|
|
640
|
+
toolName: rawName,
|
|
641
|
+
contractVersion: TOOL_CONTRACT_VERSION,
|
|
642
|
+
executionTimeMs: 0
|
|
643
|
+
})
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
const displayMode = tool.displayMode || tool.view?.displayMode || 'collapsed';
|
|
648
|
+
const startTime = typeof performance !== 'undefined' ? performance.now() : Date.now();
|
|
649
|
+
|
|
650
|
+
try {
|
|
651
|
+
const authorization = await this._authorizeToolCall(tool, parsedArgs, toolCall, context);
|
|
652
|
+
if (!authorization.allowed) {
|
|
653
|
+
return {
|
|
654
|
+
success: false,
|
|
655
|
+
tool,
|
|
656
|
+
toolName: tool.name,
|
|
657
|
+
displayMode,
|
|
658
|
+
args: parsedArgs,
|
|
659
|
+
error: authorization.error,
|
|
660
|
+
executionTimeMs: 0,
|
|
661
|
+
result: null,
|
|
662
|
+
outcome: ToolOutcome.fromError(authorization.error, {
|
|
663
|
+
toolId: tool.id,
|
|
664
|
+
toolName: tool.name,
|
|
665
|
+
contractVersion: tool.contractVersion || TOOL_CONTRACT_VERSION,
|
|
666
|
+
executionTimeMs: 0
|
|
667
|
+
})
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
if (context.signal && context.signal.aborted) {
|
|
671
|
+
throw new Error('Ejecución de herramienta cancelada por el usuario.');
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
const ToolRuntime = getToolRuntime();
|
|
675
|
+
const executionContext = ToolRuntime?.createToolExecutionContext
|
|
676
|
+
? ToolRuntime.createToolExecutionContext({ displayMode, ...context })
|
|
677
|
+
: { displayMode, ...context };
|
|
678
|
+
const execResult = await tool.execute(parsedArgs, executionContext);
|
|
679
|
+
const endTime = typeof performance !== 'undefined' ? performance.now() : Date.now();
|
|
680
|
+
const elapsed = parseFloat((endTime - startTime).toFixed(2));
|
|
681
|
+
|
|
682
|
+
const meta = {
|
|
683
|
+
toolId: tool.id,
|
|
684
|
+
toolName: tool.name,
|
|
685
|
+
contractVersion: tool.contractVersion || TOOL_CONTRACT_VERSION,
|
|
686
|
+
executionTimeMs: elapsed,
|
|
687
|
+
displayMode
|
|
688
|
+
};
|
|
689
|
+
return {
|
|
690
|
+
success: execResult?.success !== false,
|
|
691
|
+
tool: tool,
|
|
692
|
+
toolName: tool.name,
|
|
693
|
+
displayMode,
|
|
694
|
+
args: parsedArgs,
|
|
695
|
+
result: execResult,
|
|
696
|
+
outcome: ToolOutcome.fromExecution(execResult, meta),
|
|
697
|
+
executionTimeMs: elapsed,
|
|
698
|
+
error: execResult?.error
|
|
699
|
+
};
|
|
700
|
+
} catch (err) {
|
|
701
|
+
const endTime = typeof performance !== 'undefined' ? performance.now() : Date.now();
|
|
702
|
+
const elapsed = parseFloat((endTime - startTime).toFixed(2));
|
|
703
|
+
|
|
704
|
+
const meta = {
|
|
705
|
+
toolId: tool.id,
|
|
706
|
+
toolName: tool.name,
|
|
707
|
+
contractVersion: tool.contractVersion || TOOL_CONTRACT_VERSION,
|
|
708
|
+
executionTimeMs: elapsed,
|
|
709
|
+
displayMode
|
|
710
|
+
};
|
|
711
|
+
return {
|
|
712
|
+
success: false,
|
|
713
|
+
tool: tool,
|
|
714
|
+
toolName: tool.name,
|
|
715
|
+
displayMode,
|
|
716
|
+
args: parsedArgs,
|
|
717
|
+
error: err.message || String(err),
|
|
718
|
+
executionTimeMs: elapsed,
|
|
719
|
+
result: null,
|
|
720
|
+
outcome: ToolOutcome.fromError(err, meta)
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
/**
|
|
726
|
+
* Despacha una llamada a herramienta gestionando el ciclo completo:
|
|
727
|
+
* - Parseo seguro de argumentos
|
|
728
|
+
* - Creación inicial e inserción de la tarjeta DOM en vivo
|
|
729
|
+
* - Logging previo a consola de depuración
|
|
730
|
+
* - Ejecución asíncrona a través de executeToolCall
|
|
731
|
+
* - Actualización reactiva de la tarjeta DOM con los resultados
|
|
732
|
+
* - Formateo de la respuesta textual para el historial del chat y markdown
|
|
733
|
+
*/
|
|
734
|
+
async dispatchToolCall(toolCall, options = {}) {
|
|
735
|
+
const {
|
|
736
|
+
container,
|
|
737
|
+
onLog,
|
|
738
|
+
attachListeners,
|
|
739
|
+
scrollToBottom,
|
|
740
|
+
language = 'es'
|
|
741
|
+
} = options;
|
|
742
|
+
|
|
743
|
+
const rawFuncName = toolCall?.function?.name || '';
|
|
744
|
+
const parsedArgs = this.parseArguments(toolCall?.function?.arguments);
|
|
745
|
+
const ToolCards = getToolCards();
|
|
746
|
+
|
|
747
|
+
// 1. Crear e insertar la tarjeta DOM en vivo con estado de carga
|
|
748
|
+
let cardEl = null;
|
|
749
|
+
if (ToolCards && ToolCards.createLiveToolCard && container && typeof document !== 'undefined') {
|
|
750
|
+
cardEl = ToolCards.createLiveToolCard(rawFuncName, parsedArgs);
|
|
751
|
+
if (cardEl) {
|
|
752
|
+
container.appendChild(cardEl);
|
|
753
|
+
if (typeof attachListeners === 'function') attachListeners(cardEl);
|
|
754
|
+
if (typeof scrollToBottom === 'function') scrollToBottom();
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
// 2. Logging previo
|
|
759
|
+
if (typeof onLog === 'function') {
|
|
760
|
+
onLog('tool', `${rawFuncName}:\n${JSON.stringify(parsedArgs, null, 2)}`);
|
|
761
|
+
onLog('raw', `>>> TOOL CALL ${rawFuncName}:\n${JSON.stringify(parsedArgs, null, 2)}`);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
// 3. Ejecutar la herramienta a través de executeToolCall
|
|
765
|
+
const execRes = await this.executeToolCall(toolCall, {
|
|
766
|
+
lang: language,
|
|
767
|
+
...options,
|
|
768
|
+
requestToolAuthorization: ToolCards?.promptToolAuthorization
|
|
769
|
+
? async (pendingToolCall, authorization) => {
|
|
770
|
+
if (typeof scrollToBottom === 'function') scrollToBottom();
|
|
771
|
+
return ToolCards.promptToolAuthorization(cardEl, pendingToolCall, authorization);
|
|
772
|
+
}
|
|
773
|
+
: undefined
|
|
774
|
+
});
|
|
775
|
+
|
|
776
|
+
// 4. Actualizar la tarjeta DOM con el resultado
|
|
777
|
+
if (ToolCards && ToolCards.updateLiveToolCard && cardEl) {
|
|
778
|
+
ToolCards.updateLiveToolCard(cardEl, rawFuncName, parsedArgs, execRes.result || execRes, execRes.executionTimeMs, {
|
|
779
|
+
displayMode: execRes.displayMode || execRes.tool?.displayMode,
|
|
780
|
+
outcome: execRes.outcome
|
|
781
|
+
});
|
|
782
|
+
if (typeof attachListeners === 'function') attachListeners(cardEl);
|
|
783
|
+
if (typeof scrollToBottom === 'function') scrollToBottom();
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// 5. Formatear la salida para el mensaje `tool` sin conocer tipos concretos.
|
|
787
|
+
const resolvedTool = execRes.tool;
|
|
788
|
+
let resultText = '';
|
|
789
|
+
try {
|
|
790
|
+
const serialized = resolvedTool
|
|
791
|
+
? resolvedTool.serializeResultForModel(parsedArgs, execRes.result, execRes.outcome)
|
|
792
|
+
: (execRes.error || 'Error de ejecución de herramienta.');
|
|
793
|
+
resultText = typeof serialized === 'string' ? serialized : JSON.stringify(serialized);
|
|
794
|
+
} catch (err) {
|
|
795
|
+
resultText = `Error al serializar el resultado de ${rawFuncName}: ${err.message || String(err)}`;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// 6. Logging posterior
|
|
799
|
+
if (typeof onLog === 'function') {
|
|
800
|
+
onLog('tool', `${rawFuncName} output (${execRes.executionTimeMs || 0}ms):\n${String(resultText).substring(0, 300)}`);
|
|
801
|
+
onLog('raw', `<<< TOOL RESULT ${rawFuncName} (${execRes.executionTimeMs || 0}ms):\n${resultText}`);
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
// 7. Formatear bloque Markdown para portapapeles/exportación desde la tool.
|
|
805
|
+
let toolMd = '';
|
|
806
|
+
try {
|
|
807
|
+
toolMd = resolvedTool
|
|
808
|
+
? resolvedTool.formatDispatchMarkdown(parsedArgs, execRes.result, execRes.outcome)
|
|
809
|
+
: `> ⚙️ **${rawFuncName}**\n> \`\`\`\n> ${String(resultText).slice(0, 300)}\n> \`\`\``;
|
|
810
|
+
} catch (err) {
|
|
811
|
+
toolMd = `> ❌ **${rawFuncName}**: ${err.message || String(err)}`;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
return {
|
|
815
|
+
success: execRes.success !== false,
|
|
816
|
+
result: execRes.result,
|
|
817
|
+
resultText,
|
|
818
|
+
markdownBlock: toolMd,
|
|
819
|
+
cardElement: cardEl,
|
|
820
|
+
executionTimeMs: execRes.executionTimeMs,
|
|
821
|
+
error: execRes.error,
|
|
822
|
+
toolName: rawFuncName,
|
|
823
|
+
args: parsedArgs
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
/**
|
|
829
|
+
* Entorno de ejecución agéntico avanzado (AgentRuntime).
|
|
830
|
+
* Orquesta el ciclo de vida de razonamiento y acción (ReAct), múltiples pasos,
|
|
831
|
+
* control de tiempo/timeout, reintentos de herramientas, cancelación,
|
|
832
|
+
* detección de bucles infinitos, presupuestos de tokens y síntesis final.
|
|
833
|
+
*/
|
|
834
|
+
class AgentRuntime {
|
|
835
|
+
constructor(options = {}) {
|
|
836
|
+
this.registry = options.registry || new ToolRegistry();
|
|
837
|
+
this.executor = options.executor || new ToolExecutor(this.registry);
|
|
838
|
+
this.maxSteps = options.maxSteps || options.maxTurns || (options.appConfig && options.appConfig.maxAgentTurns) || (options.config && options.config.maxAgentTurns) || 15;
|
|
839
|
+
this.timeoutMs = options.timeoutMs || 0; // 0 = sin límite global de tiempo
|
|
840
|
+
this.stepTimeoutMs = options.stepTimeoutMs || 60000; // 60s por paso
|
|
841
|
+
this.maxRetries = options.maxRetries !== undefined ? options.maxRetries : 1;
|
|
842
|
+
this.loopThreshold = options.loopThreshold || 2;
|
|
843
|
+
this.autoSynthesize = options.autoSynthesize !== false;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
/**
|
|
847
|
+
* Genera una huella única para la llamada a herramienta para detectar repeticiones cíclicas.
|
|
848
|
+
*/
|
|
849
|
+
getToolCallFingerprint(toolCall) {
|
|
850
|
+
if (!toolCall || !toolCall.function) return '';
|
|
851
|
+
const name = toolCall.function.name || '';
|
|
852
|
+
const args = typeof toolCall.function.arguments === 'object'
|
|
853
|
+
? JSON.stringify(toolCall.function.arguments)
|
|
854
|
+
: String(toolCall.function.arguments || '').trim();
|
|
855
|
+
return `${name}:${args}`;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
/**
|
|
859
|
+
* Ejecuta una llamada a herramienta con soporte de reintentos y captura de errores.
|
|
860
|
+
*/
|
|
861
|
+
async executeToolWithRetries(toolCall, context = {}, maxRetries = 0, onRetry = null) {
|
|
862
|
+
let attempts = 0;
|
|
863
|
+
let lastResult = null;
|
|
864
|
+
|
|
865
|
+
while (attempts <= maxRetries) {
|
|
866
|
+
if (context.signal && context.signal.aborted) {
|
|
867
|
+
return {
|
|
868
|
+
success: false,
|
|
869
|
+
toolName: toolCall?.function?.name || '',
|
|
870
|
+
error: 'Ejecución cancelada por el usuario.',
|
|
871
|
+
executionTimeMs: 0,
|
|
872
|
+
cancelled: true
|
|
873
|
+
};
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
lastResult = await this.executor.executeToolCall(toolCall, context);
|
|
877
|
+
if (lastResult.success) {
|
|
878
|
+
return lastResult;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
attempts++;
|
|
882
|
+
if (attempts <= maxRetries && !lastResult.cancelled) {
|
|
883
|
+
if (typeof onRetry === 'function') {
|
|
884
|
+
onRetry(toolCall, attempts, maxRetries, lastResult.error);
|
|
885
|
+
}
|
|
886
|
+
// Pausa corta antes de reintentar
|
|
887
|
+
await new Promise(r => setTimeout(r, 50));
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
return lastResult;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
/**
|
|
895
|
+
* Ejecuta el ciclo completo de ejecución agéntica multi-turno.
|
|
896
|
+
*/
|
|
897
|
+
async execute(params = {}) {
|
|
898
|
+
const {
|
|
899
|
+
apiUrl,
|
|
900
|
+
apiType,
|
|
901
|
+
apiKey,
|
|
902
|
+
model,
|
|
903
|
+
messages = [],
|
|
904
|
+
temperature = 0.7,
|
|
905
|
+
reasoningEffort = 'medium',
|
|
906
|
+
reasoningTransport = 'auto',
|
|
907
|
+
enableTools = true,
|
|
908
|
+
toolFilterOptions = {},
|
|
909
|
+
signal = null,
|
|
910
|
+
maxSteps = this.maxSteps,
|
|
911
|
+
timeoutMs = this.timeoutMs,
|
|
912
|
+
stepTimeoutMs = this.stepTimeoutMs,
|
|
913
|
+
maxRetries = this.maxRetries,
|
|
914
|
+
loopThreshold = this.loopThreshold,
|
|
915
|
+
autoSynthesize = this.autoSynthesize,
|
|
916
|
+
synthesizeOnLoop = true,
|
|
917
|
+
api: customApi = null,
|
|
918
|
+
prepareMessages = null,
|
|
919
|
+
resolveToolDefinitions = null,
|
|
920
|
+
dispatchToolCall = null,
|
|
921
|
+
summarizeHistory = null,
|
|
922
|
+
contextOptions = {},
|
|
923
|
+
createMessageId = null,
|
|
924
|
+
appendFinalMessage = false,
|
|
925
|
+
onBeforeRequest = null,
|
|
926
|
+
callbacks = {}
|
|
927
|
+
} = params;
|
|
928
|
+
|
|
929
|
+
const API = customApi || getAPI();
|
|
930
|
+
if (!API || !API.streamChatCompletion) {
|
|
931
|
+
throw new Error('Módulo ChatAPI no disponible para ejecución agéntica.');
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
const ContextManager = getContextManager();
|
|
935
|
+
const safeContextOptions = contextOptions && typeof contextOptions === 'object'
|
|
936
|
+
? contextOptions
|
|
937
|
+
: {};
|
|
938
|
+
|
|
939
|
+
const startTime = typeof performance !== 'undefined' ? performance.now() : Date.now();
|
|
940
|
+
|
|
941
|
+
// Configuración de cancelación y timeouts
|
|
942
|
+
let isTimedOut = false;
|
|
943
|
+
let isCancelled = false;
|
|
944
|
+
const internalAbortController = new AbortController();
|
|
945
|
+
let timeoutTimer = null;
|
|
946
|
+
|
|
947
|
+
if (timeoutMs > 0) {
|
|
948
|
+
timeoutTimer = setTimeout(() => {
|
|
949
|
+
isTimedOut = true;
|
|
950
|
+
internalAbortController.abort();
|
|
951
|
+
if (callbacks.onTimeout) {
|
|
952
|
+
callbacks.onTimeout(timeoutMs, stepIndex);
|
|
953
|
+
}
|
|
954
|
+
}, timeoutMs);
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
let onSignalAbort = null;
|
|
958
|
+
if (signal) {
|
|
959
|
+
if (signal.aborted) {
|
|
960
|
+
isCancelled = true;
|
|
961
|
+
internalAbortController.abort();
|
|
962
|
+
} else {
|
|
963
|
+
onSignalAbort = () => {
|
|
964
|
+
isCancelled = true;
|
|
965
|
+
internalAbortController.abort();
|
|
966
|
+
if (callbacks.onAbort) callbacks.onAbort();
|
|
967
|
+
};
|
|
968
|
+
signal.addEventListener('abort', onSignalAbort, { once: true });
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
const combinedSignal = internalAbortController.signal;
|
|
973
|
+
|
|
974
|
+
let stepIndex = 0;
|
|
975
|
+
let workingMessages = [...messages];
|
|
976
|
+
let finalAccumulatedText = '';
|
|
977
|
+
let finalReasoningText = '';
|
|
978
|
+
let lastStats = null;
|
|
979
|
+
const toolExecutions = [];
|
|
980
|
+
const toolCallSignatures = [];
|
|
981
|
+
let compressionUnavailable = false;
|
|
982
|
+
let status = 'completed';
|
|
983
|
+
let executionError = null;
|
|
984
|
+
|
|
985
|
+
while (stepIndex < maxSteps) {
|
|
986
|
+
if (combinedSignal.aborted) {
|
|
987
|
+
status = isTimedOut ? 'timeout' : 'cancelled';
|
|
988
|
+
executionError = new Error(isTimedOut ? 'Tiempo límite de ejecución agéntica excedido.' : 'Ejecución agéntica cancelada por el usuario.');
|
|
989
|
+
break;
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
if (callbacks.onStepStart) {
|
|
993
|
+
callbacks.onStepStart(stepIndex);
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
// Automatic context compaction is independent from the agent_checkpoint
|
|
997
|
+
// tool. It replaces the previous checkpoint and later dialogue atomically.
|
|
998
|
+
if (!compressionUnavailable && ContextManager && typeof ContextManager.shouldCompress === 'function' &&
|
|
999
|
+
typeof ContextManager.compressHistory === 'function' &&
|
|
1000
|
+
typeof summarizeHistory === 'function' &&
|
|
1001
|
+
ContextManager.shouldCompress(workingMessages, { ...safeContextOptions, model, providerType: apiType })) {
|
|
1002
|
+
const compacted = await ContextManager.compressHistory({
|
|
1003
|
+
messages: workingMessages,
|
|
1004
|
+
summarizeFn: summarizeHistory,
|
|
1005
|
+
options: { ...safeContextOptions, model, providerType: apiType }
|
|
1006
|
+
});
|
|
1007
|
+
if (compacted.compressed) {
|
|
1008
|
+
workingMessages = compacted.messages;
|
|
1009
|
+
} else {
|
|
1010
|
+
// A failed or unavailable summarizer cannot make progress during
|
|
1011
|
+
// this execution; retrying it before every agent step only adds calls.
|
|
1012
|
+
compressionUnavailable = true;
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
// 1. Optimización dinámica de presupuesto de contexto (Context Budget)
|
|
1017
|
+
let stepMessages = workingMessages;
|
|
1018
|
+
let contextDiagnostics = null;
|
|
1019
|
+
if (typeof prepareMessages === 'function') {
|
|
1020
|
+
const prepared = await prepareMessages(workingMessages, {
|
|
1021
|
+
stepIndex,
|
|
1022
|
+
model,
|
|
1023
|
+
apiType,
|
|
1024
|
+
isSynthesis: false
|
|
1025
|
+
});
|
|
1026
|
+
if (Array.isArray(prepared)) {
|
|
1027
|
+
stepMessages = prepared;
|
|
1028
|
+
} else if (prepared && Array.isArray(prepared.messages)) {
|
|
1029
|
+
stepMessages = prepared.messages;
|
|
1030
|
+
contextDiagnostics = prepared.diagnostics || null;
|
|
1031
|
+
}
|
|
1032
|
+
} else if (ContextManager && ContextManager.buildOptimizedContext) {
|
|
1033
|
+
try {
|
|
1034
|
+
const opt = ContextManager.buildOptimizedContext(workingMessages, {
|
|
1035
|
+
...safeContextOptions,
|
|
1036
|
+
model,
|
|
1037
|
+
providerType: apiType
|
|
1038
|
+
});
|
|
1039
|
+
if (opt && opt.messages) {
|
|
1040
|
+
stepMessages = opt.messages;
|
|
1041
|
+
}
|
|
1042
|
+
} catch (e) {
|
|
1043
|
+
stepMessages = workingMessages;
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
if (callbacks.onContextPrepared) callbacks.onContextPrepared(contextDiagnostics, stepIndex);
|
|
1047
|
+
|
|
1048
|
+
// 2. Definición de herramientas activas
|
|
1049
|
+
const activeToolDefs = enableTools
|
|
1050
|
+
? (typeof resolveToolDefinitions === 'function'
|
|
1051
|
+
? await resolveToolDefinitions({ stepIndex, workingMessages })
|
|
1052
|
+
: this.registry.getDefinitions(toolFilterOptions))
|
|
1053
|
+
: [];
|
|
1054
|
+
|
|
1055
|
+
let currentStepText = '';
|
|
1056
|
+
let currentStepReasoning = '';
|
|
1057
|
+
let stepToolCalls = null;
|
|
1058
|
+
let stepStats = null;
|
|
1059
|
+
let streamError = null;
|
|
1060
|
+
|
|
1061
|
+
try {
|
|
1062
|
+
const streamResult = await API.streamChatCompletion({
|
|
1063
|
+
apiUrl,
|
|
1064
|
+
apiType,
|
|
1065
|
+
apiKey,
|
|
1066
|
+
model,
|
|
1067
|
+
messages: stepMessages,
|
|
1068
|
+
temperature,
|
|
1069
|
+
reasoningEffort,
|
|
1070
|
+
reasoningTransport,
|
|
1071
|
+
tools: activeToolDefs,
|
|
1072
|
+
enableTools: activeToolDefs.length > 0,
|
|
1073
|
+
enableAgentJs: toolFilterOptions.enableAgentJs !== false,
|
|
1074
|
+
enableAgentWeb: toolFilterOptions.enableAgentWeb !== false,
|
|
1075
|
+
enableAgentSearch: toolFilterOptions.enableAgentSearch !== false,
|
|
1076
|
+
enableAgentChart: toolFilterOptions.enableAgentChart !== false,
|
|
1077
|
+
signal: combinedSignal,
|
|
1078
|
+
onBeforeRequest,
|
|
1079
|
+
onGenerationStatus: status => callbacks.onGenerationStatus?.(status),
|
|
1080
|
+
|
|
1081
|
+
onReasoningChunk: (chunk, accumulated) => {
|
|
1082
|
+
currentStepReasoning = accumulated;
|
|
1083
|
+
if (callbacks.onReasoningChunk) callbacks.onReasoningChunk(chunk, accumulated, stepIndex);
|
|
1084
|
+
},
|
|
1085
|
+
onLog: (logData) => {
|
|
1086
|
+
if (callbacks.onLog) callbacks.onLog(logData);
|
|
1087
|
+
},
|
|
1088
|
+
onChunk: (fullTextSoFar, delta, stats) => {
|
|
1089
|
+
currentStepText = fullTextSoFar;
|
|
1090
|
+
if (callbacks.onChunk) callbacks.onChunk(fullTextSoFar, delta, stats, stepIndex);
|
|
1091
|
+
},
|
|
1092
|
+
onDone: (finalText, stats, toolCalls, reasoning) => {
|
|
1093
|
+
currentStepText = finalText || currentStepText;
|
|
1094
|
+
currentStepReasoning = reasoning || currentStepReasoning;
|
|
1095
|
+
stepStats = stats;
|
|
1096
|
+
stepToolCalls = toolCalls;
|
|
1097
|
+
},
|
|
1098
|
+
onError: (error) => {
|
|
1099
|
+
streamError = error;
|
|
1100
|
+
if (callbacks.onError) callbacks.onError(error);
|
|
1101
|
+
}
|
|
1102
|
+
});
|
|
1103
|
+
|
|
1104
|
+
if (streamError) {
|
|
1105
|
+
if (combinedSignal.aborted) {
|
|
1106
|
+
status = isTimedOut ? 'timeout' : 'cancelled';
|
|
1107
|
+
executionError = new Error(isTimedOut ? 'Tiempo límite de ejecución agéntica excedido.' : 'Ejecución agéntica cancelada por el usuario.');
|
|
1108
|
+
} else {
|
|
1109
|
+
status = 'error';
|
|
1110
|
+
executionError = streamError;
|
|
1111
|
+
if (callbacks.onError) callbacks.onError(streamError);
|
|
1112
|
+
}
|
|
1113
|
+
break;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
if (streamResult) {
|
|
1117
|
+
currentStepText = streamResult.accumulatedText || currentStepText;
|
|
1118
|
+
currentStepReasoning = streamResult.accumulatedReasoning || currentStepReasoning;
|
|
1119
|
+
stepToolCalls = streamResult.toolCalls || stepToolCalls;
|
|
1120
|
+
stepStats = streamResult.stats || stepStats;
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
lastStats = stepStats || lastStats;
|
|
1124
|
+
if (currentStepReasoning) {
|
|
1125
|
+
finalReasoningText += (finalReasoningText ? '\n' : '') + currentStepReasoning;
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
// Caso A: Sin llamadas a herramientas -> Turno final
|
|
1129
|
+
if (!stepToolCalls || stepToolCalls.length === 0) {
|
|
1130
|
+
// If the model returned empty text after a tool turn, log it and move on
|
|
1131
|
+
if ((!currentStepText || currentStepText.trim() === '') && stepIndex > 0 && workingMessages.length > 0 && workingMessages[workingMessages.length - 1].role === 'tool') {
|
|
1132
|
+
if (callbacks.onLog) callbacks.onLog({ type: 'warn', text: 'Model ended the tool loop without producing a response.' });
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
finalAccumulatedText = currentStepText;
|
|
1136
|
+
if (appendFinalMessage) {
|
|
1137
|
+
workingMessages.push({
|
|
1138
|
+
id: typeof createMessageId === 'function'
|
|
1139
|
+
? createMessageId('final', { stepIndex })
|
|
1140
|
+
: `msg_turn_${stepIndex}_final`,
|
|
1141
|
+
role: 'assistant',
|
|
1142
|
+
content: currentStepText
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
if (callbacks.onStepDone) {
|
|
1146
|
+
callbacks.onStepDone(stepIndex, {
|
|
1147
|
+
type: 'final_response',
|
|
1148
|
+
text: currentStepText,
|
|
1149
|
+
stats: lastStats
|
|
1150
|
+
});
|
|
1151
|
+
}
|
|
1152
|
+
status = 'completed';
|
|
1153
|
+
break;
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
// Caso B: Llamadas a herramientas detectadas (soporte para llamadas individuales y en paralelo)
|
|
1157
|
+
const currentSignatures = stepToolCalls.map(tc => this.getToolCallFingerprint(tc));
|
|
1158
|
+
const allRepeated = currentSignatures.length > 0 && currentSignatures.every(sig => {
|
|
1159
|
+
return toolCallSignatures.filter(s => s === sig).length >= loopThreshold;
|
|
1160
|
+
});
|
|
1161
|
+
|
|
1162
|
+
// Detección y protección contra bucles infinitos
|
|
1163
|
+
if (allRepeated) {
|
|
1164
|
+
status = 'loop_detected';
|
|
1165
|
+
if (callbacks.onLoopDetected) {
|
|
1166
|
+
callbacks.onLoopDetected(stepToolCalls[0], loopThreshold + 1, stepIndex);
|
|
1167
|
+
}
|
|
1168
|
+
const loopWarning = '\n\n> ⚠️ *[Infinite Loop Protection]*: Tools were repeatedly invoked with identical parameters without progress. Halting agent turn loop.';
|
|
1169
|
+
currentStepText = (currentStepText || '') + loopWarning;
|
|
1170
|
+
finalAccumulatedText = currentStepText;
|
|
1171
|
+
if (appendFinalMessage) {
|
|
1172
|
+
workingMessages.push({
|
|
1173
|
+
id: typeof createMessageId === 'function'
|
|
1174
|
+
? createMessageId('final', { stepIndex })
|
|
1175
|
+
: `msg_turn_${stepIndex}_final`,
|
|
1176
|
+
role: 'assistant',
|
|
1177
|
+
content: currentStepText
|
|
1178
|
+
});
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
if (callbacks.onStepDone) {
|
|
1182
|
+
callbacks.onStepDone(stepIndex, {
|
|
1183
|
+
type: 'loop_detected',
|
|
1184
|
+
text: currentStepText,
|
|
1185
|
+
assistantMsg: {
|
|
1186
|
+
id: typeof createMessageId === 'function'
|
|
1187
|
+
? createMessageId('final', { stepIndex })
|
|
1188
|
+
: `msg_turn_${stepIndex}_final`,
|
|
1189
|
+
role: 'assistant',
|
|
1190
|
+
content: currentStepText
|
|
1191
|
+
}
|
|
1192
|
+
});
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
if (autoSynthesize && synthesizeOnLoop && !combinedSignal.aborted) {
|
|
1196
|
+
if (callbacks.onSynthesize) callbacks.onSynthesize(stepIndex);
|
|
1197
|
+
try {
|
|
1198
|
+
const synthRes = await API.streamChatCompletion({
|
|
1199
|
+
apiUrl,
|
|
1200
|
+
apiType,
|
|
1201
|
+
apiKey,
|
|
1202
|
+
model,
|
|
1203
|
+
messages: [
|
|
1204
|
+
...workingMessages,
|
|
1205
|
+
{ role: 'assistant', content: currentStepText }
|
|
1206
|
+
],
|
|
1207
|
+
temperature,
|
|
1208
|
+
reasoningEffort,
|
|
1209
|
+
reasoningTransport,
|
|
1210
|
+
enableTools: false,
|
|
1211
|
+
signal: combinedSignal,
|
|
1212
|
+
onGenerationStatus: status => callbacks.onGenerationStatus?.(status),
|
|
1213
|
+
onChunk: (fullTextSoFar, delta, stats) => {
|
|
1214
|
+
finalAccumulatedText = fullTextSoFar;
|
|
1215
|
+
if (callbacks.onChunk) callbacks.onChunk(fullTextSoFar, delta, stats);
|
|
1216
|
+
},
|
|
1217
|
+
onDone: (finalText, stats) => {
|
|
1218
|
+
finalAccumulatedText = finalText || finalAccumulatedText;
|
|
1219
|
+
lastStats = stats || lastStats;
|
|
1220
|
+
}
|
|
1221
|
+
});
|
|
1222
|
+
} catch (e) {}
|
|
1223
|
+
}
|
|
1224
|
+
break;
|
|
1225
|
+
}
|
|
1226
|
+
for (const sig of currentSignatures) {
|
|
1227
|
+
toolCallSignatures.push(sig);
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
const trimmedStepText = (currentStepText || '').trim();
|
|
1231
|
+
if (
|
|
1232
|
+
trimmedStepText.startsWith('<|') ||
|
|
1233
|
+
trimmedStepText.startsWith('<tool_call') ||
|
|
1234
|
+
trimmedStepText.startsWith('<function_call') ||
|
|
1235
|
+
trimmedStepText.startsWith('call:') ||
|
|
1236
|
+
trimmedStepText.startsWith('{"name"') ||
|
|
1237
|
+
trimmedStepText.startsWith('```json\n{"name"') ||
|
|
1238
|
+
trimmedStepText.startsWith('download_pdf(') ||
|
|
1239
|
+
trimmedStepText.startsWith('downloadpdf(') ||
|
|
1240
|
+
trimmedStepText.startsWith('fetch_web_page(') ||
|
|
1241
|
+
trimmedStepText.startsWith('fetchwebpage(') ||
|
|
1242
|
+
trimmedStepText.startsWith('search_web(') ||
|
|
1243
|
+
trimmedStepText.startsWith('searchweb(') ||
|
|
1244
|
+
trimmedStepText.startsWith('execute_javascript(') ||
|
|
1245
|
+
trimmedStepText.startsWith('executejs(')
|
|
1246
|
+
) {
|
|
1247
|
+
currentStepText = '';
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
const executedToolMessages = [];
|
|
1251
|
+
const stepExecResults = [];
|
|
1252
|
+
|
|
1253
|
+
for (let i = 0; i < stepToolCalls.length; i++) {
|
|
1254
|
+
if (combinedSignal.aborted) break;
|
|
1255
|
+
const call = stepToolCalls[i];
|
|
1256
|
+
const toolFnName = call.function?.name || '';
|
|
1257
|
+
|
|
1258
|
+
if (callbacks.onToolStart) {
|
|
1259
|
+
const toolInstance = this.registry.getTool(toolFnName);
|
|
1260
|
+
callbacks.onToolStart(call, toolInstance, stepIndex);
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
const toolContext = {
|
|
1264
|
+
signal: combinedSignal,
|
|
1265
|
+
...params
|
|
1266
|
+
};
|
|
1267
|
+
const execResult = typeof dispatchToolCall === 'function'
|
|
1268
|
+
? await dispatchToolCall(call, toolContext)
|
|
1269
|
+
: await this.executeToolWithRetries(
|
|
1270
|
+
call,
|
|
1271
|
+
toolContext,
|
|
1272
|
+
maxRetries,
|
|
1273
|
+
(tc, attempt, total, err) => {
|
|
1274
|
+
if (callbacks.onRetry) {
|
|
1275
|
+
callbacks.onRetry(tc, attempt, total, err, stepIndex);
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
);
|
|
1279
|
+
stepExecResults.push(execResult);
|
|
1280
|
+
|
|
1281
|
+
let toolResponseContent = '';
|
|
1282
|
+
let toolImages = null;
|
|
1283
|
+
if (execResult.success && execResult.result !== undefined) {
|
|
1284
|
+
const serialized = execResult.resultText !== undefined
|
|
1285
|
+
? execResult.resultText
|
|
1286
|
+
: execResult.tool.serializeResultForModel(execResult.args, execResult.result, execResult.outcome);
|
|
1287
|
+
const serializedText = typeof serialized === 'string' ? serialized : JSON.stringify(serialized);
|
|
1288
|
+
|
|
1289
|
+
// Las imágenes RAG se preservan como metadatos del mensaje de herramienta.
|
|
1290
|
+
// ChatEngine las transforma en evidencia multimodal en la siguiente petición.
|
|
1291
|
+
const ragDataUrl = execResult.result?.dataUrl;
|
|
1292
|
+
const ragMimeType = execResult.result?.mimeType;
|
|
1293
|
+
if (ragDataUrl && ragMimeType) {
|
|
1294
|
+
toolResponseContent = serializedText;
|
|
1295
|
+
toolImages = [{
|
|
1296
|
+
dataUrl: ragDataUrl,
|
|
1297
|
+
imageRef: execResult.result.imageRef,
|
|
1298
|
+
documentTitle: execResult.result.documentTitle,
|
|
1299
|
+
page: execResult.result.page
|
|
1300
|
+
}];
|
|
1301
|
+
} else {
|
|
1302
|
+
toolResponseContent = serializedText;
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
} else {
|
|
1306
|
+
toolResponseContent = JSON.stringify({
|
|
1307
|
+
success: false,
|
|
1308
|
+
error: execResult.error || 'Error desconocido ejecutando la herramienta.'
|
|
1309
|
+
});
|
|
1310
|
+
if (callbacks.onToolError) {
|
|
1311
|
+
callbacks.onToolError(call, execResult.error, stepIndex);
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
if (callbacks.onToolComplete) {
|
|
1316
|
+
callbacks.onToolComplete(call, execResult, toolResponseContent, stepIndex);
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
toolExecutions.push({
|
|
1320
|
+
step: stepIndex,
|
|
1321
|
+
toolName: toolFnName || 'tool',
|
|
1322
|
+
args: execResult.args || call.function?.arguments,
|
|
1323
|
+
result: execResult.result,
|
|
1324
|
+
success: execResult.success,
|
|
1325
|
+
executionTimeMs: execResult.executionTimeMs || 0,
|
|
1326
|
+
error: execResult.error || null
|
|
1327
|
+
});
|
|
1328
|
+
|
|
1329
|
+
const toolMessage = {
|
|
1330
|
+
id: typeof createMessageId === 'function'
|
|
1331
|
+
? createMessageId('tool', { stepIndex, toolCall: call, toolIndex: i })
|
|
1332
|
+
: `msg_turn_${stepIndex}_tool_${call.id || i}_${Date.now()}`,
|
|
1333
|
+
role: 'tool',
|
|
1334
|
+
tool_call_id: call.id || `call_${Date.now()}_${i}`,
|
|
1335
|
+
name: toolFnName || 'tool',
|
|
1336
|
+
content: toolResponseContent
|
|
1337
|
+
};
|
|
1338
|
+
if (toolImages) toolMessage.images = toolImages;
|
|
1339
|
+
executedToolMessages.push(toolMessage);
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
// Actualizar historial asegurando el emparejamiento estricto assistant(tool_calls) <-> tool(results)
|
|
1343
|
+
const assistantMsg = {
|
|
1344
|
+
id: typeof createMessageId === 'function'
|
|
1345
|
+
? createMessageId('assistant', { stepIndex })
|
|
1346
|
+
: `msg_turn_${stepIndex}_assistant`,
|
|
1347
|
+
role: 'assistant',
|
|
1348
|
+
content: currentStepText || null,
|
|
1349
|
+
tool_calls: stepToolCalls
|
|
1350
|
+
};
|
|
1351
|
+
|
|
1352
|
+
workingMessages.push(assistantMsg);
|
|
1353
|
+
for (const tMsg of executedToolMessages) {
|
|
1354
|
+
workingMessages.push(tMsg);
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
if (callbacks.onStepDone) {
|
|
1358
|
+
callbacks.onStepDone(stepIndex, {
|
|
1359
|
+
type: 'tool_execution',
|
|
1360
|
+
toolCall: stepToolCalls[0],
|
|
1361
|
+
toolCalls: stepToolCalls,
|
|
1362
|
+
execResult: stepExecResults[0],
|
|
1363
|
+
execResults: stepExecResults,
|
|
1364
|
+
assistantMsg,
|
|
1365
|
+
toolMsg: executedToolMessages[0],
|
|
1366
|
+
toolMsgs: executedToolMessages
|
|
1367
|
+
});
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
stepIndex++;
|
|
1371
|
+
} catch (err) {
|
|
1372
|
+
if (combinedSignal.aborted) {
|
|
1373
|
+
status = isTimedOut ? 'timeout' : 'cancelled';
|
|
1374
|
+
executionError = new Error(isTimedOut ? 'Tiempo límite de ejecución agéntica excedido.' : 'Ejecución agéntica cancelada por el usuario.');
|
|
1375
|
+
} else {
|
|
1376
|
+
status = 'error';
|
|
1377
|
+
executionError = err;
|
|
1378
|
+
if (callbacks.onError) callbacks.onError(err);
|
|
1379
|
+
}
|
|
1380
|
+
break;
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
// Si se alcanzó el límite máximo de iteraciones
|
|
1385
|
+
if (stepIndex >= maxSteps && status === 'completed') {
|
|
1386
|
+
status = 'max_steps';
|
|
1387
|
+
if (workingMessages.length > 0 && workingMessages[workingMessages.length - 1].role === 'tool' && autoSynthesize && !combinedSignal.aborted) {
|
|
1388
|
+
if (callbacks.onSynthesize) callbacks.onSynthesize(stepIndex);
|
|
1389
|
+
try {
|
|
1390
|
+
const hasRagTool = workingMessages.some(m => m.name === 'search_knowledge_base' || m.name === 'read_knowledge_chunk' || m.name === 'list_documents');
|
|
1391
|
+
const synthPrompt = hasRagTool
|
|
1392
|
+
? 'Based on the information obtained by the previous tools, answer my initial query directly. If the requested information or data was not found in the consulted documents, clearly state that no data was found to answer the question, rather than summarizing everything or dumping consulted chunks.'
|
|
1393
|
+
: 'Please provide a complete, structured, and detailed final summary answering my query based on all the information obtained from the tools.';
|
|
1394
|
+
|
|
1395
|
+
let synthMessages = workingMessages;
|
|
1396
|
+
if (typeof prepareMessages === 'function') {
|
|
1397
|
+
const prepared = await prepareMessages(workingMessages, {
|
|
1398
|
+
stepIndex,
|
|
1399
|
+
model,
|
|
1400
|
+
apiType,
|
|
1401
|
+
isSynthesis: true
|
|
1402
|
+
});
|
|
1403
|
+
synthMessages = Array.isArray(prepared)
|
|
1404
|
+
? prepared
|
|
1405
|
+
: (Array.isArray(prepared?.messages) ? prepared.messages : workingMessages);
|
|
1406
|
+
}
|
|
1407
|
+
synthMessages = [...synthMessages, { role: 'user', content: synthPrompt }];
|
|
1408
|
+
const synthRes = await API.streamChatCompletion({
|
|
1409
|
+
apiUrl,
|
|
1410
|
+
apiType,
|
|
1411
|
+
apiKey,
|
|
1412
|
+
model,
|
|
1413
|
+
messages: synthMessages,
|
|
1414
|
+
temperature,
|
|
1415
|
+
reasoningEffort,
|
|
1416
|
+
reasoningTransport,
|
|
1417
|
+
enableTools: false,
|
|
1418
|
+
toolChoice: 'none',
|
|
1419
|
+
signal: combinedSignal,
|
|
1420
|
+
onBeforeRequest,
|
|
1421
|
+
onGenerationStatus: status => callbacks.onGenerationStatus?.(status),
|
|
1422
|
+
onChunk: (fullTextSoFar, delta, stats) => {
|
|
1423
|
+
finalAccumulatedText = fullTextSoFar;
|
|
1424
|
+
if (callbacks.onChunk) callbacks.onChunk(fullTextSoFar, delta, stats);
|
|
1425
|
+
},
|
|
1426
|
+
onDone: (finalText, stats) => {
|
|
1427
|
+
finalAccumulatedText = finalText || finalAccumulatedText;
|
|
1428
|
+
lastStats = stats || lastStats;
|
|
1429
|
+
}
|
|
1430
|
+
});
|
|
1431
|
+
if (synthRes && synthRes.accumulatedText) {
|
|
1432
|
+
finalAccumulatedText = synthRes.accumulatedText;
|
|
1433
|
+
}
|
|
1434
|
+
} catch (e) {
|
|
1435
|
+
if (typeof console !== 'undefined' && console.warn) {
|
|
1436
|
+
console.warn('[ChatAgentCore] Error during auto-synthesis:', e);
|
|
1437
|
+
}
|
|
1438
|
+
if (callbacks && typeof callbacks.onLog === 'function') {
|
|
1439
|
+
callbacks.onLog({ type: 'warning', text: `[Auto-synthesis] Fallback activado tras error: ${e?.message || String(e)}` });
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
if (!finalAccumulatedText || finalAccumulatedText.trim() === '') {
|
|
1444
|
+
const hasRagTool = workingMessages.some(m => m.name === 'search_knowledge_base' || m.name === 'read_knowledge_chunk' || m.name === 'list_documents');
|
|
1445
|
+
if (hasRagTool) {
|
|
1446
|
+
finalAccumulatedText = 'No data was found in the consulted documents to answer the question.';
|
|
1447
|
+
} else {
|
|
1448
|
+
const toolContents = workingMessages
|
|
1449
|
+
.filter(m => m.role === 'tool' && m.content)
|
|
1450
|
+
.map(m => m.content)
|
|
1451
|
+
.filter(Boolean);
|
|
1452
|
+
if (toolContents.length > 0) {
|
|
1453
|
+
finalAccumulatedText = '### Summary of Consulted Information\n\n' + toolContents.join('\n\n---\n\n');
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
if (appendFinalMessage && finalAccumulatedText) {
|
|
1458
|
+
workingMessages.push({
|
|
1459
|
+
id: typeof createMessageId === 'function'
|
|
1460
|
+
? createMessageId('final', { stepIndex })
|
|
1461
|
+
: `msg_turn_${stepIndex}_final`,
|
|
1462
|
+
role: 'assistant',
|
|
1463
|
+
content: finalAccumulatedText
|
|
1464
|
+
});
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
if (timeoutTimer) {
|
|
1470
|
+
clearTimeout(timeoutTimer);
|
|
1471
|
+
}
|
|
1472
|
+
if (signal && onSignalAbort && typeof signal.removeEventListener === 'function') {
|
|
1473
|
+
signal.removeEventListener('abort', onSignalAbort);
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
const endTime = typeof performance !== 'undefined' ? performance.now() : Date.now();
|
|
1477
|
+
const totalElapsedMs = Math.round(endTime - startTime);
|
|
1478
|
+
|
|
1479
|
+
if (callbacks.onDone) {
|
|
1480
|
+
callbacks.onDone(finalAccumulatedText, lastStats, stepIndex);
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
return {
|
|
1484
|
+
success: status === 'completed' || status === 'max_steps' || status === 'loop_detected',
|
|
1485
|
+
status,
|
|
1486
|
+
finalText: finalAccumulatedText,
|
|
1487
|
+
reasoningText: finalReasoningText,
|
|
1488
|
+
stepsCount: stepIndex,
|
|
1489
|
+
maxStepsReached: status === 'max_steps',
|
|
1490
|
+
loopDetected: status === 'loop_detected',
|
|
1491
|
+
toolExecutions,
|
|
1492
|
+
history: workingMessages,
|
|
1493
|
+
stats: {
|
|
1494
|
+
totalTimeMs: totalElapsedMs,
|
|
1495
|
+
tokens: lastStats?.tokens || 0,
|
|
1496
|
+
tokensPerSec: lastStats?.tokensPerSec || 0,
|
|
1497
|
+
cachedTokens: lastStats?.cachedTokens || 0,
|
|
1498
|
+
cacheCreationTokens: lastStats?.cacheCreationTokens || 0,
|
|
1499
|
+
promptTokens: lastStats?.promptTokens || 0,
|
|
1500
|
+
completionTokens: lastStats?.completionTokens || 0,
|
|
1501
|
+
totalTokens: lastStats?.totalTokens || 0,
|
|
1502
|
+
reasoningTokens: lastStats?.reasoningTokens || 0,
|
|
1503
|
+
ttftSec: lastStats?.ttftSec || 0,
|
|
1504
|
+
totalSec: lastStats?.totalSec || ((totalElapsedMs / 1000).toFixed(2)),
|
|
1505
|
+
isEstimated: lastStats?.isEstimated ?? true
|
|
1506
|
+
},
|
|
1507
|
+
error: executionError
|
|
1508
|
+
};
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
/**
|
|
1513
|
+
* Orquestador Agéntico del Ciclo de Vida de Conversación (AgentCore).
|
|
1514
|
+
* Mantiene compatibilidad total con la API previa extendiendo AgentRuntime.
|
|
1515
|
+
*/
|
|
1516
|
+
class AgentCore extends AgentRuntime {
|
|
1517
|
+
constructor(options = {}) {
|
|
1518
|
+
super(options);
|
|
1519
|
+
this.maxTurns = this.maxSteps;
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
/**
|
|
1523
|
+
* Alias compatible con la firma previa runConversationLoop.
|
|
1524
|
+
*/
|
|
1525
|
+
async runConversationLoop(params = {}) {
|
|
1526
|
+
return this.execute(params);
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
const globalRegistry = new ToolRegistry();
|
|
1531
|
+
const globalExecutor = new ToolExecutor(globalRegistry);
|
|
1532
|
+
const globalRuntime = new AgentRuntime({ registry: globalRegistry, executor: globalExecutor });
|
|
1533
|
+
const globalAgent = new AgentCore({ registry: globalRegistry, executor: globalExecutor });
|
|
1534
|
+
|
|
1535
|
+
return {
|
|
1536
|
+
TOOL_CONTRACT_VERSION,
|
|
1537
|
+
ToolOutcome,
|
|
1538
|
+
validateToolContract,
|
|
1539
|
+
Tool,
|
|
1540
|
+
BaseToolProvider,
|
|
1541
|
+
BuiltinToolProvider,
|
|
1542
|
+
ToolRegistry,
|
|
1543
|
+
ToolExecutor,
|
|
1544
|
+
AgentRuntime,
|
|
1545
|
+
AgentCore,
|
|
1546
|
+
registry: globalRegistry,
|
|
1547
|
+
executor: globalExecutor,
|
|
1548
|
+
runtime: globalRuntime,
|
|
1549
|
+
agent: globalAgent,
|
|
1550
|
+
dispatchToolCall: (toolCall, options) => globalExecutor.dispatchToolCall(toolCall, options),
|
|
1551
|
+
executeToolCall: (toolCall, context) => globalExecutor.executeToolCall(toolCall, context)
|
|
1552
|
+
};
|
|
1553
|
+
});
|