@codelia/core 0.1.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/index.js ADDED
@@ -0,0 +1,2283 @@
1
+ // src/config/register.ts
2
+ import { configRegistry } from "@codelia/config";
3
+
4
+ // src/models/openai.ts
5
+ var OPENAI_DEFAULT_MODEL = "gpt-5.2-codex";
6
+ var OPENAI_DEFAULT_REASONING_EFFORT = "medium";
7
+ var OPENAI_MODELS = [
8
+ {
9
+ id: OPENAI_DEFAULT_MODEL,
10
+ provider: "openai",
11
+ aliases: ["default"]
12
+ },
13
+ {
14
+ id: "gpt-5.2",
15
+ provider: "openai"
16
+ },
17
+ {
18
+ id: "gpt-5.3-codex",
19
+ provider: "openai"
20
+ },
21
+ {
22
+ id: "gpt-5.2-2025-12-11",
23
+ provider: "openai"
24
+ },
25
+ {
26
+ id: "gpt-5.2-pro",
27
+ provider: "openai"
28
+ },
29
+ {
30
+ id: "gpt-5.2-pro-2025-12-11",
31
+ provider: "openai"
32
+ },
33
+ {
34
+ id: "gpt-5.1",
35
+ provider: "openai"
36
+ },
37
+ {
38
+ id: "gpt-5.1-2025-11-13",
39
+ provider: "openai"
40
+ },
41
+ {
42
+ id: "gpt-5",
43
+ provider: "openai"
44
+ },
45
+ {
46
+ id: "gpt-5-mini",
47
+ provider: "openai"
48
+ },
49
+ {
50
+ id: "gpt-5-mini-2025-08-07",
51
+ provider: "openai"
52
+ },
53
+ {
54
+ id: "gpt-5-nano",
55
+ provider: "openai"
56
+ },
57
+ {
58
+ id: "gpt-5-nano-2025-08-07",
59
+ provider: "openai"
60
+ },
61
+ {
62
+ id: "gpt-5.1-codex",
63
+ provider: "openai"
64
+ },
65
+ {
66
+ id: "gpt-5.1-codex-max",
67
+ provider: "openai"
68
+ },
69
+ {
70
+ id: "gpt-5.1-codex-mini",
71
+ provider: "openai"
72
+ },
73
+ {
74
+ id: "gpt-5-codex",
75
+ provider: "openai"
76
+ },
77
+ {
78
+ id: "gpt-5-codex-mini",
79
+ provider: "openai"
80
+ }
81
+ ];
82
+
83
+ // src/config/register.ts
84
+ configRegistry.registerDefaults({
85
+ model: {
86
+ provider: "openai",
87
+ name: OPENAI_DEFAULT_MODEL,
88
+ reasoning: OPENAI_DEFAULT_REASONING_EFFORT
89
+ }
90
+ });
91
+
92
+ // src/content/stringify.ts
93
+ var stringifyUnknown = (value) => {
94
+ try {
95
+ return JSON.stringify(value);
96
+ } catch {
97
+ return String(value);
98
+ }
99
+ };
100
+ var stringifyPart = (part, options) => {
101
+ if (part.type === "text") {
102
+ return part.text;
103
+ }
104
+ if (part.type === "image_url") {
105
+ if (options.mode === "log") {
106
+ return `[image:${part.image_url.media_type ?? "unknown"}]`;
107
+ }
108
+ return "[image]";
109
+ }
110
+ if (part.type === "document") {
111
+ if (options.mode === "log") {
112
+ return "[document:application/pdf]";
113
+ }
114
+ return "[document]";
115
+ }
116
+ if (part.type === "other") {
117
+ const head = `[other:${part.provider}/${part.kind}]`;
118
+ if (options.mode === "log" && options.includeOtherPayload) {
119
+ return `${head} ${stringifyUnknown(part.payload)}`;
120
+ }
121
+ return head;
122
+ }
123
+ return "[content]";
124
+ };
125
+ var stringifyContentParts = (content, options = {}) => {
126
+ const normalized = {
127
+ mode: options.mode ?? "display",
128
+ joiner: options.joiner ?? (options.mode === "log" ? "\n" : ""),
129
+ includeOtherPayload: options.includeOtherPayload ?? false
130
+ };
131
+ const text = content.map((part) => stringifyPart(part, normalized)).join(normalized.joiner);
132
+ return text || stringifyUnknown(content);
133
+ };
134
+ var stringifyContent = (content, options = {}) => {
135
+ if (content == null) return "";
136
+ if (typeof content === "string") return content;
137
+ return stringifyContentParts(content, options);
138
+ };
139
+
140
+ // src/history/store.ts
141
+ var MessageHistoryAdapter = class {
142
+ messages = [];
143
+ hasSystem = false;
144
+ enqueueSystem(system) {
145
+ if (system && !this.hasSystem) {
146
+ this.messages.push(system);
147
+ this.hasSystem = true;
148
+ }
149
+ }
150
+ enqueueUserMessage(content) {
151
+ const message = {
152
+ role: "user",
153
+ content
154
+ };
155
+ this.messages.push(message);
156
+ }
157
+ enqueueToolResult(message) {
158
+ this.messages.push(message);
159
+ }
160
+ commitModelResponse(response) {
161
+ this.messages.push(...response.messages);
162
+ }
163
+ prepareInvokeInput(params) {
164
+ return {
165
+ messages: this.messages,
166
+ tools: params.tools ?? null,
167
+ toolChoice: params.toolChoice ?? null
168
+ };
169
+ }
170
+ getViewMessages() {
171
+ return this.messages;
172
+ }
173
+ replaceViewMessages(messages) {
174
+ this.messages = messages;
175
+ this.hasSystem = messages.some((message) => message.role === "system");
176
+ }
177
+ };
178
+
179
+ // src/llm/openai/history.ts
180
+ var OpenAIHistoryAdapter = class extends MessageHistoryAdapter {
181
+ };
182
+
183
+ // src/models/anthropic.ts
184
+ var ANTHROPIC_DEFAULT_MODEL = "claude-sonnet-4-5";
185
+ var ANTHROPIC_MODELS = [
186
+ {
187
+ id: ANTHROPIC_DEFAULT_MODEL,
188
+ provider: "anthropic",
189
+ aliases: ["default"]
190
+ },
191
+ {
192
+ id: "claude-opus-4-6",
193
+ provider: "anthropic"
194
+ },
195
+ {
196
+ id: "claude-opus-4-5",
197
+ provider: "anthropic"
198
+ },
199
+ {
200
+ id: "claude-opus-4-5-20251201",
201
+ provider: "anthropic"
202
+ },
203
+ {
204
+ id: "claude-sonnet-4-5-20250929",
205
+ provider: "anthropic"
206
+ },
207
+ {
208
+ id: "claude-haiku-4-5",
209
+ provider: "anthropic"
210
+ },
211
+ {
212
+ id: "claude-haiku-4-5-20250929",
213
+ provider: "anthropic"
214
+ }
215
+ ];
216
+
217
+ // src/models/google.ts
218
+ var GOOGLE_MODELS = [
219
+ {
220
+ id: "gemini-3-pro-preview",
221
+ provider: "google"
222
+ },
223
+ {
224
+ id: "gemini-3-pro-image-preview",
225
+ provider: "google"
226
+ },
227
+ {
228
+ id: "gemini-3-flash-preview",
229
+ provider: "google"
230
+ }
231
+ ];
232
+
233
+ // src/models/registry.ts
234
+ function createModelRegistry(specs) {
235
+ const registry = {
236
+ modelsById: {},
237
+ aliasesByProvider: {
238
+ openai: {},
239
+ anthropic: {},
240
+ google: {}
241
+ }
242
+ };
243
+ registerModels(registry, specs);
244
+ return registry;
245
+ }
246
+ function registerModels(registry, specs) {
247
+ for (const spec of specs) {
248
+ registry.modelsById[spec.id] = spec;
249
+ const aliasBucket = registry.aliasesByProvider[spec.provider];
250
+ for (const alias of spec.aliases ?? []) {
251
+ aliasBucket[alias] = spec.id;
252
+ }
253
+ }
254
+ }
255
+ function resolveModel(registry, idOrAlias, provider) {
256
+ const direct = registry.modelsById[idOrAlias];
257
+ if (direct) {
258
+ return direct;
259
+ }
260
+ if (provider) {
261
+ const aliasId = registry.aliasesByProvider[provider][idOrAlias];
262
+ return aliasId ? registry.modelsById[aliasId] : void 0;
263
+ }
264
+ let resolved;
265
+ for (const providerName of Object.keys(
266
+ registry.aliasesByProvider
267
+ )) {
268
+ const aliasId = registry.aliasesByProvider[providerName][idOrAlias];
269
+ if (!aliasId) {
270
+ continue;
271
+ }
272
+ if (resolved) {
273
+ return void 0;
274
+ }
275
+ resolved = registry.modelsById[aliasId];
276
+ }
277
+ return resolved;
278
+ }
279
+ function listModels(registry, provider) {
280
+ const all = Object.values(registry.modelsById);
281
+ return provider ? all.filter((model) => model.provider === provider) : all;
282
+ }
283
+ function cloneAliases(aliasesByProvider) {
284
+ return {
285
+ openai: { ...aliasesByProvider.openai },
286
+ anthropic: { ...aliasesByProvider.anthropic },
287
+ google: { ...aliasesByProvider.google }
288
+ };
289
+ }
290
+ function applyModelMetadata(registry, index) {
291
+ const next = {
292
+ modelsById: { ...registry.modelsById },
293
+ aliasesByProvider: cloneAliases(registry.aliasesByProvider)
294
+ };
295
+ for (const [providerId, providerModels] of Object.entries(index.models)) {
296
+ if (providerId !== "openai" && providerId !== "anthropic" && providerId !== "google") {
297
+ continue;
298
+ }
299
+ const provider = providerId;
300
+ for (const [modelId, entry] of Object.entries(providerModels)) {
301
+ const fullId = `${provider}/${modelId}`;
302
+ const spec = resolveModel(next, fullId, provider) ?? resolveModel(next, modelId, provider);
303
+ if (!spec) continue;
304
+ const limits = entry.limits;
305
+ if (!limits) continue;
306
+ next.modelsById[spec.id] = {
307
+ ...spec,
308
+ contextWindow: spec.contextWindow ?? limits.contextWindow,
309
+ maxInputTokens: spec.maxInputTokens ?? limits.inputTokens,
310
+ maxOutputTokens: spec.maxOutputTokens ?? limits.outputTokens
311
+ };
312
+ }
313
+ }
314
+ return next;
315
+ }
316
+
317
+ // src/models/index.ts
318
+ var DEFAULT_MODEL_REGISTRY = createModelRegistry([
319
+ ...OPENAI_MODELS,
320
+ ...ANTHROPIC_MODELS,
321
+ ...GOOGLE_MODELS
322
+ ]);
323
+
324
+ // src/services/compaction/service.ts
325
+ var DEFAULT_THRESHOLD_RATIO = 0.8;
326
+ var DEFAULT_RETAIN_LAST_TURNS = 1;
327
+ var DEFAULT_SUMMARY_PROMPT = "Summarize the conversation so it can be continued later. Focus on decisions, results, constraints, and next steps. Keep it concise and factual.";
328
+ var DEFAULT_RETAIN_PROMPT = "List concrete details that must be preserved verbatim. Include tool output refs, file paths, identifiers, commands, TODOs, and any critical decisions.";
329
+ var CompactionService = class _CompactionService {
330
+ config;
331
+ modelRegistry;
332
+ constructor(config, deps) {
333
+ this.config = _CompactionService.normalizeConfig(config);
334
+ this.modelRegistry = deps.modelRegistry;
335
+ }
336
+ async shouldCompact(llm, usage) {
337
+ if (!this.config.enabled) {
338
+ return false;
339
+ }
340
+ if (!this.config.auto) {
341
+ return false;
342
+ }
343
+ if (!usage) {
344
+ return false;
345
+ }
346
+ const contextLimit = await this.resolveContextLimit(llm, usage);
347
+ const threshold = Math.floor(contextLimit * this.config.thresholdRatio);
348
+ return usage.total_tokens >= threshold;
349
+ }
350
+ async compact(llm, messages, options) {
351
+ if (!this.config.enabled) {
352
+ return {
353
+ compacted: false,
354
+ compactedMessages: messages,
355
+ usage: null
356
+ };
357
+ }
358
+ const preparedMessages = this.prepareMessagesForSummary(messages);
359
+ const prompt = this.buildCompactionPrompt();
360
+ const interruptMessage = {
361
+ role: "user",
362
+ content: prompt
363
+ };
364
+ let response;
365
+ try {
366
+ response = await llm.ainvoke({
367
+ messages: [...preparedMessages, interruptMessage],
368
+ tools: null,
369
+ toolChoice: "none",
370
+ ...options?.signal ? { signal: options.signal } : {},
371
+ ...this.config.model ? { model: this.config.model } : {}
372
+ });
373
+ } catch (error) {
374
+ if (error instanceof Error && (error.name === "AbortError" || error.name === "APIUserAbortError" || error.name === "AbortSignal")) {
375
+ throw error;
376
+ }
377
+ return {
378
+ compacted: false,
379
+ compactedMessages: messages,
380
+ usage: null
381
+ };
382
+ }
383
+ const responseText = extractAssistantText(response.messages);
384
+ const parsed = this.parseCompactionResponse(responseText);
385
+ const summary = parsed.summary || parsed.fallbackSummary;
386
+ const retain = parsed.retain;
387
+ if (!summary && !retain) {
388
+ return {
389
+ compacted: false,
390
+ compactedMessages: messages,
391
+ usage: response.usage ?? null
392
+ };
393
+ }
394
+ const compactedMessages = this.buildCompactedMessages(
395
+ messages,
396
+ retain,
397
+ summary
398
+ );
399
+ return {
400
+ compacted: true,
401
+ compactedMessages,
402
+ usage: response.usage ?? null
403
+ };
404
+ }
405
+ prepareMessagesForSummary(messages) {
406
+ if (messages.length === 0) return messages;
407
+ const prepared = messages.map((message) => ({ ...message }));
408
+ const last = prepared[prepared.length - 1];
409
+ if (last.role === "assistant" && last.tool_calls?.length) {
410
+ if (last.content) {
411
+ const replacement = {
412
+ role: "assistant",
413
+ content: last.content,
414
+ name: last.name
415
+ };
416
+ prepared[prepared.length - 1] = replacement;
417
+ } else {
418
+ prepared.pop();
419
+ }
420
+ }
421
+ return prepared;
422
+ }
423
+ buildCompactionPrompt() {
424
+ const lines = [
425
+ "You are summarizing the conversation for context compaction.",
426
+ "Respond only with the XML-like tags below and nothing else:"
427
+ ];
428
+ if (this.config.retainPrompt !== null) {
429
+ lines.push("<retain>...</retain>");
430
+ }
431
+ lines.push("<summary>...</summary>", "");
432
+ if (this.config.retainPrompt !== null) {
433
+ lines.push("Retain instructions:", this.config.retainPrompt.trim());
434
+ lines.push(
435
+ ...this.config.retainDirectives.map((directive) => `- ${directive}`)
436
+ );
437
+ lines.push("");
438
+ }
439
+ lines.push("Summary instructions:", this.config.summaryPrompt.trim());
440
+ lines.push(
441
+ ...this.config.summaryDirectives.map((directive) => `- ${directive}`)
442
+ );
443
+ return lines.join("\n").trim();
444
+ }
445
+ parseCompactionResponse(text) {
446
+ const retain = this.config.retainPrompt === null ? "" : extractTag(text, "retain");
447
+ const summary = extractTag(text, "summary");
448
+ const fallbackSummary = text.replace(/<retain>[\s\S]*?<\/retain>/gi, "").replace(/<summary>|<\/summary>/gi, "").trim();
449
+ return {
450
+ retain: retain.trim(),
451
+ summary: summary.trim(),
452
+ fallbackSummary
453
+ };
454
+ }
455
+ buildCompactedMessages(messages, retain, summary) {
456
+ const systemMessages = [];
457
+ const nonSystemMessages = [];
458
+ for (const message of messages) {
459
+ if (message.role === "system") {
460
+ systemMessages.push(message);
461
+ } else {
462
+ nonSystemMessages.push(message);
463
+ }
464
+ }
465
+ const tail = this.getLastTurns(
466
+ nonSystemMessages,
467
+ this.config.retainLastTurns
468
+ );
469
+ const compacted = [...systemMessages];
470
+ if (retain) {
471
+ compacted.push({
472
+ role: "user",
473
+ content: retain
474
+ });
475
+ }
476
+ if (summary) {
477
+ compacted.push({
478
+ role: "user",
479
+ content: summary
480
+ });
481
+ }
482
+ compacted.push(...tail);
483
+ return compacted;
484
+ }
485
+ getLastTurns(messages, retainLastTurns) {
486
+ if (retainLastTurns <= 0) return [];
487
+ let remaining = retainLastTurns;
488
+ let startIndex = 0;
489
+ for (let i = messages.length - 1; i >= 0; i -= 1) {
490
+ if (messages[i].role === "user") {
491
+ remaining -= 1;
492
+ if (remaining === 0) {
493
+ startIndex = i;
494
+ break;
495
+ }
496
+ }
497
+ }
498
+ if (remaining > 0) {
499
+ startIndex = 0;
500
+ }
501
+ return messages.slice(startIndex);
502
+ }
503
+ async resolveContextLimit(llm, usage) {
504
+ const modelId = usage.model ?? llm.model;
505
+ const modelSpec = this.resolveModelSpecWithSnapshotFallback(
506
+ llm.provider,
507
+ modelId
508
+ );
509
+ if (modelSpec?.contextWindow && modelSpec.contextWindow > 0) {
510
+ return modelSpec.contextWindow;
511
+ }
512
+ if (modelSpec?.maxInputTokens && modelSpec.maxInputTokens > 0) {
513
+ return modelSpec.maxInputTokens;
514
+ }
515
+ throw new Error(
516
+ `Missing context limit for ${llm.provider}/${modelId} in model registry`
517
+ );
518
+ }
519
+ resolveModelSpecWithSnapshotFallback(provider, modelId) {
520
+ const direct = resolveModel(this.modelRegistry, modelId, provider);
521
+ if (direct) return direct;
522
+ const baseId = stripSnapshotSuffix(modelId);
523
+ if (!baseId || baseId === modelId) return void 0;
524
+ return resolveModel(this.modelRegistry, baseId, provider);
525
+ }
526
+ static normalizeConfig(config) {
527
+ const retainLastTurnsRaw = config.retainLastTurns ?? DEFAULT_RETAIN_LAST_TURNS;
528
+ return {
529
+ enabled: config.enabled ?? true,
530
+ auto: config.auto ?? true,
531
+ thresholdRatio: config.thresholdRatio ?? DEFAULT_THRESHOLD_RATIO,
532
+ model: config.model ?? null,
533
+ summaryPrompt: config.summaryPrompt ?? DEFAULT_SUMMARY_PROMPT,
534
+ summaryDirectives: config.summaryDirectives ?? [],
535
+ retainPrompt: config.retainPrompt === void 0 ? DEFAULT_RETAIN_PROMPT : config.retainPrompt,
536
+ retainDirectives: config.retainDirectives ?? [],
537
+ retainLastTurns: Math.max(0, Math.floor(retainLastTurnsRaw))
538
+ };
539
+ }
540
+ };
541
+ var extractTag = (text, tag) => {
542
+ const regex = new RegExp(`<${tag}>([\\s\\S]*?)<\\/${tag}>`, "i");
543
+ const match = text.match(regex);
544
+ return match?.[1]?.trim() ?? "";
545
+ };
546
+ var stripSnapshotSuffix = (modelId) => modelId.replace(/-[0-9]{4}-[0-9]{2}-[0-9]{2}$/, "");
547
+ var extractAssistantText = (messages) => messages.flatMap((message) => {
548
+ if (message.role !== "assistant" || message.content == null) {
549
+ return [];
550
+ }
551
+ if (typeof message.content === "string") {
552
+ return [message.content];
553
+ }
554
+ return [
555
+ message.content.map((part) => {
556
+ if (part.type === "text") return part.text;
557
+ if (part.type === "other") {
558
+ try {
559
+ return JSON.stringify(part.payload);
560
+ } catch {
561
+ return String(part.payload);
562
+ }
563
+ }
564
+ return "";
565
+ }).join("")
566
+ ];
567
+ }).join("\n").trim();
568
+
569
+ // src/services/tool-output-cache/service.ts
570
+ var DEFAULT_MAX_MESSAGE_BYTES = 50 * 1024;
571
+ var DEFAULT_CONTEXT_RATIO = 0.25;
572
+ var MIN_CONTEXT_BUDGET = 2e4;
573
+ var MAX_CONTEXT_BUDGET = 6e4;
574
+ var APPROX_BYTES_PER_TOKEN = 4;
575
+ var clamp = (value, min, max) => Math.max(min, Math.min(max, value));
576
+ var contentToText = (content) => {
577
+ if (typeof content === "string") return content;
578
+ return content.map((part) => {
579
+ switch (part.type) {
580
+ case "text":
581
+ return part.text;
582
+ case "image_url":
583
+ return "[image]";
584
+ case "document":
585
+ return "[document]";
586
+ case "other":
587
+ return `[other:${part.provider}/${part.kind}]`;
588
+ default:
589
+ return "[content]";
590
+ }
591
+ }).join("");
592
+ };
593
+ var estimateTokens = (text) => Math.ceil(Buffer.byteLength(text, "utf8") / APPROX_BYTES_PER_TOKEN);
594
+ var shouldBypassImmediateTruncation = (toolName) => toolName === "tool_output_cache" || toolName === "tool_output_cache_grep";
595
+ var truncateForContext = (content, maxMessageBytes) => {
596
+ if (!content) return { output: "", truncated: false };
597
+ const lines = content.split(/\r?\n/);
598
+ const outputLines = [];
599
+ let bytes = 0;
600
+ let modified = false;
601
+ for (const line of lines) {
602
+ const size = Buffer.byteLength(line, "utf8") + (outputLines.length ? 1 : 0);
603
+ if (bytes + size > maxMessageBytes) {
604
+ modified = true;
605
+ break;
606
+ }
607
+ outputLines.push(line);
608
+ bytes += size;
609
+ }
610
+ return { output: outputLines.join("\n"), truncated: modified };
611
+ };
612
+ var ToolOutputCacheService = class {
613
+ config;
614
+ modelRegistry;
615
+ store;
616
+ constructor(config, deps) {
617
+ this.config = config;
618
+ this.modelRegistry = deps.modelRegistry;
619
+ this.store = deps.store;
620
+ }
621
+ async processToolMessage(message) {
622
+ if (this.config.enabled === false) return message;
623
+ const raw = contentToText(message.content);
624
+ const outputRef = await this.persistToolOutput(message, raw);
625
+ const truncated = shouldBypassImmediateTruncation(message.tool_name) ? { output: raw, truncated: false } : truncateForContext(
626
+ raw,
627
+ this.config.maxMessageBytes ?? DEFAULT_MAX_MESSAGE_BYTES
628
+ );
629
+ const trimmed = truncated.truncated;
630
+ let output = truncated.output;
631
+ if (trimmed) {
632
+ const refLabel = outputRef?.id ? `; ref=${outputRef.id}` : "";
633
+ output += `
634
+
635
+ [tool output truncated${refLabel}]`;
636
+ }
637
+ return {
638
+ ...message,
639
+ content: output,
640
+ output_ref: outputRef,
641
+ trimmed: trimmed || message.trimmed
642
+ };
643
+ }
644
+ async trimMessages(llm, messages) {
645
+ if (this.config.enabled === false) {
646
+ return { messages, trimmed: false };
647
+ }
648
+ const budget = await this.resolveContextBudgetTokens(llm);
649
+ let total = 0;
650
+ for (const message of messages) {
651
+ if (message.role !== "tool") continue;
652
+ total += estimateTokens(contentToText(message.content));
653
+ }
654
+ if (total <= budget) return { messages, trimmed: false };
655
+ const updated = messages.map((message) => ({ ...message }));
656
+ let trimmedAny = false;
657
+ for (const message of updated) {
658
+ if (message.role !== "tool") continue;
659
+ if (total <= budget) break;
660
+ const toolMessage = message;
661
+ const contentText = contentToText(toolMessage.content);
662
+ const refId = toolMessage.output_ref?.id ?? null;
663
+ const placeholder = refId ? `[tool output trimmed; ref=${refId}]` : "[tool output trimmed]";
664
+ const tokens = estimateTokens(contentText);
665
+ const placeholderTokens = estimateTokens(placeholder);
666
+ toolMessage.content = placeholder;
667
+ toolMessage.trimmed = true;
668
+ total = Math.max(0, total - tokens + placeholderTokens);
669
+ trimmedAny = true;
670
+ }
671
+ return { messages: updated, trimmed: trimmedAny };
672
+ }
673
+ async persistToolOutput(message, content) {
674
+ if (!this.store) return void 0;
675
+ try {
676
+ const result = await this.store.save({
677
+ tool_call_id: message.tool_call_id,
678
+ tool_name: message.tool_name,
679
+ content,
680
+ is_error: message.is_error
681
+ });
682
+ return result;
683
+ } catch {
684
+ return void 0;
685
+ }
686
+ }
687
+ async resolveContextBudgetTokens(llm) {
688
+ if (this.config.contextBudgetTokens !== void 0 && this.config.contextBudgetTokens !== null) {
689
+ return this.config.contextBudgetTokens;
690
+ }
691
+ const modelSpec = resolveModel(this.modelRegistry, llm.model, llm.provider);
692
+ const contextLimit = modelSpec?.contextWindow ?? modelSpec?.maxInputTokens ?? null;
693
+ if (!contextLimit || contextLimit <= 0) {
694
+ return MAX_CONTEXT_BUDGET;
695
+ }
696
+ const derived = Math.floor(contextLimit * DEFAULT_CONTEXT_RATIO);
697
+ return clamp(derived, MIN_CONTEXT_BUDGET, MAX_CONTEXT_BUDGET);
698
+ }
699
+ };
700
+
701
+ // src/services/usage/service.ts
702
+ var TokenUsageService = class {
703
+ constructor(config) {
704
+ this.config = config;
705
+ this.usageSummary = {
706
+ total_calls: 0,
707
+ total_tokens: 0,
708
+ total_input_tokens: 0,
709
+ total_output_tokens: 0,
710
+ total_cached_input_tokens: 0,
711
+ total_cache_creation_tokens: 0,
712
+ total_cost_usd: 0,
713
+ by_model: {}
714
+ };
715
+ }
716
+ usageSummary;
717
+ lastUsage = null;
718
+ updateUsageSummary(usage) {
719
+ this.lastUsage = usage ?? null;
720
+ if (!this.config.enabled || !usage) {
721
+ return;
722
+ }
723
+ this.usageSummary.total_calls++;
724
+ this.usageSummary.total_tokens += usage.total_tokens;
725
+ this.usageSummary.total_input_tokens += usage.input_tokens;
726
+ this.usageSummary.total_output_tokens += usage.output_tokens;
727
+ this.usageSummary.total_cached_input_tokens += usage.input_cached_tokens ?? 0;
728
+ this.usageSummary.total_cache_creation_tokens += usage.input_cache_creation_tokens ?? 0;
729
+ const model = usage.model ?? "unknown";
730
+ if (!this.usageSummary.by_model[model]) {
731
+ this.usageSummary.by_model[model] = {
732
+ calls: 1,
733
+ input_tokens: usage.input_tokens,
734
+ output_tokens: usage.output_tokens,
735
+ cached_input_tokens: usage.input_cached_tokens ?? 0,
736
+ cache_creation_tokens: usage.input_cache_creation_tokens ?? 0,
737
+ total_tokens: usage.total_tokens
738
+ };
739
+ } else {
740
+ this.usageSummary.by_model[model].calls++;
741
+ this.usageSummary.by_model[model].input_tokens += usage.input_tokens;
742
+ this.usageSummary.by_model[model].output_tokens += usage.output_tokens;
743
+ this.usageSummary.by_model[model].cached_input_tokens += usage.input_cached_tokens ?? 0;
744
+ this.usageSummary.by_model[model].cache_creation_tokens += usage.input_cache_creation_tokens ?? 0;
745
+ this.usageSummary.by_model[model].total_tokens += usage.total_tokens;
746
+ }
747
+ }
748
+ getUsageSummary() {
749
+ return this.usageSummary;
750
+ }
751
+ getLastUsage() {
752
+ return this.lastUsage;
753
+ }
754
+ };
755
+
756
+ // src/tools/done.ts
757
+ var TaskComplete = class extends Error {
758
+ finalMessage;
759
+ constructor(finalMessage) {
760
+ super("Task complete");
761
+ this.name = "TaskComplete";
762
+ this.finalMessage = finalMessage;
763
+ }
764
+ };
765
+
766
+ // src/agent/agent.ts
767
+ var DEFAULT_MAX_ITERATIONS = 200;
768
+ function toolResultToContent(result) {
769
+ if (result.type === "text") return result.text;
770
+ if (result.type === "parts") return result.parts;
771
+ try {
772
+ return JSON.stringify(result.value);
773
+ } catch {
774
+ return String(result.value);
775
+ }
776
+ }
777
+ var collectModelOutput = (messages) => {
778
+ const reasoningTexts = [];
779
+ const assistantTexts = [];
780
+ const toolCalls = [];
781
+ for (const message of messages) {
782
+ if (message.role === "reasoning") {
783
+ const text = message.content ?? "";
784
+ if (text) {
785
+ reasoningTexts.push(text);
786
+ }
787
+ continue;
788
+ }
789
+ if (message.role !== "assistant") {
790
+ continue;
791
+ }
792
+ if (message.content) {
793
+ const text = stringifyContent(message.content, { mode: "display" });
794
+ if (text) {
795
+ assistantTexts.push(text);
796
+ }
797
+ }
798
+ if (message.tool_calls?.length) {
799
+ toolCalls.push(...message.tool_calls);
800
+ }
801
+ }
802
+ return { reasoningTexts, assistantTexts, toolCalls };
803
+ };
804
+ var nowIso = () => (/* @__PURE__ */ new Date()).toISOString();
805
+ var createAbortError = () => {
806
+ const error = new Error("Operation aborted");
807
+ error.name = "AbortError";
808
+ return error;
809
+ };
810
+ var isAbortError = (error) => error instanceof Error && (error.name === "AbortError" || error.name === "APIUserAbortError" || error.name === "AbortSignal");
811
+ var throwIfAborted = (signal) => {
812
+ if (signal?.aborted) {
813
+ throw createAbortError();
814
+ }
815
+ };
816
+ var Agent = class {
817
+ llm;
818
+ tools;
819
+ systemPrompt;
820
+ maxIterations;
821
+ toolChoice;
822
+ requireDoneTool;
823
+ compactionService;
824
+ toolOutputCacheService;
825
+ services;
826
+ modelRegistry;
827
+ canExecuteTool;
828
+ //private readonly dependencyOverrides?: DependencyOverrides;
829
+ history;
830
+ usageService;
831
+ constructor(options) {
832
+ this.llm = options.llm;
833
+ this.history = this.llm.provider === "openai" ? new OpenAIHistoryAdapter() : new MessageHistoryAdapter();
834
+ this.tools = options.tools;
835
+ this.systemPrompt = options.systemPrompt ?? void 0;
836
+ this.maxIterations = options.maxIterations ?? DEFAULT_MAX_ITERATIONS;
837
+ this.toolChoice = options.toolChoice ?? void 0;
838
+ this.requireDoneTool = options.requireDoneTool ?? false;
839
+ this.services = options.services ?? {};
840
+ this.modelRegistry = options.modelRegistry ?? DEFAULT_MODEL_REGISTRY;
841
+ this.compactionService = options.compaction === null ? null : new CompactionService(options.compaction ?? {}, {
842
+ modelRegistry: this.modelRegistry
843
+ });
844
+ this.toolOutputCacheService = options.toolOutputCache === null ? null : new ToolOutputCacheService(options.toolOutputCache ?? {}, {
845
+ modelRegistry: this.modelRegistry,
846
+ store: this.services.toolOutputCacheStore ?? null
847
+ });
848
+ this.usageService = new TokenUsageService({
849
+ enabled: options.enableUsageTracking ?? true,
850
+ thresholdRatio: 0.5
851
+ });
852
+ this.canExecuteTool = options.canExecuteTool;
853
+ }
854
+ getUsageSummary() {
855
+ return this.usageService.getUsageSummary();
856
+ }
857
+ getContextLeftPercent() {
858
+ const usage = this.usageService.getLastUsage();
859
+ if (!usage) {
860
+ return null;
861
+ }
862
+ const modelId = usage.model ?? this.llm.model;
863
+ const modelSpec = resolveModel(
864
+ this.modelRegistry,
865
+ modelId,
866
+ this.llm.provider
867
+ );
868
+ const contextLimit = modelSpec?.contextWindow ?? modelSpec?.maxInputTokens ?? null;
869
+ if (!contextLimit || contextLimit <= 0) {
870
+ return null;
871
+ }
872
+ const used = usage.total_tokens;
873
+ if (!Number.isFinite(used) || used <= 0) {
874
+ return 100;
875
+ }
876
+ const leftRatio = 1 - used / contextLimit;
877
+ const percent = Math.round(leftRatio * 100);
878
+ return Math.max(0, Math.min(100, percent));
879
+ }
880
+ getHistoryMessages() {
881
+ return this.history.getViewMessages();
882
+ }
883
+ replaceHistoryMessages(messages) {
884
+ this.history.replaceViewMessages(messages);
885
+ }
886
+ async *checkAndCompact(signal, options = {}) {
887
+ throwIfAborted(signal);
888
+ await this.trimToolOutputs();
889
+ const shouldCompact = options.force ? true : await this.compactionService?.shouldCompact(
890
+ this.llm,
891
+ this.usageService.getLastUsage()
892
+ );
893
+ if (shouldCompact && this.compactionService) {
894
+ throwIfAborted(signal);
895
+ const startEvent = {
896
+ type: "compaction_start",
897
+ timestamp: Date.now()
898
+ };
899
+ yield startEvent;
900
+ const { compacted, compactedMessages, usage } = await this.compactionService.compact(
901
+ this.llm,
902
+ this.history.getViewMessages(),
903
+ { signal }
904
+ );
905
+ this.usageService.updateUsageSummary(usage);
906
+ if (compacted && compactedMessages) {
907
+ this.history.replaceViewMessages(compactedMessages);
908
+ }
909
+ const completeEvent = {
910
+ type: "compaction_complete",
911
+ timestamp: Date.now(),
912
+ compacted
913
+ };
914
+ yield completeEvent;
915
+ }
916
+ }
917
+ async trimToolOutputs() {
918
+ if (!this.toolOutputCacheService) return;
919
+ const { messages, trimmed } = await this.toolOutputCacheService.trimMessages(
920
+ this.llm,
921
+ this.history.getViewMessages()
922
+ );
923
+ if (trimmed) {
924
+ this.history.replaceViewMessages(messages);
925
+ }
926
+ }
927
+ async processToolMessage(message) {
928
+ if (!this.toolOutputCacheService) return message;
929
+ return this.toolOutputCacheService.processToolMessage(message);
930
+ }
931
+ buildToolContext(signal) {
932
+ const deps = /* @__PURE__ */ Object.create(null);
933
+ const cache = /* @__PURE__ */ new Map();
934
+ const resolve = async (key) => {
935
+ if (cache.has(key.id)) {
936
+ return cache.get(key.id);
937
+ }
938
+ const value = await key.create();
939
+ cache.set(key.id, value);
940
+ return value;
941
+ };
942
+ return {
943
+ deps,
944
+ resolve,
945
+ signal,
946
+ now: () => /* @__PURE__ */ new Date()
947
+ };
948
+ }
949
+ recordLlmRequest(session, input) {
950
+ if (!session) return null;
951
+ const seq = (session.invoke_seq ?? 0) + 1;
952
+ session.invoke_seq = seq;
953
+ const modelName = input.model ?? this.llm.model;
954
+ session.append({
955
+ type: "llm.request",
956
+ run_id: session.run_id,
957
+ ts: nowIso(),
958
+ seq,
959
+ model: {
960
+ provider: this.llm.provider,
961
+ name: modelName
962
+ },
963
+ input: {
964
+ messages: input.messages,
965
+ tools: input.tools ?? null,
966
+ tool_choice: input.toolChoice ?? null,
967
+ model: input.model
968
+ }
969
+ });
970
+ return seq;
971
+ }
972
+ recordLlmResponse(session, seq, response) {
973
+ if (!session || seq === null) return;
974
+ session.append({
975
+ type: "llm.response",
976
+ run_id: session.run_id,
977
+ ts: nowIso(),
978
+ seq,
979
+ output: {
980
+ messages: response.messages,
981
+ usage: response.usage ?? null,
982
+ stop_reason: response.stop_reason ?? null,
983
+ provider_meta: response.provider_meta ?? null
984
+ }
985
+ });
986
+ }
987
+ async run(message, options = {}) {
988
+ let finalResponse = "";
989
+ for await (const event of this.runStream(message, options)) {
990
+ if (event.type === "final") {
991
+ finalResponse = event.content;
992
+ break;
993
+ }
994
+ }
995
+ return finalResponse;
996
+ }
997
+ async *runStream(message, options = {}) {
998
+ const session = options.session;
999
+ const signal = options.signal;
1000
+ const forceCompaction = options.forceCompaction ?? false;
1001
+ if (this.systemPrompt) {
1002
+ const systemMessage = {
1003
+ role: "system",
1004
+ content: this.systemPrompt
1005
+ };
1006
+ this.history.enqueueSystem(systemMessage);
1007
+ }
1008
+ if (forceCompaction) {
1009
+ yield* this.checkAndCompact(signal, { force: true });
1010
+ const finalResponseEvent2 = {
1011
+ type: "final",
1012
+ content: "Compaction run completed."
1013
+ };
1014
+ yield finalResponseEvent2;
1015
+ return;
1016
+ }
1017
+ this.history.enqueueUserMessage(message);
1018
+ let iterations = 0;
1019
+ while (iterations < this.maxIterations) {
1020
+ iterations++;
1021
+ throwIfAborted(signal);
1022
+ await this.trimToolOutputs();
1023
+ throwIfAborted(signal);
1024
+ const invokeInput = this.history.prepareInvokeInput({
1025
+ tools: this.tools.map((t) => t.definition),
1026
+ toolChoice: this.toolChoice
1027
+ });
1028
+ const seq = this.recordLlmRequest(session, invokeInput);
1029
+ const response = await this.llm.ainvoke({
1030
+ ...invokeInput,
1031
+ ...signal ? { signal } : {}
1032
+ });
1033
+ this.recordLlmResponse(session, seq, response);
1034
+ this.usageService.updateUsageSummary(response.usage);
1035
+ this.history.commitModelResponse(response);
1036
+ const { reasoningTexts, assistantTexts, toolCalls } = collectModelOutput(
1037
+ response.messages
1038
+ );
1039
+ for (const reasoningText of reasoningTexts) {
1040
+ const reasoningEvent = {
1041
+ type: "reasoning",
1042
+ content: reasoningText,
1043
+ timestamp: Date.now()
1044
+ };
1045
+ yield reasoningEvent;
1046
+ }
1047
+ const hasToolCalls = toolCalls.length > 0;
1048
+ const shouldEmitFinalOnly = !hasToolCalls && !this.requireDoneTool;
1049
+ if (!shouldEmitFinalOnly) {
1050
+ for (const assistantText of assistantTexts) {
1051
+ const textEvent = {
1052
+ type: "text",
1053
+ content: assistantText,
1054
+ timestamp: Date.now()
1055
+ };
1056
+ yield textEvent;
1057
+ }
1058
+ }
1059
+ if (!hasToolCalls) {
1060
+ if (!this.requireDoneTool) {
1061
+ yield* this.checkAndCompact(signal);
1062
+ const finalText = assistantTexts.join("\n").trim();
1063
+ const finalResponseEvent2 = {
1064
+ type: "final",
1065
+ content: finalText
1066
+ };
1067
+ yield finalResponseEvent2;
1068
+ return;
1069
+ } else {
1070
+ yield* this.checkAndCompact(signal);
1071
+ }
1072
+ continue;
1073
+ }
1074
+ let stepNumber = 0;
1075
+ for (const toolCall of toolCalls) {
1076
+ stepNumber++;
1077
+ let jsonArgs;
1078
+ try {
1079
+ jsonArgs = JSON.parse(toolCall.function.arguments);
1080
+ } catch (e) {
1081
+ if (e instanceof SyntaxError) {
1082
+ jsonArgs = { _raw: toolCall.function.arguments };
1083
+ } else {
1084
+ throw e;
1085
+ }
1086
+ }
1087
+ const stepStartEvent = {
1088
+ type: "step_start",
1089
+ step_id: toolCall.id,
1090
+ title: toolCall.function.name,
1091
+ step_number: stepNumber
1092
+ };
1093
+ yield stepStartEvent;
1094
+ const toolCallEvent = {
1095
+ type: "tool_call",
1096
+ tool: toolCall.function.name,
1097
+ args: jsonArgs,
1098
+ tool_call_id: toolCall.id
1099
+ };
1100
+ yield toolCallEvent;
1101
+ const startTime = Date.now();
1102
+ try {
1103
+ throwIfAborted(signal);
1104
+ const execution = await this.executeToolCall(toolCall, signal);
1105
+ const rawOutput = stringifyContent(execution.message.content, {
1106
+ mode: "display"
1107
+ });
1108
+ const processedMessage = await this.processToolMessage(
1109
+ execution.message
1110
+ );
1111
+ if (session) {
1112
+ session.append({
1113
+ type: "tool.output",
1114
+ run_id: session.run_id,
1115
+ ts: nowIso(),
1116
+ tool: toolCall.function.name,
1117
+ tool_call_id: toolCall.id,
1118
+ result_raw: rawOutput,
1119
+ is_error: processedMessage.is_error,
1120
+ output_ref: processedMessage.output_ref
1121
+ });
1122
+ }
1123
+ this.history.enqueueToolResult(processedMessage);
1124
+ const toolResultEvent = {
1125
+ type: "tool_result",
1126
+ tool: toolCall.function.name,
1127
+ result: stringifyContent(processedMessage.content, {
1128
+ mode: "display"
1129
+ }),
1130
+ tool_call_id: toolCall.id,
1131
+ is_error: processedMessage.is_error
1132
+ };
1133
+ yield toolResultEvent;
1134
+ const durationMs = Date.now() - startTime;
1135
+ const stepCompleteEvent = {
1136
+ type: "step_complete",
1137
+ step_id: toolCall.id,
1138
+ status: execution.message.is_error ? "error" : "completed",
1139
+ duration_ms: durationMs
1140
+ };
1141
+ yield stepCompleteEvent;
1142
+ if (execution.done) {
1143
+ const finalResponseEvent2 = {
1144
+ type: "final",
1145
+ content: execution.finalMessage ?? assistantTexts.join("\n").trim()
1146
+ };
1147
+ yield finalResponseEvent2;
1148
+ return;
1149
+ }
1150
+ } catch (error) {
1151
+ if (isAbortError(error)) {
1152
+ throw error;
1153
+ }
1154
+ const content = `Error: ${error instanceof Error ? error.message : String(error)}`;
1155
+ const errorToolMessage = {
1156
+ role: "tool",
1157
+ tool_call_id: toolCall.id,
1158
+ tool_name: toolCall.function.name,
1159
+ content,
1160
+ is_error: true
1161
+ };
1162
+ const processedMessage = await this.processToolMessage(errorToolMessage);
1163
+ const rawOutput = stringifyContent(errorToolMessage.content, {
1164
+ mode: "display"
1165
+ });
1166
+ if (session) {
1167
+ session.append({
1168
+ type: "tool.output",
1169
+ run_id: session.run_id,
1170
+ ts: nowIso(),
1171
+ tool: toolCall.function.name,
1172
+ tool_call_id: toolCall.id,
1173
+ result_raw: rawOutput,
1174
+ is_error: true,
1175
+ output_ref: processedMessage.output_ref
1176
+ });
1177
+ }
1178
+ this.history.enqueueToolResult(processedMessage);
1179
+ const toolResultEvent = {
1180
+ type: "tool_result",
1181
+ tool: toolCall.function.name,
1182
+ result: stringifyContent(processedMessage.content, {
1183
+ mode: "display"
1184
+ }),
1185
+ tool_call_id: toolCall.id,
1186
+ is_error: true
1187
+ };
1188
+ yield toolResultEvent;
1189
+ const durationMs = Date.now() - startTime;
1190
+ const stepCompleteEvent = {
1191
+ type: "step_complete",
1192
+ step_id: toolCall.id,
1193
+ status: "error",
1194
+ duration_ms: durationMs
1195
+ };
1196
+ yield stepCompleteEvent;
1197
+ }
1198
+ }
1199
+ yield* this.checkAndCompact(signal);
1200
+ }
1201
+ const finalResponse = await this.generateFinalResponse(session, signal);
1202
+ const finalResponseEvent = {
1203
+ type: "final",
1204
+ content: finalResponse
1205
+ };
1206
+ yield finalResponseEvent;
1207
+ return;
1208
+ }
1209
+ async generateFinalResponse(session, signal) {
1210
+ const summaryMessage = {
1211
+ role: "user",
1212
+ content: `You are generating the final response for the user after the agent reached max iterations. Summarize what was completed, what is pending, and any blockers. Be concise, user-facing, and do not mention internal agent mechanics. If there is a clear next step, suggest it as a short list.`
1213
+ };
1214
+ try {
1215
+ const input = {
1216
+ messages: [...this.history.getViewMessages(), summaryMessage],
1217
+ // temporal messages for summary
1218
+ tools: null,
1219
+ // no tools are allowed at this point
1220
+ toolChoice: "none"
1221
+ };
1222
+ const seq = this.recordLlmRequest(session, input);
1223
+ const summary = await this.llm.ainvoke({
1224
+ ...input,
1225
+ ...signal ? { signal } : {}
1226
+ });
1227
+ this.recordLlmResponse(session, seq, summary);
1228
+ this.usageService.updateUsageSummary(summary.usage);
1229
+ const { assistantTexts } = collectModelOutput(summary.messages);
1230
+ const finalResponse = `[Max Iterations Reached]
1231
+
1232
+ ${assistantTexts.join("\n").trim()}`;
1233
+ return finalResponse;
1234
+ } catch {
1235
+ if (signal?.aborted) {
1236
+ throw createAbortError();
1237
+ }
1238
+ return "[Max Iterations Reached]\n\nSummary unavailable due to an internal error.";
1239
+ }
1240
+ }
1241
+ async executeToolCall(toolCall, signal) {
1242
+ const toolName = toolCall.function.name;
1243
+ const tool = this.tools.find((t) => t.name === toolName);
1244
+ if (!tool) {
1245
+ return {
1246
+ message: {
1247
+ role: "tool",
1248
+ tool_call_id: toolCall.id,
1249
+ tool_name: toolName,
1250
+ content: `Error: Unknown tool '${toolName}'`,
1251
+ is_error: true
1252
+ }
1253
+ };
1254
+ }
1255
+ if (this.canExecuteTool) {
1256
+ try {
1257
+ const decision = await this.canExecuteTool(
1258
+ toolCall,
1259
+ toolCall.function.arguments,
1260
+ this.buildToolContext(signal)
1261
+ );
1262
+ if (decision.decision === "deny") {
1263
+ const deniedContent = `Permission denied${decision.reason ? `: ${decision.reason}` : ""}`;
1264
+ return {
1265
+ message: {
1266
+ role: "tool",
1267
+ tool_call_id: toolCall.id,
1268
+ tool_name: toolName,
1269
+ content: deniedContent,
1270
+ is_error: true
1271
+ },
1272
+ ...decision.stop_turn ? {
1273
+ done: true,
1274
+ finalMessage: "Permission request was denied. Turn stopped. Please send your next input to continue."
1275
+ } : {}
1276
+ };
1277
+ }
1278
+ } catch (error) {
1279
+ return {
1280
+ message: {
1281
+ role: "tool",
1282
+ tool_call_id: toolCall.id,
1283
+ tool_name: toolName,
1284
+ content: `Permission check failed: ${error instanceof Error ? error.message : String(error)}`,
1285
+ is_error: true
1286
+ }
1287
+ };
1288
+ }
1289
+ }
1290
+ try {
1291
+ const result = await tool.executeRaw(
1292
+ toolCall.function.arguments,
1293
+ this.buildToolContext(signal)
1294
+ );
1295
+ return {
1296
+ message: {
1297
+ role: "tool",
1298
+ tool_call_id: toolCall.id,
1299
+ tool_name: toolName,
1300
+ content: toolResultToContent(result)
1301
+ }
1302
+ };
1303
+ } catch (error) {
1304
+ if (error instanceof TaskComplete) {
1305
+ return {
1306
+ message: {
1307
+ role: "tool",
1308
+ tool_call_id: toolCall.id,
1309
+ tool_name: toolName,
1310
+ content: "Task complete"
1311
+ },
1312
+ done: true,
1313
+ finalMessage: error.finalMessage
1314
+ };
1315
+ }
1316
+ return {
1317
+ message: {
1318
+ role: "tool",
1319
+ tool_call_id: toolCall.id,
1320
+ tool_name: toolName,
1321
+ content: `Error: ${error instanceof Error ? error.message : String(error)}`,
1322
+ is_error: true
1323
+ }
1324
+ };
1325
+ }
1326
+ }
1327
+ };
1328
+
1329
+ // src/llm/anthropic/chat.ts
1330
+ import Anthropic from "@anthropic-ai/sdk";
1331
+
1332
+ // src/llm/anthropic/serializer.ts
1333
+ var isRecord = (value) => typeof value === "object" && value !== null;
1334
+ var stringifyUnknown2 = (value) => {
1335
+ if (value == null) return "";
1336
+ if (typeof value === "string") return value;
1337
+ try {
1338
+ return JSON.stringify(value);
1339
+ } catch {
1340
+ return String(value);
1341
+ }
1342
+ };
1343
+ var formatOtherPart = (part) => {
1344
+ const payloadText = stringifyUnknown2(part.payload);
1345
+ return payloadText ? `[other:${part.provider}/${part.kind}] ${payloadText}` : `[other:${part.provider}/${part.kind}]`;
1346
+ };
1347
+ var parseDataUrl = (url) => {
1348
+ const match = /^data:([^;]+);base64,(.+)$/.exec(url);
1349
+ if (!match) return null;
1350
+ return { mediaType: match[1], data: match[2] };
1351
+ };
1352
+ var isSupportedImageMediaType = (value) => value === "image/png" || value === "image/jpeg" || value === "image/webp" || value === "image/gif";
1353
+ var isAnthropicContentBlock = (value) => {
1354
+ if (!isRecord(value) || typeof value.type !== "string") return false;
1355
+ switch (value.type) {
1356
+ case "text":
1357
+ return typeof value.text === "string";
1358
+ case "image":
1359
+ return isRecord(value.source);
1360
+ case "document":
1361
+ return isRecord(value.source);
1362
+ case "search_result":
1363
+ return true;
1364
+ default:
1365
+ return false;
1366
+ }
1367
+ };
1368
+ var contentPartsToText = (content) => {
1369
+ if (content == null) return "";
1370
+ if (typeof content === "string") return content;
1371
+ return content.map((part) => {
1372
+ switch (part.type) {
1373
+ case "text":
1374
+ return part.text;
1375
+ case "image_url":
1376
+ return "[image]";
1377
+ case "document":
1378
+ return "[document]";
1379
+ case "other":
1380
+ return `[other:${part.provider}/${part.kind}]`;
1381
+ default:
1382
+ return "[content]";
1383
+ }
1384
+ }).join("");
1385
+ };
1386
+ var toTextBlock = (text) => ({
1387
+ type: "text",
1388
+ text
1389
+ });
1390
+ var toContentBlocks = (content) => {
1391
+ if (content == null) return [];
1392
+ if (typeof content === "string") {
1393
+ return content ? [toTextBlock(content)] : [];
1394
+ }
1395
+ return content.map((part) => {
1396
+ switch (part.type) {
1397
+ case "text":
1398
+ return toTextBlock(part.text);
1399
+ case "image_url": {
1400
+ const dataUrl = parseDataUrl(part.image_url.url);
1401
+ if (dataUrl) {
1402
+ const mediaType = part.image_url.media_type ?? (isSupportedImageMediaType(dataUrl.mediaType) ? dataUrl.mediaType : void 0);
1403
+ if (!mediaType) {
1404
+ return toTextBlock(part.image_url.url);
1405
+ }
1406
+ return {
1407
+ type: "image",
1408
+ source: {
1409
+ type: "base64",
1410
+ media_type: mediaType,
1411
+ data: dataUrl.data
1412
+ }
1413
+ };
1414
+ }
1415
+ return toTextBlock(part.image_url.url);
1416
+ }
1417
+ case "document":
1418
+ return toTextBlock("[document]");
1419
+ case "other":
1420
+ if (part.provider === "anthropic" && isAnthropicContentBlock(part.payload)) {
1421
+ return part.payload;
1422
+ }
1423
+ return toTextBlock(formatOtherPart(part));
1424
+ default:
1425
+ return toTextBlock("");
1426
+ }
1427
+ });
1428
+ };
1429
+ var ensureToolResultBlocks = (blocks) => blocks.length > 0 ? blocks : [toTextBlock("")];
1430
+ var ensureMessageBlocks = (blocks) => blocks.length > 0 ? blocks : [toTextBlock("")];
1431
+ var toToolUseBlock = (call) => {
1432
+ let input = call.function.arguments;
1433
+ if (call.function.arguments) {
1434
+ try {
1435
+ input = JSON.parse(call.function.arguments);
1436
+ } catch {
1437
+ input = { value: call.function.arguments };
1438
+ }
1439
+ }
1440
+ return {
1441
+ type: "tool_use",
1442
+ id: call.id,
1443
+ name: call.function.name,
1444
+ input
1445
+ };
1446
+ };
1447
+ var toToolResultBlock = (message) => {
1448
+ const content = typeof message.content === "string" ? message.content : ensureToolResultBlocks(toContentBlocks(message.content));
1449
+ return {
1450
+ type: "tool_result",
1451
+ tool_use_id: message.tool_call_id,
1452
+ content,
1453
+ ...message.is_error ? { is_error: true } : {}
1454
+ };
1455
+ };
1456
+ var toAnthropicTools = (tools) => {
1457
+ if (!tools || tools.length === 0) return void 0;
1458
+ return tools.map((tool) => ({
1459
+ name: tool.name,
1460
+ description: tool.description,
1461
+ input_schema: tool.parameters
1462
+ }));
1463
+ };
1464
+ var toAnthropicToolChoice = (choice) => {
1465
+ if (!choice) return void 0;
1466
+ if (choice === "auto") return { type: "auto" };
1467
+ if (choice === "required") return { type: "any" };
1468
+ if (choice === "none") return void 0;
1469
+ return { type: "tool", name: choice };
1470
+ };
1471
+ var toAnthropicMessages = (messages) => {
1472
+ const systemParts = [];
1473
+ const output = [];
1474
+ for (const message of messages) {
1475
+ switch (message.role) {
1476
+ case "system": {
1477
+ const text = contentPartsToText(message.content);
1478
+ if (text) systemParts.push(text);
1479
+ break;
1480
+ }
1481
+ case "reasoning":
1482
+ break;
1483
+ case "tool": {
1484
+ output.push({
1485
+ role: "user",
1486
+ content: [toToolResultBlock(message)]
1487
+ });
1488
+ break;
1489
+ }
1490
+ case "assistant": {
1491
+ const blocks = [];
1492
+ blocks.push(...toContentBlocks(message.content));
1493
+ if (message.tool_calls?.length) {
1494
+ blocks.push(...message.tool_calls.map(toToolUseBlock));
1495
+ }
1496
+ output.push({
1497
+ role: "assistant",
1498
+ content: ensureMessageBlocks(blocks)
1499
+ });
1500
+ break;
1501
+ }
1502
+ case "user": {
1503
+ const blocks = ensureToolResultBlocks(toContentBlocks(message.content));
1504
+ output.push({
1505
+ role: "user",
1506
+ content: blocks
1507
+ });
1508
+ break;
1509
+ }
1510
+ default:
1511
+ break;
1512
+ }
1513
+ }
1514
+ const system = systemParts.length ? systemParts.join("\n\n") : void 0;
1515
+ return { system, messages: output };
1516
+ };
1517
+ var extractText = (blocks) => blocks.filter((block) => block.type === "text").map((block) => block.text).join("");
1518
+ var toUsage = (response) => {
1519
+ if (!response.usage) return null;
1520
+ const inputTokens = response.usage.input_tokens ?? 0;
1521
+ const outputTokens = response.usage.output_tokens ?? 0;
1522
+ return {
1523
+ model: response.model ?? "",
1524
+ input_tokens: inputTokens,
1525
+ input_cached_tokens: response.usage.cache_read_input_tokens ?? null,
1526
+ input_cache_creation_tokens: response.usage.cache_creation_input_tokens ?? null,
1527
+ output_tokens: outputTokens,
1528
+ total_tokens: inputTokens + outputTokens
1529
+ };
1530
+ };
1531
+ var toChatInvokeCompletion = (response) => {
1532
+ const blocks = response.content ?? [];
1533
+ const messages = [];
1534
+ for (const block of blocks) {
1535
+ switch (block.type) {
1536
+ case "text":
1537
+ messages.push({
1538
+ role: "assistant",
1539
+ content: block.text
1540
+ });
1541
+ break;
1542
+ case "tool_use":
1543
+ messages.push({
1544
+ role: "assistant",
1545
+ content: null,
1546
+ tool_calls: [
1547
+ {
1548
+ id: block.id,
1549
+ type: "function",
1550
+ function: {
1551
+ name: block.name,
1552
+ arguments: isRecord(block.input) ? JSON.stringify(block.input) : JSON.stringify({ value: block.input })
1553
+ },
1554
+ provider_meta: block
1555
+ }
1556
+ ]
1557
+ });
1558
+ break;
1559
+ case "thinking":
1560
+ messages.push({
1561
+ role: "reasoning",
1562
+ content: block.thinking,
1563
+ raw_item: block
1564
+ });
1565
+ break;
1566
+ case "redacted_thinking":
1567
+ messages.push({
1568
+ role: "reasoning",
1569
+ content: "[redacted]",
1570
+ raw_item: block
1571
+ });
1572
+ break;
1573
+ default:
1574
+ messages.push({
1575
+ role: "assistant",
1576
+ content: [
1577
+ {
1578
+ type: "other",
1579
+ provider: "anthropic",
1580
+ kind: block.type,
1581
+ payload: block
1582
+ }
1583
+ ]
1584
+ });
1585
+ break;
1586
+ }
1587
+ }
1588
+ if (messages.length === 0) {
1589
+ const fallback = extractText(blocks);
1590
+ if (fallback) {
1591
+ messages.push({ role: "assistant", content: fallback });
1592
+ }
1593
+ }
1594
+ return {
1595
+ messages,
1596
+ usage: toUsage(response),
1597
+ stop_reason: response.stop_reason ?? response.stop_sequence ?? null,
1598
+ provider_meta: {
1599
+ response_id: response.id,
1600
+ model: response.model,
1601
+ raw_output_text: stringifyUnknown2(extractText(blocks))
1602
+ }
1603
+ };
1604
+ };
1605
+
1606
+ // src/llm/anthropic/chat.ts
1607
+ var PROVIDER_NAME = "anthropic";
1608
+ var DEFAULT_MODEL = ANTHROPIC_DEFAULT_MODEL;
1609
+ var DEFAULT_MAX_TOKENS = 4096;
1610
+ var ChatAnthropic = class {
1611
+ provider = PROVIDER_NAME;
1612
+ model;
1613
+ client;
1614
+ defaultMaxTokens;
1615
+ constructor(options = {}) {
1616
+ this.client = options.client ?? new Anthropic(options.clientOptions);
1617
+ this.model = options.model ?? DEFAULT_MODEL;
1618
+ this.defaultMaxTokens = options.maxTokens ?? DEFAULT_MAX_TOKENS;
1619
+ }
1620
+ async ainvoke(input, verbose = false) {
1621
+ const {
1622
+ messages,
1623
+ tools: toolDefs,
1624
+ toolChoice,
1625
+ options,
1626
+ model,
1627
+ signal
1628
+ } = input;
1629
+ const { system, messages: anthropicMessages } = toAnthropicMessages(messages);
1630
+ const enableTools = toolChoice !== "none";
1631
+ const tools = enableTools ? toAnthropicTools(toolDefs) : void 0;
1632
+ const tool_choice = enableTools ? toAnthropicToolChoice(toolChoice) : void 0;
1633
+ const { max_tokens, ...rest } = options ?? {};
1634
+ const request = {
1635
+ model: model ?? this.model,
1636
+ max_tokens: max_tokens ?? this.defaultMaxTokens,
1637
+ messages: anthropicMessages,
1638
+ ...rest,
1639
+ ...system ? { system } : {},
1640
+ ...tools ? { tools } : {},
1641
+ ...tool_choice ? { tool_choice } : {}
1642
+ };
1643
+ if (verbose) {
1644
+ console.debug(request);
1645
+ }
1646
+ const response = await this.client.messages.create(
1647
+ request,
1648
+ signal ? {
1649
+ signal
1650
+ } : void 0
1651
+ );
1652
+ if (verbose) {
1653
+ console.debug(response);
1654
+ }
1655
+ return toChatInvokeCompletion(response);
1656
+ }
1657
+ };
1658
+
1659
+ // src/llm/openai/chat.ts
1660
+ import OpenAI from "openai";
1661
+
1662
+ // src/llm/openai/response-utils.ts
1663
+ var isRecord2 = (value) => typeof value === "object" && value !== null;
1664
+ var stringifyUnknown3 = (value) => {
1665
+ if (value == null) return "";
1666
+ if (typeof value === "string") return value;
1667
+ try {
1668
+ return JSON.stringify(value);
1669
+ } catch {
1670
+ return String(value);
1671
+ }
1672
+ };
1673
+ var formatOtherPart2 = (part) => {
1674
+ const payloadText = stringifyUnknown3(part.payload);
1675
+ return payloadText ? `[other:${part.provider}/${part.kind}] ${payloadText}` : `[other:${part.provider}/${part.kind}]`;
1676
+ };
1677
+ var isOpenAiInputContent = (value) => {
1678
+ if (!isRecord2(value)) return false;
1679
+ const type = value.type;
1680
+ if (type === "input_text") {
1681
+ return typeof value.text === "string";
1682
+ }
1683
+ if (type === "input_image") {
1684
+ return typeof value.image_url === "string";
1685
+ }
1686
+ if (type === "input_file") {
1687
+ return typeof value.file_data === "string" || typeof value.file_id === "string";
1688
+ }
1689
+ return false;
1690
+ };
1691
+ var extractOutputText = (items) => {
1692
+ const texts = [];
1693
+ for (const item of items) {
1694
+ if (item.type !== "message") continue;
1695
+ for (const part of item.content ?? []) {
1696
+ if (part.type === "output_text") {
1697
+ texts.push(part.text);
1698
+ }
1699
+ }
1700
+ }
1701
+ return texts.join("");
1702
+ };
1703
+ var toResponseInputContent = (part) => {
1704
+ switch (part.type) {
1705
+ case "text":
1706
+ return { type: "input_text", text: part.text };
1707
+ case "image_url":
1708
+ return {
1709
+ type: "input_image",
1710
+ image_url: part.image_url.url,
1711
+ detail: part.image_url.detail ?? "auto"
1712
+ };
1713
+ case "document":
1714
+ return {
1715
+ type: "input_file",
1716
+ file_data: part.source.data,
1717
+ filename: "document.pdf"
1718
+ };
1719
+ case "other":
1720
+ if (part.provider === "openai" && isOpenAiInputContent(part.payload)) {
1721
+ return part.payload;
1722
+ }
1723
+ return { type: "input_text", text: formatOtherPart2(part) };
1724
+ default:
1725
+ return { type: "input_text", text: "" };
1726
+ }
1727
+ };
1728
+ var toResponseInputContents = (content) => {
1729
+ if (content == null) {
1730
+ return "";
1731
+ }
1732
+ if (typeof content === "string") {
1733
+ return content;
1734
+ }
1735
+ return content.map(toResponseInputContent);
1736
+ };
1737
+ var toFunctionCallOutput = (content) => {
1738
+ if (typeof content === "string") {
1739
+ return content;
1740
+ }
1741
+ return content.map(toResponseInputContent);
1742
+ };
1743
+
1744
+ // src/llm/openai/serializer.ts
1745
+ var toOpenAiOtherPart = (kind, payload) => ({
1746
+ type: "other",
1747
+ provider: "openai",
1748
+ kind,
1749
+ payload
1750
+ });
1751
+ var toAssistantOutputMessageContent = (item) => {
1752
+ const contents = item.content ?? [];
1753
+ if (contents.length === 0) {
1754
+ return null;
1755
+ }
1756
+ const parts = contents.map((part) => {
1757
+ if (part.type === "output_text") {
1758
+ return { type: "text", text: part.text };
1759
+ }
1760
+ return toOpenAiOtherPart(part.type, part);
1761
+ });
1762
+ return parts;
1763
+ };
1764
+ var stringifyUnknown4 = (value) => {
1765
+ if (value == null) return "";
1766
+ if (typeof value === "string") return value;
1767
+ try {
1768
+ return JSON.stringify(value);
1769
+ } catch {
1770
+ return String(value);
1771
+ }
1772
+ };
1773
+ var formatOtherPart3 = (part) => {
1774
+ const payloadText = stringifyUnknown4(part.payload);
1775
+ return payloadText ? `[other:${part.provider}/${part.kind}] ${payloadText}` : `[other:${part.provider}/${part.kind}]`;
1776
+ };
1777
+ var isOpenAIAssistantInputContent = (value) => {
1778
+ if (!value || typeof value !== "object") return false;
1779
+ const record = value;
1780
+ if (record.type === "output_text") {
1781
+ return typeof record.text === "string";
1782
+ }
1783
+ if (record.type === "refusal") {
1784
+ return typeof record.refusal === "string";
1785
+ }
1786
+ return false;
1787
+ };
1788
+ var toAssistantInputContent = (part) => {
1789
+ switch (part.type) {
1790
+ case "text":
1791
+ return { type: "output_text", text: part.text };
1792
+ case "other":
1793
+ if (part.provider === "openai" && isOpenAIAssistantInputContent(part.payload)) {
1794
+ return part.payload;
1795
+ }
1796
+ return { type: "output_text", text: formatOtherPart3(part) };
1797
+ case "image_url":
1798
+ return { type: "output_text", text: "[image]" };
1799
+ case "document":
1800
+ return { type: "output_text", text: "[document]" };
1801
+ default:
1802
+ return { type: "output_text", text: "" };
1803
+ }
1804
+ };
1805
+ var toAssistantInputMessageContent = (content, refusal) => {
1806
+ const parts = [];
1807
+ if (typeof content === "string") {
1808
+ if (content) {
1809
+ parts.push({ type: "output_text", text: content });
1810
+ }
1811
+ } else if (Array.isArray(content)) {
1812
+ parts.push(...content.map(toAssistantInputContent));
1813
+ }
1814
+ if (refusal) {
1815
+ parts.push({ type: "refusal", refusal });
1816
+ }
1817
+ return parts;
1818
+ };
1819
+ var extractReasoningText = (item) => {
1820
+ const summaryText = (item.summary ?? []).map(
1821
+ (part) => part && typeof part === "object" && "text" in part ? String(part.text ?? "") : ""
1822
+ ).filter((text) => text.length > 0).join("\n");
1823
+ if (summaryText) return summaryText;
1824
+ const contentText = (item.content ?? []).map(
1825
+ (part) => part && typeof part === "object" && "text" in part ? String(part.text ?? "") : ""
1826
+ ).filter((text) => text.length > 0).join("\n");
1827
+ return contentText;
1828
+ };
1829
+ var toMessageSequence = (response) => {
1830
+ const messages = [];
1831
+ for (const item of response.output) {
1832
+ switch (item.type) {
1833
+ case "reasoning": {
1834
+ messages.push({
1835
+ role: "reasoning",
1836
+ content: extractReasoningText(item),
1837
+ raw_item: item
1838
+ });
1839
+ break;
1840
+ }
1841
+ case "function_call": {
1842
+ const toolCall = {
1843
+ id: item.call_id,
1844
+ type: "function",
1845
+ function: { name: item.name, arguments: item.arguments },
1846
+ provider_meta: item
1847
+ };
1848
+ messages.push({
1849
+ role: "assistant",
1850
+ content: null,
1851
+ tool_calls: [toolCall]
1852
+ });
1853
+ break;
1854
+ }
1855
+ case "message": {
1856
+ messages.push({
1857
+ role: "assistant",
1858
+ content: toAssistantOutputMessageContent(item)
1859
+ });
1860
+ break;
1861
+ }
1862
+ default: {
1863
+ messages.push({
1864
+ role: "assistant",
1865
+ content: [toOpenAiOtherPart(item.type, item)]
1866
+ });
1867
+ break;
1868
+ }
1869
+ }
1870
+ }
1871
+ return messages;
1872
+ };
1873
+ function extractInstructions(messages) {
1874
+ const chunks = [];
1875
+ for (const message of messages) {
1876
+ if (message.role !== "system") {
1877
+ continue;
1878
+ }
1879
+ const content = message.content;
1880
+ if (content == null) {
1881
+ continue;
1882
+ }
1883
+ if (typeof content === "string") {
1884
+ const trimmed = content.trim();
1885
+ if (trimmed) {
1886
+ chunks.push(trimmed);
1887
+ }
1888
+ continue;
1889
+ }
1890
+ const text = content.map((part) => {
1891
+ switch (part.type) {
1892
+ case "text":
1893
+ return part.text;
1894
+ default:
1895
+ return "";
1896
+ }
1897
+ }).join("").trim();
1898
+ if (text) {
1899
+ chunks.push(text);
1900
+ }
1901
+ }
1902
+ return chunks.length ? chunks.join("\n\n") : void 0;
1903
+ }
1904
+ function toResponsesInput(messages) {
1905
+ const items = [];
1906
+ for (const message of messages) {
1907
+ if (message.role === "system") {
1908
+ continue;
1909
+ }
1910
+ if (message.role === "tool") {
1911
+ items.push(toFunctionCallOutputItem(message));
1912
+ continue;
1913
+ }
1914
+ if (message.role === "assistant" && message.tool_calls?.length) {
1915
+ const assistantMessageItem = toAssistantMessageItem(message);
1916
+ if (assistantMessageItem) {
1917
+ items.push(assistantMessageItem);
1918
+ }
1919
+ for (const call of message.tool_calls) {
1920
+ items.push(toFunctionCallItem(call));
1921
+ }
1922
+ continue;
1923
+ }
1924
+ if (message.role === "reasoning") {
1925
+ continue;
1926
+ }
1927
+ if (message.role === "assistant") {
1928
+ const assistantMessageItem = toAssistantMessageItem(message);
1929
+ if (assistantMessageItem) {
1930
+ items.push(assistantMessageItem);
1931
+ }
1932
+ continue;
1933
+ }
1934
+ items.push({
1935
+ type: "message",
1936
+ role: message.role,
1937
+ content: toUserMessageContent(message.content)
1938
+ });
1939
+ }
1940
+ return items;
1941
+ }
1942
+ function toResponsesTools(tools) {
1943
+ if (!tools || tools.length === 0) {
1944
+ return void 0;
1945
+ }
1946
+ return tools.map((tool) => ({
1947
+ type: "function",
1948
+ name: tool.name,
1949
+ description: tool.description,
1950
+ parameters: tool.parameters,
1951
+ strict: tool.strict ?? false
1952
+ }));
1953
+ }
1954
+ function toResponsesToolChoice(choice) {
1955
+ if (!choice) {
1956
+ return void 0;
1957
+ }
1958
+ if (choice === "auto" || choice === "required" || choice === "none") {
1959
+ return choice;
1960
+ }
1961
+ return { type: "function", name: choice };
1962
+ }
1963
+ function toChatInvokeCompletion2(response) {
1964
+ const usage = response.usage ? {
1965
+ model: response.model,
1966
+ input_tokens: response.usage.input_tokens,
1967
+ input_cached_tokens: response.usage.input_tokens_details?.cached_tokens,
1968
+ output_tokens: response.usage.output_tokens,
1969
+ total_tokens: response.usage.total_tokens
1970
+ } : null;
1971
+ const messages = toMessageSequence(response);
1972
+ if (!messages.length) {
1973
+ const fallbackText = typeof response.output_text === "string" ? response.output_text : extractOutputText(response.output);
1974
+ if (fallbackText) {
1975
+ messages.push({ role: "assistant", content: fallbackText });
1976
+ }
1977
+ }
1978
+ return {
1979
+ messages,
1980
+ usage,
1981
+ stop_reason: response.incomplete_details?.reason ?? response.status ?? null,
1982
+ provider_meta: {
1983
+ response_id: response.id
1984
+ }
1985
+ };
1986
+ }
1987
+ function toUserMessageContent(content) {
1988
+ return toResponseInputContents(content);
1989
+ }
1990
+ function toAssistantMessageItem(message) {
1991
+ const content = toAssistantInputMessageContent(
1992
+ message.content,
1993
+ message.refusal
1994
+ );
1995
+ if (content.length === 0) {
1996
+ return null;
1997
+ }
1998
+ return {
1999
+ type: "message",
2000
+ role: "assistant",
2001
+ content
2002
+ };
2003
+ }
2004
+ function toFunctionCallItem(call) {
2005
+ return {
2006
+ type: "function_call",
2007
+ call_id: call.id,
2008
+ name: call.function.name,
2009
+ arguments: call.function.arguments
2010
+ };
2011
+ }
2012
+ function toFunctionCallOutputItem(message) {
2013
+ return {
2014
+ type: "function_call_output",
2015
+ call_id: message.tool_call_id,
2016
+ output: toFunctionCallOutput(message.content)
2017
+ };
2018
+ }
2019
+
2020
+ // src/llm/openai/chat.ts
2021
+ var PROVIDER_NAME2 = "openai";
2022
+ var DEFAULT_MODEL2 = OPENAI_DEFAULT_MODEL;
2023
+ var DEFAULT_REASONING_EFFORT = OPENAI_DEFAULT_REASONING_EFFORT;
2024
+ var DEFAULT_REASONING_SUMMARY = "auto";
2025
+ var ChatOpenAI = class {
2026
+ provider = PROVIDER_NAME2;
2027
+ model;
2028
+ client;
2029
+ defaultReasoningEffort;
2030
+ defaultTextVerbosity;
2031
+ constructor(options = {}) {
2032
+ this.client = options.client ?? new OpenAI(options.clientOptions);
2033
+ this.model = options.model ?? DEFAULT_MODEL2;
2034
+ this.defaultReasoningEffort = options.reasoningEffort ?? DEFAULT_REASONING_EFFORT;
2035
+ this.defaultTextVerbosity = options.textVerbosity;
2036
+ }
2037
+ async ainvoke(input, verbose = false) {
2038
+ const {
2039
+ messages,
2040
+ tools: toolDefs,
2041
+ toolChoice,
2042
+ options,
2043
+ model,
2044
+ signal
2045
+ } = input;
2046
+ const inputItems = toResponsesInput(messages);
2047
+ const instructions = extractInstructions(messages);
2048
+ const tools = toResponsesTools(toolDefs);
2049
+ const tool_choice = toResponsesToolChoice(toolChoice);
2050
+ const { reasoningEffort, textVerbosity, ...rest } = options ?? {};
2051
+ const request = {
2052
+ model: model ?? this.model,
2053
+ input: inputItems,
2054
+ ...rest,
2055
+ ...tools ? { tools } : {},
2056
+ ...tool_choice ? { tool_choice } : {}
2057
+ };
2058
+ if (request.store === void 0) {
2059
+ request.store = false;
2060
+ }
2061
+ if (instructions) {
2062
+ request.instructions = instructions;
2063
+ }
2064
+ request.include = ["reasoning.encrypted_content"];
2065
+ const effort = reasoningEffort ?? this.defaultReasoningEffort;
2066
+ request.reasoning = { effort, summary: DEFAULT_REASONING_SUMMARY };
2067
+ const verbosity = textVerbosity ?? this.defaultTextVerbosity;
2068
+ if (verbosity) {
2069
+ request.text = {
2070
+ ...request.text ?? {},
2071
+ verbosity
2072
+ };
2073
+ }
2074
+ if (verbose) {
2075
+ console.debug(request);
2076
+ }
2077
+ const streamRequest = {
2078
+ ...request,
2079
+ stream: true
2080
+ };
2081
+ const response = await this.client.responses.stream(
2082
+ streamRequest,
2083
+ signal ? {
2084
+ signal
2085
+ } : void 0
2086
+ ).finalResponse();
2087
+ if (verbose) {
2088
+ console.debug(response);
2089
+ }
2090
+ return toChatInvokeCompletion2(response);
2091
+ }
2092
+ };
2093
+
2094
+ // src/prompts.ts
2095
+ import path from "path";
2096
+ import { fileURLToPath } from "url";
2097
+ var resolvePromptPath = () => {
2098
+ if (typeof __filename === "string") {
2099
+ return path.resolve(path.dirname(__filename), "../prompts/system.md");
2100
+ }
2101
+ if (typeof import.meta !== "undefined" && import.meta.url) {
2102
+ return fileURLToPath(new URL("../prompts/system.md", import.meta.url));
2103
+ }
2104
+ return path.resolve("prompts/system.md");
2105
+ };
2106
+ var promptPath = resolvePromptPath();
2107
+ var getDefaultSystemPromptPath = () => promptPath;
2108
+
2109
+ // src/tools/define.ts
2110
+ import { toJSONSchema } from "zod";
2111
+
2112
+ // src/types/llm/guards.ts
2113
+ var TOOL_RESULT_TYPES = /* @__PURE__ */ new Set(["text", "parts", "json"]);
2114
+ function isTextPart(value) {
2115
+ if (!value || typeof value !== "object") return false;
2116
+ const v = value;
2117
+ return v.type === "text" && typeof v.text === "string";
2118
+ }
2119
+ function isImagePart(value) {
2120
+ if (!value || typeof value !== "object") return false;
2121
+ const v = value;
2122
+ if (v.type !== "image_url") return false;
2123
+ if (!v.image_url || typeof v.image_url !== "object") return false;
2124
+ const url = v.image_url.url;
2125
+ return typeof url === "string";
2126
+ }
2127
+ function isDocumentPart(value) {
2128
+ if (!value || typeof value !== "object") return false;
2129
+ const v = value;
2130
+ if (v.type !== "document") return false;
2131
+ if (!v.source || typeof v.source !== "object") return false;
2132
+ const source = v.source;
2133
+ return typeof source.data === "string" && source.media_type === "application/pdf";
2134
+ }
2135
+ function isOtherPart(value) {
2136
+ if (!value || typeof value !== "object") return false;
2137
+ const v = value;
2138
+ return v.type === "other" && typeof v.provider === "string" && typeof v.kind === "string";
2139
+ }
2140
+ function isContentPart(value) {
2141
+ return isTextPart(value) || isImagePart(value) || isDocumentPart(value) || isOtherPart(value);
2142
+ }
2143
+ function isToolResult(value) {
2144
+ if (!value || typeof value !== "object") return false;
2145
+ const v = value;
2146
+ if (!TOOL_RESULT_TYPES.has(String(v.type))) return false;
2147
+ if (v.type === "text")
2148
+ return typeof value.text === "string";
2149
+ if (v.type === "json") return "value" in value;
2150
+ if (v.type === "parts") {
2151
+ const parts = value.parts;
2152
+ return Array.isArray(parts) && parts.every(isContentPart);
2153
+ }
2154
+ return false;
2155
+ }
2156
+
2157
+ // src/tools/define.ts
2158
+ var mapSchema = (schema, transform) => {
2159
+ if (!schema || typeof schema !== "object") return schema;
2160
+ const next = transform({ ...schema });
2161
+ if (next.properties) {
2162
+ const updated = {};
2163
+ for (const [key, value] of Object.entries(next.properties)) {
2164
+ updated[key] = mapSchema(value, transform);
2165
+ }
2166
+ next.properties = updated;
2167
+ }
2168
+ if (next.items) {
2169
+ if (Array.isArray(next.items)) {
2170
+ next.items = next.items.map(
2171
+ (item) => mapSchema(item, transform)
2172
+ );
2173
+ } else {
2174
+ next.items = mapSchema(next.items, transform);
2175
+ }
2176
+ }
2177
+ if (next.anyOf) {
2178
+ next.anyOf = next.anyOf.map(
2179
+ (item) => mapSchema(item, transform)
2180
+ );
2181
+ }
2182
+ if (next.oneOf) {
2183
+ next.oneOf = next.oneOf.map(
2184
+ (item) => mapSchema(item, transform)
2185
+ );
2186
+ }
2187
+ if (next.allOf) {
2188
+ next.allOf = next.allOf.map(
2189
+ (item) => mapSchema(item, transform)
2190
+ );
2191
+ }
2192
+ if (next.not) {
2193
+ next.not = mapSchema(next.not, transform);
2194
+ }
2195
+ return next;
2196
+ };
2197
+ var withNoAdditionalProperties = (schema) => mapSchema(schema, (next) => {
2198
+ if (next.type === "object" && next.additionalProperties === void 0) {
2199
+ next.additionalProperties = false;
2200
+ }
2201
+ return next;
2202
+ });
2203
+ var withRequiredProperties = (schema) => mapSchema(schema, (next) => {
2204
+ if (next.type === "object" && next.properties) {
2205
+ const propertyKeys = Object.keys(next.properties);
2206
+ if (propertyKeys.length > 0) {
2207
+ next.required = Array.from(
2208
+ /* @__PURE__ */ new Set([...next.required ?? [], ...propertyKeys])
2209
+ );
2210
+ }
2211
+ }
2212
+ return next;
2213
+ });
2214
+ function toToolResult(value) {
2215
+ if (isToolResult(value)) return value;
2216
+ if (typeof value === "string") return { type: "text", text: value };
2217
+ if (Array.isArray(value) && value.every(isContentPart))
2218
+ return { type: "parts", parts: value };
2219
+ return { type: "json", value };
2220
+ }
2221
+ function defineTool(toolOptions) {
2222
+ const parameters = toJSONSchema(toolOptions.input, {
2223
+ target: "draft-07",
2224
+ io: "input"
2225
+ });
2226
+ const strictParameters = withRequiredProperties(
2227
+ withNoAdditionalProperties(parameters)
2228
+ );
2229
+ return {
2230
+ name: toolOptions.name,
2231
+ description: toolOptions.description,
2232
+ definition: {
2233
+ name: toolOptions.name,
2234
+ description: toolOptions.description,
2235
+ parameters: strictParameters,
2236
+ strict: true
2237
+ },
2238
+ executeRaw: (rawArgsJson, ctx) => {
2239
+ let rawArgs;
2240
+ try {
2241
+ rawArgs = JSON.parse(rawArgsJson);
2242
+ } catch (error) {
2243
+ throw new Error(
2244
+ `Invalid tool arguments JSON for ${toolOptions.name}: ${String(error)}`
2245
+ );
2246
+ }
2247
+ const parsed = toolOptions.input.safeParse(rawArgs);
2248
+ if (!parsed.success) {
2249
+ throw new Error(
2250
+ `Tool input validation failed for ${toolOptions.name}: ${parsed.error.message}`
2251
+ );
2252
+ }
2253
+ const result = toolOptions.execute(parsed.data, ctx);
2254
+ if (result instanceof Promise) {
2255
+ return result.then((result2) => toToolResult(result2));
2256
+ }
2257
+ return Promise.resolve(toToolResult(result));
2258
+ }
2259
+ };
2260
+ }
2261
+ export {
2262
+ ANTHROPIC_DEFAULT_MODEL,
2263
+ ANTHROPIC_MODELS,
2264
+ Agent,
2265
+ ChatAnthropic,
2266
+ ChatOpenAI,
2267
+ DEFAULT_MODEL_REGISTRY,
2268
+ GOOGLE_MODELS,
2269
+ OPENAI_DEFAULT_MODEL,
2270
+ OPENAI_DEFAULT_REASONING_EFFORT,
2271
+ OPENAI_MODELS,
2272
+ TaskComplete,
2273
+ ToolOutputCacheService,
2274
+ applyModelMetadata,
2275
+ createModelRegistry,
2276
+ defineTool,
2277
+ getDefaultSystemPromptPath,
2278
+ listModels,
2279
+ registerModels,
2280
+ resolveModel,
2281
+ stringifyContent,
2282
+ stringifyContentParts
2283
+ };