@nuvin/nuvin-core 1.2.0 → 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 +111 -24
- package/dist/index.js +796 -519
- 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,307 +904,89 @@ 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
|
-
const { usage: _usage, ...extraField } = result;
|
|
454
|
-
accumulatedMessages.push({
|
|
455
|
-
...extraField,
|
|
456
|
-
role: "assistant",
|
|
457
|
-
content: result.content ?? null,
|
|
458
|
-
tool_calls: approvedCalls
|
|
459
|
-
});
|
|
460
|
-
for (const tr of toolResults) {
|
|
461
|
-
const contentStr = tr.status === "error" ? String(tr.result) : typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result);
|
|
462
|
-
accumulatedMessages.push({ role: "tool", content: contentStr, tool_call_id: tr.id, name: tr.name });
|
|
463
|
-
}
|
|
464
|
-
if (opts.signal?.aborted) throw new Error("Aborted");
|
|
465
|
-
streamedAssistantContent = "";
|
|
466
|
-
if (opts.stream && typeof this.deps.llm.streamCompletion === "function") {
|
|
467
|
-
let isFirstChunk = true;
|
|
468
|
-
result = await this.deps.llm.streamCompletion(
|
|
469
|
-
{ ...params, messages: accumulatedMessages },
|
|
470
|
-
{
|
|
471
|
-
onChunk: async (delta, usage) => {
|
|
472
|
-
try {
|
|
473
|
-
streamedAssistantContent += delta;
|
|
474
|
-
} catch {
|
|
475
|
-
}
|
|
476
|
-
const cleanDelta = isFirstChunk ? delta.replace(/^\n+/, "") : delta;
|
|
477
|
-
isFirstChunk = false;
|
|
478
|
-
const chunkEvent = {
|
|
479
|
-
type: AgentEventTypes.AssistantChunk,
|
|
480
|
-
conversationId: convo,
|
|
481
|
-
messageId: msgId,
|
|
482
|
-
delta: cleanDelta,
|
|
483
|
-
...usage && { usage }
|
|
484
|
-
};
|
|
485
|
-
await this.deps.events?.emit(chunkEvent);
|
|
486
|
-
},
|
|
487
|
-
onStreamFinish: async (finishReason, usage) => {
|
|
488
|
-
const finishEvent = {
|
|
489
|
-
type: AgentEventTypes.StreamFinish,
|
|
490
|
-
conversationId: convo,
|
|
491
|
-
messageId: msgId,
|
|
492
|
-
...finishReason && { finishReason },
|
|
493
|
-
...usage && { usage }
|
|
494
|
-
};
|
|
495
|
-
await this.deps.events?.emit(finishEvent);
|
|
496
|
-
}
|
|
497
|
-
},
|
|
498
|
-
opts.signal
|
|
499
|
-
);
|
|
500
|
-
} else {
|
|
501
|
-
result = await this.deps.llm.generateCompletion({ ...params, messages: accumulatedMessages }, opts.signal);
|
|
502
|
-
}
|
|
503
|
-
if (!result.tool_calls?.length && result.content && !finalResponseSaved) {
|
|
504
|
-
const content2 = opts.stream ? streamedAssistantContent : result.content;
|
|
505
|
-
const assistantMsg2 = {
|
|
506
|
-
id: msgId,
|
|
507
|
-
role: "assistant",
|
|
508
|
-
content: content2,
|
|
509
|
-
timestamp: this.deps.clock.iso(),
|
|
510
|
-
usage: result.usage
|
|
511
|
-
};
|
|
512
|
-
await this.deps.memory.append(convo, [assistantMsg2]);
|
|
513
|
-
finalResponseSaved = true;
|
|
514
|
-
if (content2.trim()) {
|
|
515
|
-
const messageEvent = {
|
|
516
|
-
type: AgentEventTypes.AssistantMessage,
|
|
517
|
-
conversationId: convo,
|
|
518
|
-
messageId: msgId,
|
|
519
|
-
content: content2,
|
|
520
|
-
...result.usage && { usage: result.usage }
|
|
521
|
-
};
|
|
522
|
-
await this.deps.events?.emit(messageEvent);
|
|
523
|
-
}
|
|
907
|
+
await this.events?.emit(messageEvent);
|
|
524
908
|
}
|
|
525
909
|
}
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
content: result.content,
|
|
535
|
-
...result.usage && { usage: result.usage }
|
|
536
|
-
};
|
|
537
|
-
await this.deps.events?.emit(messageEvent);
|
|
538
|
-
}
|
|
539
|
-
const responseContent = toolApprovalDenied ? denialMessage : result.content;
|
|
540
|
-
const resp = {
|
|
541
|
-
id: msgId,
|
|
542
|
-
content: responseContent,
|
|
543
|
-
role: MessageRoles.Assistant,
|
|
544
|
-
timestamp,
|
|
545
|
-
metadata: {
|
|
546
|
-
model: this.cfg.model,
|
|
547
|
-
provider: "echo",
|
|
548
|
-
agentId: this.cfg.id,
|
|
549
|
-
responseTime: t1 - t0,
|
|
550
|
-
promptTokens: result.usage?.prompt_tokens,
|
|
551
|
-
completionTokens: result.usage?.completion_tokens,
|
|
552
|
-
totalTokens: result.usage?.total_tokens,
|
|
553
|
-
estimatedCost: this.deps.cost.estimate(this.cfg.model, result.usage),
|
|
554
|
-
toolCalls: allToolResults.length
|
|
555
|
-
}
|
|
556
|
-
};
|
|
557
|
-
await this.deps.events?.emit({
|
|
558
|
-
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,
|
|
559
918
|
conversationId: convo,
|
|
560
919
|
messageId: msgId,
|
|
561
|
-
|
|
562
|
-
usage: result.usage
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
handleToolApproval(approvalId, decision, approvedCalls) {
|
|
583
|
-
const approval = this.pendingApprovals.get(approvalId);
|
|
584
|
-
if (!approval) {
|
|
585
|
-
console.warn(`[Orchestrator] Received approval for unknown or already processed ID: ${approvalId}`);
|
|
586
|
-
return;
|
|
587
|
-
}
|
|
588
|
-
this.pendingApprovals.delete(approvalId);
|
|
589
|
-
if (decision === "deny") {
|
|
590
|
-
approval.reject(new Error("Tool execution denied by user"));
|
|
591
|
-
} else if (decision === "approve_all" || decision === "approve") {
|
|
592
|
-
approval.resolve(approvedCalls || []);
|
|
593
|
-
} else {
|
|
594
|
-
approval.reject(new Error(`Invalid approval decision: ${decision}`));
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
toInvocations(toolCalls) {
|
|
598
|
-
return toolCalls.map((tc) => {
|
|
599
|
-
let parameters = {};
|
|
600
|
-
try {
|
|
601
|
-
parameters = JSON.parse(tc.function.arguments || "{}");
|
|
602
|
-
} catch {
|
|
603
|
-
parameters = {};
|
|
604
|
-
}
|
|
605
|
-
return { id: tc.id, name: tc.function.name, parameters };
|
|
606
|
-
});
|
|
607
|
-
}
|
|
608
|
-
};
|
|
609
|
-
|
|
610
|
-
// context.ts
|
|
611
|
-
var toProviderContent = (content) => {
|
|
612
|
-
if (content === null || content === void 0) {
|
|
613
|
-
return "";
|
|
614
|
-
}
|
|
615
|
-
if (typeof content === "string") {
|
|
616
|
-
return content;
|
|
617
|
-
}
|
|
618
|
-
if (content.type === "parts") {
|
|
619
|
-
const providerParts = [];
|
|
620
|
-
for (const part of content.parts) {
|
|
621
|
-
if (part.type === "text") {
|
|
622
|
-
if (part.text.length > 0) {
|
|
623
|
-
providerParts.push({ type: "text", text: part.text });
|
|
624
|
-
}
|
|
625
|
-
continue;
|
|
626
|
-
}
|
|
627
|
-
const label = part.altText ?? (part.name ? `Image attachment: ${part.name}` : void 0);
|
|
628
|
-
if (label) {
|
|
629
|
-
providerParts.push({ type: "text", text: label });
|
|
630
|
-
}
|
|
631
|
-
const url = `data:${part.mimeType};base64,${part.data}`;
|
|
632
|
-
providerParts.push({ type: "image_url", image_url: { url } });
|
|
633
|
-
}
|
|
634
|
-
return providerParts.length > 0 ? providerParts : [];
|
|
635
|
-
}
|
|
636
|
-
return [];
|
|
637
|
-
};
|
|
638
|
-
var SimpleContextBuilder = class {
|
|
639
|
-
toProviderMessages(history, systemPrompt, newUserContent) {
|
|
640
|
-
const transformed = [];
|
|
641
|
-
for (const m of history) {
|
|
642
|
-
const providerContent = toProviderContent(m.content);
|
|
643
|
-
if (m.role === "user") {
|
|
644
|
-
transformed.push({ role: "user", content: providerContent ?? "" });
|
|
645
|
-
} else if (m.role === "assistant") {
|
|
646
|
-
if (m.tool_calls && m.tool_calls.length > 0) {
|
|
647
|
-
transformed.push({
|
|
648
|
-
...m,
|
|
649
|
-
role: "assistant",
|
|
650
|
-
content: providerContent ?? null,
|
|
651
|
-
tool_calls: m.tool_calls
|
|
652
|
-
});
|
|
653
|
-
} else {
|
|
654
|
-
transformed.push({
|
|
655
|
-
...m,
|
|
656
|
-
role: "assistant",
|
|
657
|
-
content: providerContent ?? ""
|
|
658
|
-
});
|
|
659
|
-
}
|
|
660
|
-
} else if (m.role === "tool") {
|
|
661
|
-
if (m.tool_call_id) {
|
|
662
|
-
transformed.push({
|
|
663
|
-
role: "tool",
|
|
664
|
-
content: typeof providerContent === "string" ? providerContent : providerContent ?? "",
|
|
665
|
-
tool_call_id: m.tool_call_id,
|
|
666
|
-
name: m.name
|
|
667
|
-
});
|
|
668
|
-
}
|
|
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
|
|
669
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;
|
|
951
|
+
}
|
|
952
|
+
async waitForToolApproval(toolCalls, conversationId, messageId) {
|
|
953
|
+
const approvalId = this.ids.uuid();
|
|
954
|
+
return new Promise((resolve6, reject) => {
|
|
955
|
+
this.pendingApprovals.set(approvalId, { resolve: resolve6, reject });
|
|
956
|
+
this.events?.emit({
|
|
957
|
+
type: AgentEventTypes.ToolApprovalRequired,
|
|
958
|
+
conversationId,
|
|
959
|
+
messageId,
|
|
960
|
+
toolCalls,
|
|
961
|
+
approvalId
|
|
962
|
+
});
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
handleToolApproval(approvalId, decision, approvedCalls) {
|
|
966
|
+
const approval = this.pendingApprovals.get(approvalId);
|
|
967
|
+
if (!approval) {
|
|
968
|
+
console.warn(`[Orchestrator] Received approval for unknown or already processed ID: ${approvalId}`);
|
|
969
|
+
return;
|
|
670
970
|
}
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
})
|
|
675
|
-
|
|
971
|
+
this.pendingApprovals.delete(approvalId);
|
|
972
|
+
if (decision === "deny") {
|
|
973
|
+
approval.reject(new Error("Tool execution denied by user"));
|
|
974
|
+
} else if (decision === "approve_all" || decision === "approve") {
|
|
975
|
+
approval.resolve(approvedCalls || []);
|
|
976
|
+
} else {
|
|
977
|
+
approval.reject(new Error(`Invalid approval decision: ${decision}`));
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
toInvocations(toolCalls) {
|
|
981
|
+
return toolCalls.map((tc) => {
|
|
982
|
+
let parameters = {};
|
|
983
|
+
try {
|
|
984
|
+
parameters = JSON.parse(tc.function.arguments || "{}");
|
|
985
|
+
} catch {
|
|
986
|
+
parameters = {};
|
|
987
|
+
}
|
|
988
|
+
return { id: tc.id, name: tc.function.name, parameters };
|
|
989
|
+
});
|
|
676
990
|
}
|
|
677
991
|
};
|
|
678
992
|
|
|
@@ -937,7 +1251,12 @@ async function buildTree(dir, rootDir, gitignore, options, depth, fileCount) {
|
|
|
937
1251
|
if (depth >= options.maxDepth || fileCount.count >= options.maxFiles) {
|
|
938
1252
|
return [];
|
|
939
1253
|
}
|
|
940
|
-
|
|
1254
|
+
let entries;
|
|
1255
|
+
try {
|
|
1256
|
+
entries = await fs2.readdir(dir, { withFileTypes: true });
|
|
1257
|
+
} catch {
|
|
1258
|
+
return [];
|
|
1259
|
+
}
|
|
941
1260
|
const lines = [];
|
|
942
1261
|
const sortedEntries = entries.sort((a, b) => {
|
|
943
1262
|
if (a.isDirectory() && !b.isDirectory()) return -1;
|
|
@@ -981,123 +1300,6 @@ async function generateFolderTree(rootDir, options = {}) {
|
|
|
981
1300
|
return [header, ...lines].join("\n");
|
|
982
1301
|
}
|
|
983
1302
|
|
|
984
|
-
// persistent/memory.ts
|
|
985
|
-
var InMemoryMemory = class {
|
|
986
|
-
store = /* @__PURE__ */ new Map();
|
|
987
|
-
async get(key) {
|
|
988
|
-
return this.store.get(key) ?? [];
|
|
989
|
-
}
|
|
990
|
-
async set(key, items) {
|
|
991
|
-
this.store.set(key, [...items]);
|
|
992
|
-
}
|
|
993
|
-
async append(key, items) {
|
|
994
|
-
const existing = this.store.get(key) ?? [];
|
|
995
|
-
this.store.set(key, [...existing, ...items]);
|
|
996
|
-
}
|
|
997
|
-
async delete(key) {
|
|
998
|
-
this.store.delete(key);
|
|
999
|
-
}
|
|
1000
|
-
async keys() {
|
|
1001
|
-
return Array.from(this.store.keys());
|
|
1002
|
-
}
|
|
1003
|
-
async clear() {
|
|
1004
|
-
this.store.clear();
|
|
1005
|
-
}
|
|
1006
|
-
async exportSnapshot() {
|
|
1007
|
-
const snap = {};
|
|
1008
|
-
for (const [k, v] of this.store.entries()) snap[k] = [...v];
|
|
1009
|
-
return snap;
|
|
1010
|
-
}
|
|
1011
|
-
async importSnapshot(snapshot) {
|
|
1012
|
-
this.store.clear();
|
|
1013
|
-
for (const [k, v] of Object.entries(snapshot)) this.store.set(k, [...v]);
|
|
1014
|
-
}
|
|
1015
|
-
};
|
|
1016
|
-
var JsonFileMemoryPersistence = class {
|
|
1017
|
-
constructor(filename = "history.json") {
|
|
1018
|
-
this.filename = filename;
|
|
1019
|
-
}
|
|
1020
|
-
async load() {
|
|
1021
|
-
try {
|
|
1022
|
-
const fs10 = await import("fs");
|
|
1023
|
-
if (!fs10.existsSync(this.filename)) return {};
|
|
1024
|
-
const text = fs10.readFileSync(this.filename, "utf-8");
|
|
1025
|
-
const data = JSON.parse(text);
|
|
1026
|
-
return typeof data === "object" && data ? data : {};
|
|
1027
|
-
} catch {
|
|
1028
|
-
console.warn(`Failed to load memory from ${this.filename}`);
|
|
1029
|
-
return {};
|
|
1030
|
-
}
|
|
1031
|
-
}
|
|
1032
|
-
async save(snapshot) {
|
|
1033
|
-
try {
|
|
1034
|
-
const fs10 = await import("fs");
|
|
1035
|
-
const path9 = await import("path");
|
|
1036
|
-
const dir = path9.dirname(this.filename);
|
|
1037
|
-
if (dir && dir !== "." && !fs10.existsSync(dir)) {
|
|
1038
|
-
fs10.mkdirSync(dir, { recursive: true });
|
|
1039
|
-
}
|
|
1040
|
-
fs10.writeFileSync(this.filename, JSON.stringify(snapshot, null, 2), "utf-8");
|
|
1041
|
-
} catch (err2) {
|
|
1042
|
-
console.warn(`Failed to save memory to ${this.filename}`, err2);
|
|
1043
|
-
}
|
|
1044
|
-
}
|
|
1045
|
-
};
|
|
1046
|
-
var PersistedMemory = class {
|
|
1047
|
-
constructor(persistence) {
|
|
1048
|
-
this.persistence = persistence;
|
|
1049
|
-
}
|
|
1050
|
-
inner = new InMemoryMemory();
|
|
1051
|
-
initialized = false;
|
|
1052
|
-
async ensureInitialized() {
|
|
1053
|
-
if (this.initialized) return;
|
|
1054
|
-
const snap = await this.persistence.load();
|
|
1055
|
-
if (snap && typeof snap === "object") await this.inner.importSnapshot(snap);
|
|
1056
|
-
this.initialized = true;
|
|
1057
|
-
}
|
|
1058
|
-
async save() {
|
|
1059
|
-
const snap = await this.inner.exportSnapshot();
|
|
1060
|
-
await this.persistence.save(snap);
|
|
1061
|
-
}
|
|
1062
|
-
async get(key) {
|
|
1063
|
-
await this.ensureInitialized();
|
|
1064
|
-
return this.inner.get(key);
|
|
1065
|
-
}
|
|
1066
|
-
async set(key, items) {
|
|
1067
|
-
await this.ensureInitialized();
|
|
1068
|
-
await this.inner.set(key, items);
|
|
1069
|
-
await this.save();
|
|
1070
|
-
}
|
|
1071
|
-
async append(key, items) {
|
|
1072
|
-
await this.ensureInitialized();
|
|
1073
|
-
await this.inner.append(key, items);
|
|
1074
|
-
await this.save();
|
|
1075
|
-
}
|
|
1076
|
-
async delete(key) {
|
|
1077
|
-
await this.ensureInitialized();
|
|
1078
|
-
await this.inner.delete(key);
|
|
1079
|
-
await this.save();
|
|
1080
|
-
}
|
|
1081
|
-
async keys() {
|
|
1082
|
-
await this.ensureInitialized();
|
|
1083
|
-
return this.inner.keys();
|
|
1084
|
-
}
|
|
1085
|
-
async clear() {
|
|
1086
|
-
await this.ensureInitialized();
|
|
1087
|
-
await this.inner.clear();
|
|
1088
|
-
await this.save();
|
|
1089
|
-
}
|
|
1090
|
-
async exportSnapshot() {
|
|
1091
|
-
await this.ensureInitialized();
|
|
1092
|
-
return this.inner.exportSnapshot();
|
|
1093
|
-
}
|
|
1094
|
-
async importSnapshot(snapshot) {
|
|
1095
|
-
await this.ensureInitialized();
|
|
1096
|
-
await this.inner.importSnapshot(snapshot);
|
|
1097
|
-
await this.save();
|
|
1098
|
-
}
|
|
1099
|
-
};
|
|
1100
|
-
|
|
1101
1303
|
// persistent/metadata-memory.ts
|
|
1102
1304
|
var MemoryPortMetadataAdapter = class {
|
|
1103
1305
|
constructor(memory, prefix = "__metadata__") {
|
|
@@ -1232,6 +1434,27 @@ var ConversationStore = class {
|
|
|
1232
1434
|
};
|
|
1233
1435
|
await this.metadataMemory.set(conversationId, updatedMetadata);
|
|
1234
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
|
+
}
|
|
1235
1458
|
async updateTopic(conversationId, topic) {
|
|
1236
1459
|
const metadata = await this.metadataMemory.get(conversationId);
|
|
1237
1460
|
const updatedMetadata = {
|
|
@@ -1294,41 +1517,6 @@ var ConversationContext = class {
|
|
|
1294
1517
|
}
|
|
1295
1518
|
};
|
|
1296
1519
|
|
|
1297
|
-
// id.ts
|
|
1298
|
-
var SimpleId = class {
|
|
1299
|
-
uuid() {
|
|
1300
|
-
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
1301
|
-
const r = Math.random() * 16 | 0;
|
|
1302
|
-
const v = c === "x" ? r : r & 3 | 8;
|
|
1303
|
-
return v.toString(16);
|
|
1304
|
-
});
|
|
1305
|
-
}
|
|
1306
|
-
};
|
|
1307
|
-
|
|
1308
|
-
// clock.ts
|
|
1309
|
-
var SystemClock = class {
|
|
1310
|
-
now() {
|
|
1311
|
-
return Date.now();
|
|
1312
|
-
}
|
|
1313
|
-
iso(dateMs) {
|
|
1314
|
-
return new Date(dateMs ?? Date.now()).toISOString();
|
|
1315
|
-
}
|
|
1316
|
-
};
|
|
1317
|
-
|
|
1318
|
-
// cost.ts
|
|
1319
|
-
var SimpleCost = class {
|
|
1320
|
-
estimate(_model, usage) {
|
|
1321
|
-
return usage?.cost;
|
|
1322
|
-
}
|
|
1323
|
-
};
|
|
1324
|
-
|
|
1325
|
-
// reminders.ts
|
|
1326
|
-
var NoopReminders = class {
|
|
1327
|
-
enhance(content, _opts) {
|
|
1328
|
-
return [content];
|
|
1329
|
-
}
|
|
1330
|
-
};
|
|
1331
|
-
|
|
1332
1520
|
// todo-store.ts
|
|
1333
1521
|
var TodoStore = class {
|
|
1334
1522
|
constructor(memory) {
|
|
@@ -3627,9 +3815,12 @@ var AgentFilePersistence = class {
|
|
|
3627
3815
|
* Load all agents from the agents directory
|
|
3628
3816
|
*/
|
|
3629
3817
|
async loadAll() {
|
|
3630
|
-
this.ensureAgentsDir();
|
|
3631
3818
|
const agents = [];
|
|
3632
3819
|
try {
|
|
3820
|
+
this.ensureAgentsDir();
|
|
3821
|
+
if (!fs8.existsSync(this.agentsDir)) {
|
|
3822
|
+
return agents;
|
|
3823
|
+
}
|
|
3633
3824
|
const files = fs8.readdirSync(this.agentsDir);
|
|
3634
3825
|
const yamlFiles = files.filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
|
|
3635
3826
|
for (const file of yamlFiles) {
|
|
@@ -3642,8 +3833,7 @@ var AgentFilePersistence = class {
|
|
|
3642
3833
|
console.warn(`Failed to load agent from ${file}:`, error);
|
|
3643
3834
|
}
|
|
3644
3835
|
}
|
|
3645
|
-
} catch (
|
|
3646
|
-
console.warn("Failed to read agents directory:", error);
|
|
3836
|
+
} catch (_error) {
|
|
3647
3837
|
}
|
|
3648
3838
|
return agents;
|
|
3649
3839
|
}
|
|
@@ -3774,6 +3964,7 @@ function normalizeUsage(usage) {
|
|
|
3774
3964
|
const prompt_tokens = usage.prompt_tokens ?? (typeof usageObj.input_tokens === "number" ? usageObj.input_tokens : void 0);
|
|
3775
3965
|
const completion_tokens = usage.completion_tokens ?? (typeof usageObj.output_tokens === "number" ? usageObj.output_tokens : void 0);
|
|
3776
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);
|
|
3777
3968
|
return {
|
|
3778
3969
|
prompt_tokens,
|
|
3779
3970
|
completion_tokens,
|
|
@@ -3781,7 +3972,7 @@ function normalizeUsage(usage) {
|
|
|
3781
3972
|
...usage.reasoning_tokens !== void 0 && { reasoning_tokens: usage.reasoning_tokens },
|
|
3782
3973
|
...usage.prompt_tokens_details && { prompt_tokens_details: usage.prompt_tokens_details },
|
|
3783
3974
|
...usage.completion_tokens_details && { completion_tokens_details: usage.completion_tokens_details },
|
|
3784
|
-
...
|
|
3975
|
+
...cost !== void 0 && { cost },
|
|
3785
3976
|
...usage.cost_details && { cost_details: usage.cost_details }
|
|
3786
3977
|
};
|
|
3787
3978
|
}
|
|
@@ -4574,6 +4765,105 @@ function createTransport(inner, defaultBaseUrl, apiKey, baseUrl, version) {
|
|
|
4574
4765
|
return new SimpleBearerAuthTransport(inner, defaultBaseUrl, apiKey, baseUrl, version);
|
|
4575
4766
|
}
|
|
4576
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
|
+
|
|
4577
4867
|
// llm-providers/llm-github.ts
|
|
4578
4868
|
var GithubLLM = class extends BaseLLM {
|
|
4579
4869
|
opts;
|
|
@@ -4604,7 +4894,7 @@ var GithubLLM = class extends BaseLLM {
|
|
|
4604
4894
|
throw new LLMError(text || `Failed to fetch models: ${res.status}`, res.status);
|
|
4605
4895
|
}
|
|
4606
4896
|
const body = await res.json();
|
|
4607
|
-
return body.data;
|
|
4897
|
+
return body.data.map((m) => normalizeModelInfo("github", m));
|
|
4608
4898
|
}
|
|
4609
4899
|
handleError(error, model) {
|
|
4610
4900
|
if (error instanceof LLMError) {
|
|
@@ -5137,11 +5427,13 @@ var GenericLLM = class extends BaseLLM {
|
|
|
5137
5427
|
opts;
|
|
5138
5428
|
includeUsage;
|
|
5139
5429
|
modelConfig;
|
|
5430
|
+
providerName;
|
|
5140
5431
|
constructor(baseUrl, modelConfig, opts = {}) {
|
|
5141
|
-
const { enablePromptCaching = false, includeUsage = false, ...restOpts } = opts;
|
|
5432
|
+
const { enablePromptCaching = false, includeUsage = false, providerName = "unknown", ...restOpts } = opts;
|
|
5142
5433
|
super(opts.apiUrl || baseUrl, { enablePromptCaching });
|
|
5143
5434
|
this.includeUsage = includeUsage;
|
|
5144
5435
|
this.modelConfig = modelConfig;
|
|
5436
|
+
this.providerName = providerName;
|
|
5145
5437
|
this.opts = restOpts;
|
|
5146
5438
|
}
|
|
5147
5439
|
createTransport() {
|
|
@@ -5159,7 +5451,10 @@ var GenericLLM = class extends BaseLLM {
|
|
|
5159
5451
|
throw new Error("Provider does not support getModels");
|
|
5160
5452
|
}
|
|
5161
5453
|
if (Array.isArray(this.modelConfig)) {
|
|
5162
|
-
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
|
+
});
|
|
5163
5458
|
}
|
|
5164
5459
|
if (typeof this.modelConfig === "string") {
|
|
5165
5460
|
const transport2 = this.createTransport();
|
|
@@ -5169,7 +5464,7 @@ var GenericLLM = class extends BaseLLM {
|
|
|
5169
5464
|
throw new Error(`Failed to fetch models: ${res2.status} ${text}`);
|
|
5170
5465
|
}
|
|
5171
5466
|
const data2 = await res2.json();
|
|
5172
|
-
return data2.data;
|
|
5467
|
+
return data2.data.map((m) => normalizeModelInfo(this.providerName, m));
|
|
5173
5468
|
}
|
|
5174
5469
|
const transport = this.createTransport();
|
|
5175
5470
|
const res = await transport.get("/models", void 0, signal);
|
|
@@ -5178,7 +5473,7 @@ var GenericLLM = class extends BaseLLM {
|
|
|
5178
5473
|
throw new Error(`Failed to fetch models: ${res.status} ${text}`);
|
|
5179
5474
|
}
|
|
5180
5475
|
const data = await res.json();
|
|
5181
|
-
return data.data;
|
|
5476
|
+
return data.data.map((m) => normalizeModelInfo(this.providerName, m));
|
|
5182
5477
|
}
|
|
5183
5478
|
async generateCompletion(params, signal) {
|
|
5184
5479
|
let enhancedParams = params;
|
|
@@ -5237,6 +5532,7 @@ function createLLM(providerName, options = {}, customProviders) {
|
|
|
5237
5532
|
const modelConfig = normalizeModelConfig(config);
|
|
5238
5533
|
return new GenericLLM(config.baseUrl, modelConfig, {
|
|
5239
5534
|
...options,
|
|
5535
|
+
providerName: config.name,
|
|
5240
5536
|
enablePromptCaching: options.enablePromptCaching ?? config.features.promptCaching,
|
|
5241
5537
|
includeUsage: options.includeUsage ?? config.features.includeUsage
|
|
5242
5538
|
});
|
|
@@ -5522,31 +5818,6 @@ function isValidConfig(value) {
|
|
|
5522
5818
|
}
|
|
5523
5819
|
return true;
|
|
5524
5820
|
}
|
|
5525
|
-
|
|
5526
|
-
// events.ts
|
|
5527
|
-
var PersistingConsoleEventPort = class {
|
|
5528
|
-
memory;
|
|
5529
|
-
maxPerConversation;
|
|
5530
|
-
writeQueue = Promise.resolve();
|
|
5531
|
-
constructor(opts) {
|
|
5532
|
-
this.memory = opts?.memory ?? new PersistedMemory(new JsonFileMemoryPersistence(opts?.filename || "events.json"));
|
|
5533
|
-
this.maxPerConversation = opts?.maxPerConversation ?? 500;
|
|
5534
|
-
}
|
|
5535
|
-
async emit(event) {
|
|
5536
|
-
this.writeQueue = this.writeQueue.then(async () => {
|
|
5537
|
-
try {
|
|
5538
|
-
const key = event?.conversationId ?? "default";
|
|
5539
|
-
const existing = await this.memory.get(key);
|
|
5540
|
-
const next = [...existing, { ...event }];
|
|
5541
|
-
const max = this.maxPerConversation;
|
|
5542
|
-
const trimmed = max > 0 && next.length > max ? next.slice(next.length - max) : next;
|
|
5543
|
-
await this.memory.set(key, trimmed);
|
|
5544
|
-
} catch {
|
|
5545
|
-
}
|
|
5546
|
-
});
|
|
5547
|
-
return this.writeQueue;
|
|
5548
|
-
}
|
|
5549
|
-
};
|
|
5550
5821
|
export {
|
|
5551
5822
|
AGENT_CREATOR_SYSTEM_PROMPT,
|
|
5552
5823
|
AgentEventTypes,
|
|
@@ -5570,11 +5841,13 @@ export {
|
|
|
5570
5841
|
GithubLLM,
|
|
5571
5842
|
InMemoryMemory,
|
|
5572
5843
|
InMemoryMetadata,
|
|
5844
|
+
InMemoryMetricsPort,
|
|
5573
5845
|
JsonFileMemoryPersistence,
|
|
5574
5846
|
LLMError,
|
|
5575
5847
|
LLMResolver,
|
|
5576
5848
|
MCPToolPort,
|
|
5577
5849
|
MemoryPortMetadataAdapter,
|
|
5850
|
+
NoopMetricsPort,
|
|
5578
5851
|
NoopReminders,
|
|
5579
5852
|
PersistedMemory,
|
|
5580
5853
|
PersistingConsoleEventPort,
|
|
@@ -5587,10 +5860,14 @@ export {
|
|
|
5587
5860
|
buildAgentCreationPrompt,
|
|
5588
5861
|
buildInjectedSystem,
|
|
5589
5862
|
canonicalizeTerminalPaste,
|
|
5863
|
+
createEmptySnapshot,
|
|
5590
5864
|
createLLM,
|
|
5591
5865
|
generateFolderTree,
|
|
5592
5866
|
getAvailableProviders,
|
|
5867
|
+
getFallbackLimits,
|
|
5593
5868
|
loadMCPConfig,
|
|
5869
|
+
normalizeModelInfo,
|
|
5870
|
+
normalizeModelLimits,
|
|
5594
5871
|
normalizeNewlines,
|
|
5595
5872
|
renderTemplate,
|
|
5596
5873
|
resolveBackspaces,
|