@cognipeer/agent-sdk 0.4.8 → 0.5.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.mjs CHANGED
@@ -1,10 +1,11 @@
1
1
  import { z, ZodError } from 'zod';
2
2
  import { createRequire } from 'module';
3
+ import { zodToJsonSchema } from 'zod-to-json-schema';
3
4
  import fs from 'fs';
4
5
  import path from 'path';
5
- import { Buffer } from 'buffer';
6
+ import { Buffer as Buffer$1 } from 'buffer';
6
7
  import { nanoid } from 'nanoid';
7
- import crypto from 'crypto';
8
+ import crypto, { createHash, createHmac } from 'crypto';
8
9
  import AjvModule from 'ajv';
9
10
 
10
11
  // src/adapters/langchain.ts
@@ -124,12 +125,377 @@ function createSimpleTool({
124
125
  };
125
126
  }
126
127
 
128
+ // src/structuredOutput/types.ts
129
+ function getNestedValue(obj, path2) {
130
+ let current = obj;
131
+ for (const key of path2) {
132
+ if (current == null || typeof current !== "object") return void 0;
133
+ current = current[key];
134
+ }
135
+ return current;
136
+ }
137
+ function formatZodError(error, rawContent) {
138
+ const fieldErrors = error.issues.map((issue) => ({
139
+ path: issue.path.join(".") || "root",
140
+ expected: issue.expected ?? issue.code,
141
+ received: issue.received ?? "unknown",
142
+ message: issue.message
143
+ }));
144
+ return {
145
+ type: "validation_error",
146
+ message: `Schema validation failed: ${fieldErrors.map((f) => `${f.path}: ${f.message}`).join("; ")}`,
147
+ fieldErrors,
148
+ rawContent
149
+ };
150
+ }
151
+
152
+ // src/structuredOutput/nativeStrategy.ts
153
+ var NativeJsonSchemaStrategy = class {
154
+ constructor() {
155
+ this.kind = "native";
156
+ }
157
+ resolveJsonPointer(document, pointer) {
158
+ if (!pointer.startsWith("#/")) {
159
+ return void 0;
160
+ }
161
+ const segments = pointer.slice(2).split("/").map((segment) => segment.replace(/~1/g, "/").replace(/~0/g, "~"));
162
+ let current = document;
163
+ for (const segment of segments) {
164
+ if (!current || typeof current !== "object" || !(segment in current)) {
165
+ return void 0;
166
+ }
167
+ current = current[segment];
168
+ }
169
+ return current;
170
+ }
171
+ unwrapRootObjectSchema(schema) {
172
+ if (schema.type === "object") {
173
+ return schema;
174
+ }
175
+ const ref = typeof schema.$ref === "string" ? schema.$ref : null;
176
+ if (!ref) {
177
+ return schema;
178
+ }
179
+ const resolved = this.resolveJsonPointer(schema, ref);
180
+ if (!resolved || typeof resolved !== "object" || Array.isArray(resolved)) {
181
+ return schema;
182
+ }
183
+ const resolvedSchema = { ...resolved };
184
+ if (resolvedSchema.type !== "object") {
185
+ return schema;
186
+ }
187
+ if (schema.definitions && resolvedSchema.definitions === void 0) {
188
+ resolvedSchema.definitions = schema.definitions;
189
+ }
190
+ if (schema.$defs && resolvedSchema.$defs === void 0) {
191
+ resolvedSchema.$defs = schema.$defs;
192
+ }
193
+ return resolvedSchema;
194
+ }
195
+ normalizeOpenAIStrictSchema(schema) {
196
+ const visit = (node) => {
197
+ if (!node || typeof node !== "object") return node;
198
+ if (Array.isArray(node)) {
199
+ return node.map(visit);
200
+ }
201
+ const clone2 = { ...node };
202
+ if (clone2.properties && typeof clone2.properties === "object") {
203
+ const normalizedProperties = {};
204
+ for (const [key, value] of Object.entries(clone2.properties)) {
205
+ normalizedProperties[key] = visit(value);
206
+ }
207
+ clone2.properties = normalizedProperties;
208
+ clone2.required = Object.keys(normalizedProperties);
209
+ if (clone2.additionalProperties === void 0) {
210
+ clone2.additionalProperties = false;
211
+ }
212
+ }
213
+ if (clone2.items) {
214
+ clone2.items = visit(clone2.items);
215
+ }
216
+ if (clone2.definitions && typeof clone2.definitions === "object") {
217
+ const normalizedDefinitions = {};
218
+ for (const [key, value] of Object.entries(clone2.definitions)) {
219
+ normalizedDefinitions[key] = visit(value);
220
+ }
221
+ clone2.definitions = normalizedDefinitions;
222
+ }
223
+ if (clone2.$defs && typeof clone2.$defs === "object") {
224
+ const normalizedDefs = {};
225
+ for (const [key, value] of Object.entries(clone2.$defs)) {
226
+ normalizedDefs[key] = visit(value);
227
+ }
228
+ clone2.$defs = normalizedDefs;
229
+ }
230
+ for (const compositeKey of ["anyOf", "oneOf", "allOf"]) {
231
+ if (Array.isArray(clone2[compositeKey])) {
232
+ clone2[compositeKey] = clone2[compositeKey].map(visit);
233
+ }
234
+ }
235
+ return clone2;
236
+ };
237
+ return visit(schema);
238
+ }
239
+ /**
240
+ * Convert a Zod schema to the JSON Schema object used in response_format.
241
+ */
242
+ toJsonSchema(schema, name = "structured_response") {
243
+ const result = zodToJsonSchema(schema, {
244
+ name,
245
+ openaiStrictMode: true,
246
+ nameStrategy: "duplicate-ref",
247
+ $refStrategy: "extract-to-root",
248
+ nullableStrategy: "property"
249
+ });
250
+ const normalized = this.normalizeOpenAIStrictSchema(result);
251
+ return this.unwrapRootObjectSchema(normalized);
252
+ }
253
+ /**
254
+ * Build response_format configuration for the model call.
255
+ * Returns the object that should be spread into model invocation options.
256
+ */
257
+ buildResponseFormat(schema, name) {
258
+ const schemaName = name || "structured_response";
259
+ const jsonSchema = this.toJsonSchema(schema, schemaName);
260
+ delete jsonSchema["$schema"];
261
+ return {
262
+ response_format: {
263
+ type: "json_schema",
264
+ json_schema: {
265
+ name: schemaName,
266
+ strict: true,
267
+ schema: jsonSchema
268
+ }
269
+ }
270
+ };
271
+ }
272
+ /**
273
+ * Parse and validate model response content.
274
+ * With native support, content is guaranteed JSON — but we still validate with Zod
275
+ * for type safety and field-level error reporting.
276
+ */
277
+ extractOutput(content, schema) {
278
+ try {
279
+ const raw = JSON.parse(content);
280
+ const validated = schema.parse(raw);
281
+ return { data: validated };
282
+ } catch (e) {
283
+ if (e?.issues) {
284
+ return { error: formatZodError(e, content) };
285
+ }
286
+ return {
287
+ error: {
288
+ type: "parse_error",
289
+ message: `Failed to parse native structured output: ${e?.message}`,
290
+ rawContent: content
291
+ }
292
+ };
293
+ }
294
+ }
295
+ buildCorrectionPrompt(error, previousAttempt) {
296
+ const fieldIssues = error.issues.map((issue) => {
297
+ const pathStr = issue.path.join(".") || "root";
298
+ const received = getNestedValue(previousAttempt, issue.path);
299
+ return `- "${pathStr}": ${issue.message} (received: ${JSON.stringify(received)})`;
300
+ }).join("\n");
301
+ return [
302
+ "Your previous JSON response had validation errors:",
303
+ fieldIssues,
304
+ "",
305
+ "Please output corrected JSON matching the schema."
306
+ ].join("\n");
307
+ }
308
+ };
309
+
310
+ // src/tool.ts
311
+ function createTool({
312
+ name,
313
+ description,
314
+ schema,
315
+ func,
316
+ needsApproval,
317
+ approvalPrompt,
318
+ approvalDefaults,
319
+ maxExecutionsPerRun
320
+ }) {
321
+ const execute = async (input) => func(input);
322
+ const toolRecord = {
323
+ name,
324
+ description,
325
+ schema,
326
+ // Our runtime prefers invoke but fallback helpers keep compatibility with LC-style tools.
327
+ invoke: execute,
328
+ call: execute,
329
+ run: execute,
330
+ func: execute
331
+ };
332
+ if (typeof needsApproval === "boolean") {
333
+ toolRecord.needsApproval = needsApproval;
334
+ }
335
+ if (approvalPrompt !== void 0) {
336
+ toolRecord.approvalPrompt = approvalPrompt;
337
+ }
338
+ if (approvalDefaults !== void 0) {
339
+ toolRecord.approvalDefaults = approvalDefaults;
340
+ }
341
+ if (maxExecutionsPerRun !== void 0) {
342
+ toolRecord.maxExecutionsPerRun = maxExecutionsPerRun;
343
+ }
344
+ toolRecord.__source = toolRecord.__source || "smart";
345
+ toolRecord.__impl = func;
346
+ return toolRecord;
347
+ }
348
+
349
+ // src/structuredOutput/toolStrategy.ts
350
+ var ToolBasedStrategy = class {
351
+ constructor() {
352
+ this.kind = "tool_based";
353
+ }
354
+ /**
355
+ * Create the `response` tool for a given Zod schema.
356
+ * The tool validates input against the schema and returns a sentinel object
357
+ * that the tools node picks up to finalize the structured output.
358
+ */
359
+ createResponseTool(schema) {
360
+ return createTool({
361
+ name: "response",
362
+ description: "Finalize the answer by returning the final structured JSON matching the required schema. Call exactly once when you are fully done, then stop.",
363
+ schema,
364
+ func: async (data) => {
365
+ try {
366
+ const validated = schema.parse ? schema.parse(data) : data;
367
+ return { __finalStructuredOutput: true, data: validated };
368
+ } catch (e) {
369
+ const zodError = e;
370
+ if (zodError?.issues) {
371
+ const soError = formatZodError(zodError);
372
+ return {
373
+ error: "Schema validation failed",
374
+ details: soError.message,
375
+ fieldErrors: soError.fieldErrors
376
+ };
377
+ }
378
+ return { error: "Schema validation failed", details: e?.message };
379
+ }
380
+ }
381
+ });
382
+ }
383
+ /**
384
+ * Try to extract structured output from raw assistant text content.
385
+ * Used as a last resort when the model writes JSON directly instead of calling the tool.
386
+ */
387
+ extractFromText(content, schema) {
388
+ let jsonText = null;
389
+ const fenced = content.match(/```(?:json)?\n([\s\S]*?)```/i);
390
+ if (fenced && fenced[1]) {
391
+ jsonText = fenced[1].trim();
392
+ } else {
393
+ const braceIdx = content.indexOf("{");
394
+ const bracketIdx = content.indexOf("[");
395
+ const candidates = [braceIdx, bracketIdx].filter((i) => i >= 0).sort((a, b) => a - b);
396
+ if (candidates.length > 0) {
397
+ jsonText = content.slice(candidates[0]).trim();
398
+ }
399
+ }
400
+ try {
401
+ const raw = JSON.parse(jsonText ?? content);
402
+ const validated = schema.parse(raw);
403
+ return { data: validated };
404
+ } catch (e) {
405
+ if (e?.issues) {
406
+ return { error: formatZodError(e, content) };
407
+ }
408
+ return {
409
+ error: {
410
+ type: "parse_error",
411
+ message: `Failed to parse JSON from assistant message: ${e?.message}`,
412
+ rawContent: content
413
+ }
414
+ };
415
+ }
416
+ }
417
+ buildCorrectionPrompt(error, previousAttempt) {
418
+ const fieldIssues = error.issues.map((issue) => {
419
+ const pathStr = issue.path.join(".") || "root";
420
+ const received = getNestedValue(previousAttempt, issue.path);
421
+ return `- "${pathStr}": ${issue.message} (received: ${JSON.stringify(received)})`;
422
+ }).join("\n");
423
+ return [
424
+ "Your previous structured output had validation errors:",
425
+ fieldIssues,
426
+ "",
427
+ "Please call `response` again with corrected values matching the schema."
428
+ ].join("\n");
429
+ }
430
+ };
431
+
432
+ // src/structuredOutput/resolver.ts
433
+ function resolveStrategy(model) {
434
+ const capabilities = getModelCapabilities(model);
435
+ if (capabilities.structuredOutput === "native") {
436
+ return new NativeJsonSchemaStrategy();
437
+ }
438
+ return new ToolBasedStrategy();
439
+ }
440
+ function getModelCapabilities(model) {
441
+ if (model?.capabilities && typeof model.capabilities === "object") {
442
+ return model.capabilities;
443
+ }
444
+ const provider = detectProvider(model);
445
+ if (provider) {
446
+ return {
447
+ provider,
448
+ structuredOutput: supportsNativeStructuredOutput(provider) ? "native" : "tool_based",
449
+ strictToolCalling: requiresStrictToolCalling(provider),
450
+ streaming: true
451
+ };
452
+ }
453
+ return { structuredOutput: "tool_based" };
454
+ }
455
+ function detectProvider(model) {
456
+ if (!model || typeof model !== "object") return void 0;
457
+ if (typeof model.provider === "string") return model.provider;
458
+ const lcModel = model._lc;
459
+ if (lcModel) {
460
+ return detectProviderFromLCModel(lcModel);
461
+ }
462
+ const modelName = model.modelName || model.model || "";
463
+ if (modelName.startsWith("gpt-") || modelName.startsWith("o1") || modelName.startsWith("o3") || modelName.startsWith("o4")) return "openai";
464
+ if (modelName.startsWith("claude")) return "anthropic";
465
+ if (modelName.startsWith("gemini")) return "google";
466
+ if (modelName.startsWith("amazon.") || modelName.startsWith("anthropic.")) return "bedrock";
467
+ return void 0;
468
+ }
469
+ function detectProviderFromLCModel(lcModel) {
470
+ if (!lcModel) return void 0;
471
+ const className = lcModel.constructor?.name || "";
472
+ if (className.includes("ChatOpenAI") || className.includes("AzureChatOpenAI")) return "openai";
473
+ if (className.includes("ChatAnthropic")) return "anthropic";
474
+ if (className.includes("ChatGoogleGenerativeAI") || className.includes("ChatVertexAI")) return "google";
475
+ if (className.includes("ChatBedrock") || className.includes("BedrockChat")) return "bedrock";
476
+ if (className.includes("ChatGroq")) return "groq";
477
+ if (className.includes("ChatMistralAI")) return "mistral";
478
+ const llmType = typeof lcModel._llmType === "function" ? lcModel._llmType() : lcModel._llmType;
479
+ if (typeof llmType === "string") {
480
+ if (llmType.includes("openai")) return "openai";
481
+ if (llmType.includes("anthropic")) return "anthropic";
482
+ if (llmType.includes("google")) return "google";
483
+ }
484
+ return void 0;
485
+ }
486
+ function supportsNativeStructuredOutput(provider) {
487
+ return ["openai", "google", "groq", "mistral"].includes(provider);
488
+ }
489
+ function requiresStrictToolCalling(provider) {
490
+ return ["openai"].includes(provider);
491
+ }
492
+
127
493
  // src/model.ts
128
494
  function isSmartModel(m) {
129
495
  return !!m && typeof m === "object" && typeof m.invoke === "function";
130
496
  }
131
- function withTools(model, tools) {
132
- if (model?.bindTools) return model.bindTools(tools);
497
+ function withTools(model, tools, options) {
498
+ if (model?.bindTools) return model.bindTools(tools, options);
133
499
  return model;
134
500
  }
135
501
  function fromLangchainModel(lcModel) {
@@ -174,7 +540,8 @@ function fromLangchainModel(lcModel) {
174
540
  role: response.role || "assistant",
175
541
  content,
176
542
  tool_calls: response.tool_calls,
177
- usage: response.usage,
543
+ usage: response.usage ?? response.usage_metadata ?? response.response_metadata?.token_usage ?? response.response_metadata?.tokenUsage,
544
+ usage_metadata: response.usage_metadata,
178
545
  response_metadata: response.response_metadata,
179
546
  ...response
180
547
  };
@@ -190,14 +557,14 @@ function fromLangchainModel(lcModel) {
190
557
  yield chunk;
191
558
  }
192
559
  },
193
- bindTools: (tools) => {
560
+ bindTools: (tools, options) => {
194
561
  const lcReady = toLangchainTools(tools);
195
562
  if (typeof lcModel.bindTools === "function") {
196
- const bound = lcModel.bindTools(lcReady);
563
+ const bound = lcModel.bindTools(lcReady, options);
197
564
  return fromLangchainModel(bound);
198
565
  }
199
566
  if (typeof lcModel.bind === "function") {
200
- const bound = lcModel.bind({ tools: lcReady });
567
+ const bound = lcModel.bind({ tools: lcReady, ...options || {} });
201
568
  return fromLangchainModel(bound);
202
569
  }
203
570
  return adapted;
@@ -205,48 +572,10 @@ function fromLangchainModel(lcModel) {
205
572
  modelName: lcModel.modelName || lcModel._modelId || lcModel._llmType || lcModel.name,
206
573
  _lc: lcModel
207
574
  };
575
+ adapted.capabilities = getModelCapabilities(adapted);
208
576
  return adapted;
209
577
  }
210
578
 
211
- // src/tool.ts
212
- function createTool({
213
- name,
214
- description,
215
- schema,
216
- func,
217
- needsApproval,
218
- approvalPrompt,
219
- approvalDefaults,
220
- maxExecutionsPerRun
221
- }) {
222
- const execute = async (input) => func(input);
223
- const toolRecord = {
224
- name,
225
- description,
226
- schema,
227
- // Our runtime prefers invoke but fallback helpers keep compatibility with LC-style tools.
228
- invoke: execute,
229
- call: execute,
230
- run: execute,
231
- func: execute
232
- };
233
- if (typeof needsApproval === "boolean") {
234
- toolRecord.needsApproval = needsApproval;
235
- }
236
- if (approvalPrompt !== void 0) {
237
- toolRecord.approvalPrompt = approvalPrompt;
238
- }
239
- if (approvalDefaults !== void 0) {
240
- toolRecord.approvalDefaults = approvalDefaults;
241
- }
242
- if (maxExecutionsPerRun !== void 0) {
243
- toolRecord.maxExecutionsPerRun = maxExecutionsPerRun;
244
- }
245
- toolRecord.__source = toolRecord.__source || "smart";
246
- toolRecord.__impl = func;
247
- return toolRecord;
248
- }
249
-
250
579
  // src/prompts.ts
251
580
  var DEFAULT_TODO_LIST_PROMPT = `Planning tools are available when they materially improve execution.
252
581
 
@@ -329,23 +658,39 @@ function normalizeUsage(raw) {
329
658
  raw.totalTokens,
330
659
  (typeof prompt === "number" ? prompt : 0) + (typeof completion === "number" ? completion : 0)
331
660
  );
332
- const promptDetailsSrc = raw.prompt_tokens_details || raw.promptTokensDetails || raw.input_token_details || raw.inputTokenDetails || {};
333
- const completionDetailsSrc = raw.completion_tokens_details || raw.completionTokensDetails || raw.output_token_details || raw.outputTokenDetails || {};
661
+ const promptDetailsSrc = raw.prompt_tokens_details || raw.promptTokensDetails || raw.input_token_details || raw.inputTokenDetails || raw.input_tokens_details || raw.inputTokensDetails || {};
662
+ const completionDetailsSrc = raw.completion_tokens_details || raw.completionTokensDetails || raw.output_token_details || raw.outputTokenDetails || raw.output_tokens_details || raw.outputTokensDetails || {};
663
+ const cachedTokens = num(
664
+ promptDetailsSrc.cached_tokens,
665
+ promptDetailsSrc.cachedTokens,
666
+ promptDetailsSrc.cached,
667
+ promptDetailsSrc.cache_read,
668
+ promptDetailsSrc.cacheRead,
669
+ promptDetailsSrc.cache_read_tokens,
670
+ raw.cached_input_tokens,
671
+ raw.cachedInputTokens,
672
+ raw.cached_prompt_tokens,
673
+ raw.cachedPromptTokens,
674
+ raw.cache_read_input_tokens,
675
+ raw.cacheReadInputTokens
676
+ );
334
677
  const normalized = {
335
678
  prompt_tokens: safe(prompt),
336
679
  completion_tokens: safe(completion),
337
680
  total_tokens: safe(total || (prompt || 0) + (completion || 0)),
338
681
  prompt_tokens_details: {
339
- cached_tokens: safe(
340
- promptDetailsSrc.cached_tokens || promptDetailsSrc.cached || promptDetailsSrc.cache_read || raw.cached_input_tokens || raw.cached_prompt_tokens || raw.cache_read_input_tokens
341
- ),
342
- audio_tokens: safe(promptDetailsSrc.audio_tokens || promptDetailsSrc.audio)
682
+ cached_tokens: safe(cachedTokens),
683
+ audio_tokens: safe(num(promptDetailsSrc.audio_tokens, promptDetailsSrc.audioTokens, promptDetailsSrc.audio))
343
684
  },
344
685
  completion_tokens_details: {
345
- reasoning_tokens: safe(completionDetailsSrc.reasoning_tokens),
346
- audio_tokens: safe(completionDetailsSrc.audio_tokens),
347
- accepted_prediction_tokens: safe(completionDetailsSrc.accepted_prediction_tokens),
348
- rejected_prediction_tokens: safe(completionDetailsSrc.rejected_prediction_tokens)
686
+ reasoning_tokens: safe(num(completionDetailsSrc.reasoning_tokens, completionDetailsSrc.reasoningTokens)),
687
+ audio_tokens: safe(num(completionDetailsSrc.audio_tokens, completionDetailsSrc.audioTokens)),
688
+ accepted_prediction_tokens: safe(
689
+ num(completionDetailsSrc.accepted_prediction_tokens, completionDetailsSrc.acceptedPredictionTokens)
690
+ ),
691
+ rejected_prediction_tokens: safe(
692
+ num(completionDetailsSrc.rejected_prediction_tokens, completionDetailsSrc.rejectedPredictionTokens)
693
+ )
349
694
  },
350
695
  raw
351
696
  };
@@ -455,21 +800,21 @@ var METADATA_PATHS = {
455
800
  "lc_kwargs.provider"
456
801
  ]
457
802
  };
458
- function getNestedValue(obj, path2) {
803
+ function getNestedValue2(obj, path2) {
459
804
  if (!obj || typeof path2 !== "string") return void 0;
460
805
  return path2.split(".").reduce((current, key) => current?.[key], obj);
461
806
  }
462
807
  function extractMetadata(model, paths) {
463
808
  if (!model) return void 0;
464
809
  for (const path2 of paths) {
465
- const value = getNestedValue(model, path2);
810
+ const value = getNestedValue2(model, path2);
466
811
  const str = coerceToString(value);
467
812
  if (str) return str;
468
813
  }
469
814
  const maybeLc = model?._lc;
470
815
  if (maybeLc) {
471
816
  for (const path2 of paths) {
472
- const value = getNestedValue(maybeLc, path2);
817
+ const value = getNestedValue2(maybeLc, path2);
473
818
  const str = coerceToString(value);
474
819
  if (str) return str;
475
820
  }
@@ -477,7 +822,7 @@ function extractMetadata(model, paths) {
477
822
  const maybeClient = model?.client || model?.api || model?.service;
478
823
  if (maybeClient) {
479
824
  for (const path2 of paths) {
480
- const value = getNestedValue(maybeClient, path2);
825
+ const value = getNestedValue2(maybeClient, path2);
481
826
  const str = coerceToString(value);
482
827
  if (str) return str;
483
828
  }
@@ -509,7 +854,7 @@ function inferModelFromMessages(messageList) {
509
854
  const message = messageList[i];
510
855
  if (!message) continue;
511
856
  for (const path2 of messagePaths) {
512
- const value = getNestedValue(message, path2);
857
+ const value = getNestedValue2(message, path2);
513
858
  const str = coerceToString(value);
514
859
  if (str) return str;
515
860
  }
@@ -528,7 +873,7 @@ function inferProviderFromMessages(messageList) {
528
873
  const message = messageList[i];
529
874
  if (!message) continue;
530
875
  for (const path2 of messagePaths) {
531
- const value = getNestedValue(message, path2);
876
+ const value = getNestedValue2(message, path2);
532
877
  const str = coerceToString(value);
533
878
  if (str) return str;
534
879
  }
@@ -1514,10 +1859,10 @@ function sanitizeTracePayload(value) {
1514
1859
  function estimatePayloadBytes(value) {
1515
1860
  try {
1516
1861
  const json = JSON.stringify(value);
1517
- return Buffer.byteLength(json ?? "", "utf8");
1862
+ return Buffer$1.byteLength(json ?? "", "utf8");
1518
1863
  } catch {
1519
1864
  try {
1520
- return Buffer.byteLength(String(value ?? ""), "utf8");
1865
+ return Buffer$1.byteLength(String(value ?? ""), "utf8");
1521
1866
  } catch {
1522
1867
  return 0;
1523
1868
  }
@@ -1539,7 +1884,13 @@ function createAgentCoreNode(opts) {
1539
1884
  tracing: opts.tracing
1540
1885
  };
1541
1886
  const tools = runtime.tools ?? [];
1542
- const modelWithTools = runtime.model?.bindTools ? runtime.model.bindTools(tools) : runtime.model;
1887
+ const shouldUseStrictToolCalling = Boolean(
1888
+ runtime.responseFormat && runtime.model?.capabilities?.strictToolCalling
1889
+ );
1890
+ const modelWithTools = runtime.model?.bindTools ? runtime.model.bindTools(
1891
+ tools,
1892
+ shouldUseStrictToolCalling ? { strict: true } : void 0
1893
+ ) : runtime.model;
1543
1894
  const traceSession = state.ctx?.__traceSession;
1544
1895
  const actorName = runtime.name ?? opts.name ?? "agent";
1545
1896
  const actorVersion = runtime.version ?? opts.version;
@@ -1616,6 +1967,7 @@ function createAgentCoreNode(opts) {
1616
1967
  const streamingEnabled = Boolean(state.ctx?.__streaming);
1617
1968
  const cancellationToken = state.ctx?.__cancellationToken;
1618
1969
  const abortSignal = state.ctx?.__abortSignal;
1970
+ const responseFormat = runtime.responseFormat;
1619
1971
  const extractText = (chunk) => {
1620
1972
  if (chunk == null) return "";
1621
1973
  if (typeof chunk === "string") return chunk;
@@ -1634,7 +1986,8 @@ function createAgentCoreNode(opts) {
1634
1986
  if (streamingEnabled && typeof modelWithTools.stream === "function") {
1635
1987
  let streamedText = "";
1636
1988
  let streamedMessage;
1637
- for await (const chunk of modelWithTools.stream(normalizedMessages, { signal: abortSignal, cancellationToken })) {
1989
+ const streamOptions = { signal: abortSignal, cancellationToken, ...responseFormat };
1990
+ for await (const chunk of modelWithTools.stream(normalizedMessages, streamOptions)) {
1638
1991
  if (cancellationToken && cancellationToken.isCancellationRequested || abortSignal?.aborted) {
1639
1992
  break;
1640
1993
  }
@@ -1657,7 +2010,7 @@ function createAgentCoreNode(opts) {
1657
2010
  response = { role: "assistant", content: streamedText };
1658
2011
  }
1659
2012
  } else {
1660
- response = await modelWithTools.invoke(normalizedMessages, { signal: abortSignal, cancellationToken });
2013
+ response = await modelWithTools.invoke(normalizedMessages, { signal: abortSignal, cancellationToken, ...responseFormat });
1661
2014
  }
1662
2015
  } catch (err) {
1663
2016
  const durationMs2 = Date.now() - start;
@@ -1679,7 +2032,7 @@ function createAgentCoreNode(opts) {
1679
2032
  ...state.messages,
1680
2033
  response
1681
2034
  ];
1682
- const rawUsage = response?.usage || response?.response_metadata?.token_usage || response?.response_metadata?.tokenUsage || response?.response_metadata?.usage;
2035
+ const rawUsage = response?.usage || response?.usage_metadata || response?.response_metadata?.token_usage || response?.response_metadata?.tokenUsage || response?.response_metadata?.usage || response?.response_metadata?.usage_metadata || response?.response_metadata?.usageMetadata;
1683
2036
  const normalized = normalizeUsage(rawUsage);
1684
2037
  const modelName = getModelName(runtime.model || opts.model) || "unknown_model";
1685
2038
  const durationMs = Date.now() - start;
@@ -2204,6 +2557,16 @@ function countSuccessfulToolExecutions(toolHistory, toolName) {
2204
2557
  if (!Array.isArray(toolHistory)) return 0;
2205
2558
  return toolHistory.filter((entry) => entry?.toolName === toolName && (entry?.status === "success" || entry?.status === "handoff")).length;
2206
2559
  }
2560
+ function normalizeToolCall(call) {
2561
+ if (!call || typeof call !== "object") return null;
2562
+ const name = call.name ?? call.tool ?? call.function?.name ?? call.function_call?.name;
2563
+ if (typeof name !== "string" || name.length === 0) return null;
2564
+ return {
2565
+ id: call.id,
2566
+ name,
2567
+ args: call.args ?? call.arguments ?? call.input ?? call.function?.arguments ?? call.function_call?.arguments
2568
+ };
2569
+ }
2207
2570
  function createToolsNode(initialTools, opts) {
2208
2571
  return async (state) => {
2209
2572
  const runtime = state.agent || {
@@ -2238,12 +2601,14 @@ function createToolsNode(initialTools, opts) {
2238
2601
  if (anyTool._stateRef && typeof anyTool._stateRef === "object") {
2239
2602
  anyTool._stateRef.toolHistory = state.toolHistory;
2240
2603
  anyTool._stateRef.toolHistoryArchived = state.toolHistoryArchived;
2604
+ anyTool._stateRef.pendingApprovals = pendingApprovals;
2605
+ anyTool._stateRef.ctx = state.ctx || (state.ctx = {});
2241
2606
  anyTool._stateRef.__onEvent = onEvent;
2242
2607
  }
2243
2608
  }
2244
2609
  const last = state.messages[state.messages.length - 1];
2245
2610
  let toolCount = state.toolCallCount || 0;
2246
- const toolCalls = Array.isArray(last?.tool_calls) ? last.tool_calls : [];
2611
+ const toolCalls = Array.isArray(last?.tool_calls) ? last.tool_calls.map((toolCall) => normalizeToolCall(toolCall)).filter((toolCall) => toolCall !== null) : [];
2247
2612
  const toolHistory = state.toolHistory || [];
2248
2613
  const toolHistoryArchived = state.toolHistoryArchived || [];
2249
2614
  const remaining = Math.max(0, limits.maxToolCalls - toolCount);
@@ -2302,6 +2667,13 @@ function createToolsNode(initialTools, opts) {
2302
2667
  args = validatedArgs.value;
2303
2668
  }
2304
2669
  const toolName = tool.name || tc.name;
2670
+ const toolStateRef = tool._stateRef && typeof tool._stateRef === "object" ? tool._stateRef : null;
2671
+ if (toolStateRef) {
2672
+ toolStateRef.pendingApprovals = pendingApprovals;
2673
+ toolStateRef.ctx = state.ctx || (state.ctx = {});
2674
+ toolStateRef.__currentToolCallId = toolCallId;
2675
+ delete toolStateRef.__awaitingApproval;
2676
+ }
2305
2677
  const maxExecutionsPerRun = normalizeMaxExecutionsPerRun(tool.maxExecutionsPerRun);
2306
2678
  if (maxExecutionsPerRun !== null) {
2307
2679
  const currentExecutions = countSuccessfulToolExecutions(toolHistory, toolName);
@@ -2414,6 +2786,22 @@ function createToolsNode(initialTools, opts) {
2414
2786
  toolCount += 1;
2415
2787
  return { status: "success", approval: approvalEntry };
2416
2788
  }
2789
+ if (output && typeof output === "object" && output.__awaitingToolApproval) {
2790
+ if (toolStateRef?.ctx && typeof toolStateRef.ctx === "object") {
2791
+ state.ctx = toolStateRef.ctx;
2792
+ }
2793
+ const pendingApproval = pendingApprovals.find(
2794
+ (entry) => entry.toolCallId === output.toolCallId || entry.id === output.approvalId
2795
+ );
2796
+ if (!pendingApproval) {
2797
+ throw new Error(`Tool ${toolName} requested approval but no pending approval entry was recorded.`);
2798
+ }
2799
+ awaitingApproval = true;
2800
+ return {
2801
+ status: "awaiting_approval",
2802
+ approval: pendingApproval
2803
+ };
2804
+ }
2417
2805
  if (output && typeof output === "object" && output.__finalStructuredOutput) {
2418
2806
  if (!state.ctx) state.ctx = {};
2419
2807
  state.ctx.__structuredOutputParsed = output.data;
@@ -2836,6 +3224,153 @@ function resolveToolApprovalState(state, resolution) {
2836
3224
  };
2837
3225
  }
2838
3226
 
3227
+ // src/structuredOutput/manager.ts
3228
+ var StructuredOutputManager = class {
3229
+ constructor(schema, strategy, config) {
3230
+ this.schema = schema;
3231
+ this.strategy = strategy;
3232
+ this.maxRetries = config?.maxRetries ?? 2;
3233
+ }
3234
+ // --- Setup Phase ---
3235
+ /**
3236
+ * Get the `response` tool for tool-based strategy.
3237
+ * Returns undefined for native strategy (no tool needed).
3238
+ */
3239
+ getResponseTool() {
3240
+ if (this.strategy.kind !== "tool_based") return void 0;
3241
+ return this.strategy.createResponseTool(this.schema);
3242
+ }
3243
+ /**
3244
+ * Get response_format config for native strategy.
3245
+ * Returns undefined for tool-based strategy.
3246
+ */
3247
+ getResponseFormat() {
3248
+ if (this.strategy.kind !== "native") return void 0;
3249
+ return this.strategy.buildResponseFormat(this.schema);
3250
+ }
3251
+ /**
3252
+ * Build the system prompt hint that tells the model how to produce structured output.
3253
+ */
3254
+ buildSystemPromptHint() {
3255
+ if (this.strategy.kind === "native") {
3256
+ return [
3257
+ "A structured output schema is active.",
3258
+ "Your response MUST be valid JSON matching the output schema.",
3259
+ "Do NOT wrap the JSON in code fences or add any text before/after it."
3260
+ ].join("\n");
3261
+ }
3262
+ return [
3263
+ "A structured output schema is active.",
3264
+ "Do NOT output the final JSON directly as an assistant message.",
3265
+ "When completely finished, call tool `response` passing the final JSON matching the schema as its arguments (direct object).",
3266
+ "Call it exactly once then STOP producing further assistant messages."
3267
+ ].join("\n");
3268
+ }
3269
+ /**
3270
+ * Build a nudge message for when the model didn't produce structured output.
3271
+ * Used by the agent loop when it needs to retry.
3272
+ */
3273
+ buildNudgeMessage(isLastAttempt) {
3274
+ if (this.strategy.kind === "native") {
3275
+ return {
3276
+ role: "system",
3277
+ content: [
3278
+ "A structured output schema is active.",
3279
+ "You MUST respond with valid JSON matching the schema.",
3280
+ "Do NOT include any text outside the JSON object.",
3281
+ isLastAttempt ? "This is your FINAL attempt. Produce the JSON NOW." : ""
3282
+ ].filter(Boolean).join("\n")
3283
+ };
3284
+ }
3285
+ return {
3286
+ role: "system",
3287
+ content: [
3288
+ "A structured output schema is active.",
3289
+ "You MUST now call tool `response` with the final JSON object that matches the schema.",
3290
+ "Do not write the JSON in the assistant message.",
3291
+ "Call `response` exactly once, then stop.",
3292
+ isLastAttempt ? "Do NOT call any other tools. Produce the response NOW based on whatever information you have gathered so far." : ""
3293
+ ].filter(Boolean).join("\n")
3294
+ };
3295
+ }
3296
+ /**
3297
+ * Build a correction message when schema validation failed.
3298
+ */
3299
+ buildCorrectionMessage(error, _previousAttempt) {
3300
+ if (error.fieldErrors && error.fieldErrors.length > 0) {
3301
+ const fieldIssues = error.fieldErrors.map((f) => `- "${f.path}": ${f.message} (received: ${JSON.stringify(f.received)})`).join("\n");
3302
+ const action = this.strategy.kind === "native" ? "Please output corrected JSON matching the schema." : "Please call `response` again with corrected values matching the schema.";
3303
+ return {
3304
+ role: "system",
3305
+ content: [
3306
+ "Your previous structured output had validation errors:",
3307
+ fieldIssues,
3308
+ "",
3309
+ action
3310
+ ].join("\n")
3311
+ };
3312
+ }
3313
+ return this.buildNudgeMessage(false);
3314
+ }
3315
+ // --- Parse Phase ---
3316
+ /**
3317
+ * Try to parse structured output from the tool-based finalization result.
3318
+ * Called when __structuredOutputParsed is available in ctx.
3319
+ */
3320
+ parseFromToolResult(data) {
3321
+ return { success: true, data, attempts: 1 };
3322
+ }
3323
+ /**
3324
+ * Try to parse structured output from assistant message content.
3325
+ * For native strategy: content should be valid JSON.
3326
+ * For tool-based strategy: last-resort fallback extraction from text.
3327
+ */
3328
+ parseFromContent(content) {
3329
+ if (this.strategy.kind === "native") {
3330
+ const result2 = this.strategy.extractOutput(content, this.schema);
3331
+ if ("data" in result2) {
3332
+ return { success: true, data: result2.data, attempts: 1 };
3333
+ }
3334
+ return { success: false, error: result2.error, attempts: 1, rawContent: content };
3335
+ }
3336
+ const result = this.strategy.extractFromText(content, this.schema);
3337
+ if ("data" in result) {
3338
+ return { success: true, data: result.data, attempts: 1 };
3339
+ }
3340
+ return { success: false, error: result.error, attempts: 1, rawContent: content };
3341
+ }
3342
+ /**
3343
+ * Create a "no output" error result.
3344
+ */
3345
+ noOutputResult(attempts, rawContent) {
3346
+ return {
3347
+ success: false,
3348
+ error: {
3349
+ type: "no_output",
3350
+ message: "Model did not produce structured output after all retry attempts.",
3351
+ rawContent
3352
+ },
3353
+ attempts,
3354
+ rawContent
3355
+ };
3356
+ }
3357
+ /**
3358
+ * Create a "max retries" error result.
3359
+ */
3360
+ maxRetriesResult(lastError, attempts, rawContent) {
3361
+ return {
3362
+ success: false,
3363
+ error: {
3364
+ ...lastError,
3365
+ type: "max_retries",
3366
+ message: `Structured output failed after ${attempts} attempts: ${lastError.message}`
3367
+ },
3368
+ attempts,
3369
+ rawContent
3370
+ };
3371
+ }
3372
+ };
3373
+
2839
3374
  // src/agent.ts
2840
3375
  function isSyntheticSummaryMessage(message) {
2841
3376
  if (!message) return false;
@@ -2887,14 +3422,13 @@ function createAgent(opts) {
2887
3422
  const resolver = createResolverNode();
2888
3423
  const agentCore = createAgentCoreNode(opts);
2889
3424
  const toolsBase = [...opts.tools ?? []];
2890
- if (opts.outputSchema) {
2891
- const responseTool = createTool({
2892
- name: "response",
2893
- description: "Finalize the answer by returning the final structured JSON matching the required schema. Call exactly once when you are fully done, then stop.",
2894
- schema: opts.outputSchema,
2895
- func: async (data) => ({ __finalStructuredOutput: true, data })
2896
- });
2897
- toolsBase.push(responseTool);
3425
+ const soManager = opts.outputSchema ? new StructuredOutputManager(opts.outputSchema, resolveStrategy(opts.model)) : void 0;
3426
+ if (soManager) {
3427
+ const modelCapabilities = getModelCapabilities(opts.model);
3428
+ const responseTool = soManager.getResponseTool();
3429
+ if (responseTool && modelCapabilities.structuredOutput !== "native") {
3430
+ toolsBase.push(responseTool);
3431
+ }
2898
3432
  }
2899
3433
  const toolsNode = createToolsNode(toolsBase, opts);
2900
3434
  const finalizeNode = createToolLimitFinalizeNode();
@@ -2929,6 +3463,7 @@ function createAgent(opts) {
2929
3463
  limits: opts.limits,
2930
3464
  useTodoList: void 0,
2931
3465
  outputSchema: opts.outputSchema,
3466
+ responseFormat: soManager?.getResponseFormat(),
2932
3467
  tracing: opts.tracing
2933
3468
  };
2934
3469
  const summarizationThreshold = getActiveSummarizationThreshold(opts);
@@ -3067,10 +3602,11 @@ function createAgent(opts) {
3067
3602
  });
3068
3603
  const tokenCount = countMessagesTokens(tokCountMessages);
3069
3604
  if (tokenCount > summarizationThreshold) {
3605
+ const summarizationExhausted = !!state.ctx?.__summarizationExhausted;
3070
3606
  const hasFreshSummary = (state.messages || []).some(
3071
3607
  (m) => m.role === "tool" && typeof m.content === "string" && (m.content === "SUMMARIZED" || m.content.startsWith("SUMMARIZED_TOOL_RESPONSE"))
3072
3608
  );
3073
- if (hasFreshSummary && tokenCount <= summarizationThreshold * 1.15) ; else {
3609
+ if (hasFreshSummary && tokenCount <= summarizationThreshold * 1.15) ; else if (summarizationExhausted) ; else {
3074
3610
  const ctx = { ...state.ctx || {}, __needsSummarization: true };
3075
3611
  state = { ...state, ctx };
3076
3612
  break;
@@ -3147,7 +3683,15 @@ function createAgent(opts) {
3147
3683
  state = { ...state, ...await finalizeNode(state) };
3148
3684
  continue;
3149
3685
  }
3150
- if (toolCalls.length === 0) break;
3686
+ if (toolCalls.length === 0) {
3687
+ if (soManager && !state.ctx?.__finalizedDueToStructuredOutput && !state.ctx?.__structuredOutputForceFinalize) {
3688
+ const nudge = soManager.buildNudgeMessage(false);
3689
+ const ctx = { ...state.ctx || {}, __structuredOutputForceFinalize: true };
3690
+ state = { ...state, messages: [...state.messages, nudge], ctx };
3691
+ continue;
3692
+ }
3693
+ break;
3694
+ }
3151
3695
  onProgress?.({ stage: "tools", message: "Running tools" });
3152
3696
  if (cancelIfRequested("before_tools")) break;
3153
3697
  state = { ...state, ...await toolsNode(state) };
@@ -3156,35 +3700,28 @@ function createAgent(opts) {
3156
3700
  if (checkpointIfRequested("after_tools")) break;
3157
3701
  if (state.ctx?.__finalizedDueToStructuredOutput) break;
3158
3702
  }
3159
- if (opts.outputSchema && !state.ctx?.__finalizedDueToStructuredOutput) {
3160
- const last = state.messages[state.messages.length - 1];
3161
- const lastHasToolCalls = Array.isArray(last?.tool_calls) && last.tool_calls.length > 0;
3162
- if (!lastHasToolCalls) {
3163
- const forceMsg = {
3164
- role: "system",
3165
- content: [
3166
- "A structured output schema is active.",
3167
- "You MUST now call tool `response` with the final JSON object that matches the schema.",
3168
- "Do not write the JSON in the assistant message.",
3169
- "Call `response` exactly once, then stop."
3170
- ].join("\n")
3171
- };
3172
- const alreadyForced = Boolean(state.ctx?.__structuredOutputForceFinalize);
3173
- if (!alreadyForced) {
3174
- const ctx = { ...state.ctx || {}, __structuredOutputForceFinalize: true };
3175
- state = { ...state, messages: [...state.messages, forceMsg], ctx };
3176
- try {
3177
- state = { ...state, ...await agentCore(state) };
3178
- const lastAfter = state.messages[state.messages.length - 1];
3179
- const toolCallsAfter = Array.isArray(lastAfter?.tool_calls) ? lastAfter.tool_calls : [];
3180
- if (toolCallsAfter.length > 0) {
3181
- state = { ...state, ...await toolsNode(state) };
3182
- }
3183
- } catch (err) {
3184
- const errMsg = err instanceof Error ? err.message : String(err);
3185
- emit?.({ type: "metadata", error: `Structured output force-finalize failed: ${errMsg}` });
3186
- throw err;
3703
+ if (soManager && !state.ctx?.__finalizedDueToStructuredOutput) {
3704
+ const maxPostLoopRetries = soManager.maxRetries;
3705
+ for (let postRetry = 0; postRetry < maxPostLoopRetries; postRetry++) {
3706
+ if (state.ctx?.__finalizedDueToStructuredOutput) break;
3707
+ const last = state.messages[state.messages.length - 1];
3708
+ const lastHasToolCalls = Array.isArray(last?.tool_calls) && last.tool_calls.length > 0;
3709
+ if (!lastHasToolCalls) {
3710
+ const isLastAttempt = postRetry === maxPostLoopRetries - 1;
3711
+ const nudge = soManager.buildNudgeMessage(isLastAttempt);
3712
+ state = { ...state, messages: [...state.messages, nudge] };
3713
+ }
3714
+ try {
3715
+ state = { ...state, ...await agentCore(state) };
3716
+ const lastAfter = state.messages[state.messages.length - 1];
3717
+ const toolCallsAfter = Array.isArray(lastAfter?.tool_calls) ? lastAfter.tool_calls : [];
3718
+ if (toolCallsAfter.length > 0) {
3719
+ state = { ...state, ...await toolsNode(state) };
3187
3720
  }
3721
+ } catch (err) {
3722
+ const errMsg = err instanceof Error ? err.message : String(err);
3723
+ emit?.({ type: "metadata", error: `Structured output force-finalize failed: ${errMsg}` });
3724
+ throw err;
3188
3725
  }
3189
3726
  }
3190
3727
  }
@@ -3274,23 +3811,22 @@ function createAgent(opts) {
3274
3811
  const finalAssistantMsg = getLastAssistantMessage(res.messages);
3275
3812
  const content = getMessageText(finalAssistantMsg);
3276
3813
  let parsed = void 0;
3277
- const schema = opts.outputSchema;
3278
- if (schema && res.ctx?.__structuredOutputParsed) {
3279
- parsed = res.ctx.__structuredOutputParsed;
3280
- } else if (schema && content) {
3281
- let jsonText = null;
3282
- const fenced = content.match(/```(?:json)?\n([\s\S]*?)```/i);
3283
- if (fenced && fenced[1]) jsonText = fenced[1].trim();
3284
- else {
3285
- const braceIdx = content.indexOf("{");
3286
- const bracketIdx = content.indexOf("[");
3287
- const start = [braceIdx, bracketIdx].filter((i) => i >= 0).sort((a, b) => a - b)[0];
3288
- if (start !== void 0) jsonText = content.slice(start).trim();
3289
- }
3290
- try {
3291
- const raw = JSON.parse(jsonText ?? content);
3292
- parsed = schema.parse(raw);
3293
- } catch {
3814
+ let outputError = void 0;
3815
+ if (soManager) {
3816
+ if (res.ctx?.__structuredOutputParsed) {
3817
+ parsed = res.ctx.__structuredOutputParsed;
3818
+ } else if (content) {
3819
+ const fallbackResult = soManager.parseFromContent(content);
3820
+ if (fallbackResult.success) {
3821
+ parsed = fallbackResult.data;
3822
+ } else {
3823
+ outputError = fallbackResult.error;
3824
+ }
3825
+ } else {
3826
+ const noResult = soManager.noOutputResult(1);
3827
+ if (!noResult.success) {
3828
+ outputError = noResult.error;
3829
+ }
3294
3830
  }
3295
3831
  }
3296
3832
  emit({ type: "finalAnswer", content });
@@ -3301,6 +3837,7 @@ function createAgent(opts) {
3301
3837
  return {
3302
3838
  content,
3303
3839
  output: parsed,
3840
+ outputError,
3304
3841
  metadata: { usage: res.usage },
3305
3842
  messages: res.messages,
3306
3843
  state: res
@@ -3424,7 +3961,7 @@ function createAgentNode(opts) {
3424
3961
  ...messages,
3425
3962
  response
3426
3963
  ];
3427
- const rawUsage = response?.usage || response?.response_metadata?.token_usage || response?.response_metadata?.usage;
3964
+ const rawUsage = response?.usage || response?.usage_metadata || response?.response_metadata?.token_usage || response?.response_metadata?.tokenUsage || response?.response_metadata?.usage || response?.response_metadata?.usage_metadata || response?.response_metadata?.usageMetadata;
3428
3965
  const normalized = normalizeUsage(rawUsage);
3429
3966
  const modelName = getModelName(runtime.model || opts.model) || "unknown_model";
3430
3967
  const providerName = getProviderName(runtime.model || opts.model);
@@ -3483,14 +4020,23 @@ function collectRecentTurns(messages, lastTurnsToKeep) {
3483
4020
  if (lastTurnsToKeep <= 0) return [];
3484
4021
  const systemPrefix = messages.filter((message, index) => index === 0 && message.role === "system");
3485
4022
  const body = systemPrefix.length > 0 ? messages.slice(1) : [...messages];
3486
- let userTurnCount = 0;
4023
+ const totalUserTurns = body.filter((m) => m.role === "user").length;
4024
+ const countAssistantTurns = totalUserTurns < lastTurnsToKeep;
4025
+ let turnCount = 0;
3487
4026
  const collected = [];
3488
4027
  for (let index = body.length - 1; index >= 0; index -= 1) {
3489
4028
  const message = body[index];
3490
4029
  collected.unshift(message);
3491
- if (message.role === "user") {
3492
- userTurnCount += 1;
3493
- if (userTurnCount >= lastTurnsToKeep) break;
4030
+ const isTurnBoundary = countAssistantTurns ? message.role === "assistant" : message.role === "user";
4031
+ if (isTurnBoundary) {
4032
+ turnCount += 1;
4033
+ if (turnCount >= lastTurnsToKeep) break;
4034
+ }
4035
+ }
4036
+ if (countAssistantTurns && collected.length > 0 && collected[0].role !== "user") {
4037
+ const firstUserMsg = body.find((m) => m.role === "user");
4038
+ if (firstUserMsg && !collected.includes(firstUserMsg)) {
4039
+ collected.unshift(firstUserMsg);
3494
4040
  }
3495
4041
  }
3496
4042
  return [...systemPrefix, ...collected];
@@ -3530,16 +4076,53 @@ function clampToBudget(messages, maxContextTokens) {
3530
4076
  if (tokenCount <= maxContextTokens) return working;
3531
4077
  const firstNonSystem = working.findIndex((message, index) => !(index === 0 && message.role === "system"));
3532
4078
  if (firstNonSystem < 0) break;
3533
- working.splice(firstNonSystem, 1);
3534
- }
3535
- return working;
3536
- }
3537
- function buildModelMessages(state, config) {
3538
- const rawMessages = state.messages || [];
3539
- if (config.context.policy === "raw") {
3540
- return clampToBudget(rawMessages, config.limits.maxContextTokens);
3541
- }
3542
- const recentMessages = collectRecentTurns(rawMessages, config.context.lastTurnsToKeep);
4079
+ const target = working[firstNonSystem];
4080
+ if (target.role === "assistant" && Array.isArray(target.tool_calls) && target.tool_calls.length > 0) {
4081
+ const toolCallIds = new Set(
4082
+ target.tool_calls.map((tc) => tc.id).filter(Boolean)
4083
+ );
4084
+ working = working.filter((m, idx) => {
4085
+ if (idx === firstNonSystem) return false;
4086
+ if (m.role === "tool" && m.tool_call_id && toolCallIds.has(m.tool_call_id)) return false;
4087
+ return true;
4088
+ });
4089
+ } else if (target.role === "tool") {
4090
+ const toolCallId = target.tool_call_id;
4091
+ let parentIdx = -1;
4092
+ if (toolCallId) {
4093
+ for (let i = firstNonSystem - 1; i >= 0; i--) {
4094
+ const m = working[i];
4095
+ if (m.role === "assistant" && Array.isArray(m.tool_calls) && m.tool_calls.some((tc) => tc.id === toolCallId)) {
4096
+ parentIdx = i;
4097
+ break;
4098
+ }
4099
+ }
4100
+ }
4101
+ if (parentIdx >= 0) {
4102
+ const parent = working[parentIdx];
4103
+ const parentToolCallIds = new Set(
4104
+ (parent.tool_calls || []).map((tc) => tc.id).filter(Boolean)
4105
+ );
4106
+ working = working.filter((m, idx) => {
4107
+ if (idx === parentIdx) return false;
4108
+ if (m.role === "tool" && m.tool_call_id && parentToolCallIds.has(m.tool_call_id)) return false;
4109
+ return true;
4110
+ });
4111
+ } else {
4112
+ working.splice(firstNonSystem, 1);
4113
+ }
4114
+ } else {
4115
+ working.splice(firstNonSystem, 1);
4116
+ }
4117
+ }
4118
+ return working;
4119
+ }
4120
+ function buildModelMessages(state, config) {
4121
+ const rawMessages = state.messages || [];
4122
+ if (config.context.policy === "raw") {
4123
+ return clampToBudget(rawMessages, config.limits.maxContextTokens);
4124
+ }
4125
+ const recentMessages = collectRecentTurns(rawMessages, config.context.lastTurnsToKeep);
3543
4126
  const latestSummary = state.summaryRecords?.[state.summaryRecords.length - 1];
3544
4127
  const summaryText = renderStructuredSummary(latestSummary);
3545
4128
  const memoryText = renderMemoryBlock(state.memoryFacts);
@@ -4180,6 +4763,12 @@ function calculateAdherenceScore(todoList) {
4180
4763
  const completedCount = todoList.filter((item) => item.status === "completed").length;
4181
4764
  return todoList.length === 0 ? 1 : Number((completedCount / todoList.length).toFixed(2));
4182
4765
  }
4766
+ function formatTodoListSummary(todoList) {
4767
+ if (!Array.isArray(todoList) || todoList.length === 0) {
4768
+ return "No plan items.";
4769
+ }
4770
+ return todoList.map((item) => `${item.id}. ${item.title || item.step || item.description || `Step ${item.id}`} [${item.status}]`).join("\n");
4771
+ }
4183
4772
  function createContextTools(stateRef, opts) {
4184
4773
  const tools = [];
4185
4774
  if (opts?.planningEnabled) {
@@ -4192,11 +4781,43 @@ function createContextTools(stateRef, opts) {
4192
4781
  todoList: z.array(z.union([todoWriteItemSchema, todoUpdateItemSchema])).optional()
4193
4782
  }),
4194
4783
  func: async ({ operation, expectedVersion, todoList }) => {
4195
- const onEvent = manageTodo._stateRef?.__onEvent;
4784
+ const toolStateRef = manageTodo._stateRef;
4785
+ const onEvent = toolStateRef?.__onEvent;
4786
+ const emitPlanEvent = (list) => {
4787
+ const version = stateRef.planVersion || 1;
4788
+ const adherenceScore = stateRef.adherenceScore || 0;
4789
+ const planData = {
4790
+ source: "manage_todo_list",
4791
+ operation,
4792
+ version,
4793
+ adherenceScore,
4794
+ count: Array.isArray(list) ? list.length : 0
4795
+ };
4796
+ onEvent?.({ type: "plan", todoList: list, ...planData });
4797
+ const traceSession = toolStateRef?.ctx?.__traceSession;
4798
+ recordTraceEvent(traceSession, {
4799
+ type: "plan",
4800
+ label: `Plan ${operation}`,
4801
+ actor: { scope: "agent", name: "manage_todo_list", role: "planner" },
4802
+ sections: [
4803
+ {
4804
+ kind: "summary",
4805
+ label: "Todo List",
4806
+ content: formatTodoListSummary(list)
4807
+ },
4808
+ {
4809
+ kind: "metadata",
4810
+ label: "Plan Metadata",
4811
+ data: planData
4812
+ }
4813
+ ],
4814
+ debug: planData
4815
+ });
4816
+ };
4196
4817
  const currentVersion = stateRef.planVersion || 0;
4197
4818
  if (operation === "read") {
4198
4819
  const list = stateRef.todoList || [];
4199
- onEvent?.({ type: "plan", source: "manage_todo_list", operation: "read", todoList: list, version: stateRef.planVersion || 1, adherenceScore: stateRef.adherenceScore || 0 });
4820
+ emitPlanEvent(list);
4200
4821
  return list;
4201
4822
  }
4202
4823
  if (typeof expectedVersion === "number" && expectedVersion !== currentVersion) {
@@ -4261,9 +4882,10 @@ function createContextTools(stateRef, opts) {
4261
4882
  adherenceScore: stateRef.adherenceScore || 0
4262
4883
  };
4263
4884
  }
4264
- const patchIds = /* @__PURE__ */ new Set();
4885
+ const currentMap = new Map(currentList.map((item) => [item.id, item]));
4886
+ const patchMap = /* @__PURE__ */ new Map();
4265
4887
  for (const item of parsed.data) {
4266
- if (patchIds.has(item.id)) {
4888
+ if (patchMap.has(item.id)) {
4267
4889
  return {
4268
4890
  status: "error",
4269
4891
  operation,
@@ -4272,10 +4894,7 @@ function createContextTools(stateRef, opts) {
4272
4894
  adherenceScore: stateRef.adherenceScore || 0
4273
4895
  };
4274
4896
  }
4275
- patchIds.add(item.id);
4276
- }
4277
- const currentMap = new Map(currentList.map((item) => [item.id, item]));
4278
- for (const item of parsed.data) {
4897
+ patchMap.set(item.id, item);
4279
4898
  if (!currentMap.has(item.id)) {
4280
4899
  return {
4281
4900
  status: "error",
@@ -4287,7 +4906,7 @@ function createContextTools(stateRef, opts) {
4287
4906
  }
4288
4907
  }
4289
4908
  const mergedList = currentList.map((item) => {
4290
- const patch = parsed.data.find((candidate) => candidate.id === item.id);
4909
+ const patch = patchMap.get(item.id);
4291
4910
  return patch ? normalizeTodoItem(patch, item) : item;
4292
4911
  });
4293
4912
  const validation = validatePlanInvariants(mergedList, false);
@@ -4311,7 +4930,7 @@ function createContextTools(stateRef, opts) {
4311
4930
  version: stateRef.planVersion || 1,
4312
4931
  adherenceScore: stateRef.adherenceScore || 0
4313
4932
  };
4314
- onEvent?.({ type: "plan", source: "manage_todo_list", operation, todoList: stateRef.todoList, version: stateRef.planVersion || 1, adherenceScore: stateRef.adherenceScore || 0 });
4933
+ emitPlanEvent(stateRef.todoList || []);
4315
4934
  return payload;
4316
4935
  }
4317
4936
  });
@@ -4336,23 +4955,6 @@ function createContextTools(stateRef, opts) {
4336
4955
  });
4337
4956
  getTool._stateRef = stateRef;
4338
4957
  tools.push(getTool);
4339
- if (opts?.outputSchema) {
4340
- const responseTool = createTool({
4341
- name: "response",
4342
- description: "Finalize the answer by returning the final structured JSON matching the required schema. Call exactly once when you are fully done, then stop.",
4343
- schema: opts.outputSchema.shape ? opts.outputSchema : z.any(),
4344
- func: async (data) => {
4345
- try {
4346
- const validated = opts.outputSchema.parse ? opts.outputSchema.parse(data) : data;
4347
- return { __finalStructuredOutput: true, data: validated };
4348
- } catch (e) {
4349
- return { error: "Schema validation failed", details: e?.message };
4350
- }
4351
- }
4352
- });
4353
- responseTool._stateRef = stateRef;
4354
- tools.push(responseTool);
4355
- }
4356
4958
  return tools;
4357
4959
  }
4358
4960
 
@@ -4561,7 +5163,7 @@ function createSmartAgent(opts) {
4561
5163
  useTodoList: planningEnabled
4562
5164
  };
4563
5165
  const stateRef = { toolHistory: void 0, toolHistoryArchived: void 0, todoList: void 0, planVersion: 0, adherenceScore: 0 };
4564
- const contextTools = createContextTools(stateRef, { planningEnabled, outputSchema: void 0 });
5166
+ const contextTools = createContextTools(stateRef, { planningEnabled });
4565
5167
  const mergedTools = [...opts.tools ?? [], ...contextTools];
4566
5168
  const base = createAgent({ ...runtimeOpts, tools: mergedTools });
4567
5169
  base.__runtime.runtimeProfile = resolved.runtimeProfile;
@@ -4570,12 +5172,8 @@ function createSmartAgent(opts) {
4570
5172
  const summarizer = summarizationEnabled ? createContextSummarizeNode(runtimeOpts) : void 0;
4571
5173
  const decideBefore = resolverDecisionFactory(runtimeOpts, summarizationEnabled);
4572
5174
  const decideAfter = toolsDecisionFactory(runtimeOpts, summarizationEnabled);
4573
- const structuredOutputHint = opts.outputSchema ? [
4574
- "A structured output schema is active.",
4575
- "Do NOT output the final JSON directly as an assistant message.",
4576
- "When completely finished, call tool `response` passing the final JSON matching the schema as its arguments (direct object).",
4577
- "Call it exactly once then STOP producing further assistant messages."
4578
- ].join("\n") : "";
5175
+ const soManager = opts.outputSchema ? new StructuredOutputManager(opts.outputSchema, resolveStrategy(opts.model)) : void 0;
5176
+ const structuredOutputHint = soManager ? soManager.buildSystemPromptHint() : "";
4579
5177
  const runtimeHint = [
4580
5178
  resolved.runtimeProfile === resolved.baseProfile ? `Runtime profile: ${resolved.runtimeProfile}.` : `Runtime profile: ${resolved.runtimeProfile} (base: ${resolved.baseProfile}).`,
4581
5179
  `Context policy: ${resolved.context.policy}.`,
@@ -4645,8 +5243,13 @@ function createSmartAgent(opts) {
4645
5243
  const next = summarizationEnabled ? decideBefore(state) : "agent";
4646
5244
  if (next === "contextSummarize" && summarizer) {
4647
5245
  const delta = await summarizer(state);
4648
- if (!delta || Object.keys(delta).length === 0) ; else {
4649
- state = await persistLatestSummary(syncPlanState({ ...state, ...delta }));
5246
+ if (!delta || Object.keys(delta).length === 0) {
5247
+ const ctx = { ...state.ctx || {}, __summarizationExhausted: true };
5248
+ state = { ...state, ctx };
5249
+ } else {
5250
+ const ctx = { ...state.ctx || {} };
5251
+ delete ctx.__summarizationExhausted;
5252
+ state = await persistLatestSummary(syncPlanState({ ...state, ...delta, ctx }));
4650
5253
  rawMessages = [...state.messages || rawMessages];
4651
5254
  continue;
4652
5255
  }
@@ -4679,20 +5282,32 @@ function createSmartAgent(opts) {
4679
5282
  state = { ...state, ctx };
4680
5283
  const delta = await summarizer(state);
4681
5284
  if (delta && Object.keys(delta).length > 0) {
4682
- state = await persistLatestSummary(syncPlanState({ ...state, ...delta }));
5285
+ const successCtx = { ...state.ctx || {} };
5286
+ delete successCtx.__summarizationExhausted;
5287
+ state = await persistLatestSummary(syncPlanState({ ...state, ...delta, ctx: successCtx }));
4683
5288
  rawMessages = [...state.messages || rawMessages];
4684
5289
  continue;
5290
+ } else {
5291
+ const exhaustedCtx = { ...state.ctx || {}, __summarizationExhausted: true };
5292
+ state = { ...state, ctx: exhaustedCtx };
5293
+ continue;
4685
5294
  }
4686
5295
  }
4687
5296
  if (state.ctx?.__finalizedDueToStructuredOutput) break;
5297
+ if (opts.outputSchema && lastResult?.output != null) break;
4688
5298
  if (summarizationEnabled) {
4689
5299
  const after = decideAfter(state);
4690
5300
  if (after === "contextSummarize" && summarizer) {
4691
5301
  const delta = await summarizer(state);
4692
5302
  if (delta && Object.keys(delta).length > 0) {
4693
- state = await persistLatestSummary(syncPlanState({ ...state, ...delta }));
5303
+ const successCtx = { ...state.ctx || {} };
5304
+ delete successCtx.__summarizationExhausted;
5305
+ state = await persistLatestSummary(syncPlanState({ ...state, ...delta, ctx: successCtx }));
4694
5306
  rawMessages = [...state.messages || rawMessages];
4695
5307
  continue;
5308
+ } else {
5309
+ const exhaustedCtx = { ...state.ctx || {}, __summarizationExhausted: true };
5310
+ state = { ...state, ctx: exhaustedCtx };
4696
5311
  }
4697
5312
  }
4698
5313
  }
@@ -5135,6 +5750,1740 @@ function createCodeGuardrail(options) {
5135
5750
  });
5136
5751
  }
5137
5752
 
5138
- export { DEFAULT_PROFILE_CONFIGS, GuardrailPhase, InMemoryMemoryStore, agentVerdictRule, applyTokenLimits, buildSystemPrompt, captureSnapshot, codePresenceRule, cognipeerSink, countApproxTokens, countMessagesTokens, createAgent, createAgentNode, createCodeGuardrail, createContextSummarizeNode, createContextTools, createGuardrail, createJsonGuardrail, createRegexGuardrail, createResolverNode, createSmartAgent, createTool, createToolLimitFinalizeNode, createToolsNode, customCallbackRule, customSink, evaluateGuardrails, fileSink, fromLangchainModel, fromLangchainTools, generateSpanId, generateTraceId, getResolvedSmartConfig, httpSink, isSmartModel, jsonSchemaRule, normalizeSmartAgentOptions, otlpSink, readMemoryFacts, regexRule, renderMemoryFacts, resolveMemoryStore, resolveToolApprovalState, restoreSnapshot, runSmartAgentEvalHarness, startStreamingSession, traceSessionToOtlp, withTools, writeSummaryFactsToMemory };
5753
+ // src/providers/types.ts
5754
+ function emptyUsage() {
5755
+ return {
5756
+ inputTokens: 0,
5757
+ outputTokens: 0,
5758
+ totalTokens: 0,
5759
+ cachedInputTokens: 0,
5760
+ cachedWriteTokens: 0,
5761
+ cachedOutputTokens: 0,
5762
+ reasoningTokens: 0
5763
+ };
5764
+ }
5765
+ var ProviderError = class extends Error {
5766
+ constructor(message, provider, statusCode, responseBody) {
5767
+ super(message);
5768
+ this.provider = provider;
5769
+ this.statusCode = statusCode;
5770
+ this.responseBody = responseBody;
5771
+ this.name = "ProviderError";
5772
+ }
5773
+ };
5774
+
5775
+ // src/providers/base.ts
5776
+ var BaseProvider = class {
5777
+ };
5778
+
5779
+ // src/providers/utils/sse.ts
5780
+ async function* parseSSEStream(body, signal) {
5781
+ const reader = body.getReader();
5782
+ const decoder = new TextDecoder();
5783
+ let buffer = "";
5784
+ try {
5785
+ while (true) {
5786
+ if (signal?.aborted) ;
5787
+ const { done, value } = await reader.read();
5788
+ if (done) break;
5789
+ buffer += decoder.decode(value, { stream: true });
5790
+ const parts = buffer.split(/\r?\n\r?\n/);
5791
+ buffer = parts.pop() ?? "";
5792
+ for (const part of parts) {
5793
+ if (!part.trim()) continue;
5794
+ const event = parseSSEBlock(part);
5795
+ if (event) yield event;
5796
+ }
5797
+ }
5798
+ if (buffer.trim()) {
5799
+ const event = parseSSEBlock(buffer);
5800
+ if (event) yield event;
5801
+ }
5802
+ } finally {
5803
+ reader.releaseLock();
5804
+ }
5805
+ }
5806
+ function parseSSEBlock(block) {
5807
+ let event;
5808
+ let id;
5809
+ let retry;
5810
+ const dataLines = [];
5811
+ for (const line of block.split(/\r?\n/)) {
5812
+ if (line.startsWith(":")) continue;
5813
+ const colonIdx = line.indexOf(":");
5814
+ let field;
5815
+ let value;
5816
+ if (colonIdx === -1) {
5817
+ field = line;
5818
+ value = "";
5819
+ } else {
5820
+ field = line.slice(0, colonIdx);
5821
+ value = line.slice(colonIdx + 1);
5822
+ if (value.startsWith(" ")) value = value.slice(1);
5823
+ }
5824
+ switch (field) {
5825
+ case "event":
5826
+ event = value;
5827
+ break;
5828
+ case "data":
5829
+ dataLines.push(value);
5830
+ break;
5831
+ case "id":
5832
+ id = value;
5833
+ break;
5834
+ case "retry": {
5835
+ const n = parseInt(value, 10);
5836
+ if (!isNaN(n)) retry = n;
5837
+ break;
5838
+ }
5839
+ }
5840
+ }
5841
+ if (dataLines.length === 0) return null;
5842
+ return { event, data: dataLines.join("\n"), id, retry };
5843
+ }
5844
+
5845
+ // src/providers/openai.ts
5846
+ var OpenAIProvider = class extends BaseProvider {
5847
+ constructor(config) {
5848
+ super();
5849
+ this.providerName = "openai";
5850
+ this.apiKey = config.apiKey;
5851
+ this.baseURL = (config.baseURL ?? "https://api.openai.com/v1").replace(/\/$/, "");
5852
+ this.defaultModel = config.defaultModel ?? "gpt-4o";
5853
+ this.defaultHeaders = {
5854
+ ...config.defaultHeaders,
5855
+ ...config.organization ? { "OpenAI-Organization": config.organization } : {}
5856
+ };
5857
+ }
5858
+ async complete(request) {
5859
+ const body = this.buildRequestBody(request, false);
5860
+ const res = await this.doFetch(body);
5861
+ const json = await res.json();
5862
+ return this.parseResponse(json);
5863
+ }
5864
+ async *completeStream(request) {
5865
+ const body = this.buildRequestBody(request, true);
5866
+ const res = await this.doFetch(body);
5867
+ if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
5868
+ let id = "";
5869
+ let model = "";
5870
+ const toolCallBuffers = /* @__PURE__ */ new Map();
5871
+ let usage;
5872
+ for await (const event of parseSSEStream(res.body)) {
5873
+ if (event.data === "[DONE]") break;
5874
+ let chunk;
5875
+ try {
5876
+ chunk = JSON.parse(event.data);
5877
+ } catch {
5878
+ continue;
5879
+ }
5880
+ id = chunk.id ?? id;
5881
+ model = chunk.model ?? model;
5882
+ const choice = chunk.choices?.[0];
5883
+ if (chunk.usage) {
5884
+ usage = this.extractUsage(chunk.usage);
5885
+ }
5886
+ if (!choice) {
5887
+ if (usage) yield { id, model, delta: {}, usage };
5888
+ continue;
5889
+ }
5890
+ const delta = choice.delta ?? {};
5891
+ const out = {
5892
+ id,
5893
+ model,
5894
+ delta: {},
5895
+ finishReason: mapFinishReason(choice.finish_reason)
5896
+ };
5897
+ if (delta.content) {
5898
+ out.delta.content = delta.content;
5899
+ }
5900
+ if (delta.tool_calls) {
5901
+ const partials = [];
5902
+ for (const tc of delta.tool_calls) {
5903
+ const idx = tc.index ?? 0;
5904
+ if (!toolCallBuffers.has(idx)) {
5905
+ toolCallBuffers.set(idx, { id: tc.id ?? "", name: "", arguments: "" });
5906
+ }
5907
+ const buf = toolCallBuffers.get(idx);
5908
+ if (tc.id) buf.id = tc.id;
5909
+ if (tc.function?.name) buf.name = tc.function.name;
5910
+ if (tc.function?.arguments) buf.arguments += tc.function.arguments;
5911
+ partials.push({ id: buf.id, name: buf.name || void 0, arguments: tc.function?.arguments });
5912
+ }
5913
+ out.delta.toolCalls = partials;
5914
+ }
5915
+ if (usage) out.usage = usage;
5916
+ yield out;
5917
+ }
5918
+ }
5919
+ // ─── Internals ───────────────────────────────────────────────────────────
5920
+ buildRequestBody(request, stream) {
5921
+ const body = {
5922
+ model: request.model || this.defaultModel,
5923
+ messages: request.messages.map((m) => this.toOpenAIMessage(m)),
5924
+ stream
5925
+ };
5926
+ if (stream) {
5927
+ body.stream_options = { include_usage: true };
5928
+ }
5929
+ if (request.temperature != null) body.temperature = request.temperature;
5930
+ if (request.maxTokens != null) body.max_tokens = request.maxTokens;
5931
+ if (request.topP != null) body.top_p = request.topP;
5932
+ if (request.stop) body.stop = request.stop;
5933
+ if (request.tools?.length) {
5934
+ body.tools = request.tools.map((t) => this.toOpenAITool(t));
5935
+ }
5936
+ if (request.toolChoice != null) {
5937
+ if (typeof request.toolChoice === "string") {
5938
+ body.tool_choice = request.toolChoice;
5939
+ } else {
5940
+ body.tool_choice = { type: "function", function: { name: request.toolChoice.name } };
5941
+ }
5942
+ }
5943
+ if (request.responseFormat) {
5944
+ if (request.responseFormat.type === "json_schema") {
5945
+ body.response_format = {
5946
+ type: "json_schema",
5947
+ json_schema: {
5948
+ name: request.responseFormat.name ?? "response",
5949
+ schema: request.responseFormat.schema,
5950
+ strict: true
5951
+ }
5952
+ };
5953
+ } else if (request.responseFormat.type === "json_object") {
5954
+ body.response_format = { type: "json_object" };
5955
+ }
5956
+ }
5957
+ if (request.extra) {
5958
+ Object.assign(body, request.extra);
5959
+ }
5960
+ return body;
5961
+ }
5962
+ toOpenAIMessage(m) {
5963
+ const msg = { role: m.role };
5964
+ if (typeof m.content === "string") {
5965
+ msg.content = m.content;
5966
+ } else if (Array.isArray(m.content)) {
5967
+ msg.content = m.content.map((part) => {
5968
+ if (part.type === "text") return { type: "text", text: part.text };
5969
+ if (part.type === "image") {
5970
+ const src = part.source;
5971
+ const url = src.type === "url" ? src.url : `data:${src.mediaType};base64,${src.data}`;
5972
+ return { type: "image_url", image_url: { url } };
5973
+ }
5974
+ return part;
5975
+ });
5976
+ }
5977
+ if (m.role === "assistant" && m.toolCalls?.length) {
5978
+ msg.tool_calls = m.toolCalls.map((tc) => ({
5979
+ id: tc.id,
5980
+ type: "function",
5981
+ function: { name: tc.name, arguments: tc.arguments }
5982
+ }));
5983
+ }
5984
+ if (m.role === "tool" && m.toolCallId) {
5985
+ msg.tool_call_id = m.toolCallId;
5986
+ }
5987
+ if (m.name) msg.name = m.name;
5988
+ return msg;
5989
+ }
5990
+ toOpenAITool(t) {
5991
+ const fn = {
5992
+ name: t.name,
5993
+ description: t.description,
5994
+ parameters: t.parameters
5995
+ };
5996
+ if (t.strict) {
5997
+ fn.strict = true;
5998
+ }
5999
+ return { type: "function", function: fn };
6000
+ }
6001
+ async doFetch(body) {
6002
+ const url = `${this.baseURL}/chat/completions`;
6003
+ const res = await fetch(url, {
6004
+ method: "POST",
6005
+ headers: {
6006
+ "Content-Type": "application/json",
6007
+ Authorization: `Bearer ${this.apiKey}`,
6008
+ ...this.defaultHeaders
6009
+ },
6010
+ body: JSON.stringify(body)
6011
+ });
6012
+ if (!res.ok) {
6013
+ const text = await res.text().catch(() => "");
6014
+ throw new ProviderError(
6015
+ `OpenAI API error ${res.status}: ${text}`,
6016
+ this.providerName,
6017
+ res.status,
6018
+ text
6019
+ );
6020
+ }
6021
+ return res;
6022
+ }
6023
+ parseResponse(json) {
6024
+ const choice = json.choices?.[0];
6025
+ const message = choice?.message ?? {};
6026
+ const toolCalls = (message.tool_calls ?? []).map((tc) => ({
6027
+ id: tc.id,
6028
+ name: tc.function?.name ?? "",
6029
+ arguments: tc.function?.arguments ?? "{}"
6030
+ }));
6031
+ return {
6032
+ id: json.id ?? "",
6033
+ model: json.model ?? "",
6034
+ content: message.content ?? null,
6035
+ toolCalls,
6036
+ usage: this.extractUsage(json.usage),
6037
+ finishReason: mapFinishReason(choice?.finish_reason),
6038
+ raw: json
6039
+ };
6040
+ }
6041
+ extractUsage(usage) {
6042
+ if (!usage) return emptyUsage();
6043
+ const promptDetails = usage.prompt_tokens_details ?? {};
6044
+ const completionDetails = usage.completion_tokens_details ?? {};
6045
+ return {
6046
+ inputTokens: usage.prompt_tokens ?? 0,
6047
+ outputTokens: usage.completion_tokens ?? 0,
6048
+ totalTokens: usage.total_tokens ?? (usage.prompt_tokens ?? 0) + (usage.completion_tokens ?? 0),
6049
+ cachedInputTokens: promptDetails.cached_tokens ?? 0,
6050
+ cachedWriteTokens: 0,
6051
+ cachedOutputTokens: 0,
6052
+ reasoningTokens: completionDetails.reasoning_tokens ?? 0
6053
+ };
6054
+ }
6055
+ };
6056
+ function mapFinishReason(reason) {
6057
+ switch (reason) {
6058
+ case "stop":
6059
+ return "stop";
6060
+ case "tool_calls":
6061
+ return "tool_calls";
6062
+ case "length":
6063
+ return "length";
6064
+ case "content_filter":
6065
+ return "content_filter";
6066
+ default:
6067
+ return "stop";
6068
+ }
6069
+ }
6070
+
6071
+ // src/providers/anthropic.ts
6072
+ var AnthropicProvider = class extends BaseProvider {
6073
+ constructor(config) {
6074
+ super();
6075
+ this.providerName = "anthropic";
6076
+ this.apiKey = config.apiKey;
6077
+ this.baseURL = (config.baseURL ?? "https://api.anthropic.com").replace(/\/$/, "");
6078
+ this.defaultModel = config.defaultModel ?? "claude-sonnet-4-20250514";
6079
+ this.defaultHeaders = config.defaultHeaders ?? {};
6080
+ this.anthropicVersion = config.anthropicVersion ?? "2023-06-01";
6081
+ }
6082
+ async complete(request) {
6083
+ const body = this.buildRequestBody(request, false);
6084
+ const res = await this.doFetch(body);
6085
+ const json = await res.json();
6086
+ return this.parseResponse(json);
6087
+ }
6088
+ async *completeStream(request) {
6089
+ const body = this.buildRequestBody(request, true);
6090
+ const res = await this.doFetch(body);
6091
+ if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
6092
+ let id = "";
6093
+ let model = "";
6094
+ let inputTokens = 0;
6095
+ let outputTokens = 0;
6096
+ let cacheReadInputTokens = 0;
6097
+ let cacheCreationInputTokens = 0;
6098
+ const currentToolCalls = /* @__PURE__ */ new Map();
6099
+ for await (const event of parseSSEStream(res.body)) {
6100
+ let data;
6101
+ try {
6102
+ data = JSON.parse(event.data);
6103
+ } catch {
6104
+ continue;
6105
+ }
6106
+ const eventType = event.event ?? data.type;
6107
+ switch (eventType) {
6108
+ case "message_start": {
6109
+ const msg = data.message;
6110
+ id = msg?.id ?? "";
6111
+ model = msg?.model ?? "";
6112
+ if (msg?.usage) {
6113
+ inputTokens = msg.usage.input_tokens ?? 0;
6114
+ cacheReadInputTokens = msg.usage.cache_read_input_tokens ?? 0;
6115
+ cacheCreationInputTokens = msg.usage.cache_creation_input_tokens ?? 0;
6116
+ }
6117
+ break;
6118
+ }
6119
+ case "content_block_start": {
6120
+ const idx = data.index ?? 0;
6121
+ const block = data.content_block;
6122
+ if (block?.type === "tool_use") {
6123
+ currentToolCalls.set(idx, {
6124
+ id: block.id ?? "",
6125
+ name: block.name ?? "",
6126
+ arguments: ""
6127
+ });
6128
+ }
6129
+ break;
6130
+ }
6131
+ case "content_block_delta": {
6132
+ const idx = data.index ?? 0;
6133
+ const delta = data.delta;
6134
+ if (delta?.type === "text_delta" && delta.text) {
6135
+ yield {
6136
+ id,
6137
+ model,
6138
+ delta: { content: delta.text }
6139
+ };
6140
+ } else if (delta?.type === "input_json_delta" && delta.partial_json) {
6141
+ const tc = currentToolCalls.get(idx);
6142
+ if (tc) {
6143
+ tc.arguments += delta.partial_json;
6144
+ yield {
6145
+ id,
6146
+ model,
6147
+ delta: {
6148
+ toolCalls: [{ id: tc.id, name: tc.name, arguments: delta.partial_json }]
6149
+ }
6150
+ };
6151
+ }
6152
+ }
6153
+ break;
6154
+ }
6155
+ case "content_block_stop":
6156
+ break;
6157
+ case "message_delta": {
6158
+ const delta = data.delta;
6159
+ if (data.usage) {
6160
+ outputTokens = data.usage.output_tokens ?? outputTokens;
6161
+ }
6162
+ const finishReason = mapAnthropicStopReason(delta?.stop_reason);
6163
+ const usage = {
6164
+ inputTokens,
6165
+ outputTokens,
6166
+ totalTokens: inputTokens + outputTokens,
6167
+ cachedInputTokens: cacheReadInputTokens,
6168
+ cachedWriteTokens: cacheCreationInputTokens,
6169
+ cachedOutputTokens: 0,
6170
+ reasoningTokens: 0
6171
+ };
6172
+ yield {
6173
+ id,
6174
+ model,
6175
+ delta: {},
6176
+ finishReason,
6177
+ usage
6178
+ };
6179
+ break;
6180
+ }
6181
+ case "message_stop":
6182
+ break;
6183
+ case "error": {
6184
+ const errMsg = data.error?.message ?? "Unknown Anthropic streaming error";
6185
+ throw new ProviderError(errMsg, this.providerName);
6186
+ }
6187
+ }
6188
+ }
6189
+ }
6190
+ // ─── Internals ───────────────────────────────────────────────────────────
6191
+ buildRequestBody(request, stream) {
6192
+ const { system, messages } = this.splitSystemMessages(request.messages);
6193
+ const body = {
6194
+ model: request.model || this.defaultModel,
6195
+ messages: messages.map((m) => this.toAnthropicMessage(m)),
6196
+ max_tokens: request.maxTokens ?? 4096,
6197
+ stream
6198
+ };
6199
+ if (system) body.system = system;
6200
+ if (request.temperature != null) body.temperature = request.temperature;
6201
+ if (request.topP != null) body.top_p = request.topP;
6202
+ if (request.stop) body.stop_sequences = request.stop;
6203
+ if (request.tools?.length) {
6204
+ body.tools = request.tools.map((t) => this.toAnthropicTool(t));
6205
+ }
6206
+ if (request.toolChoice != null) {
6207
+ if (request.toolChoice === "auto") {
6208
+ body.tool_choice = { type: "auto" };
6209
+ } else if (request.toolChoice === "required") {
6210
+ body.tool_choice = { type: "any" };
6211
+ } else if (request.toolChoice === "none") ; else {
6212
+ body.tool_choice = { type: "tool", name: request.toolChoice.name };
6213
+ }
6214
+ }
6215
+ if (request.extra) {
6216
+ Object.assign(body, request.extra);
6217
+ }
6218
+ return body;
6219
+ }
6220
+ splitSystemMessages(messages) {
6221
+ const systemParts = [];
6222
+ const rest = [];
6223
+ for (const m of messages) {
6224
+ if (m.role === "system") {
6225
+ if (typeof m.content === "string") {
6226
+ systemParts.push(m.content);
6227
+ } else {
6228
+ const text = m.content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
6229
+ if (text) systemParts.push(text);
6230
+ }
6231
+ } else {
6232
+ rest.push(m);
6233
+ }
6234
+ }
6235
+ return {
6236
+ system: systemParts.length > 0 ? systemParts.join("\n\n") : void 0,
6237
+ messages: rest
6238
+ };
6239
+ }
6240
+ toAnthropicMessage(m) {
6241
+ if (m.role === "tool" && m.toolCallId) {
6242
+ const content = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
6243
+ return {
6244
+ role: "user",
6245
+ content: [
6246
+ {
6247
+ type: "tool_result",
6248
+ tool_use_id: m.toolCallId,
6249
+ content
6250
+ }
6251
+ ]
6252
+ };
6253
+ }
6254
+ if (m.role === "assistant" && m.toolCalls?.length) {
6255
+ const content = [];
6256
+ if (typeof m.content === "string" && m.content) {
6257
+ content.push({ type: "text", text: m.content });
6258
+ }
6259
+ for (const tc of m.toolCalls) {
6260
+ content.push({
6261
+ type: "tool_use",
6262
+ id: tc.id,
6263
+ name: tc.name,
6264
+ input: safeJsonParse(tc.arguments)
6265
+ });
6266
+ }
6267
+ return { role: "assistant", content };
6268
+ }
6269
+ const msg = { role: m.role };
6270
+ if (typeof m.content === "string") {
6271
+ msg.content = m.content;
6272
+ } else if (Array.isArray(m.content)) {
6273
+ msg.content = m.content.map((part) => {
6274
+ if (part.type === "text") return { type: "text", text: part.text };
6275
+ if (part.type === "image") {
6276
+ const src = part.source;
6277
+ if (src.type === "base64") {
6278
+ return {
6279
+ type: "image",
6280
+ source: { type: "base64", media_type: src.mediaType, data: src.data }
6281
+ };
6282
+ }
6283
+ return {
6284
+ type: "image",
6285
+ source: { type: "url", url: src.url }
6286
+ };
6287
+ }
6288
+ return part;
6289
+ });
6290
+ }
6291
+ return msg;
6292
+ }
6293
+ toAnthropicTool(t) {
6294
+ return {
6295
+ name: t.name,
6296
+ description: t.description,
6297
+ input_schema: t.parameters
6298
+ };
6299
+ }
6300
+ async doFetch(body) {
6301
+ const url = `${this.baseURL}/v1/messages`;
6302
+ const res = await fetch(url, {
6303
+ method: "POST",
6304
+ headers: {
6305
+ "Content-Type": "application/json",
6306
+ "x-api-key": this.apiKey,
6307
+ "anthropic-version": this.anthropicVersion,
6308
+ ...this.defaultHeaders
6309
+ },
6310
+ body: JSON.stringify(body)
6311
+ });
6312
+ if (!res.ok) {
6313
+ const text = await res.text().catch(() => "");
6314
+ throw new ProviderError(
6315
+ `Anthropic API error ${res.status}: ${text}`,
6316
+ this.providerName,
6317
+ res.status,
6318
+ text
6319
+ );
6320
+ }
6321
+ return res;
6322
+ }
6323
+ parseResponse(json) {
6324
+ const textParts = [];
6325
+ const toolCalls = [];
6326
+ for (const block of json.content ?? []) {
6327
+ if (block.type === "text") {
6328
+ textParts.push(block.text);
6329
+ } else if (block.type === "tool_use") {
6330
+ toolCalls.push({
6331
+ id: block.id,
6332
+ name: block.name,
6333
+ arguments: typeof block.input === "string" ? block.input : JSON.stringify(block.input)
6334
+ });
6335
+ }
6336
+ }
6337
+ const usage = json.usage ?? {};
6338
+ const tokenUsage = {
6339
+ inputTokens: usage.input_tokens ?? 0,
6340
+ outputTokens: usage.output_tokens ?? 0,
6341
+ totalTokens: (usage.input_tokens ?? 0) + (usage.output_tokens ?? 0),
6342
+ cachedInputTokens: usage.cache_read_input_tokens ?? 0,
6343
+ cachedWriteTokens: usage.cache_creation_input_tokens ?? 0,
6344
+ cachedOutputTokens: 0,
6345
+ reasoningTokens: 0
6346
+ };
6347
+ return {
6348
+ id: json.id ?? "",
6349
+ model: json.model ?? "",
6350
+ content: textParts.join("") || null,
6351
+ toolCalls,
6352
+ usage: tokenUsage,
6353
+ finishReason: mapAnthropicStopReason(json.stop_reason),
6354
+ raw: json
6355
+ };
6356
+ }
6357
+ };
6358
+ function mapAnthropicStopReason(reason) {
6359
+ switch (reason) {
6360
+ case "end_turn":
6361
+ case "stop_sequence":
6362
+ return "stop";
6363
+ case "tool_use":
6364
+ return "tool_calls";
6365
+ case "max_tokens":
6366
+ return "length";
6367
+ default:
6368
+ return "stop";
6369
+ }
6370
+ }
6371
+ function safeJsonParse(str) {
6372
+ try {
6373
+ return JSON.parse(str);
6374
+ } catch {
6375
+ return {};
6376
+ }
6377
+ }
6378
+
6379
+ // src/providers/azure.ts
6380
+ var AzureProvider = class extends OpenAIProvider {
6381
+ constructor(config) {
6382
+ super({
6383
+ provider: "openai",
6384
+ apiKey: config.apiKey,
6385
+ defaultModel: config.defaultModel ?? config.deploymentName ?? "gpt-4o",
6386
+ defaultHeaders: config.defaultHeaders
6387
+ });
6388
+ this.providerName = "azure";
6389
+ this.endpoint = config.endpoint.replace(/\/$/, "");
6390
+ this.apiVersion = config.apiVersion ?? "2024-10-21";
6391
+ this.deploymentName = config.deploymentName;
6392
+ this.azureApiKey = config.apiKey;
6393
+ }
6394
+ async doFetch(body) {
6395
+ const deployment = this.deploymentName ?? body.model;
6396
+ if (!deployment) {
6397
+ throw new ProviderError("Azure requires a deploymentName or model", this.providerName);
6398
+ }
6399
+ const url = `${this.endpoint}/openai/deployments/${deployment}/chat/completions?api-version=${this.apiVersion}`;
6400
+ const { model: _model, ...bodyWithoutModel } = body;
6401
+ const res = await fetch(url, {
6402
+ method: "POST",
6403
+ headers: {
6404
+ "Content-Type": "application/json",
6405
+ "api-key": this.azureApiKey,
6406
+ ...this.defaultHeaders
6407
+ },
6408
+ body: JSON.stringify(bodyWithoutModel)
6409
+ });
6410
+ if (!res.ok) {
6411
+ const text = await res.text().catch(() => "");
6412
+ throw new ProviderError(
6413
+ `Azure OpenAI API error ${res.status}: ${text}`,
6414
+ this.providerName,
6415
+ res.status,
6416
+ text
6417
+ );
6418
+ }
6419
+ return res;
6420
+ }
6421
+ };
6422
+
6423
+ // src/providers/openaiCompatible.ts
6424
+ var OpenAICompatibleProvider = class extends OpenAIProvider {
6425
+ constructor(config) {
6426
+ super({
6427
+ provider: "openai",
6428
+ apiKey: config.apiKey,
6429
+ baseURL: config.baseURL,
6430
+ defaultModel: config.defaultModel,
6431
+ defaultHeaders: config.defaultHeaders
6432
+ });
6433
+ this.providerName = "openai-compatible";
6434
+ }
6435
+ };
6436
+ function signRequest(req) {
6437
+ const now = /* @__PURE__ */ new Date();
6438
+ const dateStamp = toDateStamp(now);
6439
+ const amzDate = toAmzDate(now);
6440
+ const parsed = new URL(req.url);
6441
+ const host = parsed.host;
6442
+ const canonicalUri = parsed.pathname || "/";
6443
+ const canonicalQuerystring = sortQueryParams(parsed.searchParams);
6444
+ const headers = {
6445
+ ...req.headers,
6446
+ host,
6447
+ "x-amz-date": amzDate
6448
+ };
6449
+ if (req.credentials.sessionToken) {
6450
+ headers["x-amz-security-token"] = req.credentials.sessionToken;
6451
+ }
6452
+ const signedHeaderKeys = Object.keys(headers).map((k) => k.toLowerCase()).sort();
6453
+ const canonicalHeaders = signedHeaderKeys.map((k) => `${k}:${headers[Object.keys(headers).find((h) => h.toLowerCase() === k)]?.trim()}`).join("\n") + "\n";
6454
+ const signedHeaders = signedHeaderKeys.join(";");
6455
+ const payloadHash = sha256(req.body);
6456
+ const canonicalRequest = [
6457
+ req.method,
6458
+ canonicalUri,
6459
+ canonicalQuerystring,
6460
+ canonicalHeaders,
6461
+ signedHeaders,
6462
+ payloadHash
6463
+ ].join("\n");
6464
+ const credentialScope = `${dateStamp}/${req.region}/${req.service}/aws4_request`;
6465
+ const stringToSign = [
6466
+ "AWS4-HMAC-SHA256",
6467
+ amzDate,
6468
+ credentialScope,
6469
+ sha256(canonicalRequest)
6470
+ ].join("\n");
6471
+ const signingKey = getSignatureKey(
6472
+ req.credentials.secretAccessKey,
6473
+ dateStamp,
6474
+ req.region,
6475
+ req.service
6476
+ );
6477
+ const signature = hmacHex(signingKey, stringToSign);
6478
+ const authorization = `AWS4-HMAC-SHA256 Credential=${req.credentials.accessKeyId}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;
6479
+ const result = {
6480
+ ...req.headers,
6481
+ "x-amz-date": amzDate,
6482
+ Authorization: authorization
6483
+ };
6484
+ if (req.credentials.sessionToken) {
6485
+ result["x-amz-security-token"] = req.credentials.sessionToken;
6486
+ }
6487
+ return result;
6488
+ }
6489
+ function toDateStamp(d) {
6490
+ return d.toISOString().replace(/[-:T]/g, "").slice(0, 8);
6491
+ }
6492
+ function toAmzDate(d) {
6493
+ return d.toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
6494
+ }
6495
+ function sha256(data) {
6496
+ return createHash("sha256").update(data, "utf8").digest("hex");
6497
+ }
6498
+ function hmac(key, data) {
6499
+ return createHmac("sha256", key).update(data, "utf8").digest();
6500
+ }
6501
+ function hmacHex(key, data) {
6502
+ return createHmac("sha256", key).update(data, "utf8").digest("hex");
6503
+ }
6504
+ function getSignatureKey(secret, dateStamp, region, service) {
6505
+ const kDate = hmac(`AWS4${secret}`, dateStamp);
6506
+ const kRegion = hmac(kDate, region);
6507
+ const kService = hmac(kRegion, service);
6508
+ return hmac(kService, "aws4_request");
6509
+ }
6510
+ function sortQueryParams(params) {
6511
+ const sorted = [...params.entries()].sort(([a], [b]) => a.localeCompare(b));
6512
+ return sorted.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&");
6513
+ }
6514
+
6515
+ // src/providers/bedrock.ts
6516
+ var BedrockProvider = class extends BaseProvider {
6517
+ constructor(config) {
6518
+ super();
6519
+ this.providerName = "bedrock";
6520
+ this.region = config.region;
6521
+ this.defaultModel = config.defaultModel ?? "anthropic.claude-sonnet-4-20250514-v1:0";
6522
+ this.defaultHeaders = config.defaultHeaders ?? {};
6523
+ this.credentials = {
6524
+ accessKeyId: config.accessKeyId ?? process.env.AWS_ACCESS_KEY_ID ?? "",
6525
+ secretAccessKey: config.secretAccessKey ?? process.env.AWS_SECRET_ACCESS_KEY ?? "",
6526
+ sessionToken: config.sessionToken ?? process.env.AWS_SESSION_TOKEN
6527
+ };
6528
+ if (!this.credentials.accessKeyId || !this.credentials.secretAccessKey) {
6529
+ throw new ProviderError(
6530
+ "AWS credentials required: provide accessKeyId/secretAccessKey or set AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY env vars",
6531
+ "bedrock"
6532
+ );
6533
+ }
6534
+ }
6535
+ async complete(request) {
6536
+ const modelId = request.model || this.defaultModel;
6537
+ const body = this.buildConverseBody(request);
6538
+ const json = await this.doFetch(modelId, body);
6539
+ return this.parseResponse(json, modelId);
6540
+ }
6541
+ async *completeStream(request) {
6542
+ const response = await this.complete(request);
6543
+ yield {
6544
+ id: response.id,
6545
+ model: response.model,
6546
+ delta: {
6547
+ content: response.content ?? void 0,
6548
+ toolCalls: response.toolCalls.length > 0 ? response.toolCalls : void 0
6549
+ },
6550
+ usage: response.usage,
6551
+ finishReason: response.finishReason
6552
+ };
6553
+ }
6554
+ // ─── Internals ───────────────────────────────────────────────────────────
6555
+ buildConverseBody(request) {
6556
+ const { system, messages } = this.splitSystemMessages(request.messages);
6557
+ const body = {
6558
+ messages: messages.map((m) => this.toBedrockMessage(m))
6559
+ };
6560
+ if (system.length > 0) {
6561
+ body.system = system.map((s) => ({ text: s }));
6562
+ }
6563
+ const inferenceConfig = {};
6564
+ if (request.maxTokens != null) inferenceConfig.maxTokens = request.maxTokens;
6565
+ if (request.temperature != null) inferenceConfig.temperature = request.temperature;
6566
+ if (request.topP != null) inferenceConfig.topP = request.topP;
6567
+ if (request.stop) inferenceConfig.stopSequences = request.stop;
6568
+ if (Object.keys(inferenceConfig).length > 0) body.inferenceConfig = inferenceConfig;
6569
+ if (request.tools?.length) {
6570
+ body.toolConfig = {
6571
+ tools: request.tools.map((t) => this.toBedrockTool(t))
6572
+ };
6573
+ if (request.toolChoice != null) {
6574
+ if (request.toolChoice === "auto") {
6575
+ body.toolConfig.toolChoice = { auto: {} };
6576
+ } else if (request.toolChoice === "required") {
6577
+ body.toolConfig.toolChoice = { any: {} };
6578
+ } else if (typeof request.toolChoice === "object") {
6579
+ body.toolConfig.toolChoice = { tool: { name: request.toolChoice.name } };
6580
+ }
6581
+ }
6582
+ }
6583
+ if (request.extra) {
6584
+ Object.assign(body, request.extra);
6585
+ }
6586
+ return body;
6587
+ }
6588
+ splitSystemMessages(messages) {
6589
+ const system = [];
6590
+ const rest = [];
6591
+ for (const m of messages) {
6592
+ if (m.role === "system") {
6593
+ const text = typeof m.content === "string" ? m.content : m.content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
6594
+ if (text) system.push(text);
6595
+ } else {
6596
+ rest.push(m);
6597
+ }
6598
+ }
6599
+ return { system, messages: rest };
6600
+ }
6601
+ toBedrockMessage(m) {
6602
+ if (m.role === "tool" && m.toolCallId) {
6603
+ const text = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
6604
+ return {
6605
+ role: "user",
6606
+ content: [
6607
+ {
6608
+ toolResult: {
6609
+ toolUseId: m.toolCallId,
6610
+ content: [{ text }],
6611
+ status: "success"
6612
+ }
6613
+ }
6614
+ ]
6615
+ };
6616
+ }
6617
+ if (m.role === "assistant" && m.toolCalls?.length) {
6618
+ const content2 = [];
6619
+ if (typeof m.content === "string" && m.content) {
6620
+ content2.push({ text: m.content });
6621
+ }
6622
+ for (const tc of m.toolCalls) {
6623
+ content2.push({
6624
+ toolUse: {
6625
+ toolUseId: tc.id,
6626
+ name: tc.name,
6627
+ input: safeJsonParse2(tc.arguments)
6628
+ }
6629
+ });
6630
+ }
6631
+ return { role: "assistant", content: content2 };
6632
+ }
6633
+ const content = [];
6634
+ if (typeof m.content === "string") {
6635
+ content.push({ text: m.content });
6636
+ } else if (Array.isArray(m.content)) {
6637
+ for (const part of m.content) {
6638
+ if (part.type === "text") {
6639
+ content.push({ text: part.text });
6640
+ } else if (part.type === "image") {
6641
+ const src = part.source;
6642
+ if (src.type === "base64") {
6643
+ content.push({
6644
+ image: {
6645
+ format: mediaTypeToFormat(src.mediaType),
6646
+ source: { bytes: src.data }
6647
+ }
6648
+ });
6649
+ }
6650
+ }
6651
+ }
6652
+ }
6653
+ return { role: m.role === "user" ? "user" : "assistant", content };
6654
+ }
6655
+ toBedrockTool(t) {
6656
+ return {
6657
+ toolSpec: {
6658
+ name: t.name,
6659
+ description: t.description,
6660
+ inputSchema: { json: t.parameters }
6661
+ }
6662
+ };
6663
+ }
6664
+ async doFetch(modelId, body) {
6665
+ const host = `bedrock-runtime.${this.region}.amazonaws.com`;
6666
+ const url = `https://${host}/model/${encodeURIComponent(modelId)}/converse`;
6667
+ const bodyStr = JSON.stringify(body);
6668
+ const headers = signRequest({
6669
+ method: "POST",
6670
+ url,
6671
+ headers: {
6672
+ "Content-Type": "application/json",
6673
+ Accept: "application/json",
6674
+ ...this.defaultHeaders
6675
+ },
6676
+ body: bodyStr,
6677
+ region: this.region,
6678
+ service: "bedrock",
6679
+ credentials: this.credentials
6680
+ });
6681
+ const res = await fetch(url, {
6682
+ method: "POST",
6683
+ headers,
6684
+ body: bodyStr
6685
+ });
6686
+ if (!res.ok) {
6687
+ const text = await res.text().catch(() => "");
6688
+ throw new ProviderError(
6689
+ `Bedrock API error ${res.status}: ${text}`,
6690
+ this.providerName,
6691
+ res.status,
6692
+ text
6693
+ );
6694
+ }
6695
+ return res.json();
6696
+ }
6697
+ parseResponse(json, modelId) {
6698
+ const output = json.output?.message;
6699
+ const textParts = [];
6700
+ const toolCalls = [];
6701
+ for (const block of output?.content ?? []) {
6702
+ if (block.text) {
6703
+ textParts.push(block.text);
6704
+ } else if (block.toolUse) {
6705
+ toolCalls.push({
6706
+ id: block.toolUse.toolUseId,
6707
+ name: block.toolUse.name,
6708
+ arguments: typeof block.toolUse.input === "string" ? block.toolUse.input : JSON.stringify(block.toolUse.input ?? {})
6709
+ });
6710
+ }
6711
+ }
6712
+ const usage = json.usage ?? {};
6713
+ const tokenUsage = {
6714
+ inputTokens: usage.inputTokens ?? 0,
6715
+ outputTokens: usage.outputTokens ?? 0,
6716
+ totalTokens: usage.totalTokens ?? (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0),
6717
+ cachedInputTokens: usage.cacheReadInputTokenCount ?? 0,
6718
+ cachedWriteTokens: usage.cacheWriteInputTokenCount ?? 0,
6719
+ cachedOutputTokens: 0,
6720
+ reasoningTokens: 0
6721
+ };
6722
+ return {
6723
+ id: json.requestId ?? json.$metadata?.requestId ?? "",
6724
+ model: modelId,
6725
+ content: textParts.join("") || null,
6726
+ toolCalls,
6727
+ usage: tokenUsage,
6728
+ finishReason: mapBedrockStopReason(json.stopReason),
6729
+ raw: json
6730
+ };
6731
+ }
6732
+ };
6733
+ function mapBedrockStopReason(reason) {
6734
+ switch (reason) {
6735
+ case "end_turn":
6736
+ case "stop_sequence":
6737
+ return "stop";
6738
+ case "tool_use":
6739
+ return "tool_calls";
6740
+ case "max_tokens":
6741
+ return "length";
6742
+ case "content_filtered":
6743
+ case "guardrail":
6744
+ return "content_filter";
6745
+ default:
6746
+ return "stop";
6747
+ }
6748
+ }
6749
+ function mediaTypeToFormat(mediaType) {
6750
+ const map = {
6751
+ "image/jpeg": "jpeg",
6752
+ "image/png": "png",
6753
+ "image/gif": "gif",
6754
+ "image/webp": "webp"
6755
+ };
6756
+ return map[mediaType] ?? "png";
6757
+ }
6758
+ function safeJsonParse2(str) {
6759
+ try {
6760
+ return JSON.parse(str);
6761
+ } catch {
6762
+ return {};
6763
+ }
6764
+ }
6765
+
6766
+ // src/providers/vertex.ts
6767
+ var VertexProvider = class extends BaseProvider {
6768
+ constructor(config) {
6769
+ super();
6770
+ this.providerName = "vertex";
6771
+ this.tokenExpiry = 0;
6772
+ this.projectId = config.projectId;
6773
+ this.location = config.location ?? "us-central1";
6774
+ this.defaultModel = config.defaultModel ?? "gemini-2.0-flash";
6775
+ this.defaultHeaders = config.defaultHeaders ?? {};
6776
+ if (config.accessToken) {
6777
+ this.accessToken = config.accessToken;
6778
+ this.tokenExpiry = Date.now() + 36e5;
6779
+ }
6780
+ if (config.serviceAccountJson) {
6781
+ this.serviceAccountJson = typeof config.serviceAccountJson === "string" ? JSON.parse(config.serviceAccountJson) : config.serviceAccountJson;
6782
+ }
6783
+ }
6784
+ async complete(request) {
6785
+ const modelId = request.model || this.defaultModel;
6786
+ const body = this.buildGeminiBody(request);
6787
+ const url = this.buildUrl(modelId, false);
6788
+ const res = await this.doFetch(url, body);
6789
+ const json = await res.json();
6790
+ return this.parseResponse(json, modelId);
6791
+ }
6792
+ async *completeStream(request) {
6793
+ const modelId = request.model || this.defaultModel;
6794
+ const body = this.buildGeminiBody(request);
6795
+ const url = this.buildUrl(modelId, true);
6796
+ const res = await this.doFetch(url, body);
6797
+ if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
6798
+ const contentType = res.headers.get("content-type") ?? "";
6799
+ if (contentType.includes("text/event-stream")) {
6800
+ for await (const event of parseSSEStream(res.body)) {
6801
+ if (event.data === "[DONE]") break;
6802
+ let chunk;
6803
+ try {
6804
+ chunk = JSON.parse(event.data);
6805
+ } catch {
6806
+ continue;
6807
+ }
6808
+ yield this.parseStreamChunk(chunk, modelId);
6809
+ }
6810
+ } else {
6811
+ const reader = res.body.getReader();
6812
+ const decoder = new TextDecoder();
6813
+ let buffer = "";
6814
+ try {
6815
+ while (true) {
6816
+ const { done, value } = await reader.read();
6817
+ if (done) break;
6818
+ buffer += decoder.decode(value, { stream: true });
6819
+ const lines = buffer.split("\n");
6820
+ buffer = lines.pop() ?? "";
6821
+ for (const line of lines) {
6822
+ const trimmed = line.trim().replace(/^,/, "").replace(/^\[/, "").replace(/\]$/, "");
6823
+ if (!trimmed || trimmed === "[" || trimmed === "]") continue;
6824
+ try {
6825
+ const chunk = JSON.parse(trimmed);
6826
+ yield this.parseStreamChunk(chunk, modelId);
6827
+ } catch {
6828
+ buffer = line + "\n" + buffer;
6829
+ break;
6830
+ }
6831
+ }
6832
+ }
6833
+ const remaining = buffer.trim().replace(/^,/, "").replace(/^\[/, "").replace(/\]$/, "");
6834
+ if (remaining && remaining !== "[" && remaining !== "]") {
6835
+ try {
6836
+ const chunk = JSON.parse(remaining);
6837
+ yield this.parseStreamChunk(chunk, modelId);
6838
+ } catch {
6839
+ }
6840
+ }
6841
+ } finally {
6842
+ reader.releaseLock();
6843
+ }
6844
+ }
6845
+ }
6846
+ // ─── Internals ───────────────────────────────────────────────────────────
6847
+ buildUrl(modelId, stream) {
6848
+ const base = `https://${this.location}-aiplatform.googleapis.com/v1`;
6849
+ const path2 = `projects/${this.projectId}/locations/${this.location}/publishers/google/models/${modelId}`;
6850
+ const action = stream ? "streamGenerateContent?alt=sse" : "generateContent";
6851
+ return `${base}/${path2}:${action}`;
6852
+ }
6853
+ buildGeminiBody(request) {
6854
+ const { system, contents } = this.buildContents(request.messages);
6855
+ const body = { contents };
6856
+ if (system) {
6857
+ body.systemInstruction = { parts: [{ text: system }] };
6858
+ }
6859
+ const generationConfig = {};
6860
+ if (request.maxTokens != null) generationConfig.maxOutputTokens = request.maxTokens;
6861
+ if (request.temperature != null) generationConfig.temperature = request.temperature;
6862
+ if (request.topP != null) generationConfig.topP = request.topP;
6863
+ if (request.stop) generationConfig.stopSequences = request.stop;
6864
+ if (request.responseFormat) {
6865
+ if (request.responseFormat.type === "json_schema" || request.responseFormat.type === "json_object") {
6866
+ generationConfig.responseMimeType = "application/json";
6867
+ if (request.responseFormat.schema) {
6868
+ generationConfig.responseSchema = request.responseFormat.schema;
6869
+ }
6870
+ }
6871
+ }
6872
+ if (Object.keys(generationConfig).length > 0) body.generationConfig = generationConfig;
6873
+ if (request.tools?.length) {
6874
+ body.tools = [
6875
+ {
6876
+ functionDeclarations: request.tools.map((t) => this.toGeminiTool(t))
6877
+ }
6878
+ ];
6879
+ if (request.toolChoice != null) {
6880
+ const mode = request.toolChoice === "auto" ? "AUTO" : request.toolChoice === "required" ? "ANY" : request.toolChoice === "none" ? "NONE" : "AUTO";
6881
+ body.toolConfig = { functionCallingConfig: { mode } };
6882
+ }
6883
+ }
6884
+ if (request.extra) {
6885
+ Object.assign(body, request.extra);
6886
+ }
6887
+ return body;
6888
+ }
6889
+ buildContents(messages) {
6890
+ const systemParts = [];
6891
+ const contents = [];
6892
+ for (const m of messages) {
6893
+ if (m.role === "system") {
6894
+ const text = typeof m.content === "string" ? m.content : m.content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
6895
+ if (text) systemParts.push(text);
6896
+ continue;
6897
+ }
6898
+ contents.push(this.toGeminiContent(m));
6899
+ }
6900
+ return {
6901
+ system: systemParts.length > 0 ? systemParts.join("\n\n") : void 0,
6902
+ contents
6903
+ };
6904
+ }
6905
+ toGeminiContent(m) {
6906
+ if (m.role === "tool" && m.toolCallId) {
6907
+ const text = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
6908
+ let response;
6909
+ try {
6910
+ response = JSON.parse(text);
6911
+ } catch {
6912
+ response = { result: text };
6913
+ }
6914
+ return {
6915
+ role: "user",
6916
+ parts: [
6917
+ {
6918
+ functionResponse: {
6919
+ name: m.name ?? m.toolCallId,
6920
+ response
6921
+ }
6922
+ }
6923
+ ]
6924
+ };
6925
+ }
6926
+ if (m.role === "assistant" && m.toolCalls?.length) {
6927
+ const parts2 = [];
6928
+ if (typeof m.content === "string" && m.content) {
6929
+ parts2.push({ text: m.content });
6930
+ }
6931
+ for (const tc of m.toolCalls) {
6932
+ parts2.push({
6933
+ functionCall: {
6934
+ name: tc.name,
6935
+ args: safeJsonParse3(tc.arguments)
6936
+ }
6937
+ });
6938
+ }
6939
+ return { role: "model", parts: parts2 };
6940
+ }
6941
+ const role = m.role === "assistant" ? "model" : "user";
6942
+ const parts = [];
6943
+ if (typeof m.content === "string") {
6944
+ parts.push({ text: m.content });
6945
+ } else if (Array.isArray(m.content)) {
6946
+ for (const part of m.content) {
6947
+ if (part.type === "text") {
6948
+ parts.push({ text: part.text });
6949
+ } else if (part.type === "image") {
6950
+ const src = part.source;
6951
+ if (src.type === "base64") {
6952
+ parts.push({
6953
+ inlineData: { mimeType: src.mediaType, data: src.data }
6954
+ });
6955
+ } else {
6956
+ parts.push({
6957
+ fileData: { mimeType: "image/jpeg", fileUri: src.url }
6958
+ });
6959
+ }
6960
+ }
6961
+ }
6962
+ }
6963
+ return { role, parts };
6964
+ }
6965
+ toGeminiTool(t) {
6966
+ return {
6967
+ name: t.name,
6968
+ description: t.description,
6969
+ parameters: t.parameters
6970
+ };
6971
+ }
6972
+ async getAccessToken() {
6973
+ if (this.accessToken && Date.now() < this.tokenExpiry) {
6974
+ return this.accessToken;
6975
+ }
6976
+ if (this.serviceAccountJson) {
6977
+ const token = await this.getTokenFromServiceAccount(this.serviceAccountJson);
6978
+ this.accessToken = token;
6979
+ this.tokenExpiry = Date.now() + 35e5;
6980
+ return token;
6981
+ }
6982
+ try {
6983
+ const { execSync } = await import('child_process');
6984
+ const token = execSync("gcloud auth print-access-token", { encoding: "utf8" }).trim();
6985
+ this.accessToken = token;
6986
+ this.tokenExpiry = Date.now() + 35e5;
6987
+ return token;
6988
+ } catch {
6989
+ throw new ProviderError(
6990
+ "No access token available. Provide accessToken, serviceAccountJson, or ensure gcloud CLI is authenticated.",
6991
+ this.providerName
6992
+ );
6993
+ }
6994
+ }
6995
+ async getTokenFromServiceAccount(sa) {
6996
+ const { createSign } = await import('crypto');
6997
+ const now = Math.floor(Date.now() / 1e3);
6998
+ const header = { alg: "RS256", typ: "JWT" };
6999
+ const claims = {
7000
+ iss: sa.client_email,
7001
+ scope: "https://www.googleapis.com/auth/cloud-platform",
7002
+ aud: "https://oauth2.googleapis.com/token",
7003
+ iat: now,
7004
+ exp: now + 3600
7005
+ };
7006
+ const segments = [
7007
+ base64url(JSON.stringify(header)),
7008
+ base64url(JSON.stringify(claims))
7009
+ ];
7010
+ const signingInput = segments.join(".");
7011
+ const sign = createSign("RSA-SHA256");
7012
+ sign.update(signingInput);
7013
+ const signature = sign.sign(sa.private_key, "base64url");
7014
+ const jwt = `${signingInput}.${signature}`;
7015
+ const tokenRes = await fetch("https://oauth2.googleapis.com/token", {
7016
+ method: "POST",
7017
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
7018
+ body: `grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=${jwt}`
7019
+ });
7020
+ if (!tokenRes.ok) {
7021
+ const text = await tokenRes.text().catch(() => "");
7022
+ throw new ProviderError(`Failed to get Vertex access token: ${text}`, this.providerName);
7023
+ }
7024
+ const tokenJson = await tokenRes.json();
7025
+ return tokenJson.access_token;
7026
+ }
7027
+ async doFetch(url, body) {
7028
+ const token = await this.getAccessToken();
7029
+ const res = await fetch(url, {
7030
+ method: "POST",
7031
+ headers: {
7032
+ "Content-Type": "application/json",
7033
+ Authorization: `Bearer ${token}`,
7034
+ ...this.defaultHeaders
7035
+ },
7036
+ body: JSON.stringify(body)
7037
+ });
7038
+ if (!res.ok) {
7039
+ const text = await res.text().catch(() => "");
7040
+ throw new ProviderError(
7041
+ `Vertex AI API error ${res.status}: ${text}`,
7042
+ this.providerName,
7043
+ res.status,
7044
+ text
7045
+ );
7046
+ }
7047
+ return res;
7048
+ }
7049
+ parseResponse(json, modelId) {
7050
+ const candidate = json.candidates?.[0];
7051
+ const parts = candidate?.content?.parts ?? [];
7052
+ const textParts = [];
7053
+ const toolCalls = [];
7054
+ let toolCallIdx = 0;
7055
+ for (const part of parts) {
7056
+ if (part.text) {
7057
+ textParts.push(part.text);
7058
+ } else if (part.functionCall) {
7059
+ toolCalls.push({
7060
+ id: `call_${toolCallIdx++}`,
7061
+ name: part.functionCall.name,
7062
+ arguments: JSON.stringify(part.functionCall.args ?? {})
7063
+ });
7064
+ }
7065
+ }
7066
+ const usageMeta = json.usageMetadata ?? {};
7067
+ const tokenUsage = {
7068
+ inputTokens: usageMeta.promptTokenCount ?? 0,
7069
+ outputTokens: usageMeta.candidatesTokenCount ?? 0,
7070
+ totalTokens: usageMeta.totalTokenCount ?? 0,
7071
+ cachedInputTokens: usageMeta.cachedContentTokenCount ?? 0,
7072
+ cachedWriteTokens: 0,
7073
+ cachedOutputTokens: 0,
7074
+ reasoningTokens: usageMeta.thoughtsTokenCount ?? 0
7075
+ };
7076
+ return {
7077
+ id: json.responseId ?? "",
7078
+ model: modelId,
7079
+ content: textParts.join("") || null,
7080
+ toolCalls,
7081
+ usage: tokenUsage,
7082
+ finishReason: mapGeminiFinishReason(candidate?.finishReason),
7083
+ raw: json
7084
+ };
7085
+ }
7086
+ parseStreamChunk(chunk, modelId) {
7087
+ const candidate = chunk.candidates?.[0];
7088
+ const parts = candidate?.content?.parts ?? [];
7089
+ const delta = {};
7090
+ for (const part of parts) {
7091
+ if (part.text) {
7092
+ delta.content = (delta.content ?? "") + part.text;
7093
+ } else if (part.functionCall) {
7094
+ if (!delta.toolCalls) delta.toolCalls = [];
7095
+ delta.toolCalls.push({
7096
+ id: `call_${delta.toolCalls.length}`,
7097
+ name: part.functionCall.name,
7098
+ arguments: JSON.stringify(part.functionCall.args ?? {})
7099
+ });
7100
+ }
7101
+ }
7102
+ let usage;
7103
+ if (chunk.usageMetadata) {
7104
+ const u = chunk.usageMetadata;
7105
+ usage = {
7106
+ inputTokens: u.promptTokenCount ?? 0,
7107
+ outputTokens: u.candidatesTokenCount ?? 0,
7108
+ totalTokens: u.totalTokenCount ?? 0,
7109
+ cachedInputTokens: u.cachedContentTokenCount ?? 0,
7110
+ cachedWriteTokens: 0,
7111
+ cachedOutputTokens: 0,
7112
+ reasoningTokens: u.thoughtsTokenCount ?? 0
7113
+ };
7114
+ }
7115
+ return {
7116
+ id: chunk.responseId ?? "",
7117
+ model: modelId,
7118
+ delta,
7119
+ usage,
7120
+ finishReason: mapGeminiFinishReason(candidate?.finishReason)
7121
+ };
7122
+ }
7123
+ };
7124
+ function mapGeminiFinishReason(reason) {
7125
+ switch (reason) {
7126
+ case "STOP":
7127
+ return "stop";
7128
+ case "MAX_TOKENS":
7129
+ return "length";
7130
+ case "SAFETY":
7131
+ case "RECITATION":
7132
+ return "content_filter";
7133
+ case "TOOL_CALLS":
7134
+ return "tool_calls";
7135
+ default:
7136
+ return "stop";
7137
+ }
7138
+ }
7139
+ function base64url(str) {
7140
+ return Buffer.from(str).toString("base64url");
7141
+ }
7142
+ function safeJsonParse3(str) {
7143
+ try {
7144
+ return JSON.parse(str);
7145
+ } catch {
7146
+ return {};
7147
+ }
7148
+ }
7149
+ function fromNativeProvider(provider, options) {
7150
+ let boundTools = options?.__tools;
7151
+ const modelName = options?.model ?? provider.defaultModel ?? provider.providerName;
7152
+ const adapted = {
7153
+ modelName,
7154
+ capabilities: resolveCapabilities(provider.providerName),
7155
+ invoke: async (messages, _invokeOptions) => {
7156
+ const request = buildRequest(messages, boundTools, options, _invokeOptions);
7157
+ const response = await provider.complete(request);
7158
+ return toBaseChatMessage(response);
7159
+ },
7160
+ stream: async function* (messages, _invokeOptions) {
7161
+ const request = buildRequest(messages, boundTools, options, _invokeOptions);
7162
+ request.stream = true;
7163
+ let fullContent = "";
7164
+ let allToolCalls = [];
7165
+ let lastUsage;
7166
+ const toolCallBuffers = /* @__PURE__ */ new Map();
7167
+ for await (const chunk of provider.completeStream(request)) {
7168
+ if (chunk.delta.content) {
7169
+ fullContent += chunk.delta.content;
7170
+ yield chunk.delta.content;
7171
+ }
7172
+ if (chunk.delta.toolCalls) {
7173
+ for (const tc of chunk.delta.toolCalls) {
7174
+ if (tc.id) {
7175
+ const existing = toolCallBuffers.get(tc.id);
7176
+ if (existing) {
7177
+ if (tc.arguments) existing.arguments += tc.arguments;
7178
+ } else {
7179
+ toolCallBuffers.set(tc.id, {
7180
+ id: tc.id,
7181
+ name: tc.name ?? "",
7182
+ arguments: tc.arguments ?? ""
7183
+ });
7184
+ }
7185
+ }
7186
+ }
7187
+ }
7188
+ if (chunk.usage) lastUsage = chunk.usage;
7189
+ }
7190
+ allToolCalls = [...toolCallBuffers.values()];
7191
+ const finalMessage = assembleMessage(fullContent, allToolCalls, lastUsage);
7192
+ yield finalMessage;
7193
+ },
7194
+ bindTools: (tools, _bindOptions) => {
7195
+ const strict = _bindOptions?.strict ?? false;
7196
+ const toolDefs = tools.map((t) => toToolDefinition(t, strict));
7197
+ return fromNativeProvider(provider, { ...options, __tools: toolDefs });
7198
+ }
7199
+ };
7200
+ return adapted;
7201
+ function buildRequest(messages, tools, opts, invokeOptions) {
7202
+ const req = {
7203
+ model: opts?.model ?? modelName,
7204
+ messages: messages.map(toUnifiedMessage)
7205
+ };
7206
+ if (opts?.temperature != null) req.temperature = opts.temperature;
7207
+ if (opts?.maxTokens != null) req.maxTokens = opts.maxTokens;
7208
+ if (tools?.length) req.tools = tools;
7209
+ if (opts?.extra) req.extra = opts.extra;
7210
+ const rf = invokeOptions?.response_format;
7211
+ if (rf && typeof rf === "object" && rf.type) {
7212
+ if (rf.type === "json_schema" && rf.json_schema) {
7213
+ req.responseFormat = {
7214
+ type: "json_schema",
7215
+ schema: rf.json_schema.schema,
7216
+ name: rf.json_schema.name
7217
+ };
7218
+ } else if (rf.type === "json_object") {
7219
+ req.responseFormat = { type: "json_object" };
7220
+ } else if (rf.type === "text") {
7221
+ req.responseFormat = { type: "text" };
7222
+ }
7223
+ }
7224
+ if (!req.responseFormat) {
7225
+ const lastMsg = messages[messages.length - 1];
7226
+ if (lastMsg && lastMsg.__responseFormat) {
7227
+ req.responseFormat = lastMsg.__responseFormat;
7228
+ }
7229
+ }
7230
+ return req;
7231
+ }
7232
+ }
7233
+ function toUnifiedMessage(msg) {
7234
+ const unified = {
7235
+ role: msg.role,
7236
+ content: convertContent(msg.content)
7237
+ };
7238
+ if (msg.name) unified.name = msg.name;
7239
+ if (msg.tool_call_id) unified.toolCallId = msg.tool_call_id;
7240
+ if (msg.tool_calls?.length) {
7241
+ unified.toolCalls = msg.tool_calls.map((tc) => ({
7242
+ id: tc.id,
7243
+ name: tc.function?.name ?? tc.name ?? "",
7244
+ arguments: tc.function?.arguments ?? tc.arguments ?? "{}"
7245
+ }));
7246
+ }
7247
+ return unified;
7248
+ }
7249
+ function convertContent(content) {
7250
+ if (typeof content === "string") return content;
7251
+ if (!Array.isArray(content)) return String(content ?? "");
7252
+ return content.map((part) => {
7253
+ if (part.type === "text" || !part.type && part.text) {
7254
+ return { type: "text", text: part.text ?? part.content ?? "" };
7255
+ }
7256
+ if (part.type === "image_url") {
7257
+ const img = part.image_url;
7258
+ if (typeof img === "string" || img?.url?.startsWith("http")) {
7259
+ return {
7260
+ type: "image",
7261
+ source: { type: "url", url: typeof img === "string" ? img : img.url }
7262
+ };
7263
+ }
7264
+ if (img?.url?.startsWith("data:")) {
7265
+ const match = img.url.match(/^data:([^;]+);base64,(.+)$/);
7266
+ if (match) {
7267
+ return {
7268
+ type: "image",
7269
+ source: { type: "base64", mediaType: match[1], data: match[2] }
7270
+ };
7271
+ }
7272
+ }
7273
+ }
7274
+ return { type: "text", text: part.text ?? part.content ?? JSON.stringify(part) };
7275
+ });
7276
+ }
7277
+ function isZodSchema(obj) {
7278
+ return obj != null && typeof obj === "object" && typeof obj.safeParse === "function" && "_def" in obj;
7279
+ }
7280
+ function convertToJsonSchema(schema, strict = false) {
7281
+ if (!schema || typeof schema !== "object") {
7282
+ return { type: "object", properties: {}, required: [], additionalProperties: false };
7283
+ }
7284
+ if (isZodSchema(schema)) {
7285
+ const converted = zodToJsonSchema(schema, {
7286
+ $refStrategy: "none",
7287
+ ...strict ? { openaiStrictMode: true } : {}
7288
+ });
7289
+ delete converted["$schema"];
7290
+ if (!converted.type) converted.type = "object";
7291
+ return converted;
7292
+ }
7293
+ if (!schema.type && schema.properties) {
7294
+ return { type: "object", ...schema };
7295
+ }
7296
+ return schema;
7297
+ }
7298
+ function normalizeStrictSchema(schema) {
7299
+ if (!schema || typeof schema !== "object") return schema;
7300
+ const clone2 = { ...schema };
7301
+ if (clone2.type === "object" || clone2.properties) {
7302
+ if (!clone2.type) clone2.type = "object";
7303
+ if (clone2.properties && typeof clone2.properties === "object") {
7304
+ const normalized = {};
7305
+ for (const [k, v] of Object.entries(clone2.properties)) {
7306
+ normalized[k] = normalizeStrictSchema(v);
7307
+ }
7308
+ clone2.properties = normalized;
7309
+ clone2.required = Object.keys(normalized);
7310
+ } else {
7311
+ clone2.properties = {};
7312
+ clone2.required = [];
7313
+ }
7314
+ clone2.additionalProperties = false;
7315
+ }
7316
+ if (clone2.items) clone2.items = normalizeStrictSchema(clone2.items);
7317
+ for (const key of ["anyOf", "oneOf", "allOf"]) {
7318
+ if (Array.isArray(clone2[key])) {
7319
+ clone2[key] = clone2[key].map((s) => normalizeStrictSchema(s));
7320
+ }
7321
+ }
7322
+ if (clone2.definitions && typeof clone2.definitions === "object") {
7323
+ const nd = {};
7324
+ for (const [k, v] of Object.entries(clone2.definitions)) {
7325
+ nd[k] = normalizeStrictSchema(v);
7326
+ }
7327
+ clone2.definitions = nd;
7328
+ }
7329
+ if (clone2.$defs && typeof clone2.$defs === "object") {
7330
+ const nd = {};
7331
+ for (const [k, v] of Object.entries(clone2.$defs)) {
7332
+ nd[k] = normalizeStrictSchema(v);
7333
+ }
7334
+ clone2.$defs = nd;
7335
+ }
7336
+ return clone2;
7337
+ }
7338
+ function toToolDefinition(tool, strict) {
7339
+ let name;
7340
+ let description;
7341
+ let parameters;
7342
+ if (tool.type === "function" && tool.function) {
7343
+ name = tool.function.name;
7344
+ description = tool.function.description ?? "";
7345
+ parameters = tool.function.parameters ?? { type: "object", properties: {}, additionalProperties: false };
7346
+ } else {
7347
+ name = tool.name ?? "unknown";
7348
+ description = tool.description ?? "";
7349
+ const rawSchema = tool.schema ?? tool.parameters;
7350
+ parameters = convertToJsonSchema(rawSchema, strict);
7351
+ }
7352
+ if (strict) {
7353
+ parameters = normalizeStrictSchema(parameters);
7354
+ }
7355
+ return { name, description, parameters, ...strict ? { strict: true } : {} };
7356
+ }
7357
+ function toBaseChatMessage(response) {
7358
+ const msg = {
7359
+ role: "assistant",
7360
+ content: response.content ?? "",
7361
+ usage: {
7362
+ prompt_tokens: response.usage.inputTokens,
7363
+ completion_tokens: response.usage.outputTokens,
7364
+ total_tokens: response.usage.totalTokens,
7365
+ prompt_tokens_details: {
7366
+ cached_tokens: response.usage.cachedInputTokens
7367
+ },
7368
+ completion_tokens_details: {
7369
+ reasoning_tokens: response.usage.reasoningTokens
7370
+ }
7371
+ },
7372
+ response_metadata: {
7373
+ token_usage: {
7374
+ prompt_tokens: response.usage.inputTokens,
7375
+ completion_tokens: response.usage.outputTokens,
7376
+ total_tokens: response.usage.totalTokens,
7377
+ cached_tokens: response.usage.cachedInputTokens,
7378
+ cache_write_tokens: response.usage.cachedWriteTokens
7379
+ },
7380
+ finish_reason: response.finishReason,
7381
+ model_name: response.model
7382
+ }
7383
+ };
7384
+ if (response.toolCalls.length > 0) {
7385
+ msg.tool_calls = response.toolCalls.map((tc) => ({
7386
+ id: tc.id,
7387
+ type: "function",
7388
+ function: { name: tc.name, arguments: tc.arguments }
7389
+ }));
7390
+ }
7391
+ return msg;
7392
+ }
7393
+ function assembleMessage(content, toolCalls, usage) {
7394
+ const msg = {
7395
+ role: "assistant",
7396
+ content
7397
+ };
7398
+ if (usage) {
7399
+ msg.usage = {
7400
+ prompt_tokens: usage.inputTokens,
7401
+ completion_tokens: usage.outputTokens,
7402
+ total_tokens: usage.totalTokens,
7403
+ prompt_tokens_details: {
7404
+ cached_tokens: usage.cachedInputTokens
7405
+ },
7406
+ completion_tokens_details: {
7407
+ reasoning_tokens: usage.reasoningTokens
7408
+ }
7409
+ };
7410
+ msg.response_metadata = {
7411
+ token_usage: {
7412
+ prompt_tokens: usage.inputTokens,
7413
+ completion_tokens: usage.outputTokens,
7414
+ total_tokens: usage.totalTokens,
7415
+ cached_tokens: usage.cachedInputTokens,
7416
+ cache_write_tokens: usage.cachedWriteTokens
7417
+ }
7418
+ };
7419
+ }
7420
+ if (toolCalls.length > 0) {
7421
+ msg.tool_calls = toolCalls.map((tc) => ({
7422
+ id: tc.id,
7423
+ type: "function",
7424
+ function: { name: tc.name, arguments: tc.arguments }
7425
+ }));
7426
+ }
7427
+ return msg;
7428
+ }
7429
+ function resolveCapabilities(provider) {
7430
+ switch (provider) {
7431
+ case "openai":
7432
+ case "azure":
7433
+ case "openai-compatible":
7434
+ return {
7435
+ structuredOutput: "native",
7436
+ strictToolCalling: true,
7437
+ streaming: true,
7438
+ provider
7439
+ };
7440
+ case "anthropic":
7441
+ return {
7442
+ structuredOutput: "tool_based",
7443
+ strictToolCalling: false,
7444
+ streaming: true,
7445
+ provider: "anthropic"
7446
+ };
7447
+ case "bedrock":
7448
+ return {
7449
+ structuredOutput: "tool_based",
7450
+ strictToolCalling: false,
7451
+ streaming: false,
7452
+ // streaming uses binary protocol
7453
+ provider: "bedrock"
7454
+ };
7455
+ case "vertex":
7456
+ return {
7457
+ structuredOutput: "native",
7458
+ strictToolCalling: false,
7459
+ streaming: true,
7460
+ provider: "google"
7461
+ };
7462
+ default:
7463
+ return { provider };
7464
+ }
7465
+ }
7466
+
7467
+ // src/providers/index.ts
7468
+ function createProvider(config) {
7469
+ switch (config.provider) {
7470
+ case "openai":
7471
+ return new OpenAIProvider(config);
7472
+ case "anthropic":
7473
+ return new AnthropicProvider(config);
7474
+ case "azure":
7475
+ return new AzureProvider(config);
7476
+ case "openai-compatible":
7477
+ return new OpenAICompatibleProvider(config);
7478
+ case "bedrock":
7479
+ return new BedrockProvider(config);
7480
+ case "vertex":
7481
+ return new VertexProvider(config);
7482
+ default:
7483
+ throw new Error(`Unknown provider: ${config.provider}`);
7484
+ }
7485
+ }
7486
+
7487
+ export { AnthropicProvider, AzureProvider, BaseProvider, BedrockProvider, DEFAULT_PROFILE_CONFIGS, GuardrailPhase, InMemoryMemoryStore, NativeJsonSchemaStrategy, OpenAICompatibleProvider, OpenAIProvider, ProviderError, StructuredOutputManager, ToolBasedStrategy, VertexProvider, agentVerdictRule, applyTokenLimits, buildSystemPrompt, captureSnapshot, codePresenceRule, cognipeerSink, countApproxTokens, countMessagesTokens, createAgent, createAgentNode, createCodeGuardrail, createContextSummarizeNode, createContextTools, createGuardrail, createJsonGuardrail, createProvider, createRegexGuardrail, createResolverNode, createSmartAgent, createTool, createToolLimitFinalizeNode, createToolsNode, customCallbackRule, customSink, emptyUsage, evaluateGuardrails, fileSink, formatZodError, fromLangchainModel, fromLangchainTools, fromNativeProvider, generateSpanId, generateTraceId, getModelCapabilities, getNestedValue, getResolvedSmartConfig, httpSink, isSmartModel, jsonSchemaRule, normalizeSmartAgentOptions, otlpSink, readMemoryFacts, regexRule, renderMemoryFacts, resolveMemoryStore, resolveStrategy, resolveToolApprovalState, restoreSnapshot, runSmartAgentEvalHarness, startStreamingSession, traceSessionToOtlp, withTools, writeSummaryFactsToMemory };
5139
7488
  //# sourceMappingURL=index.mjs.map
5140
7489
  //# sourceMappingURL=index.mjs.map