@nuvin/nuvin-core 1.2.0 → 1.3.1
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 +112 -24
- package/dist/index.js +817 -520
- 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,205 +904,56 @@ var AgentOrchestrator = class {
|
|
|
372
904
|
content: content2,
|
|
373
905
|
...result.usage && { usage: result.usage }
|
|
374
906
|
};
|
|
375
|
-
await this.
|
|
907
|
+
await this.events?.emit(messageEvent);
|
|
376
908
|
}
|
|
377
909
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
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
|
-
}
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
const t1 = this.deps.clock.now();
|
|
527
|
-
const timestamp = this.deps.clock.iso();
|
|
528
|
-
const shouldEmitFinalMessage = result.content?.trim() && !toolApprovalDenied && !finalResponseSaved;
|
|
529
|
-
if (shouldEmitFinalMessage) {
|
|
530
|
-
const messageEvent = {
|
|
531
|
-
type: AgentEventTypes.AssistantMessage,
|
|
532
|
-
conversationId: convo,
|
|
533
|
-
messageId: msgId,
|
|
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
|
-
|
|
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;
|
|
568
951
|
}
|
|
569
952
|
async waitForToolApproval(toolCalls, conversationId, messageId) {
|
|
570
|
-
const approvalId = this.
|
|
953
|
+
const approvalId = this.ids.uuid();
|
|
571
954
|
return new Promise((resolve6, reject) => {
|
|
572
955
|
this.pendingApprovals.set(approvalId, { resolve: resolve6, reject });
|
|
573
|
-
this.
|
|
956
|
+
this.events?.emit({
|
|
574
957
|
type: AgentEventTypes.ToolApprovalRequired,
|
|
575
958
|
conversationId,
|
|
576
959
|
messageId,
|
|
@@ -579,100 +962,31 @@ var AgentOrchestrator = class {
|
|
|
579
962
|
});
|
|
580
963
|
});
|
|
581
964
|
}
|
|
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
|
-
}
|
|
669
|
-
}
|
|
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
|
}
|
|
@@ -4520,11 +4711,13 @@ var BaseBearerAuthTransport = class {
|
|
|
4520
4711
|
apiKey;
|
|
4521
4712
|
baseUrl;
|
|
4522
4713
|
version;
|
|
4523
|
-
|
|
4714
|
+
customHeaders;
|
|
4715
|
+
constructor(inner, apiKey, baseUrl, version, customHeaders) {
|
|
4524
4716
|
this.inner = inner;
|
|
4525
4717
|
this.apiKey = apiKey;
|
|
4526
4718
|
this.baseUrl = baseUrl ?? this.getDefaultBaseUrl();
|
|
4527
4719
|
this.version = version;
|
|
4720
|
+
this.customHeaders = customHeaders;
|
|
4528
4721
|
}
|
|
4529
4722
|
buildFullUrl(path9) {
|
|
4530
4723
|
if (path9.startsWith("/")) {
|
|
@@ -4533,14 +4726,16 @@ var BaseBearerAuthTransport = class {
|
|
|
4533
4726
|
return path9;
|
|
4534
4727
|
}
|
|
4535
4728
|
makeAuthHeaders(headers) {
|
|
4536
|
-
if (!this.apiKey || this.apiKey.trim() === "") {
|
|
4537
|
-
throw new Error("API key missing");
|
|
4538
|
-
}
|
|
4539
4729
|
const base = headers ? { ...headers } : {};
|
|
4540
|
-
base.Authorization = `Bearer ${this.apiKey}`;
|
|
4541
4730
|
if (!base["User-Agent"] && this.version) {
|
|
4542
4731
|
base["User-Agent"] = `nuvin-cli/${this.version}`;
|
|
4543
4732
|
}
|
|
4733
|
+
if (this.apiKey && this.apiKey.trim() !== "" && !this.customHeaders?.Authorization) {
|
|
4734
|
+
base.Authorization = `Bearer ${this.apiKey}`;
|
|
4735
|
+
}
|
|
4736
|
+
if (this.customHeaders) {
|
|
4737
|
+
Object.assign(base, this.customHeaders);
|
|
4738
|
+
}
|
|
4544
4739
|
return base;
|
|
4545
4740
|
}
|
|
4546
4741
|
async get(url, headers, signal) {
|
|
@@ -4560,8 +4755,8 @@ var BaseBearerAuthTransport = class {
|
|
|
4560
4755
|
// transports/simple-bearer-transport.ts
|
|
4561
4756
|
var SimpleBearerAuthTransport = class extends BaseBearerAuthTransport {
|
|
4562
4757
|
defaultUrl;
|
|
4563
|
-
constructor(inner, defaultBaseUrl, apiKey, baseUrl, version) {
|
|
4564
|
-
super(inner, apiKey, baseUrl ?? defaultBaseUrl, version);
|
|
4758
|
+
constructor(inner, defaultBaseUrl, apiKey, baseUrl, version, customHeaders) {
|
|
4759
|
+
super(inner, apiKey, baseUrl ?? defaultBaseUrl, version, customHeaders);
|
|
4565
4760
|
this.defaultUrl = defaultBaseUrl;
|
|
4566
4761
|
}
|
|
4567
4762
|
getDefaultBaseUrl() {
|
|
@@ -4570,8 +4765,107 @@ var SimpleBearerAuthTransport = class extends BaseBearerAuthTransport {
|
|
|
4570
4765
|
};
|
|
4571
4766
|
|
|
4572
4767
|
// transports/transport-factory.ts
|
|
4573
|
-
function createTransport(inner, defaultBaseUrl, apiKey, baseUrl, version) {
|
|
4574
|
-
return new SimpleBearerAuthTransport(inner, defaultBaseUrl, apiKey, baseUrl, version);
|
|
4768
|
+
function createTransport(inner, defaultBaseUrl, apiKey, baseUrl, version, customHeaders) {
|
|
4769
|
+
return new SimpleBearerAuthTransport(inner, defaultBaseUrl, apiKey, baseUrl, version, customHeaders);
|
|
4770
|
+
}
|
|
4771
|
+
|
|
4772
|
+
// llm-providers/model-limits.ts
|
|
4773
|
+
function normalizeModelLimits(provider, model) {
|
|
4774
|
+
switch (provider.toLowerCase()) {
|
|
4775
|
+
case "github": {
|
|
4776
|
+
const capabilities = model.capabilities;
|
|
4777
|
+
const limits = capabilities?.limits;
|
|
4778
|
+
const contextWindow = limits?.max_context_window_tokens;
|
|
4779
|
+
if (!contextWindow) return null;
|
|
4780
|
+
return {
|
|
4781
|
+
contextWindow,
|
|
4782
|
+
maxOutput: limits?.max_output_tokens
|
|
4783
|
+
};
|
|
4784
|
+
}
|
|
4785
|
+
case "deepinfra": {
|
|
4786
|
+
const metadata = model.metadata;
|
|
4787
|
+
const contextLength = metadata?.context_length;
|
|
4788
|
+
if (!contextLength) return null;
|
|
4789
|
+
return {
|
|
4790
|
+
contextWindow: contextLength,
|
|
4791
|
+
maxOutput: metadata?.max_tokens
|
|
4792
|
+
};
|
|
4793
|
+
}
|
|
4794
|
+
case "moonshot": {
|
|
4795
|
+
const contextLength = model.context_length;
|
|
4796
|
+
if (!contextLength) return null;
|
|
4797
|
+
return { contextWindow: contextLength };
|
|
4798
|
+
}
|
|
4799
|
+
case "openrouter": {
|
|
4800
|
+
const topProvider = model.top_provider;
|
|
4801
|
+
const contextLength = model.context_length ?? topProvider?.context_length;
|
|
4802
|
+
if (!contextLength) return null;
|
|
4803
|
+
return {
|
|
4804
|
+
contextWindow: contextLength,
|
|
4805
|
+
maxOutput: topProvider?.max_completion_tokens
|
|
4806
|
+
};
|
|
4807
|
+
}
|
|
4808
|
+
default: {
|
|
4809
|
+
const contextLength = model.context_length;
|
|
4810
|
+
if (!contextLength) return null;
|
|
4811
|
+
return { contextWindow: contextLength };
|
|
4812
|
+
}
|
|
4813
|
+
}
|
|
4814
|
+
}
|
|
4815
|
+
var FALLBACK_LIMITS = {
|
|
4816
|
+
zai: {
|
|
4817
|
+
"glm-4.6": { contextWindow: 2e5, maxOutput: 128e3 }
|
|
4818
|
+
},
|
|
4819
|
+
openrouter: {
|
|
4820
|
+
"anthropic/claude-sonnet-4": { contextWindow: 2e5, maxOutput: 16e3 },
|
|
4821
|
+
"anthropic/claude-3.5-sonnet": { contextWindow: 2e5, maxOutput: 8192 },
|
|
4822
|
+
"openai/gpt-4o": { contextWindow: 128e3, maxOutput: 16384 },
|
|
4823
|
+
"openai/gpt-4.1": { contextWindow: 128e3, maxOutput: 32768 },
|
|
4824
|
+
"openai/gpt-4o-mini": { contextWindow: 128e3, maxOutput: 16384 },
|
|
4825
|
+
"google/gemini-pro-1.5": { contextWindow: 2097152, maxOutput: 8192 },
|
|
4826
|
+
"meta-llama/llama-3.1-70b-instruct": { contextWindow: 131072, maxOutput: 131072 }
|
|
4827
|
+
},
|
|
4828
|
+
github: {
|
|
4829
|
+
"gpt-4.1": { contextWindow: 128e3, maxOutput: 16384 },
|
|
4830
|
+
"gpt-4o": { contextWindow: 128e3, maxOutput: 16384 },
|
|
4831
|
+
"gpt-4o-mini": { contextWindow: 128e3, maxOutput: 16384 },
|
|
4832
|
+
"claude-sonnet-4": { contextWindow: 2e5, maxOutput: 16e3 },
|
|
4833
|
+
"claude-3.5-sonnet": { contextWindow: 2e5, maxOutput: 8192 },
|
|
4834
|
+
o1: { contextWindow: 2e5, maxOutput: 1e5 },
|
|
4835
|
+
"o1-mini": { contextWindow: 128e3, maxOutput: 65536 }
|
|
4836
|
+
},
|
|
4837
|
+
deepinfra: {
|
|
4838
|
+
"meta-llama/Meta-Llama-3.1-70B-Instruct": { contextWindow: 131072, maxOutput: 131072 },
|
|
4839
|
+
"meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo": { contextWindow: 131072, maxOutput: 131072 },
|
|
4840
|
+
"Qwen/Qwen2.5-72B-Instruct": { contextWindow: 131072, maxOutput: 131072 }
|
|
4841
|
+
},
|
|
4842
|
+
moonshot: {
|
|
4843
|
+
"moonshot-v1-8k": { contextWindow: 8192 },
|
|
4844
|
+
"moonshot-v1-32k": { contextWindow: 32768 },
|
|
4845
|
+
"moonshot-v1-128k": { contextWindow: 131072 },
|
|
4846
|
+
"kimi-k2-turbo-preview": { contextWindow: 262144 }
|
|
4847
|
+
},
|
|
4848
|
+
anthropic: {
|
|
4849
|
+
"claude-sonnet-4-5": { contextWindow: 2e5, maxOutput: 16e3 },
|
|
4850
|
+
"claude-3-5-sonnet-20241022": { contextWindow: 2e5, maxOutput: 8192 },
|
|
4851
|
+
"claude-3-opus-20240229": { contextWindow: 2e5, maxOutput: 4096 },
|
|
4852
|
+
"claude-3-haiku-20240307": { contextWindow: 2e5, maxOutput: 4096 }
|
|
4853
|
+
}
|
|
4854
|
+
};
|
|
4855
|
+
function getFallbackLimits(provider, model) {
|
|
4856
|
+
const providerLimits = FALLBACK_LIMITS[provider.toLowerCase()];
|
|
4857
|
+
if (!providerLimits) return null;
|
|
4858
|
+
return providerLimits[model] ?? null;
|
|
4859
|
+
}
|
|
4860
|
+
function normalizeModelInfo(provider, model) {
|
|
4861
|
+
const id = model.id;
|
|
4862
|
+
const name = model.name ?? id;
|
|
4863
|
+
const limits = normalizeModelLimits(provider, model);
|
|
4864
|
+
return {
|
|
4865
|
+
id,
|
|
4866
|
+
name,
|
|
4867
|
+
...limits ? { limits } : {}
|
|
4868
|
+
};
|
|
4575
4869
|
}
|
|
4576
4870
|
|
|
4577
4871
|
// llm-providers/llm-github.ts
|
|
@@ -4604,7 +4898,7 @@ var GithubLLM = class extends BaseLLM {
|
|
|
4604
4898
|
throw new LLMError(text || `Failed to fetch models: ${res.status}`, res.status);
|
|
4605
4899
|
}
|
|
4606
4900
|
const body = await res.json();
|
|
4607
|
-
return body.data;
|
|
4901
|
+
return body.data.map((m) => normalizeModelInfo("github", m));
|
|
4608
4902
|
}
|
|
4609
4903
|
handleError(error, model) {
|
|
4610
4904
|
if (error instanceof LLMError) {
|
|
@@ -5127,6 +5421,19 @@ var llm_provider_config_default = {
|
|
|
5127
5421
|
promptCaching: false,
|
|
5128
5422
|
getModels: true
|
|
5129
5423
|
}
|
|
5424
|
+
},
|
|
5425
|
+
{
|
|
5426
|
+
name: "kimi-for-coding",
|
|
5427
|
+
type: "openai-compat",
|
|
5428
|
+
baseUrl: "https://api.kimi.com/coding/v1",
|
|
5429
|
+
features: {
|
|
5430
|
+
promptCaching: true,
|
|
5431
|
+
getModels: true,
|
|
5432
|
+
includeUsage: true
|
|
5433
|
+
},
|
|
5434
|
+
customHeaders: {
|
|
5435
|
+
"User-Agent": "KimiCLI/0.59"
|
|
5436
|
+
}
|
|
5130
5437
|
}
|
|
5131
5438
|
]
|
|
5132
5439
|
};
|
|
@@ -5137,12 +5444,16 @@ var GenericLLM = class extends BaseLLM {
|
|
|
5137
5444
|
opts;
|
|
5138
5445
|
includeUsage;
|
|
5139
5446
|
modelConfig;
|
|
5140
|
-
|
|
5141
|
-
|
|
5447
|
+
providerName;
|
|
5448
|
+
customHeaders;
|
|
5449
|
+
constructor(baseUrl, modelConfig, opts = {}, customHeaders) {
|
|
5450
|
+
const { enablePromptCaching = false, includeUsage = false, providerName = "unknown", ...restOpts } = opts;
|
|
5142
5451
|
super(opts.apiUrl || baseUrl, { enablePromptCaching });
|
|
5143
5452
|
this.includeUsage = includeUsage;
|
|
5144
5453
|
this.modelConfig = modelConfig;
|
|
5454
|
+
this.providerName = providerName;
|
|
5145
5455
|
this.opts = restOpts;
|
|
5456
|
+
this.customHeaders = customHeaders;
|
|
5146
5457
|
}
|
|
5147
5458
|
createTransport() {
|
|
5148
5459
|
const base = new FetchTransport({
|
|
@@ -5152,14 +5463,17 @@ var GenericLLM = class extends BaseLLM {
|
|
|
5152
5463
|
maxFileSize: 5 * 1024 * 1024,
|
|
5153
5464
|
captureResponseBody: true
|
|
5154
5465
|
});
|
|
5155
|
-
return createTransport(base, this.apiUrl, this.opts.apiKey, this.opts.apiUrl, this.opts.version);
|
|
5466
|
+
return createTransport(base, this.apiUrl, this.opts.apiKey, this.opts.apiUrl, this.opts.version, this.customHeaders);
|
|
5156
5467
|
}
|
|
5157
5468
|
async getModels(signal) {
|
|
5158
5469
|
if (this.modelConfig === false) {
|
|
5159
5470
|
throw new Error("Provider does not support getModels");
|
|
5160
5471
|
}
|
|
5161
5472
|
if (Array.isArray(this.modelConfig)) {
|
|
5162
|
-
return this.modelConfig.map((m) =>
|
|
5473
|
+
return this.modelConfig.map((m) => {
|
|
5474
|
+
const raw = typeof m === "string" ? { id: m } : m;
|
|
5475
|
+
return normalizeModelInfo(this.providerName, raw);
|
|
5476
|
+
});
|
|
5163
5477
|
}
|
|
5164
5478
|
if (typeof this.modelConfig === "string") {
|
|
5165
5479
|
const transport2 = this.createTransport();
|
|
@@ -5169,7 +5483,7 @@ var GenericLLM = class extends BaseLLM {
|
|
|
5169
5483
|
throw new Error(`Failed to fetch models: ${res2.status} ${text}`);
|
|
5170
5484
|
}
|
|
5171
5485
|
const data2 = await res2.json();
|
|
5172
|
-
return data2.data;
|
|
5486
|
+
return data2.data.map((m) => normalizeModelInfo(this.providerName, m));
|
|
5173
5487
|
}
|
|
5174
5488
|
const transport = this.createTransport();
|
|
5175
5489
|
const res = await transport.get("/models", void 0, signal);
|
|
@@ -5178,7 +5492,7 @@ var GenericLLM = class extends BaseLLM {
|
|
|
5178
5492
|
throw new Error(`Failed to fetch models: ${res.status} ${text}`);
|
|
5179
5493
|
}
|
|
5180
5494
|
const data = await res.json();
|
|
5181
|
-
return data.data;
|
|
5495
|
+
return data.data.map((m) => normalizeModelInfo(this.providerName, m));
|
|
5182
5496
|
}
|
|
5183
5497
|
async generateCompletion(params, signal) {
|
|
5184
5498
|
let enhancedParams = params;
|
|
@@ -5217,6 +5531,7 @@ function mergeProviders(customProviders) {
|
|
|
5217
5531
|
type: custom.type ?? "openai-compat",
|
|
5218
5532
|
baseUrl: custom.baseUrl,
|
|
5219
5533
|
models: custom.models ?? false,
|
|
5534
|
+
customHeaders: custom.customHeaders,
|
|
5220
5535
|
features: existing?.features ?? {
|
|
5221
5536
|
promptCaching: false,
|
|
5222
5537
|
getModels: custom.models !== false,
|
|
@@ -5237,9 +5552,10 @@ function createLLM(providerName, options = {}, customProviders) {
|
|
|
5237
5552
|
const modelConfig = normalizeModelConfig(config);
|
|
5238
5553
|
return new GenericLLM(config.baseUrl, modelConfig, {
|
|
5239
5554
|
...options,
|
|
5555
|
+
providerName: config.name,
|
|
5240
5556
|
enablePromptCaching: options.enablePromptCaching ?? config.features.promptCaching,
|
|
5241
5557
|
includeUsage: options.includeUsage ?? config.features.includeUsage
|
|
5242
|
-
});
|
|
5558
|
+
}, config.customHeaders);
|
|
5243
5559
|
}
|
|
5244
5560
|
function getAvailableProviders(customProviders) {
|
|
5245
5561
|
const allProviders = mergeProviders(customProviders);
|
|
@@ -5522,31 +5838,6 @@ function isValidConfig(value) {
|
|
|
5522
5838
|
}
|
|
5523
5839
|
return true;
|
|
5524
5840
|
}
|
|
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
5841
|
export {
|
|
5551
5842
|
AGENT_CREATOR_SYSTEM_PROMPT,
|
|
5552
5843
|
AgentEventTypes,
|
|
@@ -5570,11 +5861,13 @@ export {
|
|
|
5570
5861
|
GithubLLM,
|
|
5571
5862
|
InMemoryMemory,
|
|
5572
5863
|
InMemoryMetadata,
|
|
5864
|
+
InMemoryMetricsPort,
|
|
5573
5865
|
JsonFileMemoryPersistence,
|
|
5574
5866
|
LLMError,
|
|
5575
5867
|
LLMResolver,
|
|
5576
5868
|
MCPToolPort,
|
|
5577
5869
|
MemoryPortMetadataAdapter,
|
|
5870
|
+
NoopMetricsPort,
|
|
5578
5871
|
NoopReminders,
|
|
5579
5872
|
PersistedMemory,
|
|
5580
5873
|
PersistingConsoleEventPort,
|
|
@@ -5587,10 +5880,14 @@ export {
|
|
|
5587
5880
|
buildAgentCreationPrompt,
|
|
5588
5881
|
buildInjectedSystem,
|
|
5589
5882
|
canonicalizeTerminalPaste,
|
|
5883
|
+
createEmptySnapshot,
|
|
5590
5884
|
createLLM,
|
|
5591
5885
|
generateFolderTree,
|
|
5592
5886
|
getAvailableProviders,
|
|
5887
|
+
getFallbackLimits,
|
|
5593
5888
|
loadMCPConfig,
|
|
5889
|
+
normalizeModelInfo,
|
|
5890
|
+
normalizeModelLimits,
|
|
5594
5891
|
normalizeNewlines,
|
|
5595
5892
|
renderTemplate,
|
|
5596
5893
|
resolveBackspaces,
|