@nuvin/nuvin-core 1.1.2 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/VERSION +2 -2
- package/dist/index.d.ts +127 -10
- package/dist/index.js +843 -478
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -36,6 +36,345 @@ var AgentEventTypes = {
|
|
|
36
36
|
SubAgentCompleted: "sub_agent_completed"
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
+
// metrics.ts
|
|
40
|
+
var createEmptySnapshot = () => ({
|
|
41
|
+
totalTokens: 0,
|
|
42
|
+
totalPromptTokens: 0,
|
|
43
|
+
totalCompletionTokens: 0,
|
|
44
|
+
totalCachedTokens: 0,
|
|
45
|
+
totalReasoningTokens: 0,
|
|
46
|
+
requestCount: 0,
|
|
47
|
+
llmCallCount: 0,
|
|
48
|
+
toolCallCount: 0,
|
|
49
|
+
totalTimeMs: 0,
|
|
50
|
+
totalCost: 0,
|
|
51
|
+
currentTokens: 0,
|
|
52
|
+
currentPromptTokens: 0,
|
|
53
|
+
currentCompletionTokens: 0,
|
|
54
|
+
currentCachedTokens: 0,
|
|
55
|
+
currentCost: 0
|
|
56
|
+
});
|
|
57
|
+
var NoopMetricsPort = class {
|
|
58
|
+
recordLLMCall(_usage, _cost) {
|
|
59
|
+
}
|
|
60
|
+
recordToolCall() {
|
|
61
|
+
}
|
|
62
|
+
recordRequestComplete(_responseTimeMs) {
|
|
63
|
+
}
|
|
64
|
+
setContextWindow(_limit, _usage) {
|
|
65
|
+
}
|
|
66
|
+
reset() {
|
|
67
|
+
}
|
|
68
|
+
getSnapshot() {
|
|
69
|
+
return createEmptySnapshot();
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
var InMemoryMetricsPort = class {
|
|
73
|
+
snapshot = createEmptySnapshot();
|
|
74
|
+
onChange;
|
|
75
|
+
constructor(onChange) {
|
|
76
|
+
this.onChange = onChange;
|
|
77
|
+
}
|
|
78
|
+
emit() {
|
|
79
|
+
this.onChange?.({ ...this.snapshot });
|
|
80
|
+
}
|
|
81
|
+
recordLLMCall(usage, cost) {
|
|
82
|
+
const prompt = usage.prompt_tokens ?? 0;
|
|
83
|
+
const completion = usage.completion_tokens ?? 0;
|
|
84
|
+
const total = usage.total_tokens ?? prompt + completion;
|
|
85
|
+
const cached = usage.prompt_tokens_details?.cached_tokens ?? 0;
|
|
86
|
+
const reasoning = usage.reasoning_tokens ?? usage.completion_tokens_details?.reasoning_tokens ?? 0;
|
|
87
|
+
const actualCost = cost ?? usage.cost ?? 0;
|
|
88
|
+
this.snapshot.totalTokens += total;
|
|
89
|
+
this.snapshot.totalPromptTokens += prompt;
|
|
90
|
+
this.snapshot.totalCompletionTokens += completion;
|
|
91
|
+
this.snapshot.totalCachedTokens += cached;
|
|
92
|
+
this.snapshot.totalReasoningTokens += reasoning;
|
|
93
|
+
this.snapshot.totalCost += actualCost;
|
|
94
|
+
this.snapshot.llmCallCount += 1;
|
|
95
|
+
this.snapshot.currentTokens = total;
|
|
96
|
+
this.snapshot.currentPromptTokens = prompt;
|
|
97
|
+
this.snapshot.currentCompletionTokens = completion;
|
|
98
|
+
this.snapshot.currentCachedTokens = cached;
|
|
99
|
+
this.snapshot.currentCost = actualCost;
|
|
100
|
+
this.emit();
|
|
101
|
+
}
|
|
102
|
+
recordToolCall() {
|
|
103
|
+
this.snapshot.toolCallCount += 1;
|
|
104
|
+
this.emit();
|
|
105
|
+
}
|
|
106
|
+
recordRequestComplete(responseTimeMs) {
|
|
107
|
+
this.snapshot.requestCount += 1;
|
|
108
|
+
this.snapshot.totalTimeMs += responseTimeMs;
|
|
109
|
+
this.emit();
|
|
110
|
+
}
|
|
111
|
+
setContextWindow(limit, usage) {
|
|
112
|
+
this.snapshot.contextWindowLimit = limit;
|
|
113
|
+
this.snapshot.contextWindowUsage = usage;
|
|
114
|
+
this.emit();
|
|
115
|
+
}
|
|
116
|
+
reset() {
|
|
117
|
+
this.snapshot = createEmptySnapshot();
|
|
118
|
+
this.emit();
|
|
119
|
+
}
|
|
120
|
+
getSnapshot() {
|
|
121
|
+
return { ...this.snapshot };
|
|
122
|
+
}
|
|
123
|
+
setOnChange(fn) {
|
|
124
|
+
this.onChange = fn;
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// clock.ts
|
|
129
|
+
var SystemClock = class {
|
|
130
|
+
now() {
|
|
131
|
+
return Date.now();
|
|
132
|
+
}
|
|
133
|
+
iso(dateMs) {
|
|
134
|
+
return new Date(dateMs ?? Date.now()).toISOString();
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// id.ts
|
|
139
|
+
var SimpleId = class {
|
|
140
|
+
uuid() {
|
|
141
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
142
|
+
const r = Math.random() * 16 | 0;
|
|
143
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
144
|
+
return v.toString(16);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// cost.ts
|
|
150
|
+
var SimpleCost = class {
|
|
151
|
+
estimate(_model, usage) {
|
|
152
|
+
return usage?.cost;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// reminders.ts
|
|
157
|
+
var NoopReminders = class {
|
|
158
|
+
enhance(content, _opts) {
|
|
159
|
+
return [content];
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// context.ts
|
|
164
|
+
var toProviderContent = (content) => {
|
|
165
|
+
if (content === null || content === void 0) {
|
|
166
|
+
return "";
|
|
167
|
+
}
|
|
168
|
+
if (typeof content === "string") {
|
|
169
|
+
return content;
|
|
170
|
+
}
|
|
171
|
+
if (content.type === "parts") {
|
|
172
|
+
const providerParts = [];
|
|
173
|
+
for (const part of content.parts) {
|
|
174
|
+
if (part.type === "text") {
|
|
175
|
+
if (part.text.length > 0) {
|
|
176
|
+
providerParts.push({ type: "text", text: part.text });
|
|
177
|
+
}
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
const label = part.altText ?? (part.name ? `Image attachment: ${part.name}` : void 0);
|
|
181
|
+
if (label) {
|
|
182
|
+
providerParts.push({ type: "text", text: label });
|
|
183
|
+
}
|
|
184
|
+
const url = `data:${part.mimeType};base64,${part.data}`;
|
|
185
|
+
providerParts.push({ type: "image_url", image_url: { url } });
|
|
186
|
+
}
|
|
187
|
+
return providerParts.length > 0 ? providerParts : [];
|
|
188
|
+
}
|
|
189
|
+
return [];
|
|
190
|
+
};
|
|
191
|
+
var SimpleContextBuilder = class {
|
|
192
|
+
toProviderMessages(history, systemPrompt, newUserContent) {
|
|
193
|
+
const transformed = [];
|
|
194
|
+
for (const m of history) {
|
|
195
|
+
const providerContent = toProviderContent(m.content);
|
|
196
|
+
if (m.role === "user") {
|
|
197
|
+
transformed.push({ role: "user", content: providerContent ?? "" });
|
|
198
|
+
} else if (m.role === "assistant") {
|
|
199
|
+
if (m.tool_calls && m.tool_calls.length > 0) {
|
|
200
|
+
transformed.push({
|
|
201
|
+
...m,
|
|
202
|
+
role: "assistant",
|
|
203
|
+
content: providerContent ?? null,
|
|
204
|
+
tool_calls: m.tool_calls
|
|
205
|
+
});
|
|
206
|
+
} else {
|
|
207
|
+
transformed.push({
|
|
208
|
+
...m,
|
|
209
|
+
role: "assistant",
|
|
210
|
+
content: providerContent ?? ""
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
} else if (m.role === "tool") {
|
|
214
|
+
if (m.tool_call_id) {
|
|
215
|
+
transformed.push({
|
|
216
|
+
role: "tool",
|
|
217
|
+
content: typeof providerContent === "string" ? providerContent : providerContent ?? "",
|
|
218
|
+
tool_call_id: m.tool_call_id,
|
|
219
|
+
name: m.name
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const userMsgs = newUserContent.map((c) => ({
|
|
225
|
+
role: "user",
|
|
226
|
+
content: toProviderContent(c) ?? ""
|
|
227
|
+
}));
|
|
228
|
+
return [{ role: "system", content: systemPrompt }, ...transformed, ...userMsgs];
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
// persistent/memory.ts
|
|
233
|
+
var InMemoryMemory = class {
|
|
234
|
+
store = /* @__PURE__ */ new Map();
|
|
235
|
+
async get(key) {
|
|
236
|
+
return this.store.get(key) ?? [];
|
|
237
|
+
}
|
|
238
|
+
async set(key, items) {
|
|
239
|
+
this.store.set(key, [...items]);
|
|
240
|
+
}
|
|
241
|
+
async append(key, items) {
|
|
242
|
+
const existing = this.store.get(key) ?? [];
|
|
243
|
+
this.store.set(key, [...existing, ...items]);
|
|
244
|
+
}
|
|
245
|
+
async delete(key) {
|
|
246
|
+
this.store.delete(key);
|
|
247
|
+
}
|
|
248
|
+
async keys() {
|
|
249
|
+
return Array.from(this.store.keys());
|
|
250
|
+
}
|
|
251
|
+
async clear() {
|
|
252
|
+
this.store.clear();
|
|
253
|
+
}
|
|
254
|
+
async exportSnapshot() {
|
|
255
|
+
const snap = {};
|
|
256
|
+
for (const [k, v] of this.store.entries()) snap[k] = [...v];
|
|
257
|
+
return snap;
|
|
258
|
+
}
|
|
259
|
+
async importSnapshot(snapshot) {
|
|
260
|
+
this.store.clear();
|
|
261
|
+
for (const [k, v] of Object.entries(snapshot)) this.store.set(k, [...v]);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
var JsonFileMemoryPersistence = class {
|
|
265
|
+
constructor(filename = "history.json") {
|
|
266
|
+
this.filename = filename;
|
|
267
|
+
}
|
|
268
|
+
async load() {
|
|
269
|
+
try {
|
|
270
|
+
const fs10 = await import("fs");
|
|
271
|
+
if (!fs10.existsSync(this.filename)) return {};
|
|
272
|
+
const text = fs10.readFileSync(this.filename, "utf-8");
|
|
273
|
+
const data = JSON.parse(text);
|
|
274
|
+
return typeof data === "object" && data ? data : {};
|
|
275
|
+
} catch {
|
|
276
|
+
console.warn(`Failed to load memory from ${this.filename}`);
|
|
277
|
+
return {};
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
async save(snapshot) {
|
|
281
|
+
try {
|
|
282
|
+
const fs10 = await import("fs");
|
|
283
|
+
const path9 = await import("path");
|
|
284
|
+
const dir = path9.dirname(this.filename);
|
|
285
|
+
if (dir && dir !== "." && !fs10.existsSync(dir)) {
|
|
286
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
287
|
+
}
|
|
288
|
+
fs10.writeFileSync(this.filename, JSON.stringify(snapshot, null, 2), "utf-8");
|
|
289
|
+
} catch (err2) {
|
|
290
|
+
console.warn(`Failed to save memory to ${this.filename}`, err2);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
var PersistedMemory = class {
|
|
295
|
+
constructor(persistence) {
|
|
296
|
+
this.persistence = persistence;
|
|
297
|
+
}
|
|
298
|
+
inner = new InMemoryMemory();
|
|
299
|
+
initialized = false;
|
|
300
|
+
async ensureInitialized() {
|
|
301
|
+
if (this.initialized) return;
|
|
302
|
+
const snap = await this.persistence.load();
|
|
303
|
+
if (snap && typeof snap === "object") await this.inner.importSnapshot(snap);
|
|
304
|
+
this.initialized = true;
|
|
305
|
+
}
|
|
306
|
+
async save() {
|
|
307
|
+
const snap = await this.inner.exportSnapshot();
|
|
308
|
+
await this.persistence.save(snap);
|
|
309
|
+
}
|
|
310
|
+
async get(key) {
|
|
311
|
+
await this.ensureInitialized();
|
|
312
|
+
return this.inner.get(key);
|
|
313
|
+
}
|
|
314
|
+
async set(key, items) {
|
|
315
|
+
await this.ensureInitialized();
|
|
316
|
+
await this.inner.set(key, items);
|
|
317
|
+
await this.save();
|
|
318
|
+
}
|
|
319
|
+
async append(key, items) {
|
|
320
|
+
await this.ensureInitialized();
|
|
321
|
+
await this.inner.append(key, items);
|
|
322
|
+
await this.save();
|
|
323
|
+
}
|
|
324
|
+
async delete(key) {
|
|
325
|
+
await this.ensureInitialized();
|
|
326
|
+
await this.inner.delete(key);
|
|
327
|
+
await this.save();
|
|
328
|
+
}
|
|
329
|
+
async keys() {
|
|
330
|
+
await this.ensureInitialized();
|
|
331
|
+
return this.inner.keys();
|
|
332
|
+
}
|
|
333
|
+
async clear() {
|
|
334
|
+
await this.ensureInitialized();
|
|
335
|
+
await this.inner.clear();
|
|
336
|
+
await this.save();
|
|
337
|
+
}
|
|
338
|
+
async exportSnapshot() {
|
|
339
|
+
await this.ensureInitialized();
|
|
340
|
+
return this.inner.exportSnapshot();
|
|
341
|
+
}
|
|
342
|
+
async importSnapshot(snapshot) {
|
|
343
|
+
await this.ensureInitialized();
|
|
344
|
+
await this.inner.importSnapshot(snapshot);
|
|
345
|
+
await this.save();
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
// events.ts
|
|
350
|
+
var NoopEventPort = class {
|
|
351
|
+
emit(_event) {
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
var PersistingConsoleEventPort = class {
|
|
355
|
+
memory;
|
|
356
|
+
maxPerConversation;
|
|
357
|
+
writeQueue = Promise.resolve();
|
|
358
|
+
constructor(opts) {
|
|
359
|
+
this.memory = opts?.memory ?? new PersistedMemory(new JsonFileMemoryPersistence(opts?.filename || "events.json"));
|
|
360
|
+
this.maxPerConversation = opts?.maxPerConversation ?? 500;
|
|
361
|
+
}
|
|
362
|
+
async emit(event) {
|
|
363
|
+
this.writeQueue = this.writeQueue.then(async () => {
|
|
364
|
+
try {
|
|
365
|
+
const key = event?.conversationId ?? "default";
|
|
366
|
+
const existing = await this.memory.get(key);
|
|
367
|
+
const next = [...existing, { ...event }];
|
|
368
|
+
const max = this.maxPerConversation;
|
|
369
|
+
const trimmed = max > 0 && next.length > max ? next.slice(next.length - max) : next;
|
|
370
|
+
await this.memory.set(key, trimmed);
|
|
371
|
+
} catch {
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
return this.writeQueue;
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
|
|
39
378
|
// orchestrator.ts
|
|
40
379
|
var removeAttachmentTokens = (value, attachments) => {
|
|
41
380
|
return attachments.reduce((acc, attachment) => {
|
|
@@ -107,9 +446,29 @@ var resolveDisplayText = (text, attachments, provided) => {
|
|
|
107
446
|
var AgentOrchestrator = class {
|
|
108
447
|
constructor(cfg, deps) {
|
|
109
448
|
this.cfg = cfg;
|
|
110
|
-
this.
|
|
449
|
+
this.memory = deps.memory;
|
|
450
|
+
this.tools = deps.tools;
|
|
451
|
+
this.llm = deps.llm;
|
|
452
|
+
this.context = deps.context ?? this.context;
|
|
453
|
+
this.ids = deps.ids ?? this.ids;
|
|
454
|
+
this.clock = deps.clock ?? this.clock;
|
|
455
|
+
this.cost = deps.cost ?? this.cost;
|
|
456
|
+
this.reminders = deps.reminders ?? this.reminders;
|
|
457
|
+
this.metrics = deps.metrics ?? this.metrics;
|
|
458
|
+
this.events = deps.events ?? this.events;
|
|
111
459
|
}
|
|
112
460
|
pendingApprovals = /* @__PURE__ */ new Map();
|
|
461
|
+
context = new SimpleContextBuilder();
|
|
462
|
+
ids = new SimpleId();
|
|
463
|
+
clock = new SystemClock();
|
|
464
|
+
cost = new SimpleCost();
|
|
465
|
+
reminders = new NoopReminders();
|
|
466
|
+
// private llm: LLMPort;
|
|
467
|
+
metrics = new NoopMetricsPort();
|
|
468
|
+
events = new NoopEventPort();
|
|
469
|
+
llm;
|
|
470
|
+
tools;
|
|
471
|
+
memory;
|
|
113
472
|
/**
|
|
114
473
|
* Updates the agent configuration dynamically after initialization.
|
|
115
474
|
* This allows for runtime changes to model, provider, and other settings.
|
|
@@ -122,26 +481,26 @@ var AgentOrchestrator = class {
|
|
|
122
481
|
* This preserves conversation history, MCP connections, and other state.
|
|
123
482
|
*/
|
|
124
483
|
setLLM(newLLM) {
|
|
125
|
-
this.
|
|
484
|
+
this.llm = newLLM;
|
|
126
485
|
}
|
|
127
486
|
/**
|
|
128
487
|
* Updates the tool port without reinitializing the entire orchestrator.
|
|
129
488
|
* This preserves conversation history and other state while adding/removing tools.
|
|
130
489
|
*/
|
|
131
490
|
setTools(newTools) {
|
|
132
|
-
this.
|
|
491
|
+
this.tools = newTools;
|
|
133
492
|
}
|
|
134
493
|
/**
|
|
135
494
|
* Gets the current tool port.
|
|
136
495
|
*/
|
|
137
496
|
getTools() {
|
|
138
|
-
return this.
|
|
497
|
+
return this.tools;
|
|
139
498
|
}
|
|
140
499
|
/**
|
|
141
500
|
* Gets the current LLM port.
|
|
142
501
|
*/
|
|
143
502
|
getLLM() {
|
|
144
|
-
return this.
|
|
503
|
+
return this.llm;
|
|
145
504
|
}
|
|
146
505
|
/**
|
|
147
506
|
* Gets the current agent configuration.
|
|
@@ -155,14 +514,26 @@ var AgentOrchestrator = class {
|
|
|
155
514
|
* MCP servers, and other state.
|
|
156
515
|
*/
|
|
157
516
|
setMemory(newMemory) {
|
|
158
|
-
this.
|
|
517
|
+
this.memory = newMemory;
|
|
159
518
|
}
|
|
160
519
|
/**
|
|
161
520
|
* Updates the event port without reinitializing the entire orchestrator.
|
|
162
521
|
* This is useful when switching to a new session with a different event log file.
|
|
163
522
|
*/
|
|
164
523
|
setEvents(newEvents) {
|
|
165
|
-
this.
|
|
524
|
+
this.events = newEvents;
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Updates the metrics port without reinitializing the entire orchestrator.
|
|
528
|
+
*/
|
|
529
|
+
setMetrics(newMetrics) {
|
|
530
|
+
this.metrics = newMetrics;
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Gets the current metrics port.
|
|
534
|
+
*/
|
|
535
|
+
getMetrics() {
|
|
536
|
+
return this.metrics;
|
|
166
537
|
}
|
|
167
538
|
/**
|
|
168
539
|
* Determines if a tool should bypass approval requirements.
|
|
@@ -175,10 +546,10 @@ var AgentOrchestrator = class {
|
|
|
175
546
|
}
|
|
176
547
|
async handleToolDenial(denialMessage, conversationId, messageId, accumulatedMessages, turnHistory, originalToolCalls, assistantContent, usage) {
|
|
177
548
|
const assistantMsg = {
|
|
178
|
-
id: this.
|
|
549
|
+
id: this.ids.uuid(),
|
|
179
550
|
role: "assistant",
|
|
180
551
|
content: assistantContent ?? null,
|
|
181
|
-
timestamp: this.
|
|
552
|
+
timestamp: this.clock.iso(),
|
|
182
553
|
tool_calls: originalToolCalls,
|
|
183
554
|
usage
|
|
184
555
|
};
|
|
@@ -201,15 +572,15 @@ var AgentOrchestrator = class {
|
|
|
201
572
|
id: toolCall.id,
|
|
202
573
|
role: "tool",
|
|
203
574
|
content: toolDenialResult,
|
|
204
|
-
timestamp: this.
|
|
575
|
+
timestamp: this.clock.iso(),
|
|
205
576
|
tool_call_id: toolCall.id,
|
|
206
577
|
name: toolCall.function.name
|
|
207
578
|
};
|
|
208
579
|
turnHistory.push(toolMsg);
|
|
209
580
|
toolResultMsgs.push(toolMsg);
|
|
210
581
|
}
|
|
211
|
-
await this.
|
|
212
|
-
await this.
|
|
582
|
+
await this.memory.append(conversationId, [assistantMsg, ...toolResultMsgs]);
|
|
583
|
+
await this.events?.emit({
|
|
213
584
|
type: AgentEventTypes.AssistantMessage,
|
|
214
585
|
conversationId,
|
|
215
586
|
messageId,
|
|
@@ -247,15 +618,19 @@ var AgentOrchestrator = class {
|
|
|
247
618
|
}
|
|
248
619
|
async send(content, opts = {}) {
|
|
249
620
|
const convo = opts.conversationId ?? "default";
|
|
250
|
-
const t0 = this.
|
|
251
|
-
const msgId = this.
|
|
252
|
-
const history = await this.
|
|
621
|
+
const t0 = this.clock.now();
|
|
622
|
+
const msgId = this.ids.uuid();
|
|
623
|
+
const history = await this.memory.get(convo);
|
|
253
624
|
let providerMsgs;
|
|
254
625
|
let userMessages;
|
|
255
626
|
let userDisplay;
|
|
256
627
|
let enhanced;
|
|
628
|
+
const _llm = this.getLLM();
|
|
629
|
+
if (!_llm) {
|
|
630
|
+
throw new Error("LLM provider not set");
|
|
631
|
+
}
|
|
257
632
|
if (opts.retry) {
|
|
258
|
-
providerMsgs = this.
|
|
633
|
+
providerMsgs = this.context.toProviderMessages(history, this.cfg.systemPrompt, []);
|
|
259
634
|
userMessages = [];
|
|
260
635
|
userDisplay = "[Retry]";
|
|
261
636
|
enhanced = [];
|
|
@@ -266,7 +641,7 @@ var AgentOrchestrator = class {
|
|
|
266
641
|
attachments: Array.isArray(content.attachments) ? content.attachments : []
|
|
267
642
|
};
|
|
268
643
|
const attachments = normalized.attachments;
|
|
269
|
-
enhanced = this.
|
|
644
|
+
enhanced = this.reminders.enhance(normalized.text, { conversationId: convo });
|
|
270
645
|
const enhancedCombined = enhanced.join("\n");
|
|
271
646
|
const messageParts = buildMessageParts(enhancedCombined, attachments);
|
|
272
647
|
let userContent;
|
|
@@ -279,16 +654,16 @@ var AgentOrchestrator = class {
|
|
|
279
654
|
} else {
|
|
280
655
|
userContent = enhancedCombined;
|
|
281
656
|
}
|
|
282
|
-
providerMsgs = this.
|
|
657
|
+
providerMsgs = this.context.toProviderMessages(history, this.cfg.systemPrompt, [userContent]);
|
|
283
658
|
userDisplay = resolveDisplayText(normalized.text, attachments, normalized.displayText);
|
|
284
|
-
const userTimestamp = this.
|
|
285
|
-
userMessages = [{ id: this.
|
|
286
|
-
await this.
|
|
659
|
+
const userTimestamp = this.clock.iso();
|
|
660
|
+
userMessages = [{ id: this.ids.uuid(), role: "user", content: userContent, timestamp: userTimestamp }];
|
|
661
|
+
await this.memory.append(convo, userMessages);
|
|
287
662
|
}
|
|
288
663
|
if (opts.signal?.aborted) throw new Error("Aborted");
|
|
289
|
-
const toolDefs = this.
|
|
664
|
+
const toolDefs = this.tools.getToolDefinitions(this.cfg.enabledTools ?? []);
|
|
290
665
|
const toolNames = toolDefs.map((t) => t.function.name);
|
|
291
|
-
await this.
|
|
666
|
+
await this.events?.emit({
|
|
292
667
|
type: AgentEventTypes.MessageStarted,
|
|
293
668
|
conversationId: convo,
|
|
294
669
|
messageId: msgId,
|
|
@@ -315,17 +690,166 @@ var AgentOrchestrator = class {
|
|
|
315
690
|
let toolApprovalDenied = false;
|
|
316
691
|
let denialMessage = "";
|
|
317
692
|
let finalResponseSaved = false;
|
|
318
|
-
|
|
319
|
-
|
|
693
|
+
if (opts.stream && typeof _llm.streamCompletion === "function") {
|
|
694
|
+
let isFirstChunk = true;
|
|
695
|
+
result = await _llm.streamCompletion(
|
|
696
|
+
params,
|
|
697
|
+
{
|
|
698
|
+
onChunk: async (delta, usage) => {
|
|
699
|
+
streamedAssistantContent += delta;
|
|
700
|
+
const cleanDelta = isFirstChunk ? delta.replace(/^\n+/, "") : delta;
|
|
701
|
+
isFirstChunk = false;
|
|
702
|
+
const chunkEvent = {
|
|
703
|
+
type: AgentEventTypes.AssistantChunk,
|
|
704
|
+
conversationId: convo,
|
|
705
|
+
messageId: msgId,
|
|
706
|
+
delta: cleanDelta,
|
|
707
|
+
...usage && { usage }
|
|
708
|
+
};
|
|
709
|
+
await this.events?.emit(chunkEvent);
|
|
710
|
+
},
|
|
711
|
+
onStreamFinish: async (finishReason, usage) => {
|
|
712
|
+
if (usage) {
|
|
713
|
+
const cost = this.cost.estimate(this.cfg.model, usage);
|
|
714
|
+
this.metrics?.recordLLMCall?.(usage, cost);
|
|
715
|
+
}
|
|
716
|
+
const finishEvent = {
|
|
717
|
+
type: AgentEventTypes.StreamFinish,
|
|
718
|
+
conversationId: convo,
|
|
719
|
+
messageId: msgId,
|
|
720
|
+
...finishReason && { finishReason },
|
|
721
|
+
...usage && { usage }
|
|
722
|
+
};
|
|
723
|
+
await this.events?.emit(finishEvent);
|
|
724
|
+
}
|
|
725
|
+
},
|
|
726
|
+
opts.signal
|
|
727
|
+
);
|
|
728
|
+
} else {
|
|
729
|
+
result = await _llm.generateCompletion(params, opts.signal);
|
|
730
|
+
if (result.usage) {
|
|
731
|
+
const cost = this.cost.estimate(this.cfg.model, result.usage);
|
|
732
|
+
this.metrics?.recordLLMCall?.(result.usage, cost);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
if (!result.tool_calls?.length && result.content && !finalResponseSaved) {
|
|
736
|
+
const content2 = opts.stream ? streamedAssistantContent : result.content;
|
|
737
|
+
const assistantMsg = {
|
|
738
|
+
id: msgId,
|
|
739
|
+
role: "assistant",
|
|
740
|
+
content: content2,
|
|
741
|
+
timestamp: this.clock.iso(),
|
|
742
|
+
usage: result.usage
|
|
743
|
+
};
|
|
744
|
+
await this.memory.append(convo, [assistantMsg]);
|
|
745
|
+
finalResponseSaved = true;
|
|
746
|
+
if (content2.trim()) {
|
|
747
|
+
const messageEvent = {
|
|
748
|
+
type: AgentEventTypes.AssistantMessage,
|
|
749
|
+
conversationId: convo,
|
|
750
|
+
messageId: msgId,
|
|
751
|
+
content: content2,
|
|
752
|
+
...result.usage && { usage: result.usage }
|
|
753
|
+
};
|
|
754
|
+
await this.events?.emit(messageEvent);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
while (result.tool_calls?.length) {
|
|
758
|
+
if (result.content?.trim()) {
|
|
759
|
+
const messageEvent = {
|
|
760
|
+
type: AgentEventTypes.AssistantMessage,
|
|
761
|
+
conversationId: convo,
|
|
762
|
+
messageId: msgId,
|
|
763
|
+
content: result.content,
|
|
764
|
+
...result.usage && { usage: result.usage }
|
|
765
|
+
};
|
|
766
|
+
await this.events?.emit(messageEvent);
|
|
767
|
+
}
|
|
768
|
+
if (opts.signal?.aborted) throw new Error("Aborted");
|
|
769
|
+
await this.events?.emit({
|
|
770
|
+
type: AgentEventTypes.ToolCalls,
|
|
771
|
+
conversationId: convo,
|
|
772
|
+
messageId: msgId,
|
|
773
|
+
toolCalls: result.tool_calls,
|
|
774
|
+
usage: result.usage
|
|
775
|
+
});
|
|
776
|
+
const approvalResult = await this.processToolApproval(
|
|
777
|
+
result.tool_calls,
|
|
778
|
+
convo,
|
|
779
|
+
msgId,
|
|
780
|
+
accumulatedMessages,
|
|
781
|
+
turnHistory,
|
|
782
|
+
result.content,
|
|
783
|
+
result.usage
|
|
784
|
+
);
|
|
785
|
+
if (approvalResult.wasDenied) {
|
|
786
|
+
denialMessage = approvalResult.denialMessage || "";
|
|
787
|
+
toolApprovalDenied = true;
|
|
788
|
+
break;
|
|
789
|
+
}
|
|
790
|
+
const approvedCalls = approvalResult.approvedCalls;
|
|
791
|
+
const invocations = this.toInvocations(approvedCalls);
|
|
792
|
+
if (opts.signal?.aborted) throw new Error("Aborted");
|
|
793
|
+
const toolResults = await this.tools.executeToolCalls(
|
|
794
|
+
invocations,
|
|
795
|
+
{
|
|
796
|
+
conversationId: convo,
|
|
797
|
+
agentId: this.cfg.id,
|
|
798
|
+
messageId: msgId,
|
|
799
|
+
eventPort: this.events
|
|
800
|
+
},
|
|
801
|
+
this.cfg.maxToolConcurrency ?? 3,
|
|
802
|
+
opts.signal
|
|
803
|
+
);
|
|
804
|
+
allToolResults.push(...toolResults);
|
|
805
|
+
const assistantMsg = {
|
|
806
|
+
id: this.ids.uuid(),
|
|
807
|
+
role: "assistant",
|
|
808
|
+
content: result.content ?? null,
|
|
809
|
+
timestamp: this.clock.iso(),
|
|
810
|
+
tool_calls: approvedCalls,
|
|
811
|
+
usage: result.usage
|
|
812
|
+
};
|
|
813
|
+
const toolResultMsgs = [];
|
|
814
|
+
for (const tr of toolResults) {
|
|
815
|
+
const contentStr = tr.status === "error" ? String(tr.result) : typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result);
|
|
816
|
+
toolResultMsgs.push({
|
|
817
|
+
id: tr.id,
|
|
818
|
+
role: "tool",
|
|
819
|
+
content: contentStr,
|
|
820
|
+
timestamp: this.clock.iso(),
|
|
821
|
+
tool_call_id: tr.id,
|
|
822
|
+
name: tr.name
|
|
823
|
+
});
|
|
824
|
+
this.metrics?.recordToolCall?.();
|
|
825
|
+
await this.events?.emit({
|
|
826
|
+
type: AgentEventTypes.ToolResult,
|
|
827
|
+
conversationId: convo,
|
|
828
|
+
messageId: msgId,
|
|
829
|
+
result: tr
|
|
830
|
+
});
|
|
831
|
+
}
|
|
832
|
+
await this.memory.append(convo, [assistantMsg, ...toolResultMsgs]);
|
|
833
|
+
const { usage: _usage, ...extraField } = result;
|
|
834
|
+
accumulatedMessages.push({
|
|
835
|
+
...extraField,
|
|
836
|
+
role: "assistant",
|
|
837
|
+
content: result.content ?? null,
|
|
838
|
+
tool_calls: approvedCalls
|
|
839
|
+
});
|
|
840
|
+
for (const tr of toolResults) {
|
|
841
|
+
const contentStr = tr.status === "error" ? String(tr.result) : typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result);
|
|
842
|
+
accumulatedMessages.push({ role: "tool", content: contentStr, tool_call_id: tr.id, name: tr.name });
|
|
843
|
+
}
|
|
844
|
+
if (opts.signal?.aborted) throw new Error("Aborted");
|
|
845
|
+
streamedAssistantContent = "";
|
|
846
|
+
if (opts.stream && typeof _llm.streamCompletion === "function") {
|
|
320
847
|
let isFirstChunk = true;
|
|
321
|
-
result = await
|
|
322
|
-
params,
|
|
848
|
+
result = await _llm.streamCompletion(
|
|
849
|
+
{ ...params, messages: accumulatedMessages },
|
|
323
850
|
{
|
|
324
851
|
onChunk: async (delta, usage) => {
|
|
325
|
-
|
|
326
|
-
streamedAssistantContent += delta;
|
|
327
|
-
} catch {
|
|
328
|
-
}
|
|
852
|
+
streamedAssistantContent += delta;
|
|
329
853
|
const cleanDelta = isFirstChunk ? delta.replace(/^\n+/, "") : delta;
|
|
330
854
|
isFirstChunk = false;
|
|
331
855
|
const chunkEvent = {
|
|
@@ -335,9 +859,13 @@ var AgentOrchestrator = class {
|
|
|
335
859
|
delta: cleanDelta,
|
|
336
860
|
...usage && { usage }
|
|
337
861
|
};
|
|
338
|
-
await this.
|
|
862
|
+
await this.events?.emit(chunkEvent);
|
|
339
863
|
},
|
|
340
864
|
onStreamFinish: async (finishReason, usage) => {
|
|
865
|
+
if (usage) {
|
|
866
|
+
const cost = this.cost.estimate(this.cfg.model, usage);
|
|
867
|
+
this.metrics?.recordLLMCall?.(usage, cost);
|
|
868
|
+
}
|
|
341
869
|
const finishEvent = {
|
|
342
870
|
type: AgentEventTypes.StreamFinish,
|
|
343
871
|
conversationId: convo,
|
|
@@ -345,24 +873,28 @@ var AgentOrchestrator = class {
|
|
|
345
873
|
...finishReason && { finishReason },
|
|
346
874
|
...usage && { usage }
|
|
347
875
|
};
|
|
348
|
-
await this.
|
|
876
|
+
await this.events?.emit(finishEvent);
|
|
349
877
|
}
|
|
350
878
|
},
|
|
351
879
|
opts.signal
|
|
352
880
|
);
|
|
353
881
|
} else {
|
|
354
|
-
result = await
|
|
882
|
+
result = await _llm.generateCompletion({ ...params, messages: accumulatedMessages }, opts.signal);
|
|
883
|
+
if (result.usage) {
|
|
884
|
+
const cost = this.cost.estimate(this.cfg.model, result.usage);
|
|
885
|
+
this.metrics?.recordLLMCall?.(result.usage, cost);
|
|
886
|
+
}
|
|
355
887
|
}
|
|
356
888
|
if (!result.tool_calls?.length && result.content && !finalResponseSaved) {
|
|
357
889
|
const content2 = opts.stream ? streamedAssistantContent : result.content;
|
|
358
|
-
const
|
|
890
|
+
const assistantMsg2 = {
|
|
359
891
|
id: msgId,
|
|
360
892
|
role: "assistant",
|
|
361
893
|
content: content2,
|
|
362
|
-
timestamp: this.
|
|
894
|
+
timestamp: this.clock.iso(),
|
|
363
895
|
usage: result.usage
|
|
364
896
|
};
|
|
365
|
-
await this.
|
|
897
|
+
await this.memory.append(convo, [assistantMsg2]);
|
|
366
898
|
finalResponseSaved = true;
|
|
367
899
|
if (content2.trim()) {
|
|
368
900
|
const messageEvent = {
|
|
@@ -372,199 +904,56 @@ var AgentOrchestrator = class {
|
|
|
372
904
|
content: content2,
|
|
373
905
|
...result.usage && { usage: result.usage }
|
|
374
906
|
};
|
|
375
|
-
await this.
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
while (result.tool_calls?.length) {
|
|
379
|
-
if (result.content?.trim()) {
|
|
380
|
-
const messageEvent = {
|
|
381
|
-
type: AgentEventTypes.AssistantMessage,
|
|
382
|
-
conversationId: convo,
|
|
383
|
-
messageId: msgId,
|
|
384
|
-
content: result.content,
|
|
385
|
-
...result.usage && { usage: result.usage }
|
|
386
|
-
};
|
|
387
|
-
await this.deps.events?.emit(messageEvent);
|
|
388
|
-
}
|
|
389
|
-
if (opts.signal?.aborted) throw new Error("Aborted");
|
|
390
|
-
await this.deps.events?.emit({
|
|
391
|
-
type: AgentEventTypes.ToolCalls,
|
|
392
|
-
conversationId: convo,
|
|
393
|
-
messageId: msgId,
|
|
394
|
-
toolCalls: result.tool_calls,
|
|
395
|
-
usage: result.usage
|
|
396
|
-
});
|
|
397
|
-
const approvalResult = await this.processToolApproval(
|
|
398
|
-
result.tool_calls,
|
|
399
|
-
convo,
|
|
400
|
-
msgId,
|
|
401
|
-
accumulatedMessages,
|
|
402
|
-
turnHistory,
|
|
403
|
-
result.content,
|
|
404
|
-
result.usage
|
|
405
|
-
);
|
|
406
|
-
if (approvalResult.wasDenied) {
|
|
407
|
-
denialMessage = approvalResult.denialMessage || "";
|
|
408
|
-
toolApprovalDenied = true;
|
|
409
|
-
break;
|
|
410
|
-
}
|
|
411
|
-
const approvedCalls = approvalResult.approvedCalls;
|
|
412
|
-
const invocations = this.toInvocations(approvedCalls);
|
|
413
|
-
if (opts.signal?.aborted) throw new Error("Aborted");
|
|
414
|
-
const toolResults = await this.deps.tools.executeToolCalls(
|
|
415
|
-
invocations,
|
|
416
|
-
{
|
|
417
|
-
conversationId: convo,
|
|
418
|
-
agentId: this.cfg.id,
|
|
419
|
-
messageId: msgId,
|
|
420
|
-
eventPort: this.deps.events
|
|
421
|
-
},
|
|
422
|
-
this.cfg.maxToolConcurrency ?? 3,
|
|
423
|
-
opts.signal
|
|
424
|
-
);
|
|
425
|
-
allToolResults.push(...toolResults);
|
|
426
|
-
const assistantMsg = {
|
|
427
|
-
id: this.deps.ids.uuid(),
|
|
428
|
-
role: "assistant",
|
|
429
|
-
content: result.content ?? null,
|
|
430
|
-
timestamp: this.deps.clock.iso(),
|
|
431
|
-
tool_calls: approvedCalls,
|
|
432
|
-
usage: result.usage
|
|
433
|
-
};
|
|
434
|
-
const toolResultMsgs = [];
|
|
435
|
-
for (const tr of toolResults) {
|
|
436
|
-
const contentStr = tr.status === "error" ? String(tr.result) : typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result);
|
|
437
|
-
toolResultMsgs.push({
|
|
438
|
-
id: tr.id,
|
|
439
|
-
role: "tool",
|
|
440
|
-
content: contentStr,
|
|
441
|
-
timestamp: this.deps.clock.iso(),
|
|
442
|
-
tool_call_id: tr.id,
|
|
443
|
-
name: tr.name
|
|
444
|
-
});
|
|
445
|
-
await this.deps.events?.emit({
|
|
446
|
-
type: AgentEventTypes.ToolResult,
|
|
447
|
-
conversationId: convo,
|
|
448
|
-
messageId: msgId,
|
|
449
|
-
result: tr
|
|
450
|
-
});
|
|
451
|
-
}
|
|
452
|
-
await this.deps.memory.append(convo, [assistantMsg, ...toolResultMsgs]);
|
|
453
|
-
accumulatedMessages.push({ role: "assistant", content: result.content ?? null, tool_calls: approvedCalls });
|
|
454
|
-
for (const tr of toolResults) {
|
|
455
|
-
const contentStr = tr.status === "error" ? String(tr.result) : typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result);
|
|
456
|
-
accumulatedMessages.push({ role: "tool", content: contentStr, tool_call_id: tr.id, name: tr.name });
|
|
457
|
-
}
|
|
458
|
-
if (opts.signal?.aborted) throw new Error("Aborted");
|
|
459
|
-
streamedAssistantContent = "";
|
|
460
|
-
if (opts.stream && typeof this.deps.llm.streamCompletion === "function") {
|
|
461
|
-
let isFirstChunk = true;
|
|
462
|
-
result = await this.deps.llm.streamCompletion(
|
|
463
|
-
{ ...params, messages: accumulatedMessages },
|
|
464
|
-
{
|
|
465
|
-
onChunk: async (delta, usage) => {
|
|
466
|
-
try {
|
|
467
|
-
streamedAssistantContent += delta;
|
|
468
|
-
} catch {
|
|
469
|
-
}
|
|
470
|
-
const cleanDelta = isFirstChunk ? delta.replace(/^\n+/, "") : delta;
|
|
471
|
-
isFirstChunk = false;
|
|
472
|
-
const chunkEvent = {
|
|
473
|
-
type: AgentEventTypes.AssistantChunk,
|
|
474
|
-
conversationId: convo,
|
|
475
|
-
messageId: msgId,
|
|
476
|
-
delta: cleanDelta,
|
|
477
|
-
...usage && { usage }
|
|
478
|
-
};
|
|
479
|
-
await this.deps.events?.emit(chunkEvent);
|
|
480
|
-
},
|
|
481
|
-
onStreamFinish: async (finishReason, usage) => {
|
|
482
|
-
const finishEvent = {
|
|
483
|
-
type: AgentEventTypes.StreamFinish,
|
|
484
|
-
conversationId: convo,
|
|
485
|
-
messageId: msgId,
|
|
486
|
-
...finishReason && { finishReason },
|
|
487
|
-
...usage && { usage }
|
|
488
|
-
};
|
|
489
|
-
await this.deps.events?.emit(finishEvent);
|
|
490
|
-
}
|
|
491
|
-
},
|
|
492
|
-
opts.signal
|
|
493
|
-
);
|
|
494
|
-
} else {
|
|
495
|
-
result = await this.deps.llm.generateCompletion({ ...params, messages: accumulatedMessages }, opts.signal);
|
|
496
|
-
}
|
|
497
|
-
if (!result.tool_calls?.length && result.content && !finalResponseSaved) {
|
|
498
|
-
const content2 = opts.stream ? streamedAssistantContent : result.content;
|
|
499
|
-
const assistantMsg2 = {
|
|
500
|
-
id: msgId,
|
|
501
|
-
role: "assistant",
|
|
502
|
-
content: content2,
|
|
503
|
-
timestamp: this.deps.clock.iso(),
|
|
504
|
-
usage: result.usage
|
|
505
|
-
};
|
|
506
|
-
await this.deps.memory.append(convo, [assistantMsg2]);
|
|
507
|
-
finalResponseSaved = true;
|
|
508
|
-
if (content2.trim()) {
|
|
509
|
-
const messageEvent = {
|
|
510
|
-
type: AgentEventTypes.AssistantMessage,
|
|
511
|
-
conversationId: convo,
|
|
512
|
-
messageId: msgId,
|
|
513
|
-
content: content2,
|
|
514
|
-
...result.usage && { usage: result.usage }
|
|
515
|
-
};
|
|
516
|
-
await this.deps.events?.emit(messageEvent);
|
|
517
|
-
}
|
|
907
|
+
await this.events?.emit(messageEvent);
|
|
518
908
|
}
|
|
519
909
|
}
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
content: result.content,
|
|
529
|
-
...result.usage && { usage: result.usage }
|
|
530
|
-
};
|
|
531
|
-
await this.deps.events?.emit(messageEvent);
|
|
532
|
-
}
|
|
533
|
-
const responseContent = toolApprovalDenied ? denialMessage : result.content;
|
|
534
|
-
const resp = {
|
|
535
|
-
id: msgId,
|
|
536
|
-
content: responseContent,
|
|
537
|
-
role: MessageRoles.Assistant,
|
|
538
|
-
timestamp,
|
|
539
|
-
metadata: {
|
|
540
|
-
model: this.cfg.model,
|
|
541
|
-
provider: "echo",
|
|
542
|
-
agentId: this.cfg.id,
|
|
543
|
-
responseTime: t1 - t0,
|
|
544
|
-
promptTokens: result.usage?.prompt_tokens,
|
|
545
|
-
completionTokens: result.usage?.completion_tokens,
|
|
546
|
-
totalTokens: result.usage?.total_tokens,
|
|
547
|
-
estimatedCost: this.deps.cost.estimate(this.cfg.model, result.usage),
|
|
548
|
-
toolCalls: allToolResults.length
|
|
549
|
-
}
|
|
550
|
-
};
|
|
551
|
-
await this.deps.events?.emit({
|
|
552
|
-
type: AgentEventTypes.Done,
|
|
910
|
+
}
|
|
911
|
+
const t1 = this.clock.now();
|
|
912
|
+
const timestamp = this.clock.iso();
|
|
913
|
+
this.metrics?.recordRequestComplete?.(t1 - t0);
|
|
914
|
+
const shouldEmitFinalMessage = result.content?.trim() && !toolApprovalDenied && !finalResponseSaved;
|
|
915
|
+
if (shouldEmitFinalMessage) {
|
|
916
|
+
const messageEvent = {
|
|
917
|
+
type: AgentEventTypes.AssistantMessage,
|
|
553
918
|
conversationId: convo,
|
|
554
919
|
messageId: msgId,
|
|
555
|
-
|
|
556
|
-
usage: result.usage
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
|
|
920
|
+
content: result.content,
|
|
921
|
+
...result.usage && { usage: result.usage }
|
|
922
|
+
};
|
|
923
|
+
await this.events?.emit(messageEvent);
|
|
924
|
+
}
|
|
925
|
+
const responseContent = toolApprovalDenied ? denialMessage : result.content;
|
|
926
|
+
const resp = {
|
|
927
|
+
id: msgId,
|
|
928
|
+
content: responseContent,
|
|
929
|
+
role: MessageRoles.Assistant,
|
|
930
|
+
timestamp,
|
|
931
|
+
metadata: {
|
|
932
|
+
model: this.cfg.model,
|
|
933
|
+
provider: "echo",
|
|
934
|
+
agentId: this.cfg.id,
|
|
935
|
+
responseTime: t1 - t0,
|
|
936
|
+
promptTokens: result.usage?.prompt_tokens,
|
|
937
|
+
completionTokens: result.usage?.completion_tokens,
|
|
938
|
+
totalTokens: result.usage?.total_tokens,
|
|
939
|
+
estimatedCost: this.cost.estimate(this.cfg.model, result.usage),
|
|
940
|
+
toolCalls: allToolResults.length
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
await this.events?.emit({
|
|
944
|
+
type: AgentEventTypes.Done,
|
|
945
|
+
conversationId: convo,
|
|
946
|
+
messageId: msgId,
|
|
947
|
+
responseTimeMs: t1 - t0,
|
|
948
|
+
usage: result.usage
|
|
949
|
+
});
|
|
950
|
+
return resp;
|
|
562
951
|
}
|
|
563
952
|
async waitForToolApproval(toolCalls, conversationId, messageId) {
|
|
564
|
-
const approvalId = this.
|
|
953
|
+
const approvalId = this.ids.uuid();
|
|
565
954
|
return new Promise((resolve6, reject) => {
|
|
566
955
|
this.pendingApprovals.set(approvalId, { resolve: resolve6, reject });
|
|
567
|
-
this.
|
|
956
|
+
this.events?.emit({
|
|
568
957
|
type: AgentEventTypes.ToolApprovalRequired,
|
|
569
958
|
conversationId,
|
|
570
959
|
messageId,
|
|
@@ -593,71 +982,11 @@ var AgentOrchestrator = class {
|
|
|
593
982
|
let parameters = {};
|
|
594
983
|
try {
|
|
595
984
|
parameters = JSON.parse(tc.function.arguments || "{}");
|
|
596
|
-
} catch {
|
|
597
|
-
parameters = {};
|
|
598
|
-
}
|
|
599
|
-
return { id: tc.id, name: tc.function.name, parameters };
|
|
600
|
-
});
|
|
601
|
-
}
|
|
602
|
-
};
|
|
603
|
-
|
|
604
|
-
// context.ts
|
|
605
|
-
var toProviderContent = (content) => {
|
|
606
|
-
if (content === null || content === void 0) {
|
|
607
|
-
return "";
|
|
608
|
-
}
|
|
609
|
-
if (typeof content === "string") {
|
|
610
|
-
return content;
|
|
611
|
-
}
|
|
612
|
-
if (content.type === "parts") {
|
|
613
|
-
const providerParts = [];
|
|
614
|
-
for (const part of content.parts) {
|
|
615
|
-
if (part.type === "text") {
|
|
616
|
-
if (part.text.length > 0) {
|
|
617
|
-
providerParts.push({ type: "text", text: part.text });
|
|
618
|
-
}
|
|
619
|
-
continue;
|
|
620
|
-
}
|
|
621
|
-
const label = part.altText ?? (part.name ? `Image attachment: ${part.name}` : void 0);
|
|
622
|
-
if (label) {
|
|
623
|
-
providerParts.push({ type: "text", text: label });
|
|
624
|
-
}
|
|
625
|
-
const url = `data:${part.mimeType};base64,${part.data}`;
|
|
626
|
-
providerParts.push({ type: "image_url", image_url: { url } });
|
|
627
|
-
}
|
|
628
|
-
return providerParts.length > 0 ? providerParts : [];
|
|
629
|
-
}
|
|
630
|
-
return [];
|
|
631
|
-
};
|
|
632
|
-
var SimpleContextBuilder = class {
|
|
633
|
-
toProviderMessages(history, systemPrompt, newUserContent) {
|
|
634
|
-
const transformed = [];
|
|
635
|
-
for (const m of history) {
|
|
636
|
-
const providerContent = toProviderContent(m.content);
|
|
637
|
-
if (m.role === "user") {
|
|
638
|
-
transformed.push({ role: "user", content: providerContent ?? "" });
|
|
639
|
-
} else if (m.role === "assistant") {
|
|
640
|
-
if (m.tool_calls && m.tool_calls.length > 0) {
|
|
641
|
-
transformed.push({ role: "assistant", content: providerContent ?? null, tool_calls: m.tool_calls });
|
|
642
|
-
} else {
|
|
643
|
-
transformed.push({ role: "assistant", content: providerContent ?? "" });
|
|
644
|
-
}
|
|
645
|
-
} else if (m.role === "tool") {
|
|
646
|
-
if (m.tool_call_id) {
|
|
647
|
-
transformed.push({
|
|
648
|
-
role: "tool",
|
|
649
|
-
content: typeof providerContent === "string" ? providerContent : providerContent ?? "",
|
|
650
|
-
tool_call_id: m.tool_call_id,
|
|
651
|
-
name: m.name
|
|
652
|
-
});
|
|
653
|
-
}
|
|
985
|
+
} catch {
|
|
986
|
+
parameters = {};
|
|
654
987
|
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
role: "user",
|
|
658
|
-
content: toProviderContent(c) ?? ""
|
|
659
|
-
}));
|
|
660
|
-
return [{ role: "system", content: systemPrompt }, ...transformed, ...userMsgs];
|
|
988
|
+
return { id: tc.id, name: tc.function.name, parameters };
|
|
989
|
+
});
|
|
661
990
|
}
|
|
662
991
|
};
|
|
663
992
|
|
|
@@ -922,7 +1251,12 @@ async function buildTree(dir, rootDir, gitignore, options, depth, fileCount) {
|
|
|
922
1251
|
if (depth >= options.maxDepth || fileCount.count >= options.maxFiles) {
|
|
923
1252
|
return [];
|
|
924
1253
|
}
|
|
925
|
-
|
|
1254
|
+
let entries;
|
|
1255
|
+
try {
|
|
1256
|
+
entries = await fs2.readdir(dir, { withFileTypes: true });
|
|
1257
|
+
} catch {
|
|
1258
|
+
return [];
|
|
1259
|
+
}
|
|
926
1260
|
const lines = [];
|
|
927
1261
|
const sortedEntries = entries.sort((a, b) => {
|
|
928
1262
|
if (a.isDirectory() && !b.isDirectory()) return -1;
|
|
@@ -966,123 +1300,6 @@ async function generateFolderTree(rootDir, options = {}) {
|
|
|
966
1300
|
return [header, ...lines].join("\n");
|
|
967
1301
|
}
|
|
968
1302
|
|
|
969
|
-
// persistent/memory.ts
|
|
970
|
-
var InMemoryMemory = class {
|
|
971
|
-
store = /* @__PURE__ */ new Map();
|
|
972
|
-
async get(key) {
|
|
973
|
-
return this.store.get(key) ?? [];
|
|
974
|
-
}
|
|
975
|
-
async set(key, items) {
|
|
976
|
-
this.store.set(key, [...items]);
|
|
977
|
-
}
|
|
978
|
-
async append(key, items) {
|
|
979
|
-
const existing = this.store.get(key) ?? [];
|
|
980
|
-
this.store.set(key, [...existing, ...items]);
|
|
981
|
-
}
|
|
982
|
-
async delete(key) {
|
|
983
|
-
this.store.delete(key);
|
|
984
|
-
}
|
|
985
|
-
async keys() {
|
|
986
|
-
return Array.from(this.store.keys());
|
|
987
|
-
}
|
|
988
|
-
async clear() {
|
|
989
|
-
this.store.clear();
|
|
990
|
-
}
|
|
991
|
-
async exportSnapshot() {
|
|
992
|
-
const snap = {};
|
|
993
|
-
for (const [k, v] of this.store.entries()) snap[k] = [...v];
|
|
994
|
-
return snap;
|
|
995
|
-
}
|
|
996
|
-
async importSnapshot(snapshot) {
|
|
997
|
-
this.store.clear();
|
|
998
|
-
for (const [k, v] of Object.entries(snapshot)) this.store.set(k, [...v]);
|
|
999
|
-
}
|
|
1000
|
-
};
|
|
1001
|
-
var JsonFileMemoryPersistence = class {
|
|
1002
|
-
constructor(filename = "history.json") {
|
|
1003
|
-
this.filename = filename;
|
|
1004
|
-
}
|
|
1005
|
-
async load() {
|
|
1006
|
-
try {
|
|
1007
|
-
const fs10 = await import("fs");
|
|
1008
|
-
if (!fs10.existsSync(this.filename)) return {};
|
|
1009
|
-
const text = fs10.readFileSync(this.filename, "utf-8");
|
|
1010
|
-
const data = JSON.parse(text);
|
|
1011
|
-
return typeof data === "object" && data ? data : {};
|
|
1012
|
-
} catch {
|
|
1013
|
-
console.warn(`Failed to load memory from ${this.filename}`);
|
|
1014
|
-
return {};
|
|
1015
|
-
}
|
|
1016
|
-
}
|
|
1017
|
-
async save(snapshot) {
|
|
1018
|
-
try {
|
|
1019
|
-
const fs10 = await import("fs");
|
|
1020
|
-
const path9 = await import("path");
|
|
1021
|
-
const dir = path9.dirname(this.filename);
|
|
1022
|
-
if (dir && dir !== "." && !fs10.existsSync(dir)) {
|
|
1023
|
-
fs10.mkdirSync(dir, { recursive: true });
|
|
1024
|
-
}
|
|
1025
|
-
fs10.writeFileSync(this.filename, JSON.stringify(snapshot, null, 2), "utf-8");
|
|
1026
|
-
} catch (err2) {
|
|
1027
|
-
console.warn(`Failed to save memory to ${this.filename}`, err2);
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
};
|
|
1031
|
-
var PersistedMemory = class {
|
|
1032
|
-
constructor(persistence) {
|
|
1033
|
-
this.persistence = persistence;
|
|
1034
|
-
}
|
|
1035
|
-
inner = new InMemoryMemory();
|
|
1036
|
-
initialized = false;
|
|
1037
|
-
async ensureInitialized() {
|
|
1038
|
-
if (this.initialized) return;
|
|
1039
|
-
const snap = await this.persistence.load();
|
|
1040
|
-
if (snap && typeof snap === "object") await this.inner.importSnapshot(snap);
|
|
1041
|
-
this.initialized = true;
|
|
1042
|
-
}
|
|
1043
|
-
async save() {
|
|
1044
|
-
const snap = await this.inner.exportSnapshot();
|
|
1045
|
-
await this.persistence.save(snap);
|
|
1046
|
-
}
|
|
1047
|
-
async get(key) {
|
|
1048
|
-
await this.ensureInitialized();
|
|
1049
|
-
return this.inner.get(key);
|
|
1050
|
-
}
|
|
1051
|
-
async set(key, items) {
|
|
1052
|
-
await this.ensureInitialized();
|
|
1053
|
-
await this.inner.set(key, items);
|
|
1054
|
-
await this.save();
|
|
1055
|
-
}
|
|
1056
|
-
async append(key, items) {
|
|
1057
|
-
await this.ensureInitialized();
|
|
1058
|
-
await this.inner.append(key, items);
|
|
1059
|
-
await this.save();
|
|
1060
|
-
}
|
|
1061
|
-
async delete(key) {
|
|
1062
|
-
await this.ensureInitialized();
|
|
1063
|
-
await this.inner.delete(key);
|
|
1064
|
-
await this.save();
|
|
1065
|
-
}
|
|
1066
|
-
async keys() {
|
|
1067
|
-
await this.ensureInitialized();
|
|
1068
|
-
return this.inner.keys();
|
|
1069
|
-
}
|
|
1070
|
-
async clear() {
|
|
1071
|
-
await this.ensureInitialized();
|
|
1072
|
-
await this.inner.clear();
|
|
1073
|
-
await this.save();
|
|
1074
|
-
}
|
|
1075
|
-
async exportSnapshot() {
|
|
1076
|
-
await this.ensureInitialized();
|
|
1077
|
-
return this.inner.exportSnapshot();
|
|
1078
|
-
}
|
|
1079
|
-
async importSnapshot(snapshot) {
|
|
1080
|
-
await this.ensureInitialized();
|
|
1081
|
-
await this.inner.importSnapshot(snapshot);
|
|
1082
|
-
await this.save();
|
|
1083
|
-
}
|
|
1084
|
-
};
|
|
1085
|
-
|
|
1086
1303
|
// persistent/metadata-memory.ts
|
|
1087
1304
|
var MemoryPortMetadataAdapter = class {
|
|
1088
1305
|
constructor(memory, prefix = "__metadata__") {
|
|
@@ -1217,6 +1434,27 @@ var ConversationStore = class {
|
|
|
1217
1434
|
};
|
|
1218
1435
|
await this.metadataMemory.set(conversationId, updatedMetadata);
|
|
1219
1436
|
}
|
|
1437
|
+
async recordRequestMetrics(conversationId, metrics) {
|
|
1438
|
+
const metadata = await this.metadataMemory.get(conversationId);
|
|
1439
|
+
const updatedMetadata = {
|
|
1440
|
+
...metadata,
|
|
1441
|
+
promptTokens: (metadata?.promptTokens ?? 0) + (metrics.promptTokens ?? 0),
|
|
1442
|
+
completionTokens: (metadata?.completionTokens ?? 0) + (metrics.completionTokens ?? 0),
|
|
1443
|
+
totalTokens: (metadata?.totalTokens ?? 0) + (metrics.totalTokens ?? 0),
|
|
1444
|
+
requestCount: (metadata?.requestCount ?? 0) + 1,
|
|
1445
|
+
toolCallCount: (metadata?.toolCallCount ?? 0) + (metrics.toolCalls ?? 0),
|
|
1446
|
+
totalTimeMs: (metadata?.totalTimeMs ?? 0) + (metrics.responseTimeMs ?? 0),
|
|
1447
|
+
totalPrice: (metadata?.totalPrice ?? 0) + (metrics.cost ?? 0),
|
|
1448
|
+
contextWindow: {
|
|
1449
|
+
promptTokens: metrics.promptTokens,
|
|
1450
|
+
completionTokens: metrics.completionTokens,
|
|
1451
|
+
totalTokens: metrics.totalTokens
|
|
1452
|
+
},
|
|
1453
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1454
|
+
};
|
|
1455
|
+
await this.metadataMemory.set(conversationId, updatedMetadata);
|
|
1456
|
+
return updatedMetadata;
|
|
1457
|
+
}
|
|
1220
1458
|
async updateTopic(conversationId, topic) {
|
|
1221
1459
|
const metadata = await this.metadataMemory.get(conversationId);
|
|
1222
1460
|
const updatedMetadata = {
|
|
@@ -1279,41 +1517,6 @@ var ConversationContext = class {
|
|
|
1279
1517
|
}
|
|
1280
1518
|
};
|
|
1281
1519
|
|
|
1282
|
-
// id.ts
|
|
1283
|
-
var SimpleId = class {
|
|
1284
|
-
uuid() {
|
|
1285
|
-
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
1286
|
-
const r = Math.random() * 16 | 0;
|
|
1287
|
-
const v = c === "x" ? r : r & 3 | 8;
|
|
1288
|
-
return v.toString(16);
|
|
1289
|
-
});
|
|
1290
|
-
}
|
|
1291
|
-
};
|
|
1292
|
-
|
|
1293
|
-
// clock.ts
|
|
1294
|
-
var SystemClock = class {
|
|
1295
|
-
now() {
|
|
1296
|
-
return Date.now();
|
|
1297
|
-
}
|
|
1298
|
-
iso(dateMs) {
|
|
1299
|
-
return new Date(dateMs ?? Date.now()).toISOString();
|
|
1300
|
-
}
|
|
1301
|
-
};
|
|
1302
|
-
|
|
1303
|
-
// cost.ts
|
|
1304
|
-
var SimpleCost = class {
|
|
1305
|
-
estimate(_model, usage) {
|
|
1306
|
-
return usage?.cost;
|
|
1307
|
-
}
|
|
1308
|
-
};
|
|
1309
|
-
|
|
1310
|
-
// reminders.ts
|
|
1311
|
-
var NoopReminders = class {
|
|
1312
|
-
enhance(content, _opts) {
|
|
1313
|
-
return [content];
|
|
1314
|
-
}
|
|
1315
|
-
};
|
|
1316
|
-
|
|
1317
1520
|
// todo-store.ts
|
|
1318
1521
|
var TodoStore = class {
|
|
1319
1522
|
constructor(memory) {
|
|
@@ -3612,9 +3815,12 @@ var AgentFilePersistence = class {
|
|
|
3612
3815
|
* Load all agents from the agents directory
|
|
3613
3816
|
*/
|
|
3614
3817
|
async loadAll() {
|
|
3615
|
-
this.ensureAgentsDir();
|
|
3616
3818
|
const agents = [];
|
|
3617
3819
|
try {
|
|
3820
|
+
this.ensureAgentsDir();
|
|
3821
|
+
if (!fs8.existsSync(this.agentsDir)) {
|
|
3822
|
+
return agents;
|
|
3823
|
+
}
|
|
3618
3824
|
const files = fs8.readdirSync(this.agentsDir);
|
|
3619
3825
|
const yamlFiles = files.filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
|
|
3620
3826
|
for (const file of yamlFiles) {
|
|
@@ -3627,8 +3833,7 @@ var AgentFilePersistence = class {
|
|
|
3627
3833
|
console.warn(`Failed to load agent from ${file}:`, error);
|
|
3628
3834
|
}
|
|
3629
3835
|
}
|
|
3630
|
-
} catch (
|
|
3631
|
-
console.warn("Failed to read agents directory:", error);
|
|
3836
|
+
} catch (_error) {
|
|
3632
3837
|
}
|
|
3633
3838
|
return agents;
|
|
3634
3839
|
}
|
|
@@ -3717,6 +3922,7 @@ var AgentFilePersistence = class {
|
|
|
3717
3922
|
function mergeChoices(choices) {
|
|
3718
3923
|
const contentParts = [];
|
|
3719
3924
|
const mergedToolCalls = [];
|
|
3925
|
+
const extraFields = {};
|
|
3720
3926
|
const collectText = (value) => {
|
|
3721
3927
|
if (typeof value === "string") {
|
|
3722
3928
|
const trimmed = value.trim();
|
|
@@ -3737,10 +3943,20 @@ function mergeChoices(choices) {
|
|
|
3737
3943
|
if (!msg) continue;
|
|
3738
3944
|
collectText(msg.content);
|
|
3739
3945
|
if (Array.isArray(msg.tool_calls)) mergedToolCalls.push(...msg.tool_calls);
|
|
3946
|
+
const knownKeys = ["content", "tool_calls", "role"];
|
|
3947
|
+
for (const key of Object.keys(msg)) {
|
|
3948
|
+
if (!knownKeys.includes(key)) {
|
|
3949
|
+
extraFields[key] = msg[key];
|
|
3950
|
+
}
|
|
3951
|
+
}
|
|
3740
3952
|
}
|
|
3741
3953
|
const content = contentParts.join("\n\n");
|
|
3742
3954
|
const tool_calls = mergedToolCalls.length ? mergedToolCalls : void 0;
|
|
3743
|
-
return {
|
|
3955
|
+
return {
|
|
3956
|
+
content,
|
|
3957
|
+
...tool_calls ? { tool_calls } : {},
|
|
3958
|
+
...extraFields
|
|
3959
|
+
};
|
|
3744
3960
|
}
|
|
3745
3961
|
function normalizeUsage(usage) {
|
|
3746
3962
|
if (!usage) return void 0;
|
|
@@ -3748,6 +3964,7 @@ function normalizeUsage(usage) {
|
|
|
3748
3964
|
const prompt_tokens = usage.prompt_tokens ?? (typeof usageObj.input_tokens === "number" ? usageObj.input_tokens : void 0);
|
|
3749
3965
|
const completion_tokens = usage.completion_tokens ?? (typeof usageObj.output_tokens === "number" ? usageObj.output_tokens : void 0);
|
|
3750
3966
|
const total_tokens = usage.total_tokens ?? (prompt_tokens != null && completion_tokens != null ? prompt_tokens + completion_tokens : void 0);
|
|
3967
|
+
const cost = usage.cost ?? (typeof usageObj.estimated_cost === "number" ? usageObj.estimated_cost : void 0);
|
|
3751
3968
|
return {
|
|
3752
3969
|
prompt_tokens,
|
|
3753
3970
|
completion_tokens,
|
|
@@ -3755,7 +3972,7 @@ function normalizeUsage(usage) {
|
|
|
3755
3972
|
...usage.reasoning_tokens !== void 0 && { reasoning_tokens: usage.reasoning_tokens },
|
|
3756
3973
|
...usage.prompt_tokens_details && { prompt_tokens_details: usage.prompt_tokens_details },
|
|
3757
3974
|
...usage.completion_tokens_details && { completion_tokens_details: usage.completion_tokens_details },
|
|
3758
|
-
...
|
|
3975
|
+
...cost !== void 0 && { cost },
|
|
3759
3976
|
...usage.cost_details && { cost_details: usage.cost_details }
|
|
3760
3977
|
};
|
|
3761
3978
|
}
|
|
@@ -3901,6 +4118,7 @@ var BaseLLM = class {
|
|
|
3901
4118
|
const mergedToolCalls = [];
|
|
3902
4119
|
let usage;
|
|
3903
4120
|
let lastFinishReason;
|
|
4121
|
+
const extraFields = {};
|
|
3904
4122
|
const flushEvent = (rawEvent) => {
|
|
3905
4123
|
const lines = rawEvent.split("\n");
|
|
3906
4124
|
const dataLines = [];
|
|
@@ -3941,6 +4159,16 @@ var BaseLLM = class {
|
|
|
3941
4159
|
handlers.onChunk?.(textDelta);
|
|
3942
4160
|
}
|
|
3943
4161
|
}
|
|
4162
|
+
const knownKeys = ["role", "content", "tool_calls"];
|
|
4163
|
+
for (const key of Object.keys(delta)) {
|
|
4164
|
+
if (knownKeys.includes(key)) continue;
|
|
4165
|
+
const val = delta[key];
|
|
4166
|
+
if (typeof val === "string") {
|
|
4167
|
+
extraFields[key] = (extraFields[key] || "") + val;
|
|
4168
|
+
} else if (val !== void 0 && val !== null) {
|
|
4169
|
+
extraFields[key] = val;
|
|
4170
|
+
}
|
|
4171
|
+
}
|
|
3944
4172
|
const toolDeltas = Array.isArray(delta.tool_calls) ? delta.tool_calls : [];
|
|
3945
4173
|
for (const td of toolDeltas) {
|
|
3946
4174
|
let toolCall;
|
|
@@ -3948,6 +4176,7 @@ var BaseLLM = class {
|
|
|
3948
4176
|
toolCall = mergedToolCalls.find((tc) => tc.id === td.id);
|
|
3949
4177
|
if (!toolCall) {
|
|
3950
4178
|
toolCall = {
|
|
4179
|
+
...td,
|
|
3951
4180
|
id: td.id,
|
|
3952
4181
|
type: "function",
|
|
3953
4182
|
function: { name: td.function?.name ?? "", arguments: "" }
|
|
@@ -3994,7 +4223,12 @@ var BaseLLM = class {
|
|
|
3994
4223
|
if (buffer.trim()) flushEvent(buffer);
|
|
3995
4224
|
content = content.replace(/^\n+/, "");
|
|
3996
4225
|
const tool_calls = mergedToolCalls.length ? mergedToolCalls : void 0;
|
|
3997
|
-
return {
|
|
4226
|
+
return {
|
|
4227
|
+
content,
|
|
4228
|
+
...tool_calls ? { tool_calls } : {},
|
|
4229
|
+
...usage ? { usage } : {},
|
|
4230
|
+
...extraFields
|
|
4231
|
+
};
|
|
3998
4232
|
}
|
|
3999
4233
|
};
|
|
4000
4234
|
|
|
@@ -4364,8 +4598,9 @@ var GithubAuthTransport = class {
|
|
|
4364
4598
|
method: "GET",
|
|
4365
4599
|
headers: {
|
|
4366
4600
|
Authorization: `Bearer ${this.accessToken}`,
|
|
4367
|
-
"user-agent": "GitHubCopilotChat/0.
|
|
4368
|
-
"editor-version": "vscode/1.
|
|
4601
|
+
"user-agent": "GitHubCopilotChat/0.33.1",
|
|
4602
|
+
"editor-version": "vscode/1.106.1",
|
|
4603
|
+
"x-github-api-version": "2025-10-01",
|
|
4369
4604
|
accept: "application/json"
|
|
4370
4605
|
},
|
|
4371
4606
|
signal
|
|
@@ -4454,11 +4689,12 @@ var GithubAuthTransport = class {
|
|
|
4454
4689
|
return res;
|
|
4455
4690
|
}
|
|
4456
4691
|
async postStream(url, body, headers, signal) {
|
|
4457
|
-
|
|
4692
|
+
let hdrs = this.makeAuthHeaders({ Accept: "text/event-stream", ...headers || {} }, body);
|
|
4693
|
+
if ((!this.apiKey || !hdrs.Authorization) && this.accessToken) {
|
|
4458
4694
|
await this.exchangeToken(signal);
|
|
4695
|
+
hdrs = this.makeAuthHeaders({ Accept: "text/event-stream", ...headers || {} }, body);
|
|
4459
4696
|
}
|
|
4460
4697
|
const fullUrl = this.buildFullUrl(url);
|
|
4461
|
-
const hdrs = this.makeAuthHeaders({ Accept: "text/event-stream", ...headers || {} }, body);
|
|
4462
4698
|
let res = await this.inner.postStream(fullUrl, body, hdrs, signal);
|
|
4463
4699
|
if (res.status === 401 && this.accessToken) {
|
|
4464
4700
|
await this.exchangeToken(signal);
|
|
@@ -4529,6 +4765,105 @@ function createTransport(inner, defaultBaseUrl, apiKey, baseUrl, version) {
|
|
|
4529
4765
|
return new SimpleBearerAuthTransport(inner, defaultBaseUrl, apiKey, baseUrl, version);
|
|
4530
4766
|
}
|
|
4531
4767
|
|
|
4768
|
+
// llm-providers/model-limits.ts
|
|
4769
|
+
function normalizeModelLimits(provider, model) {
|
|
4770
|
+
switch (provider.toLowerCase()) {
|
|
4771
|
+
case "github": {
|
|
4772
|
+
const capabilities = model.capabilities;
|
|
4773
|
+
const limits = capabilities?.limits;
|
|
4774
|
+
const contextWindow = limits?.max_context_window_tokens;
|
|
4775
|
+
if (!contextWindow) return null;
|
|
4776
|
+
return {
|
|
4777
|
+
contextWindow,
|
|
4778
|
+
maxOutput: limits?.max_output_tokens
|
|
4779
|
+
};
|
|
4780
|
+
}
|
|
4781
|
+
case "deepinfra": {
|
|
4782
|
+
const metadata = model.metadata;
|
|
4783
|
+
const contextLength = metadata?.context_length;
|
|
4784
|
+
if (!contextLength) return null;
|
|
4785
|
+
return {
|
|
4786
|
+
contextWindow: contextLength,
|
|
4787
|
+
maxOutput: metadata?.max_tokens
|
|
4788
|
+
};
|
|
4789
|
+
}
|
|
4790
|
+
case "moonshot": {
|
|
4791
|
+
const contextLength = model.context_length;
|
|
4792
|
+
if (!contextLength) return null;
|
|
4793
|
+
return { contextWindow: contextLength };
|
|
4794
|
+
}
|
|
4795
|
+
case "openrouter": {
|
|
4796
|
+
const topProvider = model.top_provider;
|
|
4797
|
+
const contextLength = model.context_length ?? topProvider?.context_length;
|
|
4798
|
+
if (!contextLength) return null;
|
|
4799
|
+
return {
|
|
4800
|
+
contextWindow: contextLength,
|
|
4801
|
+
maxOutput: topProvider?.max_completion_tokens
|
|
4802
|
+
};
|
|
4803
|
+
}
|
|
4804
|
+
default: {
|
|
4805
|
+
const contextLength = model.context_length;
|
|
4806
|
+
if (!contextLength) return null;
|
|
4807
|
+
return { contextWindow: contextLength };
|
|
4808
|
+
}
|
|
4809
|
+
}
|
|
4810
|
+
}
|
|
4811
|
+
var FALLBACK_LIMITS = {
|
|
4812
|
+
zai: {
|
|
4813
|
+
"glm-4.6": { contextWindow: 2e5, maxOutput: 128e3 }
|
|
4814
|
+
},
|
|
4815
|
+
openrouter: {
|
|
4816
|
+
"anthropic/claude-sonnet-4": { contextWindow: 2e5, maxOutput: 16e3 },
|
|
4817
|
+
"anthropic/claude-3.5-sonnet": { contextWindow: 2e5, maxOutput: 8192 },
|
|
4818
|
+
"openai/gpt-4o": { contextWindow: 128e3, maxOutput: 16384 },
|
|
4819
|
+
"openai/gpt-4.1": { contextWindow: 128e3, maxOutput: 32768 },
|
|
4820
|
+
"openai/gpt-4o-mini": { contextWindow: 128e3, maxOutput: 16384 },
|
|
4821
|
+
"google/gemini-pro-1.5": { contextWindow: 2097152, maxOutput: 8192 },
|
|
4822
|
+
"meta-llama/llama-3.1-70b-instruct": { contextWindow: 131072, maxOutput: 131072 }
|
|
4823
|
+
},
|
|
4824
|
+
github: {
|
|
4825
|
+
"gpt-4.1": { contextWindow: 128e3, maxOutput: 16384 },
|
|
4826
|
+
"gpt-4o": { contextWindow: 128e3, maxOutput: 16384 },
|
|
4827
|
+
"gpt-4o-mini": { contextWindow: 128e3, maxOutput: 16384 },
|
|
4828
|
+
"claude-sonnet-4": { contextWindow: 2e5, maxOutput: 16e3 },
|
|
4829
|
+
"claude-3.5-sonnet": { contextWindow: 2e5, maxOutput: 8192 },
|
|
4830
|
+
o1: { contextWindow: 2e5, maxOutput: 1e5 },
|
|
4831
|
+
"o1-mini": { contextWindow: 128e3, maxOutput: 65536 }
|
|
4832
|
+
},
|
|
4833
|
+
deepinfra: {
|
|
4834
|
+
"meta-llama/Meta-Llama-3.1-70B-Instruct": { contextWindow: 131072, maxOutput: 131072 },
|
|
4835
|
+
"meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo": { contextWindow: 131072, maxOutput: 131072 },
|
|
4836
|
+
"Qwen/Qwen2.5-72B-Instruct": { contextWindow: 131072, maxOutput: 131072 }
|
|
4837
|
+
},
|
|
4838
|
+
moonshot: {
|
|
4839
|
+
"moonshot-v1-8k": { contextWindow: 8192 },
|
|
4840
|
+
"moonshot-v1-32k": { contextWindow: 32768 },
|
|
4841
|
+
"moonshot-v1-128k": { contextWindow: 131072 },
|
|
4842
|
+
"kimi-k2-turbo-preview": { contextWindow: 262144 }
|
|
4843
|
+
},
|
|
4844
|
+
anthropic: {
|
|
4845
|
+
"claude-sonnet-4-5": { contextWindow: 2e5, maxOutput: 16e3 },
|
|
4846
|
+
"claude-3-5-sonnet-20241022": { contextWindow: 2e5, maxOutput: 8192 },
|
|
4847
|
+
"claude-3-opus-20240229": { contextWindow: 2e5, maxOutput: 4096 },
|
|
4848
|
+
"claude-3-haiku-20240307": { contextWindow: 2e5, maxOutput: 4096 }
|
|
4849
|
+
}
|
|
4850
|
+
};
|
|
4851
|
+
function getFallbackLimits(provider, model) {
|
|
4852
|
+
const providerLimits = FALLBACK_LIMITS[provider.toLowerCase()];
|
|
4853
|
+
if (!providerLimits) return null;
|
|
4854
|
+
return providerLimits[model] ?? null;
|
|
4855
|
+
}
|
|
4856
|
+
function normalizeModelInfo(provider, model) {
|
|
4857
|
+
const id = model.id;
|
|
4858
|
+
const name = model.name ?? id;
|
|
4859
|
+
const limits = normalizeModelLimits(provider, model);
|
|
4860
|
+
return {
|
|
4861
|
+
id,
|
|
4862
|
+
name,
|
|
4863
|
+
...limits ? { limits } : {}
|
|
4864
|
+
};
|
|
4865
|
+
}
|
|
4866
|
+
|
|
4532
4867
|
// llm-providers/llm-github.ts
|
|
4533
4868
|
var GithubLLM = class extends BaseLLM {
|
|
4534
4869
|
opts;
|
|
@@ -4552,6 +4887,49 @@ var GithubLLM = class extends BaseLLM {
|
|
|
4552
4887
|
accessToken: this.opts.accessToken
|
|
4553
4888
|
});
|
|
4554
4889
|
}
|
|
4890
|
+
async getModels(signal) {
|
|
4891
|
+
const res = await this.getTransport().get("/models", void 0, signal);
|
|
4892
|
+
if (!res.ok) {
|
|
4893
|
+
const text = await res.text();
|
|
4894
|
+
throw new LLMError(text || `Failed to fetch models: ${res.status}`, res.status);
|
|
4895
|
+
}
|
|
4896
|
+
const body = await res.json();
|
|
4897
|
+
return body.data.map((m) => normalizeModelInfo("github", m));
|
|
4898
|
+
}
|
|
4899
|
+
handleError(error, model) {
|
|
4900
|
+
if (error instanceof LLMError) {
|
|
4901
|
+
try {
|
|
4902
|
+
const errorBody = JSON.parse(error.message);
|
|
4903
|
+
if (errorBody?.error?.code === "unsupported_api_for_model") {
|
|
4904
|
+
throw new LLMError(
|
|
4905
|
+
`The model '${model}' is not supported for chat completions. Please select a different model using '/model'.`,
|
|
4906
|
+
error.statusCode,
|
|
4907
|
+
false
|
|
4908
|
+
// Not retryable
|
|
4909
|
+
);
|
|
4910
|
+
}
|
|
4911
|
+
} catch (e) {
|
|
4912
|
+
if (e instanceof LLMError && e.message.includes("not supported")) {
|
|
4913
|
+
throw e;
|
|
4914
|
+
}
|
|
4915
|
+
}
|
|
4916
|
+
}
|
|
4917
|
+
throw error;
|
|
4918
|
+
}
|
|
4919
|
+
async generateCompletion(params, signal) {
|
|
4920
|
+
try {
|
|
4921
|
+
return await super.generateCompletion(params, signal);
|
|
4922
|
+
} catch (error) {
|
|
4923
|
+
this.handleError(error, params.model);
|
|
4924
|
+
}
|
|
4925
|
+
}
|
|
4926
|
+
async streamCompletion(params, handlers, signal) {
|
|
4927
|
+
try {
|
|
4928
|
+
return await super.streamCompletion(params, handlers, signal);
|
|
4929
|
+
} catch (error) {
|
|
4930
|
+
this.handleError(error, params.model);
|
|
4931
|
+
}
|
|
4932
|
+
}
|
|
4555
4933
|
};
|
|
4556
4934
|
|
|
4557
4935
|
// llm-providers/llm-anthropic-aisdk.ts
|
|
@@ -5049,11 +5427,13 @@ var GenericLLM = class extends BaseLLM {
|
|
|
5049
5427
|
opts;
|
|
5050
5428
|
includeUsage;
|
|
5051
5429
|
modelConfig;
|
|
5430
|
+
providerName;
|
|
5052
5431
|
constructor(baseUrl, modelConfig, opts = {}) {
|
|
5053
|
-
const { enablePromptCaching = false, includeUsage = false, ...restOpts } = opts;
|
|
5432
|
+
const { enablePromptCaching = false, includeUsage = false, providerName = "unknown", ...restOpts } = opts;
|
|
5054
5433
|
super(opts.apiUrl || baseUrl, { enablePromptCaching });
|
|
5055
5434
|
this.includeUsage = includeUsage;
|
|
5056
5435
|
this.modelConfig = modelConfig;
|
|
5436
|
+
this.providerName = providerName;
|
|
5057
5437
|
this.opts = restOpts;
|
|
5058
5438
|
}
|
|
5059
5439
|
createTransport() {
|
|
@@ -5071,7 +5451,10 @@ var GenericLLM = class extends BaseLLM {
|
|
|
5071
5451
|
throw new Error("Provider does not support getModels");
|
|
5072
5452
|
}
|
|
5073
5453
|
if (Array.isArray(this.modelConfig)) {
|
|
5074
|
-
return this.modelConfig.map((m) =>
|
|
5454
|
+
return this.modelConfig.map((m) => {
|
|
5455
|
+
const raw = typeof m === "string" ? { id: m } : m;
|
|
5456
|
+
return normalizeModelInfo(this.providerName, raw);
|
|
5457
|
+
});
|
|
5075
5458
|
}
|
|
5076
5459
|
if (typeof this.modelConfig === "string") {
|
|
5077
5460
|
const transport2 = this.createTransport();
|
|
@@ -5081,7 +5464,7 @@ var GenericLLM = class extends BaseLLM {
|
|
|
5081
5464
|
throw new Error(`Failed to fetch models: ${res2.status} ${text}`);
|
|
5082
5465
|
}
|
|
5083
5466
|
const data2 = await res2.json();
|
|
5084
|
-
return data2.data;
|
|
5467
|
+
return data2.data.map((m) => normalizeModelInfo(this.providerName, m));
|
|
5085
5468
|
}
|
|
5086
5469
|
const transport = this.createTransport();
|
|
5087
5470
|
const res = await transport.get("/models", void 0, signal);
|
|
@@ -5090,7 +5473,7 @@ var GenericLLM = class extends BaseLLM {
|
|
|
5090
5473
|
throw new Error(`Failed to fetch models: ${res.status} ${text}`);
|
|
5091
5474
|
}
|
|
5092
5475
|
const data = await res.json();
|
|
5093
|
-
return data.data;
|
|
5476
|
+
return data.data.map((m) => normalizeModelInfo(this.providerName, m));
|
|
5094
5477
|
}
|
|
5095
5478
|
async generateCompletion(params, signal) {
|
|
5096
5479
|
let enhancedParams = params;
|
|
@@ -5149,6 +5532,7 @@ function createLLM(providerName, options = {}, customProviders) {
|
|
|
5149
5532
|
const modelConfig = normalizeModelConfig(config);
|
|
5150
5533
|
return new GenericLLM(config.baseUrl, modelConfig, {
|
|
5151
5534
|
...options,
|
|
5535
|
+
providerName: config.name,
|
|
5152
5536
|
enablePromptCaching: options.enablePromptCaching ?? config.features.promptCaching,
|
|
5153
5537
|
includeUsage: options.includeUsage ?? config.features.includeUsage
|
|
5154
5538
|
});
|
|
@@ -5434,31 +5818,6 @@ function isValidConfig(value) {
|
|
|
5434
5818
|
}
|
|
5435
5819
|
return true;
|
|
5436
5820
|
}
|
|
5437
|
-
|
|
5438
|
-
// events.ts
|
|
5439
|
-
var PersistingConsoleEventPort = class {
|
|
5440
|
-
memory;
|
|
5441
|
-
maxPerConversation;
|
|
5442
|
-
writeQueue = Promise.resolve();
|
|
5443
|
-
constructor(opts) {
|
|
5444
|
-
this.memory = opts?.memory ?? new PersistedMemory(new JsonFileMemoryPersistence(opts?.filename || "events.json"));
|
|
5445
|
-
this.maxPerConversation = opts?.maxPerConversation ?? 500;
|
|
5446
|
-
}
|
|
5447
|
-
async emit(event) {
|
|
5448
|
-
this.writeQueue = this.writeQueue.then(async () => {
|
|
5449
|
-
try {
|
|
5450
|
-
const key = event?.conversationId ?? "default";
|
|
5451
|
-
const existing = await this.memory.get(key);
|
|
5452
|
-
const next = [...existing, { ...event }];
|
|
5453
|
-
const max = this.maxPerConversation;
|
|
5454
|
-
const trimmed = max > 0 && next.length > max ? next.slice(next.length - max) : next;
|
|
5455
|
-
await this.memory.set(key, trimmed);
|
|
5456
|
-
} catch {
|
|
5457
|
-
}
|
|
5458
|
-
});
|
|
5459
|
-
return this.writeQueue;
|
|
5460
|
-
}
|
|
5461
|
-
};
|
|
5462
5821
|
export {
|
|
5463
5822
|
AGENT_CREATOR_SYSTEM_PROMPT,
|
|
5464
5823
|
AgentEventTypes,
|
|
@@ -5482,11 +5841,13 @@ export {
|
|
|
5482
5841
|
GithubLLM,
|
|
5483
5842
|
InMemoryMemory,
|
|
5484
5843
|
InMemoryMetadata,
|
|
5844
|
+
InMemoryMetricsPort,
|
|
5485
5845
|
JsonFileMemoryPersistence,
|
|
5486
5846
|
LLMError,
|
|
5487
5847
|
LLMResolver,
|
|
5488
5848
|
MCPToolPort,
|
|
5489
5849
|
MemoryPortMetadataAdapter,
|
|
5850
|
+
NoopMetricsPort,
|
|
5490
5851
|
NoopReminders,
|
|
5491
5852
|
PersistedMemory,
|
|
5492
5853
|
PersistingConsoleEventPort,
|
|
@@ -5499,10 +5860,14 @@ export {
|
|
|
5499
5860
|
buildAgentCreationPrompt,
|
|
5500
5861
|
buildInjectedSystem,
|
|
5501
5862
|
canonicalizeTerminalPaste,
|
|
5863
|
+
createEmptySnapshot,
|
|
5502
5864
|
createLLM,
|
|
5503
5865
|
generateFolderTree,
|
|
5504
5866
|
getAvailableProviders,
|
|
5867
|
+
getFallbackLimits,
|
|
5505
5868
|
loadMCPConfig,
|
|
5869
|
+
normalizeModelInfo,
|
|
5870
|
+
normalizeModelLimits,
|
|
5506
5871
|
normalizeNewlines,
|
|
5507
5872
|
renderTemplate,
|
|
5508
5873
|
resolveBackspaces,
|