@eclipse-lyra/extension-ai-system 0.7.57 → 0.7.58

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.
@@ -0,0 +1,1864 @@
1
+ import { TOPIC_SETTINGS_CHANGED, appSettings, commandRegistry, contributionRegistry, createLogger, logger, persistenceService, publish, rootContext, subscribe } from "@eclipse-lyra/core";
2
+ //#region src/core/constants.ts
3
+ var TOPIC_AI_STREAM_STARTED = "events/aiservice/streamStarted";
4
+ var TOPIC_AI_STREAM_CHUNK = "events/aiservice/streamChunk";
5
+ var TOPIC_AI_STREAM_COMPLETE = "events/aiservice/streamComplete";
6
+ var TOPIC_AI_STREAM_ERROR = "events/aiservice/streamError";
7
+ var TOPIC_AICONFIG_CHANGED = "events/aiservice/aiConfigChanged";
8
+ var TOPIC_AGENT_WORKFLOW_STARTED = "events/aiservice/agentWorkflowStarted";
9
+ var TOPIC_AGENT_WORKFLOW_COMPLETE = "events/aiservice/agentWorkflowComplete";
10
+ var TOPIC_AGENT_WORKFLOW_ERROR = "events/aiservice/agentWorkflowError";
11
+ var CID_AGENTS = "aiservice.agents";
12
+ var CID_CHAT_PROVIDERS = "aiservice.chatProviders";
13
+ var CID_PROMPT_ENHANCERS = "aiservice.promptEnhancers";
14
+ var KEY_AI_CONFIG = "aiConfig";
15
+ var DEFAULT_AGENT_ROLE = "appsupport";
16
+ var AI_CONFIG_TEMPLATE = {
17
+ "defaultProvider": "openai",
18
+ "providers": [],
19
+ "requireToolApproval": true
20
+ };
21
+ var MAX_TOOL_ITERATIONS = 10;
22
+ var MAX_RECENT_TOOL_CALLS = 5;
23
+ //#endregion
24
+ //#region src/providers/streaming/stream-parser.ts
25
+ var StreamParser = class {
26
+ constructor() {
27
+ this.decoder = new TextDecoder();
28
+ this.usage = null;
29
+ }
30
+ async *readLines(reader) {
31
+ let buffer = "";
32
+ this.usage = null;
33
+ try {
34
+ while (true) {
35
+ const { done, value } = await reader.read();
36
+ if (done) break;
37
+ buffer += this.decoder.decode(value, { stream: true });
38
+ const lines = buffer.split("\n");
39
+ buffer = lines.pop() || "";
40
+ for (const line of lines) if (line.trim()) yield* this.processLine(line);
41
+ }
42
+ if (buffer.trim()) yield* this.processLine(buffer);
43
+ yield this.makeDoneChunk();
44
+ } finally {
45
+ reader.releaseLock();
46
+ }
47
+ }
48
+ makeDoneChunk() {
49
+ const chunk = {
50
+ type: "done",
51
+ content: ""
52
+ };
53
+ if (this.usage) chunk.metadata = { usage: this.usage };
54
+ return chunk;
55
+ }
56
+ };
57
+ //#endregion
58
+ //#region src/providers/streaming/sse-parser.ts
59
+ var SSEParser = class extends StreamParser {
60
+ async *parse(reader) {
61
+ yield* this.readLines(reader);
62
+ }
63
+ async *processLine(line) {
64
+ if (!line.startsWith("data: ")) return;
65
+ const data = line.slice(6).trim();
66
+ if (data === "[DONE]") {
67
+ yield this.makeDoneChunk();
68
+ return;
69
+ }
70
+ try {
71
+ const json = JSON.parse(data);
72
+ if (json.error) {
73
+ yield {
74
+ type: "error",
75
+ content: json.error.message || "Unknown error",
76
+ metadata: json.error
77
+ };
78
+ return;
79
+ }
80
+ this.extractUsage(json);
81
+ const chunk = this.parseChunk(json);
82
+ if (chunk) yield chunk;
83
+ } catch {}
84
+ }
85
+ extractUsage(json) {
86
+ if (!json.usage) return;
87
+ const u = json.usage;
88
+ this.usage = {
89
+ promptTokens: u.prompt_tokens || 0,
90
+ completionTokens: u.completion_tokens || 0,
91
+ totalTokens: u.total_tokens || 0,
92
+ estimated: false
93
+ };
94
+ }
95
+ parseChunk(json) {
96
+ const delta = json.choices?.[0]?.delta;
97
+ const choice = json.choices?.[0];
98
+ if (delta?.content) return {
99
+ type: "token",
100
+ content: delta.content,
101
+ message: {
102
+ role: delta.role || "assistant",
103
+ content: choice?.message?.content || delta.content
104
+ }
105
+ };
106
+ if (choice?.message?.tool_calls) {
107
+ const toolCalls = this.parseToolCalls(choice.message.tool_calls, true);
108
+ if (toolCalls.length > 0) return {
109
+ type: "token",
110
+ content: "",
111
+ toolCalls
112
+ };
113
+ } else if (delta?.tool_calls || choice?.delta?.tool_calls) {
114
+ const toolCalls = this.parseToolCalls(delta?.tool_calls || choice?.delta?.tool_calls || [], false);
115
+ if (toolCalls.length > 0) return {
116
+ type: "token",
117
+ content: "",
118
+ toolCalls
119
+ };
120
+ }
121
+ return null;
122
+ }
123
+ parseToolCalls(toolCalls, isComplete) {
124
+ return toolCalls.filter((tc) => tc.function !== void 0).map((tc, idx) => ({
125
+ id: tc.id || `call_${tc.index !== void 0 ? tc.index : idx}_${Date.now()}`,
126
+ type: "function",
127
+ function: {
128
+ name: tc.function?.name || "",
129
+ arguments: tc.function?.arguments || (isComplete ? "{}" : "")
130
+ },
131
+ _index: tc.index !== void 0 ? tc.index : idx
132
+ }));
133
+ }
134
+ };
135
+ //#endregion
136
+ //#region src/providers/streaming/ollama-parser.ts
137
+ var OllamaParser = class extends StreamParser {
138
+ async *parse(reader) {
139
+ yield* this.readLines(reader);
140
+ }
141
+ async *processLine(line) {
142
+ try {
143
+ const json = JSON.parse(line);
144
+ if (json.error) {
145
+ yield {
146
+ type: "error",
147
+ content: json.error,
148
+ metadata: json
149
+ };
150
+ return;
151
+ }
152
+ if (json.done) {
153
+ this.extractUsage(json);
154
+ yield this.makeDoneChunk();
155
+ return;
156
+ }
157
+ const tokenChunk = this.parseToken(json);
158
+ if (tokenChunk) yield tokenChunk;
159
+ } catch {}
160
+ }
161
+ extractUsage(json) {
162
+ if (json.prompt_eval_count === void 0 && json.eval_count === void 0) return;
163
+ const promptTokens = json.prompt_eval_count || 0;
164
+ const completionTokens = json.eval_count || 0;
165
+ this.usage = {
166
+ promptTokens,
167
+ completionTokens,
168
+ totalTokens: promptTokens + completionTokens,
169
+ estimated: false
170
+ };
171
+ }
172
+ parseToken(json) {
173
+ if (json.message?.content) return {
174
+ type: "token",
175
+ content: json.message.content,
176
+ message: {
177
+ role: json.message.role || "assistant",
178
+ content: json.message.content
179
+ }
180
+ };
181
+ if (json.response) return {
182
+ type: "token",
183
+ content: json.response,
184
+ message: {
185
+ role: "assistant",
186
+ content: json.response
187
+ }
188
+ };
189
+ return null;
190
+ }
191
+ };
192
+ //#endregion
193
+ //#region src/providers/provider-utils.ts
194
+ async function streamToText(messages, chatConfig, providerFactory) {
195
+ let content = "";
196
+ const provider = providerFactory.getProvider(chatConfig);
197
+ for await (const chunk of provider.stream({
198
+ model: chatConfig.model,
199
+ messages,
200
+ chatConfig
201
+ })) if (chunk.type === "token") content += chunk.content;
202
+ return content;
203
+ }
204
+ function extractBaseUrl(endpoint) {
205
+ if (!endpoint) return null;
206
+ if (endpoint.includes("/v1/chat/completions")) return endpoint.replace("/v1/chat/completions", "");
207
+ if (endpoint.includes("/api/v1/chat/completions")) return endpoint.replace("/api/v1/chat/completions", "");
208
+ if (endpoint.includes("/api/chat/completion")) return endpoint.replace("/api/chat/completion", "");
209
+ try {
210
+ const url = new URL(endpoint);
211
+ return `${url.protocol}//${url.host}`;
212
+ } catch {
213
+ return null;
214
+ }
215
+ }
216
+ //#endregion
217
+ //#region src/providers/base-provider.ts
218
+ var BaseProvider = class {
219
+ createParser(contentType, endpoint) {
220
+ if (contentType.includes("text/event-stream") || endpoint.includes("openai")) return new SSEParser();
221
+ return new OllamaParser();
222
+ }
223
+ async getAvailableModels(chatProvider) {
224
+ if (!chatProvider.chatApiEndpoint) return [];
225
+ const baseUrl = extractBaseUrl(chatProvider.chatApiEndpoint);
226
+ if (!baseUrl) return [];
227
+ try {
228
+ const headers = { "Content-Type": "application/json" };
229
+ if (chatProvider.apiKey) headers["Authorization"] = `Bearer ${chatProvider.apiKey}`;
230
+ const response = await fetch(`${baseUrl}/v1/models`, {
231
+ method: "GET",
232
+ headers
233
+ });
234
+ if (!response.ok) return [];
235
+ return ((await response.json()).data || []).map((m) => ({
236
+ id: m.id,
237
+ name: m.name || m.id
238
+ }));
239
+ } catch {
240
+ return [];
241
+ }
242
+ }
243
+ async *stream(params) {
244
+ const requestBody = {
245
+ model: params.model,
246
+ stream: true,
247
+ messages: params.messages,
248
+ ...params.chatConfig.parameters
249
+ };
250
+ if (params.tools && params.tools.length > 0) {
251
+ requestBody.tools = params.tools;
252
+ requestBody.tool_choice = "auto";
253
+ }
254
+ const response = await fetch(params.chatConfig.chatApiEndpoint, {
255
+ method: "POST",
256
+ headers: {
257
+ "Authorization": `Bearer ${params.chatConfig.apiKey}`,
258
+ "Content-Type": "application/json",
259
+ "Accept": "text/event-stream"
260
+ },
261
+ body: JSON.stringify(requestBody),
262
+ signal: params.signal
263
+ });
264
+ if (!response.ok) {
265
+ const errorText = await response.text().catch(() => "Unknown error");
266
+ yield {
267
+ type: "error",
268
+ content: `HTTP ${response.status}: ${errorText}`,
269
+ metadata: { status: response.status }
270
+ };
271
+ return;
272
+ }
273
+ if (!response.body) {
274
+ yield {
275
+ type: "error",
276
+ content: "Response body is null or empty",
277
+ metadata: { status: response.status }
278
+ };
279
+ return;
280
+ }
281
+ const reader = response.body.getReader();
282
+ if (!reader) {
283
+ yield {
284
+ type: "error",
285
+ content: "Response body is not readable"
286
+ };
287
+ return;
288
+ }
289
+ const contentType = response.headers.get("content-type") || "";
290
+ const parser = this.createParser(contentType, params.chatConfig.chatApiEndpoint);
291
+ try {
292
+ for await (const chunk of parser.parse(reader)) yield chunk;
293
+ } catch (error) {
294
+ yield {
295
+ type: "error",
296
+ content: error instanceof Error ? error.message : "Failed to parse response stream",
297
+ metadata: {
298
+ error,
299
+ contentType
300
+ }
301
+ };
302
+ }
303
+ }
304
+ };
305
+ //#endregion
306
+ //#region src/providers/openai-provider.ts
307
+ var OpenAIProvider = class extends BaseProvider {
308
+ constructor(..._args) {
309
+ super(..._args);
310
+ this.name = "openai";
311
+ }
312
+ canHandle(chatProvider) {
313
+ return chatProvider.chatApiEndpoint.includes("openai") || chatProvider.chatApiEndpoint.includes("v1/chat/completions");
314
+ }
315
+ };
316
+ //#endregion
317
+ //#region src/providers/ollama-provider.ts
318
+ var OllamaProvider = class extends BaseProvider {
319
+ constructor(..._args) {
320
+ super(..._args);
321
+ this.name = "ollama";
322
+ }
323
+ canHandle(chatProvider) {
324
+ return chatProvider.name.toLowerCase() === "ollama" || chatProvider.chatApiEndpoint.includes("ollama") || chatProvider.chatApiEndpoint.includes("localhost:11434");
325
+ }
326
+ };
327
+ //#endregion
328
+ //#region src/providers/provider-factory.ts
329
+ var ProviderFactory = class {
330
+ constructor() {
331
+ this.providers = [];
332
+ this.providers.push(new OpenAIProvider());
333
+ this.providers.push(new OllamaProvider());
334
+ }
335
+ registerProvider(provider) {
336
+ this.providers.push(provider);
337
+ }
338
+ getProvider(chatProvider) {
339
+ return this.providers.find((p) => p.canHandle(chatProvider)) ?? this.providers[0];
340
+ }
341
+ getAllProviders() {
342
+ return [...this.providers];
343
+ }
344
+ };
345
+ //#endregion
346
+ //#region src/agents/agent-registry.ts
347
+ var AgentRegistry = class {
348
+ getAgentContributions() {
349
+ return contributionRegistry.getContributions(CID_AGENTS);
350
+ }
351
+ filterAndSortAgents(contributions, context) {
352
+ return contributions.filter((c) => !c.canHandle || c.canHandle(context)).sort((a, b) => (b.priority || 0) - (a.priority || 0));
353
+ }
354
+ getMatchingAgents(context, roles) {
355
+ const contributions = this.getAgentContributions();
356
+ if (contributions.length === 0) throw new Error("No agents are registered. The App Support agent should be available from the AI system extension.");
357
+ const roleFiltered = roles?.length ? contributions.filter((c) => roles.includes(c.role)) : contributions;
358
+ const active = this.filterAndSortAgents(roleFiltered, context);
359
+ if (roles?.length && active.length === 0) throw new Error(`No agents found for requested roles: ${roles.join(", ")}. Available: ${contributions.map((c) => c.role).join(", ")}`);
360
+ if (!roles?.length && active.length === 0) throw new Error(`No agents can handle the current context. Available: ${contributions.map((c) => c.role).join(", ")}`);
361
+ return active;
362
+ }
363
+ };
364
+ //#endregion
365
+ //#region src/core/message-utils.ts
366
+ function sanitizeMessageForAPI(message) {
367
+ const apiMessage = {
368
+ role: message.role,
369
+ content: message.content
370
+ };
371
+ if ("tool_call_id" in message && message.tool_call_id) apiMessage.tool_call_id = message.tool_call_id;
372
+ if ("tool_calls" in message && message.tool_calls) apiMessage.tool_calls = message.tool_calls;
373
+ return apiMessage;
374
+ }
375
+ function sanitizeMessagesForAPI(messages) {
376
+ return messages.map(sanitizeMessageForAPI);
377
+ }
378
+ //#endregion
379
+ //#region src/utils/tool-detector.ts
380
+ var logger$2 = createLogger("ToolDetector");
381
+ var GREETINGS = [
382
+ "hello",
383
+ "hi",
384
+ "hey",
385
+ "thanks",
386
+ "thank you",
387
+ "bye",
388
+ "goodbye"
389
+ ];
390
+ var ACTION_KEYWORDS = [
391
+ "create",
392
+ "open",
393
+ "delete",
394
+ "read",
395
+ "write",
396
+ "edit",
397
+ "save",
398
+ "rename",
399
+ "move",
400
+ "copy",
401
+ "list",
402
+ "show",
403
+ "display",
404
+ "run",
405
+ "execute",
406
+ "build",
407
+ "add",
408
+ "remove",
409
+ "update",
410
+ "modify",
411
+ "change",
412
+ "set",
413
+ "get",
414
+ "find",
415
+ "search",
416
+ "filter",
417
+ "sort",
418
+ "install",
419
+ "uninstall",
420
+ "load",
421
+ "import",
422
+ "export",
423
+ "generate",
424
+ "make",
425
+ "do",
426
+ "perform",
427
+ "call",
428
+ "invoke"
429
+ ];
430
+ var CONTEXT_KEYWORDS = [
431
+ "file",
432
+ "folder",
433
+ "directory",
434
+ "workspace",
435
+ "editor",
436
+ "map",
437
+ "layer",
438
+ "command",
439
+ "tool",
440
+ "extension",
441
+ "script",
442
+ "code",
443
+ "project"
444
+ ];
445
+ /**
446
+ * Heuristic tool detection: decides whether to attach tools based on prompt content
447
+ * only. Avoids a 60MB ML dependency; simple chat never triggers a large download.
448
+ */
449
+ var ToolDetector = class {
450
+ needsTools(prompt) {
451
+ if (!prompt?.trim()) return false;
452
+ const normalized = prompt.toLowerCase().trim();
453
+ if (GREETINGS.some((g) => normalized === g || normalized.startsWith(g + " "))) return false;
454
+ const hasAction = ACTION_KEYWORDS.some((k) => prompt.includes(k));
455
+ const hasContext = CONTEXT_KEYWORDS.some((k) => prompt.includes(k));
456
+ const needsTools = hasAction && (hasContext || prompt.length > 20);
457
+ if (needsTools) logger$2.info(`Heuristic: needsTools=true (action+context or long prompt)`);
458
+ return needsTools;
459
+ }
460
+ dispose() {}
461
+ };
462
+ var toolDetector = new ToolDetector();
463
+ //#endregion
464
+ //#region src/agents/prompt-builder.ts
465
+ var logger$1 = createLogger("PromptBuilder");
466
+ var PromptBuilder = class {
467
+ constructor(toolRegistry) {
468
+ this.toolRegistry = toolRegistry;
469
+ this.enhancers = [];
470
+ }
471
+ addEnhancer(enhancer) {
472
+ this.enhancers.push(enhancer);
473
+ }
474
+ async getSysPrompt(contribution, context) {
475
+ let sysPrompt = contribution.sysPrompt;
476
+ if (typeof sysPrompt === "function") sysPrompt = sysPrompt();
477
+ if (!sysPrompt || typeof sysPrompt !== "string") throw new Error(`Agent "${contribution.role}" is missing a system prompt.`);
478
+ const allEnhancers = [
479
+ ...contribution.promptEnhancers || [],
480
+ ...this.enhancers,
481
+ ...this.getContributedEnhancers()
482
+ ].sort((a, b) => (b.priority || 0) - (a.priority || 0));
483
+ let enhanced = sysPrompt;
484
+ for (const enhancer of allEnhancers) try {
485
+ const result = await enhancer.enhance(enhanced, context);
486
+ if (result && typeof result === "string") enhanced = result;
487
+ } catch (err) {
488
+ logger$1.warn("Enhancer failed:", err);
489
+ }
490
+ return enhanced;
491
+ }
492
+ rewriteChatHistoryForAgent(history, targetRole) {
493
+ return history.map((m) => {
494
+ if (m.role === "user") return {
495
+ role: m.role,
496
+ content: m.content
497
+ };
498
+ if (m.role === targetRole) return {
499
+ role: "assistant",
500
+ content: m.content
501
+ };
502
+ return {
503
+ role: "user",
504
+ content: `***Agent '${m.role}' replied:***\n${m.content}`
505
+ };
506
+ });
507
+ }
508
+ getContributedEnhancers() {
509
+ return contributionRegistry.getContributions(CID_PROMPT_ENHANCERS).map((c) => ({
510
+ ...c.enhancer,
511
+ priority: c.priority ?? c.enhancer.priority
512
+ }));
513
+ }
514
+ async build(contribution, messages, context, hooks) {
515
+ if (hooks?.beforeSend) await hooks.beforeSend(messages, context);
516
+ const sanitized = sanitizeMessagesForAPI(messages);
517
+ const rewritten = this.rewriteChatHistoryForAgent(sanitized, contribution.role);
518
+ let toolsConfig = contribution.tools;
519
+ if (typeof toolsConfig === "function") toolsConfig = await toolsConfig();
520
+ let tools;
521
+ if (toolsConfig?.enabled) if (toolsConfig.smartToolDetection) {
522
+ const lastUser = messages[messages.length - 1];
523
+ if (toolDetector.needsTools(lastUser?.content || "")) tools = this.toolRegistry.getAvailableTools(context, toolsConfig.commandFilter);
524
+ } else tools = this.toolRegistry.getAvailableTools(context, toolsConfig.commandFilter);
525
+ const sysPrompt = await this.getSysPrompt(contribution, context);
526
+ rewritten.unshift({
527
+ role: "system",
528
+ content: sysPrompt
529
+ });
530
+ return {
531
+ messages: rewritten,
532
+ userPromptIndex: rewritten.length - 1,
533
+ tools
534
+ };
535
+ }
536
+ };
537
+ //#endregion
538
+ //#region src/agents/message-processor.ts
539
+ var MessageProcessorService = class {
540
+ constructor() {
541
+ this.processors = [];
542
+ }
543
+ addProcessor(processor) {
544
+ this.processors.push(processor);
545
+ }
546
+ async process(message, contribution, context) {
547
+ const allProcessors = [...contribution.messageProcessors || [], ...this.processors].sort((a, b) => (b.priority || 0) - (a.priority || 0));
548
+ let processed = { ...message };
549
+ for (const processor of allProcessors) processed = await processor.process(processed, context);
550
+ return processed;
551
+ }
552
+ };
553
+ //#endregion
554
+ //#region src/tools/tool-call-accumulator.ts
555
+ var ToolCallAccumulator = class {
556
+ constructor() {
557
+ this.accumulatedToolCalls = /* @__PURE__ */ new Map();
558
+ this.toolCallIndexMap = /* @__PURE__ */ new Map();
559
+ }
560
+ processChunk(chunk) {
561
+ if (chunk.type !== "token" || !chunk.toolCalls?.length) return;
562
+ for (const toolCall of chunk.toolCalls) {
563
+ const callIndex = toolCall._index;
564
+ const callId = toolCall.id;
565
+ let existing;
566
+ let targetId;
567
+ if (callIndex !== void 0 && this.toolCallIndexMap.has(callIndex)) {
568
+ targetId = this.toolCallIndexMap.get(callIndex);
569
+ existing = this.accumulatedToolCalls.get(targetId);
570
+ } else if (callId && this.accumulatedToolCalls.has(callId)) {
571
+ targetId = callId;
572
+ existing = this.accumulatedToolCalls.get(targetId);
573
+ } else {
574
+ targetId = callId || `call_${callIndex !== void 0 ? callIndex : Date.now()}_${Math.random()}`;
575
+ existing = void 0;
576
+ }
577
+ if (existing) {
578
+ this.accumulatedToolCalls.set(targetId, {
579
+ id: targetId,
580
+ type: toolCall.type || existing.type,
581
+ function: {
582
+ name: toolCall.function.name || existing.function.name,
583
+ arguments: (existing.function.arguments || "") + (toolCall.function.arguments || "")
584
+ }
585
+ });
586
+ if (callIndex !== void 0 && !this.toolCallIndexMap.has(callIndex)) this.toolCallIndexMap.set(callIndex, targetId);
587
+ } else {
588
+ this.accumulatedToolCalls.set(targetId, {
589
+ ...toolCall,
590
+ id: targetId
591
+ });
592
+ if (callIndex !== void 0) this.toolCallIndexMap.set(callIndex, targetId);
593
+ }
594
+ }
595
+ }
596
+ getFinalToolCalls() {
597
+ return Array.from(this.accumulatedToolCalls.values()).filter((tc) => tc.function.name?.trim().length > 0).map((tc) => ({
598
+ ...tc,
599
+ function: {
600
+ ...tc.function,
601
+ arguments: tc.function.arguments?.trim() || "{}"
602
+ }
603
+ }));
604
+ }
605
+ reset() {
606
+ this.accumulatedToolCalls.clear();
607
+ this.toolCallIndexMap.clear();
608
+ }
609
+ };
610
+ //#endregion
611
+ //#region src/tools/tool-name-utils.ts
612
+ function sanitizeFunctionName(name) {
613
+ return name.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/^[^a-zA-Z]/, "cmd_$&").replace(/_+/g, "_").replace(/^_|_$/g, "");
614
+ }
615
+ //#endregion
616
+ //#region src/tools/tool-executor.ts
617
+ var ToolExecutor = class {
618
+ findCommand(toolCall, context) {
619
+ const sanitizedName = toolCall.function.name;
620
+ const direct = commandRegistry.getCommand(sanitizedName);
621
+ if (direct) return direct;
622
+ const allCommands = commandRegistry.listCommands();
623
+ for (const [commandId, command] of Object.entries(allCommands)) if (sanitizeFunctionName(commandId) === sanitizedName) return command;
624
+ return null;
625
+ }
626
+ parseArguments(argsStr) {
627
+ if (!argsStr?.trim() || argsStr === "{}") return {};
628
+ try {
629
+ const parsed = JSON.parse(argsStr);
630
+ return parsed && typeof parsed === "object" ? parsed : {};
631
+ } catch {
632
+ return {};
633
+ }
634
+ }
635
+ sanitizeArguments(args, command) {
636
+ if (!command?.parameters || !args || typeof args !== "object") return args || {};
637
+ const sanitizedArgs = {};
638
+ command.parameters.forEach((param) => {
639
+ const sanitizedParamName = sanitizeFunctionName(param.name);
640
+ if (sanitizedParamName in args) sanitizedArgs[param.name] = args[sanitizedParamName];
641
+ });
642
+ return sanitizedArgs;
643
+ }
644
+ async executeToolCall(toolCall, context) {
645
+ try {
646
+ const command = this.findCommand(toolCall, context);
647
+ const commandId = command?.id || toolCall.function.name;
648
+ const args = this.parseArguments(toolCall.function.arguments || "{}");
649
+ const sanitizedArgs = this.sanitizeArguments(args, command);
650
+ const freshContext = commandRegistry.createExecutionContext(sanitizedArgs);
651
+ const execContext = {
652
+ ...context,
653
+ ...freshContext,
654
+ params: sanitizedArgs
655
+ };
656
+ const commandResult = await commandRegistry.execute(commandId, execContext);
657
+ const resultMessage = {
658
+ success: true,
659
+ message: `Command "${command?.name || commandId}" executed successfully`,
660
+ command: commandId
661
+ };
662
+ if (Object.keys(sanitizedArgs).length > 0) resultMessage.parameters = sanitizedArgs;
663
+ if (commandResult != null) {
664
+ let resolved = commandResult;
665
+ if (resolved instanceof Promise) resolved = await resolved;
666
+ resultMessage.result = resolved;
667
+ if (command?.output?.length) resultMessage.output = command.output.map((v) => `${v.name}: ${v.description || v.type || "value"}`).join(", ");
668
+ }
669
+ return {
670
+ id: toolCall.id,
671
+ result: resultMessage
672
+ };
673
+ } catch (error) {
674
+ let command = null;
675
+ try {
676
+ command = this.findCommand(toolCall, context);
677
+ } catch {}
678
+ const errorMessage = error instanceof Error ? error.message : String(error);
679
+ const commandName = command?.name || toolCall.function.name;
680
+ let detailedError = errorMessage;
681
+ if (errorMessage.includes("No handler found") || errorMessage.includes("No handlers registered")) detailedError = `Command "${commandName}" cannot be executed. ${errorMessage}.`;
682
+ return {
683
+ id: toolCall.id,
684
+ result: null,
685
+ error: detailedError
686
+ };
687
+ }
688
+ }
689
+ async executeToolCalls(toolCalls, context) {
690
+ const results = [];
691
+ for (const toolCall of toolCalls) results.push(await this.executeToolCall(toolCall, context));
692
+ return results;
693
+ }
694
+ createToolCallAccumulator() {
695
+ return new ToolCallAccumulator();
696
+ }
697
+ createToolCallSignature(toolCall) {
698
+ let args = {};
699
+ try {
700
+ const parsed = JSON.parse(toolCall.function.arguments || "{}");
701
+ args = parsed && typeof parsed === "object" ? parsed : {};
702
+ } catch {
703
+ args = {};
704
+ }
705
+ const sortedArgs = Object.keys(args).sort().reduce((acc, key) => {
706
+ acc[key] = args[key];
707
+ return acc;
708
+ }, {});
709
+ return `${toolCall.function.name}:${JSON.stringify(sortedArgs)}`;
710
+ }
711
+ };
712
+ //#endregion
713
+ //#region src/tools/tool-registry.ts
714
+ var ToolRegistry = class {
715
+ commandToTool(command, context) {
716
+ const properties = {};
717
+ const required = [];
718
+ command.parameters?.forEach((param) => {
719
+ const sanitizedParamName = sanitizeFunctionName(param.name);
720
+ properties[sanitizedParamName] = {
721
+ type: param.type || "string",
722
+ description: param.description,
723
+ ...param.allowedValues && { enum: param.allowedValues }
724
+ };
725
+ if (param.required === true) required.push(sanitizedParamName);
726
+ });
727
+ return {
728
+ type: "function",
729
+ function: {
730
+ name: sanitizeFunctionName(command.id),
731
+ description: command.description || command.name,
732
+ parameters: {
733
+ type: "object",
734
+ properties,
735
+ required
736
+ }
737
+ }
738
+ };
739
+ }
740
+ getAvailableTools(context, commandFilter) {
741
+ const availableCommands = commandRegistry.listCommands();
742
+ let commandsArray = Object.values(availableCommands);
743
+ if (commandFilter) commandsArray = commandsArray.filter((cmd) => commandFilter(cmd, context));
744
+ return commandsArray.map((cmd) => this.commandToTool(cmd, context));
745
+ }
746
+ };
747
+ //#endregion
748
+ //#region src/workflows/parallel-workflow.ts
749
+ var ParallelWorkflowStrategy = class {
750
+ async execute(contributions, options, results, executeAgent) {
751
+ const chatConfig = options.chatConfig;
752
+ if (!chatConfig) throw new Error("Chat config is required");
753
+ await Promise.all(contributions.map(async (contrib) => {
754
+ try {
755
+ await executeAgent(contrib, options.chatContext.history, results.sharedState, chatConfig, options, results);
756
+ } catch (error) {
757
+ const err = error instanceof Error ? error : new Error(String(error));
758
+ results.errors.set(contrib.role, err);
759
+ options.onAgentError?.(contrib.role, err);
760
+ }
761
+ }));
762
+ }
763
+ };
764
+ //#endregion
765
+ //#region src/workflows/base-sequential-workflow.ts
766
+ var BaseSequentialWorkflow = class {
767
+ createAgentContextWithPreviousAgents(accumulatedState, options, results) {
768
+ return {
769
+ ...accumulatedState,
770
+ ...options.callContext.getProxy(),
771
+ previousAgents: Array.from(results.messages.entries()).map(([role, msg]) => ({
772
+ role,
773
+ content: msg.content
774
+ }))
775
+ };
776
+ }
777
+ updateWorkflowState(finalMessage, currentMessages, accumulatedState, agentContext, results) {
778
+ currentMessages.push(finalMessage);
779
+ accumulatedState = {
780
+ ...accumulatedState,
781
+ ...agentContext,
782
+ message: finalMessage
783
+ };
784
+ results.sharedState = accumulatedState;
785
+ return {
786
+ currentMessages,
787
+ accumulatedState
788
+ };
789
+ }
790
+ };
791
+ //#endregion
792
+ //#region src/workflows/sequential-workflow.ts
793
+ var SequentialWorkflowStrategy = class extends BaseSequentialWorkflow {
794
+ async execute(contributions, options, results, executeAgent) {
795
+ const chatConfig = options.chatConfig;
796
+ if (!chatConfig) throw new Error("Chat config is required");
797
+ let currentMessages = [...options.chatContext.history];
798
+ let accumulatedState = { ...results.sharedState };
799
+ for (const contrib of contributions) try {
800
+ const agentContext = this.createAgentContextWithPreviousAgents(accumulatedState, options, results);
801
+ const finalMessage = await executeAgent(contrib, currentMessages, accumulatedState, chatConfig, options, results);
802
+ if (!finalMessage) break;
803
+ const updated = this.updateWorkflowState(finalMessage, currentMessages, accumulatedState, agentContext, results);
804
+ currentMessages = updated.currentMessages;
805
+ accumulatedState = updated.accumulatedState;
806
+ } catch (error) {
807
+ const err = error instanceof Error ? error : new Error(String(error));
808
+ results.errors.set(contrib.role, err);
809
+ options.onAgentError?.(contrib.role, err);
810
+ break;
811
+ }
812
+ }
813
+ };
814
+ //#endregion
815
+ //#region src/workflows/conditional-workflow.ts
816
+ var ConditionalWorkflowStrategy = class extends BaseSequentialWorkflow {
817
+ async execute(contributions, options, results, executeAgent) {
818
+ const chatConfig = options.chatConfig;
819
+ if (!chatConfig) throw new Error("Chat config is required");
820
+ let currentMessages = [...options.chatContext.history];
821
+ let accumulatedState = { ...results.sharedState };
822
+ for (const contrib of contributions) try {
823
+ const agentContext = this.createAgentContextWithPreviousAgents(accumulatedState, options, results);
824
+ if (contrib.canHandle && !contrib.canHandle(agentContext)) continue;
825
+ const finalMessage = await executeAgent(contrib, currentMessages, accumulatedState, chatConfig, options, results);
826
+ if (!finalMessage) break;
827
+ const updated = this.updateWorkflowState(finalMessage, currentMessages, accumulatedState, agentContext, results);
828
+ currentMessages = updated.currentMessages;
829
+ accumulatedState = updated.accumulatedState;
830
+ } catch (error) {
831
+ const err = error instanceof Error ? error : new Error(String(error));
832
+ results.errors.set(contrib.role, err);
833
+ options.onAgentError?.(contrib.role, err);
834
+ }
835
+ }
836
+ };
837
+ //#endregion
838
+ //#region src/workflows/pipeline-workflow.ts
839
+ var PipelineWorkflowStrategy = class {
840
+ async execute(contributions, options, results, executeAgent) {
841
+ const chatConfig = options.chatConfig;
842
+ if (!chatConfig) throw new Error("Chat config is required");
843
+ let currentMessages = [...options.chatContext.history];
844
+ for (const wave of this.buildTopoOrder(contributions)) {
845
+ await Promise.all(wave.map(async (contrib) => {
846
+ try {
847
+ await executeAgent(contrib, currentMessages, results.sharedState, chatConfig, options, results);
848
+ } catch (error) {
849
+ const err = error instanceof Error ? error : new Error(String(error));
850
+ results.errors.set(contrib.role, err);
851
+ options.onAgentError?.(contrib.role, err);
852
+ }
853
+ }));
854
+ for (const contrib of wave) {
855
+ const msg = results.messages.get(contrib.role);
856
+ if (msg) currentMessages.push(msg);
857
+ }
858
+ }
859
+ }
860
+ buildTopoOrder(contributions) {
861
+ const waves = [];
862
+ const placed = /* @__PURE__ */ new Set();
863
+ while (placed.size < contributions.length) {
864
+ const wave = contributions.filter((c) => {
865
+ if (placed.has(c.role)) return false;
866
+ if (!c.consumes?.length) return true;
867
+ const allProduced = contributions.filter((other) => placed.has(other.role)).flatMap((other) => other.produces || []);
868
+ return c.consumes.every((t) => allProduced.includes(t));
869
+ });
870
+ if (wave.length === 0) {
871
+ waves.push(contributions.filter((c) => !placed.has(c.role)));
872
+ break;
873
+ }
874
+ waves.push(wave);
875
+ for (const c of wave) placed.add(c.role);
876
+ }
877
+ return waves;
878
+ }
879
+ };
880
+ //#endregion
881
+ //#region src/task/task-plan.ts
882
+ function createTaskPlan(originalPrompt, steps) {
883
+ const now = Date.now();
884
+ return {
885
+ id: `plan-${now}-${Math.random().toString(36).slice(2, 9)}`,
886
+ originalPrompt,
887
+ steps: steps.map((s) => ({
888
+ ...s,
889
+ status: "pending",
890
+ revisions: 0
891
+ })),
892
+ status: "planning",
893
+ createdAt: now,
894
+ updatedAt: now
895
+ };
896
+ }
897
+ function getNextRunnableSteps(plan) {
898
+ const completed = new Set(plan.steps.filter((s) => s.status === "completed").map((s) => s.id));
899
+ return plan.steps.filter((s) => s.status === "pending" && s.dependsOn.every((d) => completed.has(d)));
900
+ }
901
+ function isPlanComplete(plan) {
902
+ return plan.steps.every((s) => s.status === "completed" || s.status === "skipped");
903
+ }
904
+ function isPlanFailed(plan) {
905
+ return plan.steps.some((s) => s.status === "failed");
906
+ }
907
+ //#endregion
908
+ //#region src/agents/orchestrator.ts
909
+ var ORCHESTRATOR_SYS_PROMPT = `You are a task orchestrator. Given a user's complex request, decompose it into a structured execution plan.
910
+
911
+ Respond with ONLY a JSON object matching this schema (no markdown, no explanation):
912
+ {
913
+ "steps": [
914
+ {
915
+ "id": "step-1",
916
+ "role": "<agent role>",
917
+ "subTask": "<specific instruction for this step>",
918
+ "dependsOn": [],
919
+ "consumes": [],
920
+ "produces": ["<artifact-id>"]
921
+ }
922
+ ]
923
+ }
924
+
925
+ Rules:
926
+ - Each step must have a unique id (step-1, step-2, ...)
927
+ - "role" must match an available agent role
928
+ - "dependsOn" lists step IDs that must complete before this step
929
+ - "consumes" and "produces" are artifact IDs
930
+ - Steps with no dependencies can run in parallel
931
+ - Keep the plan minimal — only as many steps as needed`;
932
+ async function orchestrateTask(options) {
933
+ const messages = [{
934
+ role: "system",
935
+ content: `${ORCHESTRATOR_SYS_PROMPT}\n\nAvailable agents:\n${options.availableAgents.filter((a) => !a.isOrchestrator).map((a) => `- ${a.role}: ${a.description}`).join("\n")}`
936
+ }, {
937
+ role: "user",
938
+ content: options.prompt
939
+ }];
940
+ const responseText = await options.executeCompletion(messages, options.chatConfig);
941
+ try {
942
+ const jsonMatch = responseText.match(/\{[\s\S]*\}/);
943
+ if (!jsonMatch) throw new Error("No JSON found in orchestrator response");
944
+ const parsed = JSON.parse(jsonMatch[0]);
945
+ return createTaskPlan(options.prompt, parsed.steps || []);
946
+ } catch (error) {
947
+ const defaultAgent = options.availableAgents.find((a) => !a.isOrchestrator);
948
+ return createTaskPlan(options.prompt, [{
949
+ id: "step-1",
950
+ role: defaultAgent?.role || "appsupport",
951
+ subTask: options.prompt,
952
+ dependsOn: [],
953
+ consumes: [],
954
+ produces: ["step-1-result"]
955
+ }]);
956
+ }
957
+ }
958
+ //#endregion
959
+ //#region src/workspace/workspace.ts
960
+ var TaskWorkspace = class TaskWorkspace {
961
+ constructor(taskId, plan) {
962
+ this.artifacts = /* @__PURE__ */ new Map();
963
+ this.mailbox = /* @__PURE__ */ new Map();
964
+ this.taskId = taskId;
965
+ this.plan = plan;
966
+ }
967
+ putArtifact(artifact) {
968
+ this.artifacts.set(artifact.id, artifact);
969
+ }
970
+ getArtifact(id) {
971
+ return this.artifacts.get(id);
972
+ }
973
+ getArtifactsByType(type) {
974
+ return Array.from(this.artifacts.values()).filter((a) => a.type === type);
975
+ }
976
+ getArtifactsProducedBy(role) {
977
+ return Array.from(this.artifacts.values()).filter((a) => a.producedBy === role);
978
+ }
979
+ postMessage(message) {
980
+ const key = message.to === "*" ? "__broadcast__" : message.to;
981
+ const messages = this.mailbox.get(key) || [];
982
+ messages.push(message);
983
+ this.mailbox.set(key, messages);
984
+ }
985
+ readMessages(recipientRole) {
986
+ const direct = this.mailbox.get(recipientRole) || [];
987
+ const broadcast = this.mailbox.get("__broadcast__") || [];
988
+ return [...direct, ...broadcast];
989
+ }
990
+ clearMessages(recipientRole) {
991
+ this.mailbox.delete(recipientRole);
992
+ }
993
+ updateStepStatus(stepId, status, result) {
994
+ const step = this.plan.steps.find((s) => s.id === stepId);
995
+ if (!step) return;
996
+ step.status = status;
997
+ if (result) {
998
+ step.result = result;
999
+ this.putArtifact(result);
1000
+ }
1001
+ this.plan.updatedAt = Date.now();
1002
+ }
1003
+ getNextRunnableSteps() {
1004
+ const completed = new Set(this.plan.steps.filter((s) => s.status === "completed").map((s) => s.id));
1005
+ return this.plan.steps.filter((s) => s.status === "pending" && s.dependsOn.every((dep) => completed.has(dep)));
1006
+ }
1007
+ toJSON() {
1008
+ return {
1009
+ taskId: this.taskId,
1010
+ plan: this.plan,
1011
+ artifacts: Array.from(this.artifacts.values()),
1012
+ mailbox: Object.fromEntries(this.mailbox.entries())
1013
+ };
1014
+ }
1015
+ static fromJSON(data) {
1016
+ const ws = new TaskWorkspace(data.taskId, data.plan);
1017
+ for (const artifact of data.artifacts || []) ws.artifacts.set(artifact.id, artifact);
1018
+ for (const [key, messages] of Object.entries(data.mailbox || {})) ws.mailbox.set(key, messages);
1019
+ return ws;
1020
+ }
1021
+ };
1022
+ //#endregion
1023
+ //#region src/task/task-checkpoint.ts
1024
+ var CHECKPOINT_KEY_PREFIX = "ai_task_checkpoint_";
1025
+ var REGISTRY_KEY = "ai_task_checkpoint_registry";
1026
+ var TaskCheckpointService = class {
1027
+ async save(workspace) {
1028
+ const key = `${CHECKPOINT_KEY_PREFIX}${workspace.taskId}`;
1029
+ await appSettings.set(key, workspace.toJSON());
1030
+ }
1031
+ async restore(taskId) {
1032
+ const key = `${CHECKPOINT_KEY_PREFIX}${taskId}`;
1033
+ const data = await appSettings.get(key);
1034
+ if (!data) return null;
1035
+ return TaskWorkspace.fromJSON(data);
1036
+ }
1037
+ async delete(taskId) {
1038
+ const key = `${CHECKPOINT_KEY_PREFIX}${taskId}`;
1039
+ await appSettings.set(key, void 0);
1040
+ }
1041
+ async listCheckpoints() {
1042
+ return this.getRegistry();
1043
+ }
1044
+ async registerCheckpoint(taskId) {
1045
+ const registry = await this.getRegistry();
1046
+ if (!registry.includes(taskId)) {
1047
+ registry.push(taskId);
1048
+ await appSettings.set(REGISTRY_KEY, registry);
1049
+ }
1050
+ }
1051
+ async unregisterCheckpoint(taskId) {
1052
+ const registry = await this.getRegistry();
1053
+ await appSettings.set(REGISTRY_KEY, registry.filter((id) => id !== taskId));
1054
+ }
1055
+ async getRegistry() {
1056
+ return await appSettings.get(REGISTRY_KEY) || [];
1057
+ }
1058
+ };
1059
+ var taskCheckpointService = new TaskCheckpointService();
1060
+ //#endregion
1061
+ //#region src/task/task-runner.ts
1062
+ var TaskRunner = class {
1063
+ constructor(executeStep) {
1064
+ this.executeStep = executeStep;
1065
+ }
1066
+ async run(workspace, options) {
1067
+ const plan = workspace.plan;
1068
+ plan.status = "running";
1069
+ await taskCheckpointService.save(workspace);
1070
+ const errors = /* @__PURE__ */ new Map();
1071
+ while (true) {
1072
+ if (options.signal?.aborted) {
1073
+ plan.status = "paused";
1074
+ break;
1075
+ }
1076
+ const runnableSteps = getNextRunnableSteps(plan);
1077
+ if (runnableSteps.length === 0) break;
1078
+ await Promise.all(runnableSteps.map(async (step) => {
1079
+ workspace.updateStepStatus(step.id, "running");
1080
+ options.onStepStart?.(step);
1081
+ try {
1082
+ const artifact = await this.executeStep(step, workspace, options);
1083
+ workspace.updateStepStatus(step.id, "completed", artifact);
1084
+ options.onStepComplete?.(step, artifact);
1085
+ await taskCheckpointService.save(workspace);
1086
+ } catch (error) {
1087
+ const err = error instanceof Error ? error : new Error(String(error));
1088
+ workspace.updateStepStatus(step.id, "failed");
1089
+ errors.set(step.id, err);
1090
+ options.onStepError?.(step, err);
1091
+ }
1092
+ }));
1093
+ if (isPlanFailed(plan)) {
1094
+ plan.status = "failed";
1095
+ break;
1096
+ }
1097
+ if (isPlanComplete(plan)) {
1098
+ plan.status = "completed";
1099
+ break;
1100
+ }
1101
+ }
1102
+ return {
1103
+ plan,
1104
+ artifacts: plan.steps.filter((s) => s.result).map((s) => s.result),
1105
+ errors
1106
+ };
1107
+ }
1108
+ };
1109
+ //#endregion
1110
+ //#region src/workflows/orchestrated-workflow.ts
1111
+ /**
1112
+ * Orchestrated workflow: uses an orchestrator agent to decompose the prompt into
1113
+ * a TaskPlan and then executes it step by step via TaskRunner.
1114
+ */
1115
+ var OrchestratedWorkflowStrategy = class {
1116
+ async execute(contributions, options, results, executeAgent) {
1117
+ const chatConfig = options.chatConfig;
1118
+ if (!chatConfig) throw new Error("Chat config is required");
1119
+ const prompt = options.chatContext.history[options.chatContext.history.length - 1]?.content || "";
1120
+ const providerFactory = new ProviderFactory();
1121
+ const plan = await orchestrateTask({
1122
+ prompt,
1123
+ availableAgents: contributions,
1124
+ chatConfig,
1125
+ context: options.callContext.getProxy(),
1126
+ executeCompletion: (messages, cfg) => streamToText(messages, cfg, providerFactory)
1127
+ });
1128
+ const workspace = new TaskWorkspace(`wf-${Date.now()}`, plan);
1129
+ const byRole = new Map(contributions.map((c) => [c.role, c]));
1130
+ const combined = (await new TaskRunner(async (step, ws, _opts) => {
1131
+ const content = (await executeAgent(byRole.get(step.role) || contributions[0], [...options.chatContext.history, {
1132
+ role: "user",
1133
+ content: step.subTask
1134
+ }], results.sharedState, chatConfig, options, results))?.content || "";
1135
+ return {
1136
+ id: step.produces[0] || `${step.id}-result`,
1137
+ type: "text",
1138
+ content,
1139
+ producedBy: step.role,
1140
+ createdAt: Date.now()
1141
+ };
1142
+ }).run(workspace, {
1143
+ prompt,
1144
+ chatConfig,
1145
+ callContext: options.callContext,
1146
+ signal: options.signal
1147
+ })).artifacts.map((a) => a.content).filter(Boolean).join("\n\n");
1148
+ if (combined) results.messages.set("orchestrator", {
1149
+ role: "assistant",
1150
+ content: combined
1151
+ });
1152
+ }
1153
+ };
1154
+ //#endregion
1155
+ //#region src/agents/reviewer.ts
1156
+ var REVIEWER_SYS_PROMPT = `You are a quality reviewer. Evaluate the provided artifact against the original task.
1157
+
1158
+ Respond with ONLY a JSON object:
1159
+ {
1160
+ "verdict": "approved" | "needs-revision",
1161
+ "score": 0-100,
1162
+ "notes": "<feedback for revision, empty if approved>"
1163
+ }`;
1164
+ async function reviewArtifact(options) {
1165
+ const messages = [{
1166
+ role: "system",
1167
+ content: REVIEWER_SYS_PROMPT
1168
+ }, {
1169
+ role: "user",
1170
+ content: `Original task: ${options.originalTask}\n\nArtifact to review:\n${options.artifact.content}`
1171
+ }];
1172
+ try {
1173
+ const jsonMatch = (await options.executeCompletion(messages, options.chatConfig)).match(/\{[\s\S]*\}/);
1174
+ if (!jsonMatch) throw new Error("No JSON in reviewer response");
1175
+ const parsed = JSON.parse(jsonMatch[0]);
1176
+ return {
1177
+ verdict: parsed.verdict === "approved" ? "approved" : "needs-revision",
1178
+ score: typeof parsed.score === "number" ? parsed.score : 50,
1179
+ notes: parsed.notes || ""
1180
+ };
1181
+ } catch {
1182
+ return {
1183
+ verdict: "approved",
1184
+ score: 70,
1185
+ notes: ""
1186
+ };
1187
+ }
1188
+ }
1189
+ //#endregion
1190
+ //#region src/workflows/review-workflow.ts
1191
+ /**
1192
+ * Review workflow: a producer agent creates an artifact, a reviewer evaluates it,
1193
+ * and the producer can revise up to maxRevisions times.
1194
+ */
1195
+ var ReviewWorkflowStrategy = class {
1196
+ async execute(contributions, options, results, executeAgent) {
1197
+ const chatConfig = options.chatConfig;
1198
+ if (!chatConfig) throw new Error("Chat config is required");
1199
+ const providerFactory = new ProviderFactory();
1200
+ const producer = contributions[0];
1201
+ const reviewer = contributions.find((c) => c.reviewerFor?.includes(producer.role));
1202
+ const maxRevisions = producer.maxRevisions ?? 2;
1203
+ let currentMessages = [...options.chatContext.history];
1204
+ let revisions = 0;
1205
+ while (revisions <= maxRevisions) {
1206
+ const finalMessage = await executeAgent(producer, currentMessages, results.sharedState, chatConfig, options, results);
1207
+ if (!finalMessage) break;
1208
+ if (!reviewer) {
1209
+ results.messages.set(producer.role, finalMessage);
1210
+ break;
1211
+ }
1212
+ const review = await reviewArtifact({
1213
+ artifact: {
1214
+ id: `draft-${revisions}`,
1215
+ type: "text",
1216
+ content: finalMessage.content,
1217
+ producedBy: producer.role,
1218
+ createdAt: Date.now()
1219
+ },
1220
+ originalTask: options.chatContext.history[options.chatContext.history.length - 1]?.content || "",
1221
+ chatConfig,
1222
+ executeCompletion: (messages, cfg) => streamToText(messages, cfg, providerFactory)
1223
+ });
1224
+ if (review.verdict === "approved" || revisions >= maxRevisions) {
1225
+ results.messages.set(producer.role, finalMessage);
1226
+ break;
1227
+ }
1228
+ currentMessages = [
1229
+ ...options.chatContext.history,
1230
+ finalMessage,
1231
+ {
1232
+ role: "user",
1233
+ content: `Please revise based on this feedback: ${review.notes}`
1234
+ }
1235
+ ];
1236
+ revisions++;
1237
+ }
1238
+ }
1239
+ };
1240
+ //#endregion
1241
+ //#region src/workflows/workflow-engine.ts
1242
+ var WorkflowEngine = class {
1243
+ constructor() {
1244
+ this.strategies = new Map([
1245
+ ["parallel", new ParallelWorkflowStrategy()],
1246
+ ["sequential", new SequentialWorkflowStrategy()],
1247
+ ["conditional", new ConditionalWorkflowStrategy()],
1248
+ ["pipeline", new PipelineWorkflowStrategy()],
1249
+ ["orchestrated", new OrchestratedWorkflowStrategy()],
1250
+ ["review", new ReviewWorkflowStrategy()]
1251
+ ]);
1252
+ }
1253
+ registerStrategy(name, strategy) {
1254
+ this.strategies.set(name, strategy);
1255
+ }
1256
+ async execute(contributions, options, executeAgent) {
1257
+ const workflowId = `workflow-${Date.now()}-${Math.random()}`;
1258
+ const execution = options.execution || "parallel";
1259
+ const results = {
1260
+ messages: /* @__PURE__ */ new Map(),
1261
+ sharedState: { ...options.sharedState || {} },
1262
+ errors: /* @__PURE__ */ new Map()
1263
+ };
1264
+ publish(TOPIC_AGENT_WORKFLOW_STARTED, {
1265
+ workflowId,
1266
+ options
1267
+ });
1268
+ try {
1269
+ const strategy = this.strategies.get(execution);
1270
+ if (!strategy) throw new Error(`Unknown workflow execution strategy: ${execution}`);
1271
+ await strategy.execute(contributions, options, results, executeAgent);
1272
+ publish(TOPIC_AGENT_WORKFLOW_COMPLETE, {
1273
+ workflowId,
1274
+ results
1275
+ });
1276
+ return results;
1277
+ } catch (error) {
1278
+ const err = error instanceof Error ? error : new Error(String(error));
1279
+ publish(TOPIC_AGENT_WORKFLOW_ERROR, {
1280
+ workflowId,
1281
+ error: err
1282
+ });
1283
+ throw err;
1284
+ }
1285
+ }
1286
+ };
1287
+ //#endregion
1288
+ //#region src/tools/token-estimator.ts
1289
+ var TokenEstimator = class {
1290
+ static {
1291
+ this.AVERAGE_CHARS_PER_TOKEN = 4;
1292
+ }
1293
+ static {
1294
+ this.TOOL_DEFINITION_OVERHEAD = 50;
1295
+ }
1296
+ static {
1297
+ this.TOOL_CALL_OVERHEAD = 10;
1298
+ }
1299
+ static {
1300
+ this.MESSAGE_OVERHEAD = 4;
1301
+ }
1302
+ static estimateTokens(text) {
1303
+ if (text == null) return 0;
1304
+ let raw = "";
1305
+ if (typeof text === "string") raw = text;
1306
+ else if (typeof text === "number" || typeof text === "boolean" || typeof text === "bigint") raw = String(text);
1307
+ else if (typeof text === "object") try {
1308
+ raw = JSON.stringify(text);
1309
+ } catch {
1310
+ return 0;
1311
+ }
1312
+ else return 0;
1313
+ const trimmed = raw.trim();
1314
+ if (!trimmed) return 0;
1315
+ return Math.max(1, Math.ceil(trimmed.length / this.AVERAGE_CHARS_PER_TOKEN + trimmed.split(/\s+/).filter((w) => w.length > 0).length * .3));
1316
+ }
1317
+ static estimateMessageTokens(message) {
1318
+ let tokens = this.MESSAGE_OVERHEAD;
1319
+ if (message.content) tokens += this.estimateTokens(message.content);
1320
+ if (message.role) tokens += this.estimateTokens(message.role);
1321
+ if (message.tool_calls) for (const tc of message.tool_calls) tokens += this.estimateTokens(tc.function.name || "") + this.estimateTokens(tc.function.arguments || "{}") + this.TOOL_CALL_OVERHEAD;
1322
+ if (message.tool_call_id) tokens += this.estimateTokens(message.tool_call_id);
1323
+ return tokens;
1324
+ }
1325
+ static estimatePromptTokens(messages, tools) {
1326
+ let total = messages.reduce((sum, m) => sum + this.estimateMessageTokens(m), 0);
1327
+ if (tools?.length) for (const tool of tools) {
1328
+ total += this.TOOL_DEFINITION_OVERHEAD;
1329
+ total += this.estimateTokens(tool.function.name || "");
1330
+ total += this.estimateTokens(tool.function.description || "");
1331
+ if (tool.function.parameters) total += this.estimateTokens(JSON.stringify(tool.function.parameters));
1332
+ }
1333
+ return total;
1334
+ }
1335
+ static estimateCompletionTokens(content, toolCalls) {
1336
+ let tokens = this.estimateTokens(content);
1337
+ if (toolCalls?.length) for (const tc of toolCalls) tokens += this.TOOL_CALL_OVERHEAD + this.estimateTokens(tc.function?.name || "") + this.estimateTokens(tc.function?.arguments || "{}");
1338
+ return tokens;
1339
+ }
1340
+ };
1341
+ //#endregion
1342
+ //#region src/service/token-usage-tracker.ts
1343
+ var TOKEN_USAGE_KEY = "ai_token_usage";
1344
+ var EMPTY_USAGE = {
1345
+ promptTokens: 0,
1346
+ completionTokens: 0,
1347
+ totalTokens: 0,
1348
+ requestCount: 0
1349
+ };
1350
+ var TokenUsageTracker = class {
1351
+ constructor() {
1352
+ this.data = null;
1353
+ this.loadPromise = null;
1354
+ }
1355
+ async loadData() {
1356
+ if (this.data) return this.data;
1357
+ if (this.loadPromise) return this.loadPromise;
1358
+ this.loadPromise = (async () => {
1359
+ const stored = await persistenceService.getObject(TOKEN_USAGE_KEY);
1360
+ this.data = stored ? stored : {
1361
+ providers: {},
1362
+ total: { ...EMPTY_USAGE },
1363
+ lastUpdated: Date.now()
1364
+ };
1365
+ if (!this.data) {
1366
+ this.data = {
1367
+ providers: {},
1368
+ total: { ...EMPTY_USAGE },
1369
+ lastUpdated: Date.now()
1370
+ };
1371
+ await this.saveData();
1372
+ }
1373
+ this.loadPromise = null;
1374
+ return this.data;
1375
+ })();
1376
+ return this.loadPromise;
1377
+ }
1378
+ async saveData() {
1379
+ if (!this.data) return;
1380
+ this.data.lastUpdated = Date.now();
1381
+ await persistenceService.persistObject(TOKEN_USAGE_KEY, this.data);
1382
+ }
1383
+ async recordUsage(providerName, usage) {
1384
+ await this.loadData();
1385
+ if (!this.data) return;
1386
+ this.data.providers[providerName] ??= { ...EMPTY_USAGE };
1387
+ const provider = this.data.providers[providerName];
1388
+ provider.promptTokens += usage.promptTokens;
1389
+ provider.completionTokens += usage.completionTokens;
1390
+ provider.totalTokens += usage.totalTokens;
1391
+ provider.requestCount += 1;
1392
+ this.data.total.promptTokens += usage.promptTokens;
1393
+ this.data.total.completionTokens += usage.completionTokens;
1394
+ this.data.total.totalTokens += usage.totalTokens;
1395
+ this.data.total.requestCount += 1;
1396
+ await this.saveData();
1397
+ }
1398
+ async getProviderUsage(providerName) {
1399
+ await this.loadData();
1400
+ return this.data?.providers[providerName] || null;
1401
+ }
1402
+ async getAllProviderUsage() {
1403
+ await this.loadData();
1404
+ return this.data?.providers || {};
1405
+ }
1406
+ async getTotalUsage() {
1407
+ await this.loadData();
1408
+ return this.data?.total || { ...EMPTY_USAGE };
1409
+ }
1410
+ async reset() {
1411
+ this.data = {
1412
+ providers: {},
1413
+ total: { ...EMPTY_USAGE },
1414
+ lastUpdated: Date.now()
1415
+ };
1416
+ await this.saveData();
1417
+ }
1418
+ async resetProvider(providerName) {
1419
+ await this.loadData();
1420
+ if (!this.data) return;
1421
+ const provider = this.data.providers[providerName];
1422
+ if (!provider) return;
1423
+ this.data.total.promptTokens -= provider.promptTokens;
1424
+ this.data.total.completionTokens -= provider.completionTokens;
1425
+ this.data.total.totalTokens -= provider.totalTokens;
1426
+ this.data.total.requestCount -= provider.requestCount;
1427
+ delete this.data.providers[providerName];
1428
+ await this.saveData();
1429
+ }
1430
+ };
1431
+ var tokenUsageTracker = new TokenUsageTracker();
1432
+ //#endregion
1433
+ //#region src/service/ai-service.ts
1434
+ var AIService = class {
1435
+ constructor() {
1436
+ this.activeRequests = /* @__PURE__ */ new Map();
1437
+ this.activeTasks = /* @__PURE__ */ new Map();
1438
+ this.toolRegistry = new ToolRegistry();
1439
+ this.providerFactory = new ProviderFactory();
1440
+ this.agentRegistry = new AgentRegistry();
1441
+ this._promptBuilder = new PromptBuilder(this.toolRegistry);
1442
+ this.messageProcessor = new MessageProcessorService();
1443
+ this.toolExecutor = new ToolExecutor();
1444
+ this.workflowEngine = new WorkflowEngine();
1445
+ subscribe(TOPIC_SETTINGS_CHANGED, () => {
1446
+ this.aiConfig = void 0;
1447
+ this.configCheckPromise = void 0;
1448
+ this.checkAIConfig().then();
1449
+ });
1450
+ this.checkAIConfig().then();
1451
+ }
1452
+ get promptBuilder() {
1453
+ return this._promptBuilder;
1454
+ }
1455
+ getAgentContributions() {
1456
+ return this.agentRegistry.getAgentContributions();
1457
+ }
1458
+ async getProviders() {
1459
+ await this.checkAIConfig();
1460
+ return this.aiConfig?.providers || [];
1461
+ }
1462
+ async getDefaultProvider() {
1463
+ await this.checkAIConfig();
1464
+ const providers = await this.getProviders();
1465
+ if (this.aiConfig?.defaultProvider) {
1466
+ const config = providers.find((p) => p.name === this.aiConfig?.defaultProvider);
1467
+ if (config) return config;
1468
+ }
1469
+ return providers[0];
1470
+ }
1471
+ async setDefaultProvider(defaultProviderName) {
1472
+ await this.checkAIConfig();
1473
+ if (this.aiConfig) {
1474
+ this.aiConfig.defaultProvider = defaultProviderName;
1475
+ await appSettings.set(KEY_AI_CONFIG, this.aiConfig);
1476
+ }
1477
+ return this.getDefaultProvider();
1478
+ }
1479
+ createMessage(prompt) {
1480
+ return {
1481
+ role: "user",
1482
+ content: prompt
1483
+ };
1484
+ }
1485
+ registerStreamingFetcher(provider) {
1486
+ this.providerFactory.registerProvider(provider);
1487
+ }
1488
+ getContributedProviders() {
1489
+ return contributionRegistry.getContributions(CID_CHAT_PROVIDERS).map((c) => c.provider);
1490
+ }
1491
+ mergeProviders(existing, contributed) {
1492
+ const existingNames = new Set(existing.map((p) => p.name));
1493
+ const missing = contributed.filter((p) => !existingNames.has(p.name));
1494
+ return missing.length > 0 ? [...existing, ...missing] : existing;
1495
+ }
1496
+ async createInitialConfig() {
1497
+ const initialConfig = {
1498
+ ...AI_CONFIG_TEMPLATE,
1499
+ providers: this.getContributedProviders()
1500
+ };
1501
+ await appSettings.set(KEY_AI_CONFIG, initialConfig);
1502
+ return appSettings.get(KEY_AI_CONFIG);
1503
+ }
1504
+ async updateConfigWithMissingProviders(config) {
1505
+ const merged = this.mergeProviders(config.providers, this.getContributedProviders());
1506
+ if (merged.length === config.providers.length) return config;
1507
+ const updated = {
1508
+ ...config,
1509
+ providers: merged
1510
+ };
1511
+ await appSettings.set(KEY_AI_CONFIG, updated);
1512
+ return updated;
1513
+ }
1514
+ async checkAIConfig() {
1515
+ if (this.aiConfig) return;
1516
+ if (this.configCheckPromise) return this.configCheckPromise;
1517
+ this.configCheckPromise = this.performConfigCheck();
1518
+ return this.configCheckPromise;
1519
+ }
1520
+ async performConfigCheck() {
1521
+ try {
1522
+ this.aiConfig = await appSettings.get(KEY_AI_CONFIG);
1523
+ this.aiConfig = this.aiConfig ? await this.updateConfigWithMissingProviders(this.aiConfig) : await this.createInitialConfig();
1524
+ publish(TOPIC_AICONFIG_CHANGED, this.aiConfig);
1525
+ } finally {
1526
+ this.configCheckPromise = void 0;
1527
+ }
1528
+ }
1529
+ createAgentContext(sharedState, callContext, additional = {}) {
1530
+ return {
1531
+ ...sharedState,
1532
+ ...callContext.getProxy(),
1533
+ ...additional
1534
+ };
1535
+ }
1536
+ async *streamCompletion(options) {
1537
+ const requestId = `${Date.now()}-${Math.random()}`;
1538
+ const abortController = new AbortController();
1539
+ this.activeRequests.set(requestId, abortController);
1540
+ if (options.signal) options.signal.addEventListener("abort", () => abortController.abort());
1541
+ const effectiveSignal = options.signal || abortController.signal;
1542
+ try {
1543
+ options.onStatus?.("starting");
1544
+ publish(TOPIC_AI_STREAM_STARTED, {
1545
+ requestId,
1546
+ options
1547
+ });
1548
+ const chatConfig = options.chatConfig || await this.getDefaultProvider();
1549
+ const messages = sanitizeMessagesForAPI(options.chatContext.history);
1550
+ const provider = this.providerFactory.getProvider(chatConfig);
1551
+ const accumulator = this.toolExecutor.createToolCallAccumulator();
1552
+ let accumulatedContent = "";
1553
+ let accumulatedRole = "assistant";
1554
+ let tokenUsage;
1555
+ for await (const chunk of provider.stream({
1556
+ model: chatConfig.model,
1557
+ messages,
1558
+ chatConfig,
1559
+ tools: options.tools,
1560
+ signal: effectiveSignal
1561
+ })) {
1562
+ if (chunk.type === "error") {
1563
+ options.onStatus?.("error");
1564
+ publish(TOPIC_AI_STREAM_ERROR, {
1565
+ requestId,
1566
+ chunk
1567
+ });
1568
+ yield chunk;
1569
+ break;
1570
+ }
1571
+ if (chunk.type === "token") {
1572
+ accumulator.processChunk(chunk);
1573
+ if (!chunk.toolCalls?.length) accumulatedContent += chunk.content;
1574
+ if (chunk.message?.role) accumulatedRole = chunk.message.role;
1575
+ if (chunk.content) options.onToken?.(chunk.content);
1576
+ options.onStatus?.("streaming");
1577
+ options.onProgress?.({ received: accumulatedContent.length });
1578
+ publish(TOPIC_AI_STREAM_CHUNK, {
1579
+ requestId,
1580
+ chunk
1581
+ });
1582
+ yield chunk;
1583
+ } else if (chunk.type === "done") {
1584
+ if (chunk.metadata?.usage) tokenUsage = chunk.metadata.usage;
1585
+ options.onStatus?.("complete");
1586
+ publish(TOPIC_AI_STREAM_COMPLETE, { requestId });
1587
+ yield chunk;
1588
+ break;
1589
+ } else yield chunk;
1590
+ }
1591
+ const finalToolCalls = accumulator.getFinalToolCalls();
1592
+ const finalMessage = {
1593
+ role: accumulatedRole,
1594
+ content: accumulatedContent,
1595
+ ...finalToolCalls.length > 0 && { toolCalls: finalToolCalls }
1596
+ };
1597
+ if (!tokenUsage) {
1598
+ const promptTokens = TokenEstimator.estimatePromptTokens(messages, options.tools);
1599
+ const completionTokens = TokenEstimator.estimateCompletionTokens(accumulatedContent, finalToolCalls);
1600
+ tokenUsage = {
1601
+ promptTokens,
1602
+ completionTokens,
1603
+ totalTokens: promptTokens + completionTokens,
1604
+ estimated: true
1605
+ };
1606
+ }
1607
+ tokenUsageTracker.recordUsage(chatConfig.name, tokenUsage).catch((err) => {
1608
+ logger.error(`Failed to record token usage: ${err instanceof Error ? err.message : String(err)}`);
1609
+ });
1610
+ return {
1611
+ message: finalMessage,
1612
+ tokenUsage
1613
+ };
1614
+ } catch (error) {
1615
+ if (error instanceof Error && error.name === "AbortError") {
1616
+ options.onStatus?.("error");
1617
+ publish(TOPIC_AI_STREAM_ERROR, {
1618
+ requestId,
1619
+ error: "Request cancelled"
1620
+ });
1621
+ throw error;
1622
+ }
1623
+ options.onStatus?.("error");
1624
+ const errorMessage = error instanceof Error ? error.message : String(error);
1625
+ publish(TOPIC_AI_STREAM_ERROR, {
1626
+ requestId,
1627
+ error: errorMessage
1628
+ });
1629
+ yield {
1630
+ type: "error",
1631
+ content: errorMessage,
1632
+ metadata: { error }
1633
+ };
1634
+ throw error;
1635
+ } finally {
1636
+ this.activeRequests.delete(requestId);
1637
+ }
1638
+ }
1639
+ async handleStreamingPromptDirect(options) {
1640
+ const stream = this.streamCompletion(options);
1641
+ let lastValue;
1642
+ while (true) {
1643
+ lastValue = await stream.next();
1644
+ if (lastValue.done) return lastValue.value.message;
1645
+ const chunk = lastValue.value;
1646
+ if (chunk.type === "error") throw new Error(chunk.content);
1647
+ if (chunk.type === "done") {
1648
+ const final = await stream.next();
1649
+ if (final.done && final.value) return final.value.message;
1650
+ if (!final.done) continue;
1651
+ throw new Error("Stream completed without return value");
1652
+ }
1653
+ }
1654
+ }
1655
+ async handleStreamingPrompt(options) {
1656
+ const callContext = options.callContext || rootContext.createChild({});
1657
+ const agentContext = this.createAgentContext({}, callContext, { userPrompt: options.chatContext.history[options.chatContext.history.length - 1]?.content || "" });
1658
+ const matchingAgents = this.agentRegistry.getMatchingAgents(agentContext);
1659
+ const roles = matchingAgents.length > 0 ? matchingAgents.map((a) => a.role) : ["assistant"];
1660
+ const workflowResult = await this.executeAgentWorkflow({
1661
+ chatContext: options.chatContext,
1662
+ chatConfig: options.chatConfig,
1663
+ callContext,
1664
+ execution: "parallel",
1665
+ stream: options.stream,
1666
+ signal: options.signal,
1667
+ onToken: (_role, token) => options.onToken?.(token),
1668
+ onStatus: (_role, status) => options.onStatus?.(status),
1669
+ roles
1670
+ });
1671
+ const messages = Array.from(workflowResult.messages.values());
1672
+ return messages.length === 1 ? messages[0] : messages;
1673
+ }
1674
+ cancelRequest(requestId) {
1675
+ const controller = this.activeRequests.get(requestId);
1676
+ if (!controller) return false;
1677
+ controller.abort();
1678
+ this.activeRequests.delete(requestId);
1679
+ return true;
1680
+ }
1681
+ async executeAgentWorkflow(options) {
1682
+ const agentContext = this.createAgentContext(options.sharedState || {}, options.callContext);
1683
+ const matchingAgents = this.agentRegistry.getMatchingAgents(agentContext, options.roles);
1684
+ return this.workflowEngine.execute(matchingAgents, options, (contrib, messages, sharedState, chatConfig, workflowOptions, results) => this.executeAgent(contrib, messages, sharedState, chatConfig, workflowOptions, results));
1685
+ }
1686
+ async executeAgent(contrib, messages, sharedState, chatConfig, options, results) {
1687
+ options.onAgentStart?.(contrib.role);
1688
+ const agentContext = this.createAgentContext(sharedState, options.callContext, { userPrompt: messages[messages.length - 1]?.content || "" });
1689
+ const { messages: preparedMessages, tools } = await this._promptBuilder.build(contrib, messages, agentContext, contrib.hooks);
1690
+ const chatMessages = preparedMessages.map((msg) => {
1691
+ const chatMsg = {
1692
+ role: msg.role,
1693
+ content: msg.content
1694
+ };
1695
+ if (msg.tool_call_id) chatMsg.tool_call_id = msg.tool_call_id;
1696
+ if (msg.tool_calls) chatMsg.tool_calls = msg.tool_calls;
1697
+ return chatMsg;
1698
+ });
1699
+ let rawMessage = await this.handleStreamingPromptDirect({
1700
+ chatContext: { history: chatMessages },
1701
+ chatConfig,
1702
+ callContext: options.callContext,
1703
+ stream: options.stream ?? true,
1704
+ signal: options.signal,
1705
+ onToken: options.onToken ? (token) => options.onToken(contrib.role, token) : void 0,
1706
+ tools
1707
+ });
1708
+ let toolCallIteration = 0;
1709
+ const conversationHistory = [...preparedMessages];
1710
+ while (rawMessage.toolCalls && rawMessage.toolCalls.length > 0) {
1711
+ toolCallIteration++;
1712
+ if (toolCallIteration > 10) {
1713
+ logger.warn("Max tool call iterations reached");
1714
+ break;
1715
+ }
1716
+ let toolResults;
1717
+ if (options.requireToolApproval && options.onToolApprovalRequest) {
1718
+ const toolCallDescriptions = rawMessage.toolCalls.map((tc) => {
1719
+ let parsedArgs = {};
1720
+ try {
1721
+ parsedArgs = JSON.parse(tc.function.arguments || "{}");
1722
+ } catch {}
1723
+ return `${tc.function.name}(${Object.entries(parsedArgs).map(([k, v]) => `${k}=${v}`).join(", ")})`;
1724
+ }).join(", ");
1725
+ const approvalRequest = {
1726
+ toolCalls: rawMessage.toolCalls,
1727
+ message: `The AI wants to execute: ${toolCallDescriptions}`
1728
+ };
1729
+ if (!await options.onToolApprovalRequest(contrib.role, approvalRequest)) toolResults = rawMessage.toolCalls.map((tc) => ({
1730
+ id: tc.id,
1731
+ result: {
1732
+ success: false,
1733
+ message: "Tool execution cancelled by user",
1734
+ cancelled: true
1735
+ }
1736
+ }));
1737
+ else toolResults = await this.toolExecutor.executeToolCalls(rawMessage.toolCalls, agentContext);
1738
+ } else toolResults = await this.toolExecutor.executeToolCalls(rawMessage.toolCalls, agentContext);
1739
+ const toolMessages = toolResults.map((tr) => ({
1740
+ role: "tool",
1741
+ content: tr.error ? JSON.stringify({ error: tr.error }) : JSON.stringify(tr.result),
1742
+ tool_call_id: tr.id
1743
+ }));
1744
+ const assistantMessage = {
1745
+ role: "assistant",
1746
+ content: rawMessage.content || ""
1747
+ };
1748
+ if (rawMessage.toolCalls?.length) assistantMessage.tool_calls = rawMessage.toolCalls.filter((tc) => tc.function.name?.trim()).map((tc) => ({
1749
+ id: tc.id,
1750
+ type: tc.type,
1751
+ function: {
1752
+ name: tc.function.name,
1753
+ arguments: tc.function.arguments || "{}"
1754
+ }
1755
+ }));
1756
+ conversationHistory.push(assistantMessage, ...toolMessages);
1757
+ rawMessage = await this.handleStreamingPromptDirect({
1758
+ chatContext: { history: conversationHistory.map((m) => ({
1759
+ role: m.role,
1760
+ content: m.content,
1761
+ ...m.tool_call_id && { tool_call_id: m.tool_call_id },
1762
+ ...m.tool_calls && { tool_calls: m.tool_calls }
1763
+ })) },
1764
+ chatConfig,
1765
+ callContext: options.callContext,
1766
+ stream: options.stream ?? true,
1767
+ signal: options.signal,
1768
+ tools
1769
+ });
1770
+ if (rawMessage.content?.trim() && !rawMessage.toolCalls?.length) break;
1771
+ }
1772
+ const processedMessage = await this.messageProcessor.process(rawMessage, contrib, this.createAgentContext(sharedState, options.callContext, { message: rawMessage }));
1773
+ if (contrib.hooks?.afterReceive) await contrib.hooks.afterReceive(processedMessage, this.createAgentContext(sharedState, options.callContext));
1774
+ const finalMessage = {
1775
+ role: contrib.role,
1776
+ content: processedMessage.content
1777
+ };
1778
+ results.messages.set(contrib.role, finalMessage);
1779
+ options.onAgentComplete?.(contrib.role, finalMessage);
1780
+ return finalMessage;
1781
+ }
1782
+ async planTask(prompt, context) {
1783
+ const chatConfig = await this.getDefaultProvider();
1784
+ const contributions = this.agentRegistry.getAgentContributions();
1785
+ rootContext.createChild({});
1786
+ return orchestrateTask({
1787
+ prompt,
1788
+ availableAgents: contributions,
1789
+ chatConfig,
1790
+ context,
1791
+ executeCompletion: (messages, cfg) => streamToText(messages, cfg, this.providerFactory)
1792
+ });
1793
+ }
1794
+ async executeTask(options) {
1795
+ const chatConfig = options.chatConfig || await this.getDefaultProvider();
1796
+ const callContext = options.callContext || rootContext.createChild({});
1797
+ const contributions = this.agentRegistry.getAgentContributions();
1798
+ const plan = await this.planTask(options.prompt, callContext.getProxy());
1799
+ options.onPlanReady?.(plan);
1800
+ const workspace = new TaskWorkspace(`task-${Date.now()}`, plan);
1801
+ await taskCheckpointService.registerCheckpoint(workspace.taskId);
1802
+ const abortController = new AbortController();
1803
+ this.activeTasks.set(workspace.taskId, abortController);
1804
+ const taskOptions = {
1805
+ ...options,
1806
+ signal: options.signal ?? abortController.signal
1807
+ };
1808
+ const byRole = new Map(contributions.map((c) => [c.role, c]));
1809
+ const runner = new TaskRunner(this.createStepExecutor(byRole, contributions, chatConfig, callContext, taskOptions));
1810
+ try {
1811
+ const result = await runner.run(workspace, taskOptions);
1812
+ await taskCheckpointService.unregisterCheckpoint(workspace.taskId);
1813
+ return result;
1814
+ } finally {
1815
+ this.activeTasks.delete(workspace.taskId);
1816
+ }
1817
+ }
1818
+ async resumeTask(taskId, options) {
1819
+ const workspace = await taskCheckpointService.restore(taskId);
1820
+ if (!workspace) throw new Error(`No checkpoint found for task ${taskId}`);
1821
+ const chatConfig = options.chatConfig || await this.getDefaultProvider();
1822
+ const callContext = options.callContext || rootContext.createChild({});
1823
+ const contributions = this.agentRegistry.getAgentContributions();
1824
+ const byRole = new Map(contributions.map((c) => [c.role, c]));
1825
+ return new TaskRunner(this.createStepExecutor(byRole, contributions, chatConfig, callContext, options)).run(workspace, options);
1826
+ }
1827
+ createStepExecutor(byRole, contributions, chatConfig, callContext, options) {
1828
+ return async (step, _ws, _opts) => {
1829
+ const contrib = byRole.get(step.role) || contributions[0];
1830
+ const stepMessages = [...options.chatContext?.history || [], {
1831
+ role: "user",
1832
+ content: step.subTask
1833
+ }];
1834
+ const msg = (await this.workflowEngine.execute([contrib], {
1835
+ chatContext: { history: stepMessages },
1836
+ chatConfig,
1837
+ callContext,
1838
+ execution: "parallel",
1839
+ stream: true,
1840
+ signal: options.signal,
1841
+ roles: [contrib.role]
1842
+ }, (c, msgs, state, cfg, wopts, results) => this.executeAgent(c, msgs, state, cfg, wopts, results))).messages.get(contrib.role);
1843
+ return {
1844
+ id: step.produces[0] || `${step.id}-result`,
1845
+ type: "text",
1846
+ content: msg?.content || "",
1847
+ producedBy: step.role,
1848
+ createdAt: Date.now()
1849
+ };
1850
+ };
1851
+ }
1852
+ cancelTask(taskId) {
1853
+ const controller = this.activeTasks.get(taskId);
1854
+ if (controller) {
1855
+ controller.abort();
1856
+ this.activeTasks.delete(taskId);
1857
+ }
1858
+ }
1859
+ };
1860
+ var aiService = new AIService();
1861
+ //#endregion
1862
+ export { CID_AGENTS as A, TOPIC_AICONFIG_CHANGED as B, BaseProvider as C, SSEParser as D, OllamaParser as E, MAX_RECENT_TOOL_CALLS as F, TOPIC_AI_STREAM_COMPLETE as H, MAX_TOOL_ITERATIONS as I, TOPIC_AGENT_WORKFLOW_COMPLETE as L, CID_PROMPT_ENHANCERS as M, DEFAULT_AGENT_ROLE as N, StreamParser as O, KEY_AI_CONFIG as P, TOPIC_AGENT_WORKFLOW_ERROR as R, OpenAIProvider as S, streamToText as T, TOPIC_AI_STREAM_ERROR as U, TOPIC_AI_STREAM_CHUNK as V, TOPIC_AI_STREAM_STARTED as W, MessageProcessorService as _, WorkflowEngine as a, ProviderFactory as b, PipelineWorkflowStrategy as c, BaseSequentialWorkflow as d, ParallelWorkflowStrategy as f, ToolCallAccumulator as g, sanitizeFunctionName as h, TokenEstimator as i, CID_CHAT_PROVIDERS as j, AI_CONFIG_TEMPLATE as k, ConditionalWorkflowStrategy as l, ToolExecutor as m, EMPTY_USAGE as n, ReviewWorkflowStrategy as o, ToolRegistry as p, tokenUsageTracker as r, OrchestratedWorkflowStrategy as s, aiService as t, SequentialWorkflowStrategy as u, PromptBuilder as v, extractBaseUrl as w, OllamaProvider as x, AgentRegistry as y, TOPIC_AGENT_WORKFLOW_STARTED as z };
1863
+
1864
+ //# sourceMappingURL=ai-service-CQaQJORq.js.map