@cognipeer/agent-sdk 0.4.9 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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;
@@ -1727,235 +2080,134 @@ function createAgentCoreNode(opts) {
1727
2080
 
1728
2081
  // src/smart/runtimeConfig.ts
1729
2082
  var DEFAULT_CRITICAL_TOOLS = ["response", "manage_todo_list", "get_tool_response"];
2083
+ var BASE_DEFAULTS = {
2084
+ limits: { maxToolCalls: 8, maxParallelTools: 2, maxContextTokens: 24e3 },
2085
+ summarization: {
2086
+ enable: true,
2087
+ maxTokens: 24e3,
2088
+ summaryTriggerTokens: 17e3,
2089
+ summaryPromptMaxTokens: 7e3,
2090
+ summaryCompressionRatioTarget: 0.35,
2091
+ summaryMode: "incremental",
2092
+ promptTemplate: "",
2093
+ toolFreeCall: true,
2094
+ integrityCheck: true
2095
+ },
2096
+ context: {
2097
+ policy: "hybrid",
2098
+ lastTurnsToKeep: 8,
2099
+ toolResponsePolicy: "summarize_archive",
2100
+ archiveLargeToolResponses: true,
2101
+ retrieveArchivedToolResponseOnDemand: true,
2102
+ budget: {
2103
+ systemReserveTokens: 1200,
2104
+ goalsReserveTokens: 1200,
2105
+ recentTurnsReserveTokens: 5200,
2106
+ toolResponseReserveTokens: 2800
2107
+ }
2108
+ },
2109
+ planning: {
2110
+ mode: "off",
2111
+ replanPolicy: "on_failure",
2112
+ everyNSteps: 0
2113
+ },
2114
+ memory: {
2115
+ provider: "inMemory",
2116
+ scope: "session",
2117
+ writePolicy: "auto_important",
2118
+ readPolicy: "hybrid"
2119
+ },
2120
+ delegation: {
2121
+ mode: "role_based",
2122
+ maxDelegationDepth: 2,
2123
+ maxChildCalls: 4,
2124
+ maxParallelChild: 2,
2125
+ childContextPolicy: "scoped",
2126
+ requireJsonOutputContract: true
2127
+ },
2128
+ toolResponses: {
2129
+ maxToolResponseChars: 3600,
2130
+ maxToolResponseTokens: 1200,
2131
+ defaultPolicy: "keep_structured",
2132
+ largeResponsePolicy: "summarize_archive",
2133
+ toolResponseRetentionByTool: {},
2134
+ criticalTools: [...DEFAULT_CRITICAL_TOOLS],
2135
+ schemaValidation: "strict",
2136
+ retryOnSchemaError: true,
2137
+ fallbackPolicy: "keep_structured"
2138
+ }
2139
+ };
2140
+ function buildProfile(overrides) {
2141
+ return {
2142
+ limits: { ...BASE_DEFAULTS.limits, ...overrides.limits },
2143
+ summarization: { ...BASE_DEFAULTS.summarization, ...overrides.summarization },
2144
+ context: {
2145
+ ...BASE_DEFAULTS.context,
2146
+ ...overrides.context,
2147
+ budget: { ...BASE_DEFAULTS.context.budget, ...overrides.context?.budget }
2148
+ },
2149
+ planning: { ...BASE_DEFAULTS.planning, ...overrides.planning },
2150
+ memory: { ...BASE_DEFAULTS.memory, ...overrides.memory },
2151
+ delegation: { ...BASE_DEFAULTS.delegation, ...overrides.delegation },
2152
+ toolResponses: {
2153
+ ...BASE_DEFAULTS.toolResponses,
2154
+ ...overrides.toolResponses,
2155
+ criticalTools: overrides.toolResponses?.criticalTools ?? [...DEFAULT_CRITICAL_TOOLS],
2156
+ toolResponseRetentionByTool: overrides.toolResponses?.toolResponseRetentionByTool ?? {}
2157
+ }
2158
+ };
2159
+ }
1730
2160
  var DEFAULT_PROFILE_CONFIGS = {
1731
- fast: {
2161
+ fast: buildProfile({
1732
2162
  limits: { maxToolCalls: 4, maxParallelTools: 1, maxContextTokens: 12e3 },
1733
2163
  summarization: {
1734
- enable: true,
1735
2164
  maxTokens: 12e3,
1736
2165
  summaryTriggerTokens: 9e3,
1737
2166
  summaryPromptMaxTokens: 5e3,
1738
- summaryCompressionRatioTarget: 0.4,
1739
- summaryMode: "incremental",
1740
- promptTemplate: "",
1741
- toolFreeCall: true,
1742
- integrityCheck: true
2167
+ summaryCompressionRatioTarget: 0.4
1743
2168
  },
1744
2169
  context: {
1745
- policy: "hybrid",
1746
2170
  lastTurnsToKeep: 6,
1747
2171
  toolResponsePolicy: "keep_structured",
1748
- archiveLargeToolResponses: true,
1749
- retrieveArchivedToolResponseOnDemand: true,
1750
- budget: {
1751
- systemReserveTokens: 1e3,
1752
- goalsReserveTokens: 900,
1753
- recentTurnsReserveTokens: 3200,
1754
- toolResponseReserveTokens: 1800
1755
- }
1756
- },
1757
- planning: {
1758
- mode: "off",
1759
- replanPolicy: "on_failure",
1760
- everyNSteps: 0
1761
- },
1762
- memory: {
1763
- provider: "inMemory",
1764
- scope: "session",
1765
- writePolicy: "auto_important",
1766
- readPolicy: "recent_only"
1767
- },
1768
- delegation: {
1769
- mode: "off",
1770
- maxDelegationDepth: 1,
1771
- maxChildCalls: 2,
1772
- maxParallelChild: 1,
1773
- childContextPolicy: "minimal",
1774
- requireJsonOutputContract: true
1775
- },
1776
- toolResponses: {
1777
- maxToolResponseChars: 2400,
1778
- maxToolResponseTokens: 800,
1779
- defaultPolicy: "keep_structured",
1780
- largeResponsePolicy: "summarize_archive",
1781
- toolResponseRetentionByTool: {},
1782
- criticalTools: [...DEFAULT_CRITICAL_TOOLS],
1783
- schemaValidation: "strict",
1784
- retryOnSchemaError: true,
1785
- fallbackPolicy: "keep_structured"
1786
- }
1787
- },
1788
- balanced: {
1789
- limits: { maxToolCalls: 8, maxParallelTools: 2, maxContextTokens: 24e3 },
1790
- summarization: {
1791
- enable: true,
1792
- maxTokens: 24e3,
1793
- summaryTriggerTokens: 17e3,
1794
- summaryPromptMaxTokens: 7e3,
1795
- summaryCompressionRatioTarget: 0.35,
1796
- summaryMode: "incremental",
1797
- promptTemplate: "",
1798
- toolFreeCall: true,
1799
- integrityCheck: true
1800
- },
1801
- context: {
1802
- policy: "hybrid",
1803
- lastTurnsToKeep: 8,
1804
- toolResponsePolicy: "summarize_archive",
1805
- archiveLargeToolResponses: true,
1806
- retrieveArchivedToolResponseOnDemand: true,
1807
- budget: {
1808
- systemReserveTokens: 1200,
1809
- goalsReserveTokens: 1200,
1810
- recentTurnsReserveTokens: 5200,
1811
- toolResponseReserveTokens: 2800
1812
- }
1813
- },
1814
- planning: {
1815
- mode: "off",
1816
- replanPolicy: "on_failure",
1817
- everyNSteps: 0
2172
+ budget: { systemReserveTokens: 1e3, goalsReserveTokens: 900, recentTurnsReserveTokens: 3200, toolResponseReserveTokens: 1800 }
1818
2173
  },
1819
- memory: {
1820
- provider: "inMemory",
1821
- scope: "session",
1822
- writePolicy: "auto_important",
1823
- readPolicy: "hybrid"
1824
- },
1825
- delegation: {
1826
- mode: "role_based",
1827
- maxDelegationDepth: 2,
1828
- maxChildCalls: 4,
1829
- maxParallelChild: 2,
1830
- childContextPolicy: "scoped",
1831
- requireJsonOutputContract: true
1832
- },
1833
- toolResponses: {
1834
- maxToolResponseChars: 3600,
1835
- maxToolResponseTokens: 1200,
1836
- defaultPolicy: "keep_structured",
1837
- largeResponsePolicy: "summarize_archive",
1838
- toolResponseRetentionByTool: {},
1839
- criticalTools: [...DEFAULT_CRITICAL_TOOLS],
1840
- schemaValidation: "strict",
1841
- retryOnSchemaError: true,
1842
- fallbackPolicy: "keep_structured"
1843
- }
1844
- },
1845
- deep: {
2174
+ memory: { readPolicy: "recent_only" },
2175
+ delegation: { mode: "off", maxDelegationDepth: 1, maxChildCalls: 2, maxParallelChild: 1, childContextPolicy: "minimal" },
2176
+ toolResponses: { maxToolResponseChars: 2400, maxToolResponseTokens: 800 }
2177
+ }),
2178
+ balanced: buildProfile({}),
2179
+ deep: buildProfile({
1846
2180
  limits: { maxToolCalls: 14, maxParallelTools: 3, maxContextTokens: 42e3 },
1847
2181
  summarization: {
1848
- enable: true,
1849
2182
  maxTokens: 42e3,
1850
2183
  summaryTriggerTokens: 3e4,
1851
2184
  summaryPromptMaxTokens: 9e3,
1852
- summaryCompressionRatioTarget: 0.3,
1853
- summaryMode: "incremental",
1854
- promptTemplate: "",
1855
- toolFreeCall: true,
1856
- integrityCheck: true
2185
+ summaryCompressionRatioTarget: 0.3
1857
2186
  },
1858
2187
  context: {
1859
- policy: "hybrid",
1860
2188
  lastTurnsToKeep: 12,
1861
- toolResponsePolicy: "summarize_archive",
1862
- archiveLargeToolResponses: true,
1863
- retrieveArchivedToolResponseOnDemand: true,
1864
- budget: {
1865
- systemReserveTokens: 1400,
1866
- goalsReserveTokens: 1600,
1867
- recentTurnsReserveTokens: 7e3,
1868
- toolResponseReserveTokens: 3600
1869
- }
1870
- },
1871
- planning: {
1872
- mode: "off",
1873
- replanPolicy: "on_failure",
1874
- everyNSteps: 0
1875
- },
1876
- memory: {
1877
- provider: "inMemory",
1878
- scope: "workspace",
1879
- writePolicy: "auto_important",
1880
- readPolicy: "hybrid"
1881
- },
1882
- delegation: {
1883
- mode: "role_based",
1884
- maxDelegationDepth: 3,
1885
- maxChildCalls: 6,
1886
- maxParallelChild: 2,
1887
- childContextPolicy: "scoped",
1888
- requireJsonOutputContract: true
2189
+ budget: { systemReserveTokens: 1400, goalsReserveTokens: 1600, recentTurnsReserveTokens: 7e3, toolResponseReserveTokens: 3600 }
1889
2190
  },
1890
- toolResponses: {
1891
- maxToolResponseChars: 5400,
1892
- maxToolResponseTokens: 1800,
1893
- defaultPolicy: "keep_structured",
1894
- largeResponsePolicy: "summarize_archive",
1895
- toolResponseRetentionByTool: {},
1896
- criticalTools: [...DEFAULT_CRITICAL_TOOLS],
1897
- schemaValidation: "strict",
1898
- retryOnSchemaError: true,
1899
- fallbackPolicy: "keep_structured"
1900
- }
1901
- },
1902
- research: {
2191
+ memory: { scope: "workspace" },
2192
+ delegation: { maxDelegationDepth: 3, maxChildCalls: 6 },
2193
+ toolResponses: { maxToolResponseChars: 5400, maxToolResponseTokens: 1800 }
2194
+ }),
2195
+ research: buildProfile({
1903
2196
  limits: { maxToolCalls: 20, maxParallelTools: 4, maxContextTokens: 56e3 },
1904
2197
  summarization: {
1905
- enable: true,
1906
2198
  maxTokens: 56e3,
1907
2199
  summaryTriggerTokens: 42e3,
1908
2200
  summaryPromptMaxTokens: 1e4,
1909
- summaryCompressionRatioTarget: 0.28,
1910
- summaryMode: "incremental",
1911
- promptTemplate: "",
1912
- toolFreeCall: true,
1913
- integrityCheck: true
2201
+ summaryCompressionRatioTarget: 0.28
1914
2202
  },
1915
2203
  context: {
1916
- policy: "hybrid",
1917
2204
  lastTurnsToKeep: 20,
1918
- toolResponsePolicy: "summarize_archive",
1919
- archiveLargeToolResponses: true,
1920
- retrieveArchivedToolResponseOnDemand: true,
1921
- budget: {
1922
- systemReserveTokens: 1600,
1923
- goalsReserveTokens: 1800,
1924
- recentTurnsReserveTokens: 9e3,
1925
- toolResponseReserveTokens: 4800
1926
- }
1927
- },
1928
- planning: {
1929
- mode: "off",
1930
- replanPolicy: "on_failure",
1931
- everyNSteps: 0
1932
- },
1933
- memory: {
1934
- provider: "inMemory",
1935
- scope: "workspace",
1936
- writePolicy: "auto_important",
1937
- readPolicy: "hybrid"
2205
+ budget: { systemReserveTokens: 1600, goalsReserveTokens: 1800, recentTurnsReserveTokens: 9e3, toolResponseReserveTokens: 4800 }
1938
2206
  },
1939
- delegation: {
1940
- mode: "automatic",
1941
- maxDelegationDepth: 4,
1942
- maxChildCalls: 8,
1943
- maxParallelChild: 3,
1944
- childContextPolicy: "full",
1945
- requireJsonOutputContract: true
1946
- },
1947
- toolResponses: {
1948
- maxToolResponseChars: 7200,
1949
- maxToolResponseTokens: 2200,
1950
- defaultPolicy: "keep_structured",
1951
- largeResponsePolicy: "summarize_archive",
1952
- toolResponseRetentionByTool: {},
1953
- criticalTools: [...DEFAULT_CRITICAL_TOOLS],
1954
- schemaValidation: "strict",
1955
- retryOnSchemaError: true,
1956
- fallbackPolicy: "keep_structured"
1957
- }
1958
- }
2207
+ memory: { scope: "workspace" },
2208
+ delegation: { mode: "automatic", maxDelegationDepth: 4, maxChildCalls: 8, maxParallelChild: 3, childContextPolicy: "full" },
2209
+ toolResponses: { maxToolResponseChars: 7200, maxToolResponseTokens: 2200 }
2210
+ })
1959
2211
  };
1960
2212
  function mergeLimits(base, override) {
1961
2213
  return {
@@ -2098,19 +2350,55 @@ function countMessagesTokens(messages) {
2098
2350
  return countApproxTokens(allText) + messages.length * 4;
2099
2351
  }
2100
2352
 
2101
- // src/smart/toolResponses.ts
2102
- function safeStringify(value) {
2103
- if (typeof value === "string") return value;
2353
+ // src/utils/content.ts
2354
+ function contentToString(content) {
2104
2355
  try {
2105
- const serialized = JSON.stringify(value);
2106
- return typeof serialized === "string" ? serialized : String(value);
2107
- } catch {
2108
- return String(value);
2109
- }
2110
- }
2111
- function summarizeObject(value) {
2112
- if (value == null) return "null";
2113
- if (typeof value === "string") {
2356
+ if (content == null) return "";
2357
+ if (typeof content === "string") return content;
2358
+ if (Array.isArray(content)) {
2359
+ return content.map((p) => {
2360
+ if (!p || typeof p !== "object") return String(p);
2361
+ if (p.type === "text") return String(p.text ?? "");
2362
+ if (p.type === "image_url") {
2363
+ const img = p.image_url;
2364
+ const url = typeof img === "string" ? img : img?.url;
2365
+ const detail = typeof img === "string" ? void 0 : img?.detail;
2366
+ return `[image:${url || "unknown"}${detail ? ` detail=${detail}` : ""}]`;
2367
+ }
2368
+ return JSON.stringify(p);
2369
+ }).join("\n");
2370
+ }
2371
+ return JSON.stringify(content);
2372
+ } catch {
2373
+ return String(content);
2374
+ }
2375
+ }
2376
+ function mergeContentsToString(contents) {
2377
+ return contents.map((c) => contentToString(c)).join("\n");
2378
+ }
2379
+ function safeStringify(value, pretty = false) {
2380
+ if (value == null) return "";
2381
+ if (typeof value === "string") return value;
2382
+ try {
2383
+ return JSON.stringify(value, null, pretty ? 2 : void 0) ?? String(value);
2384
+ } catch {
2385
+ return String(value);
2386
+ }
2387
+ }
2388
+ function extractMessageText2(message) {
2389
+ if (!message) return "";
2390
+ const content = message.content;
2391
+ if (typeof content === "string") return content;
2392
+ if (Array.isArray(content)) {
2393
+ return content.map((part) => typeof part === "string" ? part : part?.text ?? part?.content ?? "").join("");
2394
+ }
2395
+ return "";
2396
+ }
2397
+
2398
+ // src/smart/toolResponses.ts
2399
+ function summarizeObject(value) {
2400
+ if (value == null) return "null";
2401
+ if (typeof value === "string") {
2114
2402
  return value.length > 240 ? `${value.slice(0, 240)}...` : value;
2115
2403
  }
2116
2404
  if (Array.isArray(value)) {
@@ -2204,6 +2492,16 @@ function countSuccessfulToolExecutions(toolHistory, toolName) {
2204
2492
  if (!Array.isArray(toolHistory)) return 0;
2205
2493
  return toolHistory.filter((entry) => entry?.toolName === toolName && (entry?.status === "success" || entry?.status === "handoff")).length;
2206
2494
  }
2495
+ function normalizeToolCall(call) {
2496
+ if (!call || typeof call !== "object") return null;
2497
+ const name = call.name ?? call.tool ?? call.function?.name ?? call.function_call?.name;
2498
+ if (typeof name !== "string" || name.length === 0) return null;
2499
+ return {
2500
+ id: call.id,
2501
+ name,
2502
+ args: call.args ?? call.arguments ?? call.input ?? call.function?.arguments ?? call.function_call?.arguments
2503
+ };
2504
+ }
2207
2505
  function createToolsNode(initialTools, opts) {
2208
2506
  return async (state) => {
2209
2507
  const runtime = state.agent || {
@@ -2238,12 +2536,14 @@ function createToolsNode(initialTools, opts) {
2238
2536
  if (anyTool._stateRef && typeof anyTool._stateRef === "object") {
2239
2537
  anyTool._stateRef.toolHistory = state.toolHistory;
2240
2538
  anyTool._stateRef.toolHistoryArchived = state.toolHistoryArchived;
2539
+ anyTool._stateRef.pendingApprovals = pendingApprovals;
2540
+ anyTool._stateRef.ctx = state.ctx || (state.ctx = {});
2241
2541
  anyTool._stateRef.__onEvent = onEvent;
2242
2542
  }
2243
2543
  }
2244
2544
  const last = state.messages[state.messages.length - 1];
2245
2545
  let toolCount = state.toolCallCount || 0;
2246
- const toolCalls = Array.isArray(last?.tool_calls) ? last.tool_calls : [];
2546
+ const toolCalls = Array.isArray(last?.tool_calls) ? last.tool_calls.map((toolCall) => normalizeToolCall(toolCall)).filter((toolCall) => toolCall !== null) : [];
2247
2547
  const toolHistory = state.toolHistory || [];
2248
2548
  const toolHistoryArchived = state.toolHistoryArchived || [];
2249
2549
  const remaining = Math.max(0, limits.maxToolCalls - toolCount);
@@ -2302,6 +2602,13 @@ function createToolsNode(initialTools, opts) {
2302
2602
  args = validatedArgs.value;
2303
2603
  }
2304
2604
  const toolName = tool.name || tc.name;
2605
+ const toolStateRef = tool._stateRef && typeof tool._stateRef === "object" ? tool._stateRef : null;
2606
+ if (toolStateRef) {
2607
+ toolStateRef.pendingApprovals = pendingApprovals;
2608
+ toolStateRef.ctx = state.ctx || (state.ctx = {});
2609
+ toolStateRef.__currentToolCallId = toolCallId;
2610
+ delete toolStateRef.__awaitingApproval;
2611
+ }
2305
2612
  const maxExecutionsPerRun = normalizeMaxExecutionsPerRun(tool.maxExecutionsPerRun);
2306
2613
  if (maxExecutionsPerRun !== null) {
2307
2614
  const currentExecutions = countSuccessfulToolExecutions(toolHistory, toolName);
@@ -2414,6 +2721,22 @@ function createToolsNode(initialTools, opts) {
2414
2721
  toolCount += 1;
2415
2722
  return { status: "success", approval: approvalEntry };
2416
2723
  }
2724
+ if (output && typeof output === "object" && output.__awaitingToolApproval) {
2725
+ if (toolStateRef?.ctx && typeof toolStateRef.ctx === "object") {
2726
+ state.ctx = toolStateRef.ctx;
2727
+ }
2728
+ const pendingApproval = pendingApprovals.find(
2729
+ (entry) => entry.toolCallId === output.toolCallId || entry.id === output.approvalId
2730
+ );
2731
+ if (!pendingApproval) {
2732
+ throw new Error(`Tool ${toolName} requested approval but no pending approval entry was recorded.`);
2733
+ }
2734
+ awaitingApproval = true;
2735
+ return {
2736
+ status: "awaiting_approval",
2737
+ approval: pendingApproval
2738
+ };
2739
+ }
2417
2740
  if (output && typeof output === "object" && output.__finalStructuredOutput) {
2418
2741
  if (!state.ctx) state.ctx = {};
2419
2742
  state.ctx.__structuredOutputParsed = output.data;
@@ -2836,7 +3159,7 @@ function resolveToolApprovalState(state, resolution) {
2836
3159
  };
2837
3160
  }
2838
3161
 
2839
- // src/agent.ts
3162
+ // src/utils/syntheticMessages.ts
2840
3163
  function isSyntheticSummaryMessage(message) {
2841
3164
  if (!message) return false;
2842
3165
  if (message.role === "tool" && message.name === "summarize_context") {
@@ -2850,13 +3173,155 @@ function isSyntheticSummaryMessage(message) {
2850
3173
  }
2851
3174
  return false;
2852
3175
  }
2853
- function getMessageText(message) {
2854
- if (typeof message?.content === "string") return message.content;
2855
- if (Array.isArray(message?.content)) {
2856
- return message.content.map((chunk) => typeof chunk === "string" ? chunk : chunk?.text ?? chunk?.content ?? "").join("");
3176
+
3177
+ // src/structuredOutput/manager.ts
3178
+ var StructuredOutputManager = class {
3179
+ constructor(schema, strategy, config) {
3180
+ this.schema = schema;
3181
+ this.strategy = strategy;
3182
+ this.maxRetries = config?.maxRetries ?? 2;
3183
+ }
3184
+ // --- Setup Phase ---
3185
+ /**
3186
+ * Get the `response` tool for tool-based strategy.
3187
+ * Returns undefined for native strategy (no tool needed).
3188
+ */
3189
+ getResponseTool() {
3190
+ if (this.strategy.kind !== "tool_based") return void 0;
3191
+ return this.strategy.createResponseTool(this.schema);
3192
+ }
3193
+ /**
3194
+ * Get response_format config for native strategy.
3195
+ * Returns undefined for tool-based strategy.
3196
+ */
3197
+ getResponseFormat() {
3198
+ if (this.strategy.kind !== "native") return void 0;
3199
+ return this.strategy.buildResponseFormat(this.schema);
3200
+ }
3201
+ /**
3202
+ * Build the system prompt hint that tells the model how to produce structured output.
3203
+ */
3204
+ buildSystemPromptHint() {
3205
+ if (this.strategy.kind === "native") {
3206
+ return [
3207
+ "A structured output schema is active.",
3208
+ "Your response MUST be valid JSON matching the output schema.",
3209
+ "Do NOT wrap the JSON in code fences or add any text before/after it."
3210
+ ].join("\n");
3211
+ }
3212
+ return [
3213
+ "A structured output schema is active.",
3214
+ "Do NOT output the final JSON directly as an assistant message.",
3215
+ "When completely finished, call tool `response` passing the final JSON matching the schema as its arguments (direct object).",
3216
+ "Call it exactly once then STOP producing further assistant messages."
3217
+ ].join("\n");
3218
+ }
3219
+ /**
3220
+ * Build a nudge message for when the model didn't produce structured output.
3221
+ * Used by the agent loop when it needs to retry.
3222
+ */
3223
+ buildNudgeMessage(isLastAttempt) {
3224
+ if (this.strategy.kind === "native") {
3225
+ return {
3226
+ role: "system",
3227
+ content: [
3228
+ "A structured output schema is active.",
3229
+ "You MUST respond with valid JSON matching the schema.",
3230
+ "Do NOT include any text outside the JSON object.",
3231
+ isLastAttempt ? "This is your FINAL attempt. Produce the JSON NOW." : ""
3232
+ ].filter(Boolean).join("\n")
3233
+ };
3234
+ }
3235
+ return {
3236
+ role: "system",
3237
+ content: [
3238
+ "A structured output schema is active.",
3239
+ "You MUST now call tool `response` with the final JSON object that matches the schema.",
3240
+ "Do not write the JSON in the assistant message.",
3241
+ "Call `response` exactly once, then stop.",
3242
+ isLastAttempt ? "Do NOT call any other tools. Produce the response NOW based on whatever information you have gathered so far." : ""
3243
+ ].filter(Boolean).join("\n")
3244
+ };
2857
3245
  }
2858
- return "";
2859
- }
3246
+ /**
3247
+ * Build a correction message when schema validation failed.
3248
+ */
3249
+ buildCorrectionMessage(error, _previousAttempt) {
3250
+ if (error.fieldErrors && error.fieldErrors.length > 0) {
3251
+ const fieldIssues = error.fieldErrors.map((f) => `- "${f.path}": ${f.message} (received: ${JSON.stringify(f.received)})`).join("\n");
3252
+ const action = this.strategy.kind === "native" ? "Please output corrected JSON matching the schema." : "Please call `response` again with corrected values matching the schema.";
3253
+ return {
3254
+ role: "system",
3255
+ content: [
3256
+ "Your previous structured output had validation errors:",
3257
+ fieldIssues,
3258
+ "",
3259
+ action
3260
+ ].join("\n")
3261
+ };
3262
+ }
3263
+ return this.buildNudgeMessage(false);
3264
+ }
3265
+ // --- Parse Phase ---
3266
+ /**
3267
+ * Try to parse structured output from the tool-based finalization result.
3268
+ * Called when __structuredOutputParsed is available in ctx.
3269
+ */
3270
+ parseFromToolResult(data) {
3271
+ return { success: true, data, attempts: 1 };
3272
+ }
3273
+ /**
3274
+ * Try to parse structured output from assistant message content.
3275
+ * For native strategy: content should be valid JSON.
3276
+ * For tool-based strategy: last-resort fallback extraction from text.
3277
+ */
3278
+ parseFromContent(content) {
3279
+ if (this.strategy.kind === "native") {
3280
+ const result2 = this.strategy.extractOutput(content, this.schema);
3281
+ if ("data" in result2) {
3282
+ return { success: true, data: result2.data, attempts: 1 };
3283
+ }
3284
+ return { success: false, error: result2.error, attempts: 1, rawContent: content };
3285
+ }
3286
+ const result = this.strategy.extractFromText(content, this.schema);
3287
+ if ("data" in result) {
3288
+ return { success: true, data: result.data, attempts: 1 };
3289
+ }
3290
+ return { success: false, error: result.error, attempts: 1, rawContent: content };
3291
+ }
3292
+ /**
3293
+ * Create a "no output" error result.
3294
+ */
3295
+ noOutputResult(attempts, rawContent) {
3296
+ return {
3297
+ success: false,
3298
+ error: {
3299
+ type: "no_output",
3300
+ message: "Model did not produce structured output after all retry attempts.",
3301
+ rawContent
3302
+ },
3303
+ attempts,
3304
+ rawContent
3305
+ };
3306
+ }
3307
+ /**
3308
+ * Create a "max retries" error result.
3309
+ */
3310
+ maxRetriesResult(lastError, attempts, rawContent) {
3311
+ return {
3312
+ success: false,
3313
+ error: {
3314
+ ...lastError,
3315
+ type: "max_retries",
3316
+ message: `Structured output failed after ${attempts} attempts: ${lastError.message}`
3317
+ },
3318
+ attempts,
3319
+ rawContent
3320
+ };
3321
+ }
3322
+ };
3323
+
3324
+ // src/agent.ts
2860
3325
  function getLastAssistantMessage(messages) {
2861
3326
  for (let index = messages.length - 1; index >= 0; index -= 1) {
2862
3327
  if (messages[index]?.role === "assistant") {
@@ -2887,14 +3352,13 @@ function createAgent(opts) {
2887
3352
  const resolver = createResolverNode();
2888
3353
  const agentCore = createAgentCoreNode(opts);
2889
3354
  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);
3355
+ const soManager = opts.outputSchema ? new StructuredOutputManager(opts.outputSchema, resolveStrategy(opts.model)) : void 0;
3356
+ if (soManager) {
3357
+ const modelCapabilities = getModelCapabilities(opts.model);
3358
+ const responseTool = soManager.getResponseTool();
3359
+ if (responseTool && modelCapabilities.structuredOutput !== "native") {
3360
+ toolsBase.push(responseTool);
3361
+ }
2898
3362
  }
2899
3363
  const toolsNode = createToolsNode(toolsBase, opts);
2900
3364
  const finalizeNode = createToolLimitFinalizeNode();
@@ -2929,6 +3393,7 @@ function createAgent(opts) {
2929
3393
  limits: opts.limits,
2930
3394
  useTodoList: void 0,
2931
3395
  outputSchema: opts.outputSchema,
3396
+ responseFormat: soManager?.getResponseFormat(),
2932
3397
  tracing: opts.tracing
2933
3398
  };
2934
3399
  const summarizationThreshold = getActiveSummarizationThreshold(opts);
@@ -3067,10 +3532,11 @@ function createAgent(opts) {
3067
3532
  });
3068
3533
  const tokenCount = countMessagesTokens(tokCountMessages);
3069
3534
  if (tokenCount > summarizationThreshold) {
3535
+ const summarizationExhausted = !!state.ctx?.__summarizationExhausted;
3070
3536
  const hasFreshSummary = (state.messages || []).some(
3071
3537
  (m) => m.role === "tool" && typeof m.content === "string" && (m.content === "SUMMARIZED" || m.content.startsWith("SUMMARIZED_TOOL_RESPONSE"))
3072
3538
  );
3073
- if (hasFreshSummary && tokenCount <= summarizationThreshold * 1.15) ; else {
3539
+ if (hasFreshSummary && tokenCount <= summarizationThreshold * 1.15) ; else if (summarizationExhausted) ; else {
3074
3540
  const ctx = { ...state.ctx || {}, __needsSummarization: true };
3075
3541
  state = { ...state, ctx };
3076
3542
  break;
@@ -3148,18 +3614,10 @@ function createAgent(opts) {
3148
3614
  continue;
3149
3615
  }
3150
3616
  if (toolCalls.length === 0) {
3151
- if (opts.outputSchema && !state.ctx?.__finalizedDueToStructuredOutput && !state.ctx?.__structuredOutputForceFinalize) {
3152
- const forceMsg = {
3153
- role: "system",
3154
- content: [
3155
- "A structured output schema is active.",
3156
- "You MUST now call tool `response` with the final JSON object that matches the schema.",
3157
- "Do not write the JSON in the assistant message.",
3158
- "Call `response` exactly once, then stop."
3159
- ].join("\n")
3160
- };
3617
+ if (soManager && !state.ctx?.__finalizedDueToStructuredOutput && !state.ctx?.__structuredOutputForceFinalize) {
3618
+ const nudge = soManager.buildNudgeMessage(false);
3161
3619
  const ctx = { ...state.ctx || {}, __structuredOutputForceFinalize: true };
3162
- state = { ...state, messages: [...state.messages, forceMsg], ctx };
3620
+ state = { ...state, messages: [...state.messages, nudge], ctx };
3163
3621
  continue;
3164
3622
  }
3165
3623
  break;
@@ -3172,23 +3630,16 @@ function createAgent(opts) {
3172
3630
  if (checkpointIfRequested("after_tools")) break;
3173
3631
  if (state.ctx?.__finalizedDueToStructuredOutput) break;
3174
3632
  }
3175
- if (opts.outputSchema && !state.ctx?.__finalizedDueToStructuredOutput) {
3176
- const last = state.messages[state.messages.length - 1];
3177
- const lastHasToolCalls = Array.isArray(last?.tool_calls) && last.tool_calls.length > 0;
3178
- if (!lastHasToolCalls) {
3179
- const alreadyForced = Boolean(state.ctx?.__structuredOutputForceFinalize);
3180
- if (alreadyForced) {
3181
- const finalNudge = {
3182
- role: "system",
3183
- content: [
3184
- "A structured output schema is active.",
3185
- "You MUST now call tool `response` with the final JSON object that matches the schema.",
3186
- "Do not write the JSON in the assistant message.",
3187
- "Call `response` exactly once, then stop.",
3188
- "Do NOT call any other tools. Produce the response NOW based on whatever information you have gathered so far."
3189
- ].join("\n")
3190
- };
3191
- state = { ...state, messages: [...state.messages, finalNudge] };
3633
+ if (soManager && !state.ctx?.__finalizedDueToStructuredOutput) {
3634
+ const maxPostLoopRetries = soManager.maxRetries;
3635
+ for (let postRetry = 0; postRetry < maxPostLoopRetries; postRetry++) {
3636
+ if (state.ctx?.__finalizedDueToStructuredOutput) break;
3637
+ const last = state.messages[state.messages.length - 1];
3638
+ const lastHasToolCalls = Array.isArray(last?.tool_calls) && last.tool_calls.length > 0;
3639
+ if (!lastHasToolCalls) {
3640
+ const isLastAttempt = postRetry === maxPostLoopRetries - 1;
3641
+ const nudge = soManager.buildNudgeMessage(isLastAttempt);
3642
+ state = { ...state, messages: [...state.messages, nudge] };
3192
3643
  }
3193
3644
  try {
3194
3645
  state = { ...state, ...await agentCore(state) };
@@ -3288,25 +3739,24 @@ function createAgent(opts) {
3288
3739
  status: "success"
3289
3740
  });
3290
3741
  const finalAssistantMsg = getLastAssistantMessage(res.messages);
3291
- const content = getMessageText(finalAssistantMsg);
3742
+ const content = extractMessageText2(finalAssistantMsg);
3292
3743
  let parsed = void 0;
3293
- const schema = opts.outputSchema;
3294
- if (schema && res.ctx?.__structuredOutputParsed) {
3295
- parsed = res.ctx.__structuredOutputParsed;
3296
- } else if (schema && content) {
3297
- let jsonText = null;
3298
- const fenced = content.match(/```(?:json)?\n([\s\S]*?)```/i);
3299
- if (fenced && fenced[1]) jsonText = fenced[1].trim();
3300
- else {
3301
- const braceIdx = content.indexOf("{");
3302
- const bracketIdx = content.indexOf("[");
3303
- const start = [braceIdx, bracketIdx].filter((i) => i >= 0).sort((a, b) => a - b)[0];
3304
- if (start !== void 0) jsonText = content.slice(start).trim();
3305
- }
3306
- try {
3307
- const raw = JSON.parse(jsonText ?? content);
3308
- parsed = schema.parse(raw);
3309
- } catch {
3744
+ let outputError = void 0;
3745
+ if (soManager) {
3746
+ if (res.ctx?.__structuredOutputParsed) {
3747
+ parsed = res.ctx.__structuredOutputParsed;
3748
+ } else if (content) {
3749
+ const fallbackResult = soManager.parseFromContent(content);
3750
+ if (fallbackResult.success) {
3751
+ parsed = fallbackResult.data;
3752
+ } else {
3753
+ outputError = fallbackResult.error;
3754
+ }
3755
+ } else {
3756
+ const noResult = soManager.noOutputResult(1);
3757
+ if (!noResult.success) {
3758
+ outputError = noResult.error;
3759
+ }
3310
3760
  }
3311
3761
  }
3312
3762
  emit({ type: "finalAnswer", content });
@@ -3317,6 +3767,7 @@ function createAgent(opts) {
3317
3767
  return {
3318
3768
  content,
3319
3769
  output: parsed,
3770
+ outputError,
3320
3771
  metadata: { usage: res.usage },
3321
3772
  messages: res.messages,
3322
3773
  state: res
@@ -3440,7 +3891,7 @@ function createAgentNode(opts) {
3440
3891
  ...messages,
3441
3892
  response
3442
3893
  ];
3443
- const rawUsage = response?.usage || response?.response_metadata?.token_usage || response?.response_metadata?.usage;
3894
+ 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;
3444
3895
  const normalized = normalizeUsage(rawUsage);
3445
3896
  const modelName = getModelName(runtime.model || opts.model) || "unknown_model";
3446
3897
  const providerName = getProviderName(runtime.model || opts.model);
@@ -3488,25 +3939,27 @@ function createAgentNode(opts) {
3488
3939
  }
3489
3940
 
3490
3941
  // src/smart/contextPolicy.ts
3491
- function messageText(message) {
3492
- if (typeof message.content === "string") return message.content;
3493
- if (Array.isArray(message.content)) {
3494
- return message.content.map((part) => typeof part === "string" ? part : part?.text ?? part?.content ?? JSON.stringify(part)).join(" ");
3495
- }
3496
- return "";
3497
- }
3498
3942
  function collectRecentTurns(messages, lastTurnsToKeep) {
3499
3943
  if (lastTurnsToKeep <= 0) return [];
3500
3944
  const systemPrefix = messages.filter((message, index) => index === 0 && message.role === "system");
3501
3945
  const body = systemPrefix.length > 0 ? messages.slice(1) : [...messages];
3502
- let userTurnCount = 0;
3946
+ const totalUserTurns = body.filter((m) => m.role === "user").length;
3947
+ const countAssistantTurns = totalUserTurns < lastTurnsToKeep;
3948
+ let turnCount = 0;
3503
3949
  const collected = [];
3504
3950
  for (let index = body.length - 1; index >= 0; index -= 1) {
3505
3951
  const message = body[index];
3506
3952
  collected.unshift(message);
3507
- if (message.role === "user") {
3508
- userTurnCount += 1;
3509
- if (userTurnCount >= lastTurnsToKeep) break;
3953
+ const isTurnBoundary = countAssistantTurns ? message.role === "assistant" : message.role === "user";
3954
+ if (isTurnBoundary) {
3955
+ turnCount += 1;
3956
+ if (turnCount >= lastTurnsToKeep) break;
3957
+ }
3958
+ }
3959
+ if (countAssistantTurns && collected.length > 0 && collected[0].role !== "user") {
3960
+ const firstUserMsg = body.find((m) => m.role === "user");
3961
+ if (firstUserMsg && !collected.includes(firstUserMsg)) {
3962
+ collected.unshift(firstUserMsg);
3510
3963
  }
3511
3964
  }
3512
3965
  return [...systemPrefix, ...collected];
@@ -3526,10 +3979,6 @@ function renderStructuredSummary(summary) {
3526
3979
  lines.push("Open questions:");
3527
3980
  lines.push(...summary.open_questions.map((question) => `- ${question}`));
3528
3981
  }
3529
- if (summary.discarded_obsolete.length > 0) {
3530
- lines.push("Discarded obsolete:");
3531
- lines.push(...summary.discarded_obsolete.map((item) => `- ${item}`));
3532
- }
3533
3982
  return lines.join("\n");
3534
3983
  }
3535
3984
  function renderMemoryBlock(facts) {
@@ -3542,11 +3991,48 @@ function renderMemoryBlock(facts) {
3542
3991
  function clampToBudget(messages, maxContextTokens) {
3543
3992
  let working = [...messages];
3544
3993
  while (working.length > 2) {
3545
- const tokenCount = countApproxTokens(working.map(messageText).join("\n"));
3994
+ const tokenCount = countApproxTokens(working.map(extractMessageText2).join("\n"));
3546
3995
  if (tokenCount <= maxContextTokens) return working;
3547
3996
  const firstNonSystem = working.findIndex((message, index) => !(index === 0 && message.role === "system"));
3548
3997
  if (firstNonSystem < 0) break;
3549
- working.splice(firstNonSystem, 1);
3998
+ const target = working[firstNonSystem];
3999
+ if (target.role === "assistant" && Array.isArray(target.tool_calls) && target.tool_calls.length > 0) {
4000
+ const toolCallIds = new Set(
4001
+ target.tool_calls.map((tc) => tc.id).filter(Boolean)
4002
+ );
4003
+ working = working.filter((m, idx) => {
4004
+ if (idx === firstNonSystem) return false;
4005
+ if (m.role === "tool" && m.tool_call_id && toolCallIds.has(m.tool_call_id)) return false;
4006
+ return true;
4007
+ });
4008
+ } else if (target.role === "tool") {
4009
+ const toolCallId = target.tool_call_id;
4010
+ let parentIdx = -1;
4011
+ if (toolCallId) {
4012
+ for (let i = firstNonSystem - 1; i >= 0; i--) {
4013
+ const m = working[i];
4014
+ if (m.role === "assistant" && Array.isArray(m.tool_calls) && m.tool_calls.some((tc) => tc.id === toolCallId)) {
4015
+ parentIdx = i;
4016
+ break;
4017
+ }
4018
+ }
4019
+ }
4020
+ if (parentIdx >= 0) {
4021
+ const parent = working[parentIdx];
4022
+ const parentToolCallIds = new Set(
4023
+ (parent.tool_calls || []).map((tc) => tc.id).filter(Boolean)
4024
+ );
4025
+ working = working.filter((m, idx) => {
4026
+ if (idx === parentIdx) return false;
4027
+ if (m.role === "tool" && m.tool_call_id && parentToolCallIds.has(m.tool_call_id)) return false;
4028
+ return true;
4029
+ });
4030
+ } else {
4031
+ working.splice(firstNonSystem, 1);
4032
+ }
4033
+ } else {
4034
+ working.splice(firstNonSystem, 1);
4035
+ }
3550
4036
  }
3551
4037
  return working;
3552
4038
  }
@@ -3579,18 +4065,6 @@ function buildModelMessages(state, config) {
3579
4065
  // src/nodes/contextSummarize.ts
3580
4066
  var systemMessage = (content) => ({ role: "system", content });
3581
4067
  var humanMessage = (content) => ({ role: "user", content });
3582
- function isSyntheticSummaryToolMessage(message) {
3583
- return message.role === "tool" && message.name === "summarize_context";
3584
- }
3585
- function isSyntheticSummaryAssistantMessage(message) {
3586
- if (message.role !== "assistant" || !Array.isArray(message.tool_calls)) {
3587
- return false;
3588
- }
3589
- return message.tool_calls.some((toolCall) => {
3590
- const toolName = toolCall?.function?.name || toolCall?.name;
3591
- return toolName === "summarize_context";
3592
- });
3593
- }
3594
4068
  function validateMessageSequence(messages) {
3595
4069
  const validated = [];
3596
4070
  const pendingToolCallIds = /* @__PURE__ */ new Set();
@@ -3815,7 +4289,7 @@ function createContextSummarizeNode(opts) {
3815
4289
  (m) => {
3816
4290
  if (m.role !== "tool") return false;
3817
4291
  if (isSummarizedToolPlaceholder(m.content)) return false;
3818
- if (isSyntheticSummaryToolMessage(m)) return false;
4292
+ if (isSyntheticSummaryMessage(m)) return false;
3819
4293
  const histEntry = findToolHistoryEntry(state, m.tool_call_id, m.name);
3820
4294
  if (histEntry?.retentionPolicy === "keep_full") return false;
3821
4295
  if (!histEntry?.retentionPolicy) {
@@ -3828,15 +4302,14 @@ function createContextSummarizeNode(opts) {
3828
4302
  if (compressableMessages.length === 0) {
3829
4303
  return {};
3830
4304
  }
3831
- const allTextBefore = messages.map((m) => typeof m.content === "string" ? m.content : Array.isArray(m.content) ? m.content.map((c) => typeof c === "string" ? c : c?.text ?? c?.content ?? "").join("") : "").join("\n");
3832
- const tokenCountBefore = countApproxTokens(allTextBefore);
4305
+ const tokenCountBefore = countMessagesTokens(messages);
3833
4306
  const summaryPromptMaxTokens = config.summaryPromptMaxTokens;
3834
4307
  const availableTokensForConversation = summaryPromptMaxTokens - 500;
3835
4308
  const messageTexts = [];
3836
4309
  let accumulatedTokens = 0;
3837
4310
  for (let i = messages.length - 1; i >= 0; i--) {
3838
4311
  const m = messages[i];
3839
- if (isSyntheticSummaryToolMessage(m) || isSyntheticSummaryAssistantMessage(m)) {
4312
+ if (isSyntheticSummaryMessage(m) || isSyntheticSummaryMessage(m)) {
3840
4313
  continue;
3841
4314
  }
3842
4315
  let content = "";
@@ -3857,13 +4330,13 @@ function createContextSummarizeNode(opts) {
3857
4330
  }
3858
4331
  let prefix = `${m.role.toUpperCase()}`;
3859
4332
  if (m.name) prefix += ` (${m.name})`;
3860
- const messageText2 = `${prefix}: ${content}`;
3861
- const messageTokens = countApproxTokens(messageText2);
4333
+ const messageText = `${prefix}: ${content}`;
4334
+ const messageTokens = countApproxTokens(messageText);
3862
4335
  if (accumulatedTokens + messageTokens > availableTokensForConversation) {
3863
4336
  messageTexts.unshift("[Earlier conversation history omitted for brevity]");
3864
4337
  break;
3865
4338
  }
3866
- messageTexts.unshift(messageText2);
4339
+ messageTexts.unshift(messageText);
3867
4340
  accumulatedTokens += messageTokens;
3868
4341
  }
3869
4342
  const conversationText = messageTexts.join("\n\n");
@@ -4005,7 +4478,7 @@ ${canonicalFacts.length > 0 ? canonicalFacts.map((fact) => `- ${fact.key}: ${fac
4005
4478
  const toolResponsesConfig = resolved.toolResponses;
4006
4479
  const newMessages = messages.map((m) => {
4007
4480
  if (m.role === "tool") {
4008
- if (isSyntheticSummaryToolMessage(m)) {
4481
+ if (isSyntheticSummaryMessage(m)) {
4009
4482
  return m;
4010
4483
  }
4011
4484
  const toolCallId = m.tool_call_id;
@@ -4063,8 +4536,7 @@ ${canonicalFacts.length > 0 ? canonicalFacts.map((fact) => `- ${fact.key}: ${fac
4063
4536
  const summaries = Array.isArray(state.summaries) ? [...state.summaries, summaryText] : [summaryText];
4064
4537
  const summaryRecords = Array.isArray(state.summaryRecords) ? [...state.summaryRecords, { ...structuredSummary || normalizeStructuredSummary(void 0, summaryText), integrity, createdAt: (/* @__PURE__ */ new Date()).toISOString() }] : [{ ...structuredSummary || normalizeStructuredSummary(void 0, summaryText), integrity, createdAt: (/* @__PURE__ */ new Date()).toISOString() }];
4065
4538
  const finalMessages = [...validatedMessages, assistantSummaryCall, toolSummaryResponse];
4066
- const allTextAfter = finalMessages.map((m) => typeof m.content === "string" ? m.content : Array.isArray(m.content) ? m.content.map((c) => typeof c === "string" ? c : c?.text ?? c?.content ?? "").join("") : "").join("\n");
4067
- const tokenCountAfter = countApproxTokens(allTextAfter);
4539
+ const tokenCountAfter = countMessagesTokens(finalMessages);
4068
4540
  const summarizationEvent = {
4069
4541
  type: "summarization",
4070
4542
  summary: summaryText,
@@ -4115,25 +4587,6 @@ ${canonicalFacts.length > 0 ? canonicalFacts.map((fact) => `- ${fact.key}: ${fac
4115
4587
  };
4116
4588
  };
4117
4589
  }
4118
-
4119
- // src/utils/tokenManager.ts
4120
- async function applyTokenLimits({
4121
- state,
4122
- limits
4123
- }) {
4124
- const messages = state.messages || [];
4125
- const budget = limits.contextTokenLimit;
4126
- let total = messages.reduce((acc, m) => acc + countApproxTokens(String(m.content || "")), 0);
4127
- if (total <= budget) return state;
4128
- const newMessages = [...messages];
4129
- while (total > budget && newMessages.length > 1) {
4130
- const idx = newMessages.findIndex((m) => m.role !== "system");
4131
- if (idx === -1) break;
4132
- const removed = newMessages.splice(idx, 1)[0];
4133
- total -= countApproxTokens(String(removed.content || ""));
4134
- }
4135
- return { ...state, messages: newMessages };
4136
- }
4137
4590
  var todoStatusSchema = z.enum(["not-started", "in-progress", "completed", "blocked"]);
4138
4591
  var todoWriteItemSchema = z.object({
4139
4592
  id: z.number().int().min(1).describe("Sequential id starting from 1"),
@@ -4196,6 +4649,12 @@ function calculateAdherenceScore(todoList) {
4196
4649
  const completedCount = todoList.filter((item) => item.status === "completed").length;
4197
4650
  return todoList.length === 0 ? 1 : Number((completedCount / todoList.length).toFixed(2));
4198
4651
  }
4652
+ function formatTodoListSummary(todoList) {
4653
+ if (!Array.isArray(todoList) || todoList.length === 0) {
4654
+ return "No plan items.";
4655
+ }
4656
+ return todoList.map((item) => `${item.id}. ${item.title || item.step || item.description || `Step ${item.id}`} [${item.status}]`).join("\n");
4657
+ }
4199
4658
  function createContextTools(stateRef, opts) {
4200
4659
  const tools = [];
4201
4660
  if (opts?.planningEnabled) {
@@ -4208,11 +4667,43 @@ function createContextTools(stateRef, opts) {
4208
4667
  todoList: z.array(z.union([todoWriteItemSchema, todoUpdateItemSchema])).optional()
4209
4668
  }),
4210
4669
  func: async ({ operation, expectedVersion, todoList }) => {
4211
- const onEvent = manageTodo._stateRef?.__onEvent;
4670
+ const toolStateRef = manageTodo._stateRef;
4671
+ const onEvent = toolStateRef?.__onEvent;
4672
+ const emitPlanEvent = (list) => {
4673
+ const version = stateRef.planVersion || 1;
4674
+ const adherenceScore = stateRef.adherenceScore || 0;
4675
+ const planData = {
4676
+ source: "manage_todo_list",
4677
+ operation,
4678
+ version,
4679
+ adherenceScore,
4680
+ count: Array.isArray(list) ? list.length : 0
4681
+ };
4682
+ onEvent?.({ type: "plan", todoList: list, ...planData });
4683
+ const traceSession = toolStateRef?.ctx?.__traceSession;
4684
+ recordTraceEvent(traceSession, {
4685
+ type: "plan",
4686
+ label: `Plan ${operation}`,
4687
+ actor: { scope: "agent", name: "manage_todo_list", role: "planner" },
4688
+ sections: [
4689
+ {
4690
+ kind: "summary",
4691
+ label: "Todo List",
4692
+ content: formatTodoListSummary(list)
4693
+ },
4694
+ {
4695
+ kind: "metadata",
4696
+ label: "Plan Metadata",
4697
+ data: planData
4698
+ }
4699
+ ],
4700
+ debug: planData
4701
+ });
4702
+ };
4212
4703
  const currentVersion = stateRef.planVersion || 0;
4213
4704
  if (operation === "read") {
4214
4705
  const list = stateRef.todoList || [];
4215
- onEvent?.({ type: "plan", source: "manage_todo_list", operation: "read", todoList: list, version: stateRef.planVersion || 1, adherenceScore: stateRef.adherenceScore || 0 });
4706
+ emitPlanEvent(list);
4216
4707
  return list;
4217
4708
  }
4218
4709
  if (typeof expectedVersion === "number" && expectedVersion !== currentVersion) {
@@ -4277,9 +4768,10 @@ function createContextTools(stateRef, opts) {
4277
4768
  adherenceScore: stateRef.adherenceScore || 0
4278
4769
  };
4279
4770
  }
4280
- const patchIds = /* @__PURE__ */ new Set();
4771
+ const currentMap = new Map(currentList.map((item) => [item.id, item]));
4772
+ const patchMap = /* @__PURE__ */ new Map();
4281
4773
  for (const item of parsed.data) {
4282
- if (patchIds.has(item.id)) {
4774
+ if (patchMap.has(item.id)) {
4283
4775
  return {
4284
4776
  status: "error",
4285
4777
  operation,
@@ -4288,10 +4780,7 @@ function createContextTools(stateRef, opts) {
4288
4780
  adherenceScore: stateRef.adherenceScore || 0
4289
4781
  };
4290
4782
  }
4291
- patchIds.add(item.id);
4292
- }
4293
- const currentMap = new Map(currentList.map((item) => [item.id, item]));
4294
- for (const item of parsed.data) {
4783
+ patchMap.set(item.id, item);
4295
4784
  if (!currentMap.has(item.id)) {
4296
4785
  return {
4297
4786
  status: "error",
@@ -4303,7 +4792,7 @@ function createContextTools(stateRef, opts) {
4303
4792
  }
4304
4793
  }
4305
4794
  const mergedList = currentList.map((item) => {
4306
- const patch = parsed.data.find((candidate) => candidate.id === item.id);
4795
+ const patch = patchMap.get(item.id);
4307
4796
  return patch ? normalizeTodoItem(patch, item) : item;
4308
4797
  });
4309
4798
  const validation = validatePlanInvariants(mergedList, false);
@@ -4327,7 +4816,7 @@ function createContextTools(stateRef, opts) {
4327
4816
  version: stateRef.planVersion || 1,
4328
4817
  adherenceScore: stateRef.adherenceScore || 0
4329
4818
  };
4330
- onEvent?.({ type: "plan", source: "manage_todo_list", operation, todoList: stateRef.todoList, version: stateRef.planVersion || 1, adherenceScore: stateRef.adherenceScore || 0 });
4819
+ emitPlanEvent(stateRef.todoList || []);
4331
4820
  return payload;
4332
4821
  }
4333
4822
  });
@@ -4352,40 +4841,10 @@ function createContextTools(stateRef, opts) {
4352
4841
  });
4353
4842
  getTool._stateRef = stateRef;
4354
4843
  tools.push(getTool);
4355
- if (opts?.outputSchema) {
4356
- const responseTool = createTool({
4357
- name: "response",
4358
- description: "Finalize the answer by returning the final structured JSON matching the required schema. Call exactly once when you are fully done, then stop.",
4359
- schema: opts.outputSchema.shape ? opts.outputSchema : z.any(),
4360
- func: async (data) => {
4361
- try {
4362
- const validated = opts.outputSchema.parse ? opts.outputSchema.parse(data) : data;
4363
- return { __finalStructuredOutput: true, data: validated };
4364
- } catch (e) {
4365
- return { error: "Schema validation failed", details: e?.message };
4366
- }
4367
- }
4368
- });
4369
- responseTool._stateRef = stateRef;
4370
- tools.push(responseTool);
4371
- }
4372
4844
  return tools;
4373
4845
  }
4374
4846
 
4375
4847
  // src/graph/decisions.ts
4376
- function isSyntheticSummaryMessage2(message) {
4377
- if (!message) return false;
4378
- if (message.role === "tool" && message.name === "summarize_context") {
4379
- return true;
4380
- }
4381
- if (message.role === "assistant" && Array.isArray(message.tool_calls)) {
4382
- return message.tool_calls.some((toolCall) => {
4383
- const toolName = toolCall?.function?.name || toolCall?.name;
4384
- return toolName === "summarize_context";
4385
- });
4386
- }
4387
- return false;
4388
- }
4389
4848
  function needsSummarization(state, opts, summarizationEnabled) {
4390
4849
  if (!summarizationEnabled) {
4391
4850
  return false;
@@ -4399,30 +4858,9 @@ function needsSummarization(state, opts, summarizationEnabled) {
4399
4858
  }
4400
4859
  const maxTok = resolved.summarization.summaryTriggerTokens;
4401
4860
  try {
4402
- const allText = (state.messages || []).filter((message) => !isSyntheticSummaryMessage2(message)).map((m) => {
4403
- if (typeof m.content === "string") return m.content;
4404
- if (Array.isArray(m.content)) {
4405
- return m.content.map((c) => typeof c === "string" ? c : c?.text ?? c?.content ?? JSON.stringify(c)).join("");
4406
- }
4407
- if (m.content && typeof m.content === "object") {
4408
- return JSON.stringify(m.content);
4409
- }
4410
- return "";
4411
- }).join("\n");
4412
- const tokenCount = countApproxTokens(allText);
4413
- const needsSum = tokenCount > maxTok;
4414
- if (process.env.DEBUG_SUMMARIZATION) {
4415
- const msgCount = (state.messages || []).length;
4416
- const toolMsgs = (state.messages || []).filter((m) => m.role === "tool");
4417
- const toolDetails = toolMsgs.map((m, i) => {
4418
- const contentStr = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
4419
- return `[${i}] id:${m.tool_call_id?.slice(-8) || "none"} len:${contentStr.length} preview:"${contentStr.slice(0, 30)}..."`;
4420
- });
4421
- console.log(`[Summarization] Messages: ${msgCount}, Tool messages: ${toolMsgs.length}`);
4422
- toolDetails.forEach((d) => console.log(` ${d}`));
4423
- console.log(`[Summarization] Token count: ${tokenCount}, Max: ${maxTok}, Needs summarization: ${needsSum}`);
4424
- }
4425
- return needsSum;
4861
+ const filtered = (state.messages || []).filter((message) => !isSyntheticSummaryMessage(message));
4862
+ const tokenCount = countMessagesTokens(filtered);
4863
+ return tokenCount > maxTok;
4426
4864
  } catch {
4427
4865
  return false;
4428
4866
  }
@@ -4551,13 +4989,6 @@ async function writeSummaryFactsToMemory(store, config, summary, sourceTurn) {
4551
4989
  }
4552
4990
  return store.get(config.memory.scope, { limit: config.context.lastTurnsToKeep + summary.stable_facts.length });
4553
4991
  }
4554
- function renderMemoryFacts(facts) {
4555
- if (facts.length === 0) return "";
4556
- return [
4557
- "Memory facts:",
4558
- ...facts.map((fact) => `- ${fact.key}: ${fact.value} (confidence ${fact.confidence.toFixed(2)})`)
4559
- ].join("\n");
4560
- }
4561
4992
 
4562
4993
  // src/smart/index.ts
4563
4994
  function createSmartAgent(opts) {
@@ -4577,7 +5008,7 @@ function createSmartAgent(opts) {
4577
5008
  useTodoList: planningEnabled
4578
5009
  };
4579
5010
  const stateRef = { toolHistory: void 0, toolHistoryArchived: void 0, todoList: void 0, planVersion: 0, adherenceScore: 0 };
4580
- const contextTools = createContextTools(stateRef, { planningEnabled, outputSchema: void 0 });
5011
+ const contextTools = createContextTools(stateRef, { planningEnabled });
4581
5012
  const mergedTools = [...opts.tools ?? [], ...contextTools];
4582
5013
  const base = createAgent({ ...runtimeOpts, tools: mergedTools });
4583
5014
  base.__runtime.runtimeProfile = resolved.runtimeProfile;
@@ -4586,12 +5017,8 @@ function createSmartAgent(opts) {
4586
5017
  const summarizer = summarizationEnabled ? createContextSummarizeNode(runtimeOpts) : void 0;
4587
5018
  const decideBefore = resolverDecisionFactory(runtimeOpts, summarizationEnabled);
4588
5019
  const decideAfter = toolsDecisionFactory(runtimeOpts, summarizationEnabled);
4589
- const structuredOutputHint = opts.outputSchema ? [
4590
- "A structured output schema is active.",
4591
- "Do NOT output the final JSON directly as an assistant message.",
4592
- "When completely finished, call tool `response` passing the final JSON matching the schema as its arguments (direct object).",
4593
- "Call it exactly once then STOP producing further assistant messages."
4594
- ].join("\n") : "";
5020
+ const soManager = opts.outputSchema ? new StructuredOutputManager(opts.outputSchema, resolveStrategy(opts.model)) : void 0;
5021
+ const structuredOutputHint = soManager ? soManager.buildSystemPromptHint() : "";
4595
5022
  const runtimeHint = [
4596
5023
  resolved.runtimeProfile === resolved.baseProfile ? `Runtime profile: ${resolved.runtimeProfile}.` : `Runtime profile: ${resolved.runtimeProfile} (base: ${resolved.baseProfile}).`,
4597
5024
  `Context policy: ${resolved.context.policy}.`,
@@ -4601,8 +5028,7 @@ function createSmartAgent(opts) {
4601
5028
  ].join("\n");
4602
5029
  function latestUserPrompt(messages) {
4603
5030
  const latest = [...messages].reverse().find((message) => message.role === "user");
4604
- if (!latest) return "";
4605
- return typeof latest.content === "string" ? latest.content : Array.isArray(latest.content) ? latest.content.map((part) => typeof part === "string" ? part : part?.text ?? part?.content ?? "").join(" ") : "";
5031
+ return latest ? extractMessageText2(latest) : "";
4606
5032
  }
4607
5033
  async function syncMemory(state) {
4608
5034
  const query = latestUserPrompt(state.messages || []);
@@ -4642,6 +5068,18 @@ function createSmartAgent(opts) {
4642
5068
  );
4643
5069
  return { role: "system", content: sys };
4644
5070
  }
5071
+ async function trySummarize(currentState, currentRawMessages) {
5072
+ if (!summarizer) return { state: currentState, rawMessages: currentRawMessages, compressed: false };
5073
+ const delta = await summarizer(currentState);
5074
+ if (!delta || Object.keys(delta).length === 0) {
5075
+ const ctx2 = { ...currentState.ctx || {}, __summarizationExhausted: true };
5076
+ return { state: { ...currentState, ctx: ctx2 }, rawMessages: currentRawMessages, compressed: false };
5077
+ }
5078
+ const ctx = { ...currentState.ctx || {} };
5079
+ delete ctx.__summarizationExhausted;
5080
+ const updated = await persistLatestSummary(syncPlanState({ ...currentState, ...delta, ctx }));
5081
+ return { state: updated, rawMessages: [...updated.messages || currentRawMessages], compressed: true };
5082
+ }
4645
5083
  const instance = {
4646
5084
  invoke: async (input, config) => {
4647
5085
  stateRef.toolHistory = input.toolHistory;
@@ -4660,12 +5098,10 @@ function createSmartAgent(opts) {
4660
5098
  state = { ...state, messages: rawMessages };
4661
5099
  const next = summarizationEnabled ? decideBefore(state) : "agent";
4662
5100
  if (next === "contextSummarize" && summarizer) {
4663
- const delta = await summarizer(state);
4664
- if (!delta || Object.keys(delta).length === 0) ; else {
4665
- state = await persistLatestSummary(syncPlanState({ ...state, ...delta }));
4666
- rawMessages = [...state.messages || rawMessages];
4667
- continue;
4668
- }
5101
+ const result = await trySummarize(state, rawMessages);
5102
+ state = result.state;
5103
+ rawMessages = result.rawMessages;
5104
+ if (result.compressed) continue;
4669
5105
  }
4670
5106
  const modelMessages = buildModelMessages(state, resolved);
4671
5107
  const res = await base.invoke({ ...state, messages: modelMessages }, config);
@@ -4693,23 +5129,20 @@ function createSmartAgent(opts) {
4693
5129
  const ctx = { ...state.ctx || {} };
4694
5130
  delete ctx.__needsSummarization;
4695
5131
  state = { ...state, ctx };
4696
- const delta = await summarizer(state);
4697
- if (delta && Object.keys(delta).length > 0) {
4698
- state = await persistLatestSummary(syncPlanState({ ...state, ...delta }));
4699
- rawMessages = [...state.messages || rawMessages];
4700
- continue;
4701
- }
5132
+ const result = await trySummarize(state, rawMessages);
5133
+ state = result.state;
5134
+ rawMessages = result.rawMessages;
5135
+ continue;
4702
5136
  }
4703
5137
  if (state.ctx?.__finalizedDueToStructuredOutput) break;
5138
+ if (opts.outputSchema && lastResult?.output != null) break;
4704
5139
  if (summarizationEnabled) {
4705
5140
  const after = decideAfter(state);
4706
5141
  if (after === "contextSummarize" && summarizer) {
4707
- const delta = await summarizer(state);
4708
- if (delta && Object.keys(delta).length > 0) {
4709
- state = await persistLatestSummary(syncPlanState({ ...state, ...delta }));
4710
- rawMessages = [...state.messages || rawMessages];
4711
- continue;
4712
- }
5142
+ const result = await trySummarize(state, rawMessages);
5143
+ state = result.state;
5144
+ rawMessages = result.rawMessages;
5145
+ if (result.compressed) continue;
4713
5146
  }
4714
5147
  }
4715
5148
  break;
@@ -4873,35 +5306,6 @@ async function runSmartAgentEvalHarness(params) {
4873
5306
  }
4874
5307
  return results;
4875
5308
  }
4876
-
4877
- // src/utils/content.ts
4878
- function contentToString(content) {
4879
- try {
4880
- if (content == null) return "";
4881
- if (typeof content === "string") return content;
4882
- if (Array.isArray(content)) {
4883
- return content.map((p) => {
4884
- if (!p || typeof p !== "object") return String(p);
4885
- if (p.type === "text") return String(p.text ?? "");
4886
- if (p.type === "image_url") {
4887
- const img = p.image_url;
4888
- const url = typeof img === "string" ? img : img?.url;
4889
- const detail = typeof img === "string" ? void 0 : img?.detail;
4890
- return `[image:${url || "unknown"}${detail ? ` detail=${detail}` : ""}]`;
4891
- }
4892
- return JSON.stringify(p);
4893
- }).join("\n");
4894
- }
4895
- return JSON.stringify(content);
4896
- } catch {
4897
- return String(content);
4898
- }
4899
- }
4900
- function mergeContentsToString(contents) {
4901
- return contents.map((c) => contentToString(c)).join("\n");
4902
- }
4903
-
4904
- // src/guardrails/checks.ts
4905
5309
  var Ajv = AjvModule?.default ?? AjvModule;
4906
5310
  var defaultAjv = new Ajv({ allErrors: true });
4907
5311
  defaultAjv.opts.strict = false;
@@ -5151,6 +5555,1740 @@ function createCodeGuardrail(options) {
5151
5555
  });
5152
5556
  }
5153
5557
 
5154
- 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 };
5558
+ // src/providers/types.ts
5559
+ function emptyUsage() {
5560
+ return {
5561
+ inputTokens: 0,
5562
+ outputTokens: 0,
5563
+ totalTokens: 0,
5564
+ cachedInputTokens: 0,
5565
+ cachedWriteTokens: 0,
5566
+ cachedOutputTokens: 0,
5567
+ reasoningTokens: 0
5568
+ };
5569
+ }
5570
+ var ProviderError = class extends Error {
5571
+ constructor(message, provider, statusCode, responseBody) {
5572
+ super(message);
5573
+ this.provider = provider;
5574
+ this.statusCode = statusCode;
5575
+ this.responseBody = responseBody;
5576
+ this.name = "ProviderError";
5577
+ }
5578
+ };
5579
+
5580
+ // src/providers/base.ts
5581
+ var BaseProvider = class {
5582
+ };
5583
+
5584
+ // src/providers/utils/sse.ts
5585
+ async function* parseSSEStream(body, signal) {
5586
+ const reader = body.getReader();
5587
+ const decoder = new TextDecoder();
5588
+ let buffer = "";
5589
+ try {
5590
+ while (true) {
5591
+ if (signal?.aborted) ;
5592
+ const { done, value } = await reader.read();
5593
+ if (done) break;
5594
+ buffer += decoder.decode(value, { stream: true });
5595
+ const parts = buffer.split(/\r?\n\r?\n/);
5596
+ buffer = parts.pop() ?? "";
5597
+ for (const part of parts) {
5598
+ if (!part.trim()) continue;
5599
+ const event = parseSSEBlock(part);
5600
+ if (event) yield event;
5601
+ }
5602
+ }
5603
+ if (buffer.trim()) {
5604
+ const event = parseSSEBlock(buffer);
5605
+ if (event) yield event;
5606
+ }
5607
+ } finally {
5608
+ reader.releaseLock();
5609
+ }
5610
+ }
5611
+ function parseSSEBlock(block) {
5612
+ let event;
5613
+ let id;
5614
+ let retry;
5615
+ const dataLines = [];
5616
+ for (const line of block.split(/\r?\n/)) {
5617
+ if (line.startsWith(":")) continue;
5618
+ const colonIdx = line.indexOf(":");
5619
+ let field;
5620
+ let value;
5621
+ if (colonIdx === -1) {
5622
+ field = line;
5623
+ value = "";
5624
+ } else {
5625
+ field = line.slice(0, colonIdx);
5626
+ value = line.slice(colonIdx + 1);
5627
+ if (value.startsWith(" ")) value = value.slice(1);
5628
+ }
5629
+ switch (field) {
5630
+ case "event":
5631
+ event = value;
5632
+ break;
5633
+ case "data":
5634
+ dataLines.push(value);
5635
+ break;
5636
+ case "id":
5637
+ id = value;
5638
+ break;
5639
+ case "retry": {
5640
+ const n = parseInt(value, 10);
5641
+ if (!isNaN(n)) retry = n;
5642
+ break;
5643
+ }
5644
+ }
5645
+ }
5646
+ if (dataLines.length === 0) return null;
5647
+ return { event, data: dataLines.join("\n"), id, retry };
5648
+ }
5649
+
5650
+ // src/providers/openai.ts
5651
+ var OpenAIProvider = class extends BaseProvider {
5652
+ constructor(config) {
5653
+ super();
5654
+ this.providerName = "openai";
5655
+ this.apiKey = config.apiKey;
5656
+ this.baseURL = (config.baseURL ?? "https://api.openai.com/v1").replace(/\/$/, "");
5657
+ this.defaultModel = config.defaultModel ?? "gpt-4o";
5658
+ this.defaultHeaders = {
5659
+ ...config.defaultHeaders,
5660
+ ...config.organization ? { "OpenAI-Organization": config.organization } : {}
5661
+ };
5662
+ }
5663
+ async complete(request) {
5664
+ const body = this.buildRequestBody(request, false);
5665
+ const res = await this.doFetch(body);
5666
+ const json = await res.json();
5667
+ return this.parseResponse(json);
5668
+ }
5669
+ async *completeStream(request) {
5670
+ const body = this.buildRequestBody(request, true);
5671
+ const res = await this.doFetch(body);
5672
+ if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
5673
+ let id = "";
5674
+ let model = "";
5675
+ const toolCallBuffers = /* @__PURE__ */ new Map();
5676
+ let usage;
5677
+ for await (const event of parseSSEStream(res.body)) {
5678
+ if (event.data === "[DONE]") break;
5679
+ let chunk;
5680
+ try {
5681
+ chunk = JSON.parse(event.data);
5682
+ } catch {
5683
+ continue;
5684
+ }
5685
+ id = chunk.id ?? id;
5686
+ model = chunk.model ?? model;
5687
+ const choice = chunk.choices?.[0];
5688
+ if (chunk.usage) {
5689
+ usage = this.extractUsage(chunk.usage);
5690
+ }
5691
+ if (!choice) {
5692
+ if (usage) yield { id, model, delta: {}, usage };
5693
+ continue;
5694
+ }
5695
+ const delta = choice.delta ?? {};
5696
+ const out = {
5697
+ id,
5698
+ model,
5699
+ delta: {},
5700
+ finishReason: mapFinishReason(choice.finish_reason)
5701
+ };
5702
+ if (delta.content) {
5703
+ out.delta.content = delta.content;
5704
+ }
5705
+ if (delta.tool_calls) {
5706
+ const partials = [];
5707
+ for (const tc of delta.tool_calls) {
5708
+ const idx = tc.index ?? 0;
5709
+ if (!toolCallBuffers.has(idx)) {
5710
+ toolCallBuffers.set(idx, { id: tc.id ?? "", name: "", arguments: "" });
5711
+ }
5712
+ const buf = toolCallBuffers.get(idx);
5713
+ if (tc.id) buf.id = tc.id;
5714
+ if (tc.function?.name) buf.name = tc.function.name;
5715
+ if (tc.function?.arguments) buf.arguments += tc.function.arguments;
5716
+ partials.push({ id: buf.id, name: buf.name || void 0, arguments: tc.function?.arguments });
5717
+ }
5718
+ out.delta.toolCalls = partials;
5719
+ }
5720
+ if (usage) out.usage = usage;
5721
+ yield out;
5722
+ }
5723
+ }
5724
+ // ─── Internals ───────────────────────────────────────────────────────────
5725
+ buildRequestBody(request, stream) {
5726
+ const body = {
5727
+ model: request.model || this.defaultModel,
5728
+ messages: request.messages.map((m) => this.toOpenAIMessage(m)),
5729
+ stream
5730
+ };
5731
+ if (stream) {
5732
+ body.stream_options = { include_usage: true };
5733
+ }
5734
+ if (request.temperature != null) body.temperature = request.temperature;
5735
+ if (request.maxTokens != null) body.max_tokens = request.maxTokens;
5736
+ if (request.topP != null) body.top_p = request.topP;
5737
+ if (request.stop) body.stop = request.stop;
5738
+ if (request.tools?.length) {
5739
+ body.tools = request.tools.map((t) => this.toOpenAITool(t));
5740
+ }
5741
+ if (request.toolChoice != null) {
5742
+ if (typeof request.toolChoice === "string") {
5743
+ body.tool_choice = request.toolChoice;
5744
+ } else {
5745
+ body.tool_choice = { type: "function", function: { name: request.toolChoice.name } };
5746
+ }
5747
+ }
5748
+ if (request.responseFormat) {
5749
+ if (request.responseFormat.type === "json_schema") {
5750
+ body.response_format = {
5751
+ type: "json_schema",
5752
+ json_schema: {
5753
+ name: request.responseFormat.name ?? "response",
5754
+ schema: request.responseFormat.schema,
5755
+ strict: true
5756
+ }
5757
+ };
5758
+ } else if (request.responseFormat.type === "json_object") {
5759
+ body.response_format = { type: "json_object" };
5760
+ }
5761
+ }
5762
+ if (request.extra) {
5763
+ Object.assign(body, request.extra);
5764
+ }
5765
+ return body;
5766
+ }
5767
+ toOpenAIMessage(m) {
5768
+ const msg = { role: m.role };
5769
+ if (typeof m.content === "string") {
5770
+ msg.content = m.content;
5771
+ } else if (Array.isArray(m.content)) {
5772
+ msg.content = m.content.map((part) => {
5773
+ if (part.type === "text") return { type: "text", text: part.text };
5774
+ if (part.type === "image") {
5775
+ const src = part.source;
5776
+ const url = src.type === "url" ? src.url : `data:${src.mediaType};base64,${src.data}`;
5777
+ return { type: "image_url", image_url: { url } };
5778
+ }
5779
+ return part;
5780
+ });
5781
+ }
5782
+ if (m.role === "assistant" && m.toolCalls?.length) {
5783
+ msg.tool_calls = m.toolCalls.map((tc) => ({
5784
+ id: tc.id,
5785
+ type: "function",
5786
+ function: { name: tc.name, arguments: tc.arguments }
5787
+ }));
5788
+ }
5789
+ if (m.role === "tool" && m.toolCallId) {
5790
+ msg.tool_call_id = m.toolCallId;
5791
+ }
5792
+ if (m.name) msg.name = m.name;
5793
+ return msg;
5794
+ }
5795
+ toOpenAITool(t) {
5796
+ const fn = {
5797
+ name: t.name,
5798
+ description: t.description,
5799
+ parameters: t.parameters
5800
+ };
5801
+ if (t.strict) {
5802
+ fn.strict = true;
5803
+ }
5804
+ return { type: "function", function: fn };
5805
+ }
5806
+ async doFetch(body) {
5807
+ const url = `${this.baseURL}/chat/completions`;
5808
+ const res = await fetch(url, {
5809
+ method: "POST",
5810
+ headers: {
5811
+ "Content-Type": "application/json",
5812
+ Authorization: `Bearer ${this.apiKey}`,
5813
+ ...this.defaultHeaders
5814
+ },
5815
+ body: JSON.stringify(body)
5816
+ });
5817
+ if (!res.ok) {
5818
+ const text = await res.text().catch(() => "");
5819
+ throw new ProviderError(
5820
+ `OpenAI API error ${res.status}: ${text}`,
5821
+ this.providerName,
5822
+ res.status,
5823
+ text
5824
+ );
5825
+ }
5826
+ return res;
5827
+ }
5828
+ parseResponse(json) {
5829
+ const choice = json.choices?.[0];
5830
+ const message = choice?.message ?? {};
5831
+ const toolCalls = (message.tool_calls ?? []).map((tc) => ({
5832
+ id: tc.id,
5833
+ name: tc.function?.name ?? "",
5834
+ arguments: tc.function?.arguments ?? "{}"
5835
+ }));
5836
+ return {
5837
+ id: json.id ?? "",
5838
+ model: json.model ?? "",
5839
+ content: message.content ?? null,
5840
+ toolCalls,
5841
+ usage: this.extractUsage(json.usage),
5842
+ finishReason: mapFinishReason(choice?.finish_reason),
5843
+ raw: json
5844
+ };
5845
+ }
5846
+ extractUsage(usage) {
5847
+ if (!usage) return emptyUsage();
5848
+ const promptDetails = usage.prompt_tokens_details ?? {};
5849
+ const completionDetails = usage.completion_tokens_details ?? {};
5850
+ return {
5851
+ inputTokens: usage.prompt_tokens ?? 0,
5852
+ outputTokens: usage.completion_tokens ?? 0,
5853
+ totalTokens: usage.total_tokens ?? (usage.prompt_tokens ?? 0) + (usage.completion_tokens ?? 0),
5854
+ cachedInputTokens: promptDetails.cached_tokens ?? 0,
5855
+ cachedWriteTokens: 0,
5856
+ cachedOutputTokens: 0,
5857
+ reasoningTokens: completionDetails.reasoning_tokens ?? 0
5858
+ };
5859
+ }
5860
+ };
5861
+ function mapFinishReason(reason) {
5862
+ switch (reason) {
5863
+ case "stop":
5864
+ return "stop";
5865
+ case "tool_calls":
5866
+ return "tool_calls";
5867
+ case "length":
5868
+ return "length";
5869
+ case "content_filter":
5870
+ return "content_filter";
5871
+ default:
5872
+ return "stop";
5873
+ }
5874
+ }
5875
+
5876
+ // src/providers/anthropic.ts
5877
+ var AnthropicProvider = class extends BaseProvider {
5878
+ constructor(config) {
5879
+ super();
5880
+ this.providerName = "anthropic";
5881
+ this.apiKey = config.apiKey;
5882
+ this.baseURL = (config.baseURL ?? "https://api.anthropic.com").replace(/\/$/, "");
5883
+ this.defaultModel = config.defaultModel ?? "claude-sonnet-4-20250514";
5884
+ this.defaultHeaders = config.defaultHeaders ?? {};
5885
+ this.anthropicVersion = config.anthropicVersion ?? "2023-06-01";
5886
+ }
5887
+ async complete(request) {
5888
+ const body = this.buildRequestBody(request, false);
5889
+ const res = await this.doFetch(body);
5890
+ const json = await res.json();
5891
+ return this.parseResponse(json);
5892
+ }
5893
+ async *completeStream(request) {
5894
+ const body = this.buildRequestBody(request, true);
5895
+ const res = await this.doFetch(body);
5896
+ if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
5897
+ let id = "";
5898
+ let model = "";
5899
+ let inputTokens = 0;
5900
+ let outputTokens = 0;
5901
+ let cacheReadInputTokens = 0;
5902
+ let cacheCreationInputTokens = 0;
5903
+ const currentToolCalls = /* @__PURE__ */ new Map();
5904
+ for await (const event of parseSSEStream(res.body)) {
5905
+ let data;
5906
+ try {
5907
+ data = JSON.parse(event.data);
5908
+ } catch {
5909
+ continue;
5910
+ }
5911
+ const eventType = event.event ?? data.type;
5912
+ switch (eventType) {
5913
+ case "message_start": {
5914
+ const msg = data.message;
5915
+ id = msg?.id ?? "";
5916
+ model = msg?.model ?? "";
5917
+ if (msg?.usage) {
5918
+ inputTokens = msg.usage.input_tokens ?? 0;
5919
+ cacheReadInputTokens = msg.usage.cache_read_input_tokens ?? 0;
5920
+ cacheCreationInputTokens = msg.usage.cache_creation_input_tokens ?? 0;
5921
+ }
5922
+ break;
5923
+ }
5924
+ case "content_block_start": {
5925
+ const idx = data.index ?? 0;
5926
+ const block = data.content_block;
5927
+ if (block?.type === "tool_use") {
5928
+ currentToolCalls.set(idx, {
5929
+ id: block.id ?? "",
5930
+ name: block.name ?? "",
5931
+ arguments: ""
5932
+ });
5933
+ }
5934
+ break;
5935
+ }
5936
+ case "content_block_delta": {
5937
+ const idx = data.index ?? 0;
5938
+ const delta = data.delta;
5939
+ if (delta?.type === "text_delta" && delta.text) {
5940
+ yield {
5941
+ id,
5942
+ model,
5943
+ delta: { content: delta.text }
5944
+ };
5945
+ } else if (delta?.type === "input_json_delta" && delta.partial_json) {
5946
+ const tc = currentToolCalls.get(idx);
5947
+ if (tc) {
5948
+ tc.arguments += delta.partial_json;
5949
+ yield {
5950
+ id,
5951
+ model,
5952
+ delta: {
5953
+ toolCalls: [{ id: tc.id, name: tc.name, arguments: delta.partial_json }]
5954
+ }
5955
+ };
5956
+ }
5957
+ }
5958
+ break;
5959
+ }
5960
+ case "content_block_stop":
5961
+ break;
5962
+ case "message_delta": {
5963
+ const delta = data.delta;
5964
+ if (data.usage) {
5965
+ outputTokens = data.usage.output_tokens ?? outputTokens;
5966
+ }
5967
+ const finishReason = mapAnthropicStopReason(delta?.stop_reason);
5968
+ const usage = {
5969
+ inputTokens,
5970
+ outputTokens,
5971
+ totalTokens: inputTokens + outputTokens,
5972
+ cachedInputTokens: cacheReadInputTokens,
5973
+ cachedWriteTokens: cacheCreationInputTokens,
5974
+ cachedOutputTokens: 0,
5975
+ reasoningTokens: 0
5976
+ };
5977
+ yield {
5978
+ id,
5979
+ model,
5980
+ delta: {},
5981
+ finishReason,
5982
+ usage
5983
+ };
5984
+ break;
5985
+ }
5986
+ case "message_stop":
5987
+ break;
5988
+ case "error": {
5989
+ const errMsg = data.error?.message ?? "Unknown Anthropic streaming error";
5990
+ throw new ProviderError(errMsg, this.providerName);
5991
+ }
5992
+ }
5993
+ }
5994
+ }
5995
+ // ─── Internals ───────────────────────────────────────────────────────────
5996
+ buildRequestBody(request, stream) {
5997
+ const { system, messages } = this.splitSystemMessages(request.messages);
5998
+ const body = {
5999
+ model: request.model || this.defaultModel,
6000
+ messages: messages.map((m) => this.toAnthropicMessage(m)),
6001
+ max_tokens: request.maxTokens ?? 4096,
6002
+ stream
6003
+ };
6004
+ if (system) body.system = system;
6005
+ if (request.temperature != null) body.temperature = request.temperature;
6006
+ if (request.topP != null) body.top_p = request.topP;
6007
+ if (request.stop) body.stop_sequences = request.stop;
6008
+ if (request.tools?.length) {
6009
+ body.tools = request.tools.map((t) => this.toAnthropicTool(t));
6010
+ }
6011
+ if (request.toolChoice != null) {
6012
+ if (request.toolChoice === "auto") {
6013
+ body.tool_choice = { type: "auto" };
6014
+ } else if (request.toolChoice === "required") {
6015
+ body.tool_choice = { type: "any" };
6016
+ } else if (request.toolChoice === "none") ; else {
6017
+ body.tool_choice = { type: "tool", name: request.toolChoice.name };
6018
+ }
6019
+ }
6020
+ if (request.extra) {
6021
+ Object.assign(body, request.extra);
6022
+ }
6023
+ return body;
6024
+ }
6025
+ splitSystemMessages(messages) {
6026
+ const systemParts = [];
6027
+ const rest = [];
6028
+ for (const m of messages) {
6029
+ if (m.role === "system") {
6030
+ if (typeof m.content === "string") {
6031
+ systemParts.push(m.content);
6032
+ } else {
6033
+ const text = m.content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
6034
+ if (text) systemParts.push(text);
6035
+ }
6036
+ } else {
6037
+ rest.push(m);
6038
+ }
6039
+ }
6040
+ return {
6041
+ system: systemParts.length > 0 ? systemParts.join("\n\n") : void 0,
6042
+ messages: rest
6043
+ };
6044
+ }
6045
+ toAnthropicMessage(m) {
6046
+ if (m.role === "tool" && m.toolCallId) {
6047
+ const content = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
6048
+ return {
6049
+ role: "user",
6050
+ content: [
6051
+ {
6052
+ type: "tool_result",
6053
+ tool_use_id: m.toolCallId,
6054
+ content
6055
+ }
6056
+ ]
6057
+ };
6058
+ }
6059
+ if (m.role === "assistant" && m.toolCalls?.length) {
6060
+ const content = [];
6061
+ if (typeof m.content === "string" && m.content) {
6062
+ content.push({ type: "text", text: m.content });
6063
+ }
6064
+ for (const tc of m.toolCalls) {
6065
+ content.push({
6066
+ type: "tool_use",
6067
+ id: tc.id,
6068
+ name: tc.name,
6069
+ input: safeJsonParse(tc.arguments)
6070
+ });
6071
+ }
6072
+ return { role: "assistant", content };
6073
+ }
6074
+ const msg = { role: m.role };
6075
+ if (typeof m.content === "string") {
6076
+ msg.content = m.content;
6077
+ } else if (Array.isArray(m.content)) {
6078
+ msg.content = m.content.map((part) => {
6079
+ if (part.type === "text") return { type: "text", text: part.text };
6080
+ if (part.type === "image") {
6081
+ const src = part.source;
6082
+ if (src.type === "base64") {
6083
+ return {
6084
+ type: "image",
6085
+ source: { type: "base64", media_type: src.mediaType, data: src.data }
6086
+ };
6087
+ }
6088
+ return {
6089
+ type: "image",
6090
+ source: { type: "url", url: src.url }
6091
+ };
6092
+ }
6093
+ return part;
6094
+ });
6095
+ }
6096
+ return msg;
6097
+ }
6098
+ toAnthropicTool(t) {
6099
+ return {
6100
+ name: t.name,
6101
+ description: t.description,
6102
+ input_schema: t.parameters
6103
+ };
6104
+ }
6105
+ async doFetch(body) {
6106
+ const url = `${this.baseURL}/v1/messages`;
6107
+ const res = await fetch(url, {
6108
+ method: "POST",
6109
+ headers: {
6110
+ "Content-Type": "application/json",
6111
+ "x-api-key": this.apiKey,
6112
+ "anthropic-version": this.anthropicVersion,
6113
+ ...this.defaultHeaders
6114
+ },
6115
+ body: JSON.stringify(body)
6116
+ });
6117
+ if (!res.ok) {
6118
+ const text = await res.text().catch(() => "");
6119
+ throw new ProviderError(
6120
+ `Anthropic API error ${res.status}: ${text}`,
6121
+ this.providerName,
6122
+ res.status,
6123
+ text
6124
+ );
6125
+ }
6126
+ return res;
6127
+ }
6128
+ parseResponse(json) {
6129
+ const textParts = [];
6130
+ const toolCalls = [];
6131
+ for (const block of json.content ?? []) {
6132
+ if (block.type === "text") {
6133
+ textParts.push(block.text);
6134
+ } else if (block.type === "tool_use") {
6135
+ toolCalls.push({
6136
+ id: block.id,
6137
+ name: block.name,
6138
+ arguments: typeof block.input === "string" ? block.input : JSON.stringify(block.input)
6139
+ });
6140
+ }
6141
+ }
6142
+ const usage = json.usage ?? {};
6143
+ const tokenUsage = {
6144
+ inputTokens: usage.input_tokens ?? 0,
6145
+ outputTokens: usage.output_tokens ?? 0,
6146
+ totalTokens: (usage.input_tokens ?? 0) + (usage.output_tokens ?? 0),
6147
+ cachedInputTokens: usage.cache_read_input_tokens ?? 0,
6148
+ cachedWriteTokens: usage.cache_creation_input_tokens ?? 0,
6149
+ cachedOutputTokens: 0,
6150
+ reasoningTokens: 0
6151
+ };
6152
+ return {
6153
+ id: json.id ?? "",
6154
+ model: json.model ?? "",
6155
+ content: textParts.join("") || null,
6156
+ toolCalls,
6157
+ usage: tokenUsage,
6158
+ finishReason: mapAnthropicStopReason(json.stop_reason),
6159
+ raw: json
6160
+ };
6161
+ }
6162
+ };
6163
+ function mapAnthropicStopReason(reason) {
6164
+ switch (reason) {
6165
+ case "end_turn":
6166
+ case "stop_sequence":
6167
+ return "stop";
6168
+ case "tool_use":
6169
+ return "tool_calls";
6170
+ case "max_tokens":
6171
+ return "length";
6172
+ default:
6173
+ return "stop";
6174
+ }
6175
+ }
6176
+ function safeJsonParse(str) {
6177
+ try {
6178
+ return JSON.parse(str);
6179
+ } catch {
6180
+ return {};
6181
+ }
6182
+ }
6183
+
6184
+ // src/providers/azure.ts
6185
+ var AzureProvider = class extends OpenAIProvider {
6186
+ constructor(config) {
6187
+ super({
6188
+ provider: "openai",
6189
+ apiKey: config.apiKey,
6190
+ defaultModel: config.defaultModel ?? config.deploymentName ?? "gpt-4o",
6191
+ defaultHeaders: config.defaultHeaders
6192
+ });
6193
+ this.providerName = "azure";
6194
+ this.endpoint = config.endpoint.replace(/\/$/, "");
6195
+ this.apiVersion = config.apiVersion ?? "2024-10-21";
6196
+ this.deploymentName = config.deploymentName;
6197
+ this.azureApiKey = config.apiKey;
6198
+ }
6199
+ async doFetch(body) {
6200
+ const deployment = this.deploymentName ?? body.model;
6201
+ if (!deployment) {
6202
+ throw new ProviderError("Azure requires a deploymentName or model", this.providerName);
6203
+ }
6204
+ const url = `${this.endpoint}/openai/deployments/${deployment}/chat/completions?api-version=${this.apiVersion}`;
6205
+ const { model: _model, ...bodyWithoutModel } = body;
6206
+ const res = await fetch(url, {
6207
+ method: "POST",
6208
+ headers: {
6209
+ "Content-Type": "application/json",
6210
+ "api-key": this.azureApiKey,
6211
+ ...this.defaultHeaders
6212
+ },
6213
+ body: JSON.stringify(bodyWithoutModel)
6214
+ });
6215
+ if (!res.ok) {
6216
+ const text = await res.text().catch(() => "");
6217
+ throw new ProviderError(
6218
+ `Azure OpenAI API error ${res.status}: ${text}`,
6219
+ this.providerName,
6220
+ res.status,
6221
+ text
6222
+ );
6223
+ }
6224
+ return res;
6225
+ }
6226
+ };
6227
+
6228
+ // src/providers/openaiCompatible.ts
6229
+ var OpenAICompatibleProvider = class extends OpenAIProvider {
6230
+ constructor(config) {
6231
+ super({
6232
+ provider: "openai",
6233
+ apiKey: config.apiKey,
6234
+ baseURL: config.baseURL,
6235
+ defaultModel: config.defaultModel,
6236
+ defaultHeaders: config.defaultHeaders
6237
+ });
6238
+ this.providerName = "openai-compatible";
6239
+ }
6240
+ };
6241
+ function signRequest(req) {
6242
+ const now = /* @__PURE__ */ new Date();
6243
+ const dateStamp = toDateStamp(now);
6244
+ const amzDate = toAmzDate(now);
6245
+ const parsed = new URL(req.url);
6246
+ const host = parsed.host;
6247
+ const canonicalUri = parsed.pathname || "/";
6248
+ const canonicalQuerystring = sortQueryParams(parsed.searchParams);
6249
+ const headers = {
6250
+ ...req.headers,
6251
+ host,
6252
+ "x-amz-date": amzDate
6253
+ };
6254
+ if (req.credentials.sessionToken) {
6255
+ headers["x-amz-security-token"] = req.credentials.sessionToken;
6256
+ }
6257
+ const signedHeaderKeys = Object.keys(headers).map((k) => k.toLowerCase()).sort();
6258
+ const canonicalHeaders = signedHeaderKeys.map((k) => `${k}:${headers[Object.keys(headers).find((h) => h.toLowerCase() === k)]?.trim()}`).join("\n") + "\n";
6259
+ const signedHeaders = signedHeaderKeys.join(";");
6260
+ const payloadHash = sha256(req.body);
6261
+ const canonicalRequest = [
6262
+ req.method,
6263
+ canonicalUri,
6264
+ canonicalQuerystring,
6265
+ canonicalHeaders,
6266
+ signedHeaders,
6267
+ payloadHash
6268
+ ].join("\n");
6269
+ const credentialScope = `${dateStamp}/${req.region}/${req.service}/aws4_request`;
6270
+ const stringToSign = [
6271
+ "AWS4-HMAC-SHA256",
6272
+ amzDate,
6273
+ credentialScope,
6274
+ sha256(canonicalRequest)
6275
+ ].join("\n");
6276
+ const signingKey = getSignatureKey(
6277
+ req.credentials.secretAccessKey,
6278
+ dateStamp,
6279
+ req.region,
6280
+ req.service
6281
+ );
6282
+ const signature = hmacHex(signingKey, stringToSign);
6283
+ const authorization = `AWS4-HMAC-SHA256 Credential=${req.credentials.accessKeyId}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;
6284
+ const result = {
6285
+ ...req.headers,
6286
+ "x-amz-date": amzDate,
6287
+ Authorization: authorization
6288
+ };
6289
+ if (req.credentials.sessionToken) {
6290
+ result["x-amz-security-token"] = req.credentials.sessionToken;
6291
+ }
6292
+ return result;
6293
+ }
6294
+ function toDateStamp(d) {
6295
+ return d.toISOString().replace(/[-:T]/g, "").slice(0, 8);
6296
+ }
6297
+ function toAmzDate(d) {
6298
+ return d.toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
6299
+ }
6300
+ function sha256(data) {
6301
+ return createHash("sha256").update(data, "utf8").digest("hex");
6302
+ }
6303
+ function hmac(key, data) {
6304
+ return createHmac("sha256", key).update(data, "utf8").digest();
6305
+ }
6306
+ function hmacHex(key, data) {
6307
+ return createHmac("sha256", key).update(data, "utf8").digest("hex");
6308
+ }
6309
+ function getSignatureKey(secret, dateStamp, region, service) {
6310
+ const kDate = hmac(`AWS4${secret}`, dateStamp);
6311
+ const kRegion = hmac(kDate, region);
6312
+ const kService = hmac(kRegion, service);
6313
+ return hmac(kService, "aws4_request");
6314
+ }
6315
+ function sortQueryParams(params) {
6316
+ const sorted = [...params.entries()].sort(([a], [b]) => a.localeCompare(b));
6317
+ return sorted.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&");
6318
+ }
6319
+
6320
+ // src/providers/bedrock.ts
6321
+ var BedrockProvider = class extends BaseProvider {
6322
+ constructor(config) {
6323
+ super();
6324
+ this.providerName = "bedrock";
6325
+ this.region = config.region;
6326
+ this.defaultModel = config.defaultModel ?? "anthropic.claude-sonnet-4-20250514-v1:0";
6327
+ this.defaultHeaders = config.defaultHeaders ?? {};
6328
+ this.credentials = {
6329
+ accessKeyId: config.accessKeyId ?? process.env.AWS_ACCESS_KEY_ID ?? "",
6330
+ secretAccessKey: config.secretAccessKey ?? process.env.AWS_SECRET_ACCESS_KEY ?? "",
6331
+ sessionToken: config.sessionToken ?? process.env.AWS_SESSION_TOKEN
6332
+ };
6333
+ if (!this.credentials.accessKeyId || !this.credentials.secretAccessKey) {
6334
+ throw new ProviderError(
6335
+ "AWS credentials required: provide accessKeyId/secretAccessKey or set AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY env vars",
6336
+ "bedrock"
6337
+ );
6338
+ }
6339
+ }
6340
+ async complete(request) {
6341
+ const modelId = request.model || this.defaultModel;
6342
+ const body = this.buildConverseBody(request);
6343
+ const json = await this.doFetch(modelId, body);
6344
+ return this.parseResponse(json, modelId);
6345
+ }
6346
+ async *completeStream(request) {
6347
+ const response = await this.complete(request);
6348
+ yield {
6349
+ id: response.id,
6350
+ model: response.model,
6351
+ delta: {
6352
+ content: response.content ?? void 0,
6353
+ toolCalls: response.toolCalls.length > 0 ? response.toolCalls : void 0
6354
+ },
6355
+ usage: response.usage,
6356
+ finishReason: response.finishReason
6357
+ };
6358
+ }
6359
+ // ─── Internals ───────────────────────────────────────────────────────────
6360
+ buildConverseBody(request) {
6361
+ const { system, messages } = this.splitSystemMessages(request.messages);
6362
+ const body = {
6363
+ messages: messages.map((m) => this.toBedrockMessage(m))
6364
+ };
6365
+ if (system.length > 0) {
6366
+ body.system = system.map((s) => ({ text: s }));
6367
+ }
6368
+ const inferenceConfig = {};
6369
+ if (request.maxTokens != null) inferenceConfig.maxTokens = request.maxTokens;
6370
+ if (request.temperature != null) inferenceConfig.temperature = request.temperature;
6371
+ if (request.topP != null) inferenceConfig.topP = request.topP;
6372
+ if (request.stop) inferenceConfig.stopSequences = request.stop;
6373
+ if (Object.keys(inferenceConfig).length > 0) body.inferenceConfig = inferenceConfig;
6374
+ if (request.tools?.length) {
6375
+ body.toolConfig = {
6376
+ tools: request.tools.map((t) => this.toBedrockTool(t))
6377
+ };
6378
+ if (request.toolChoice != null) {
6379
+ if (request.toolChoice === "auto") {
6380
+ body.toolConfig.toolChoice = { auto: {} };
6381
+ } else if (request.toolChoice === "required") {
6382
+ body.toolConfig.toolChoice = { any: {} };
6383
+ } else if (typeof request.toolChoice === "object") {
6384
+ body.toolConfig.toolChoice = { tool: { name: request.toolChoice.name } };
6385
+ }
6386
+ }
6387
+ }
6388
+ if (request.extra) {
6389
+ Object.assign(body, request.extra);
6390
+ }
6391
+ return body;
6392
+ }
6393
+ splitSystemMessages(messages) {
6394
+ const system = [];
6395
+ const rest = [];
6396
+ for (const m of messages) {
6397
+ if (m.role === "system") {
6398
+ const text = typeof m.content === "string" ? m.content : m.content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
6399
+ if (text) system.push(text);
6400
+ } else {
6401
+ rest.push(m);
6402
+ }
6403
+ }
6404
+ return { system, messages: rest };
6405
+ }
6406
+ toBedrockMessage(m) {
6407
+ if (m.role === "tool" && m.toolCallId) {
6408
+ const text = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
6409
+ return {
6410
+ role: "user",
6411
+ content: [
6412
+ {
6413
+ toolResult: {
6414
+ toolUseId: m.toolCallId,
6415
+ content: [{ text }],
6416
+ status: "success"
6417
+ }
6418
+ }
6419
+ ]
6420
+ };
6421
+ }
6422
+ if (m.role === "assistant" && m.toolCalls?.length) {
6423
+ const content2 = [];
6424
+ if (typeof m.content === "string" && m.content) {
6425
+ content2.push({ text: m.content });
6426
+ }
6427
+ for (const tc of m.toolCalls) {
6428
+ content2.push({
6429
+ toolUse: {
6430
+ toolUseId: tc.id,
6431
+ name: tc.name,
6432
+ input: safeJsonParse2(tc.arguments)
6433
+ }
6434
+ });
6435
+ }
6436
+ return { role: "assistant", content: content2 };
6437
+ }
6438
+ const content = [];
6439
+ if (typeof m.content === "string") {
6440
+ content.push({ text: m.content });
6441
+ } else if (Array.isArray(m.content)) {
6442
+ for (const part of m.content) {
6443
+ if (part.type === "text") {
6444
+ content.push({ text: part.text });
6445
+ } else if (part.type === "image") {
6446
+ const src = part.source;
6447
+ if (src.type === "base64") {
6448
+ content.push({
6449
+ image: {
6450
+ format: mediaTypeToFormat(src.mediaType),
6451
+ source: { bytes: src.data }
6452
+ }
6453
+ });
6454
+ }
6455
+ }
6456
+ }
6457
+ }
6458
+ return { role: m.role === "user" ? "user" : "assistant", content };
6459
+ }
6460
+ toBedrockTool(t) {
6461
+ return {
6462
+ toolSpec: {
6463
+ name: t.name,
6464
+ description: t.description,
6465
+ inputSchema: { json: t.parameters }
6466
+ }
6467
+ };
6468
+ }
6469
+ async doFetch(modelId, body) {
6470
+ const host = `bedrock-runtime.${this.region}.amazonaws.com`;
6471
+ const url = `https://${host}/model/${encodeURIComponent(modelId)}/converse`;
6472
+ const bodyStr = JSON.stringify(body);
6473
+ const headers = signRequest({
6474
+ method: "POST",
6475
+ url,
6476
+ headers: {
6477
+ "Content-Type": "application/json",
6478
+ Accept: "application/json",
6479
+ ...this.defaultHeaders
6480
+ },
6481
+ body: bodyStr,
6482
+ region: this.region,
6483
+ service: "bedrock",
6484
+ credentials: this.credentials
6485
+ });
6486
+ const res = await fetch(url, {
6487
+ method: "POST",
6488
+ headers,
6489
+ body: bodyStr
6490
+ });
6491
+ if (!res.ok) {
6492
+ const text = await res.text().catch(() => "");
6493
+ throw new ProviderError(
6494
+ `Bedrock API error ${res.status}: ${text}`,
6495
+ this.providerName,
6496
+ res.status,
6497
+ text
6498
+ );
6499
+ }
6500
+ return res.json();
6501
+ }
6502
+ parseResponse(json, modelId) {
6503
+ const output = json.output?.message;
6504
+ const textParts = [];
6505
+ const toolCalls = [];
6506
+ for (const block of output?.content ?? []) {
6507
+ if (block.text) {
6508
+ textParts.push(block.text);
6509
+ } else if (block.toolUse) {
6510
+ toolCalls.push({
6511
+ id: block.toolUse.toolUseId,
6512
+ name: block.toolUse.name,
6513
+ arguments: typeof block.toolUse.input === "string" ? block.toolUse.input : JSON.stringify(block.toolUse.input ?? {})
6514
+ });
6515
+ }
6516
+ }
6517
+ const usage = json.usage ?? {};
6518
+ const tokenUsage = {
6519
+ inputTokens: usage.inputTokens ?? 0,
6520
+ outputTokens: usage.outputTokens ?? 0,
6521
+ totalTokens: usage.totalTokens ?? (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0),
6522
+ cachedInputTokens: usage.cacheReadInputTokenCount ?? 0,
6523
+ cachedWriteTokens: usage.cacheWriteInputTokenCount ?? 0,
6524
+ cachedOutputTokens: 0,
6525
+ reasoningTokens: 0
6526
+ };
6527
+ return {
6528
+ id: json.requestId ?? json.$metadata?.requestId ?? "",
6529
+ model: modelId,
6530
+ content: textParts.join("") || null,
6531
+ toolCalls,
6532
+ usage: tokenUsage,
6533
+ finishReason: mapBedrockStopReason(json.stopReason),
6534
+ raw: json
6535
+ };
6536
+ }
6537
+ };
6538
+ function mapBedrockStopReason(reason) {
6539
+ switch (reason) {
6540
+ case "end_turn":
6541
+ case "stop_sequence":
6542
+ return "stop";
6543
+ case "tool_use":
6544
+ return "tool_calls";
6545
+ case "max_tokens":
6546
+ return "length";
6547
+ case "content_filtered":
6548
+ case "guardrail":
6549
+ return "content_filter";
6550
+ default:
6551
+ return "stop";
6552
+ }
6553
+ }
6554
+ function mediaTypeToFormat(mediaType) {
6555
+ const map = {
6556
+ "image/jpeg": "jpeg",
6557
+ "image/png": "png",
6558
+ "image/gif": "gif",
6559
+ "image/webp": "webp"
6560
+ };
6561
+ return map[mediaType] ?? "png";
6562
+ }
6563
+ function safeJsonParse2(str) {
6564
+ try {
6565
+ return JSON.parse(str);
6566
+ } catch {
6567
+ return {};
6568
+ }
6569
+ }
6570
+
6571
+ // src/providers/vertex.ts
6572
+ var VertexProvider = class extends BaseProvider {
6573
+ constructor(config) {
6574
+ super();
6575
+ this.providerName = "vertex";
6576
+ this.tokenExpiry = 0;
6577
+ this.projectId = config.projectId;
6578
+ this.location = config.location ?? "us-central1";
6579
+ this.defaultModel = config.defaultModel ?? "gemini-2.0-flash";
6580
+ this.defaultHeaders = config.defaultHeaders ?? {};
6581
+ if (config.accessToken) {
6582
+ this.accessToken = config.accessToken;
6583
+ this.tokenExpiry = Date.now() + 36e5;
6584
+ }
6585
+ if (config.serviceAccountJson) {
6586
+ this.serviceAccountJson = typeof config.serviceAccountJson === "string" ? JSON.parse(config.serviceAccountJson) : config.serviceAccountJson;
6587
+ }
6588
+ }
6589
+ async complete(request) {
6590
+ const modelId = request.model || this.defaultModel;
6591
+ const body = this.buildGeminiBody(request);
6592
+ const url = this.buildUrl(modelId, false);
6593
+ const res = await this.doFetch(url, body);
6594
+ const json = await res.json();
6595
+ return this.parseResponse(json, modelId);
6596
+ }
6597
+ async *completeStream(request) {
6598
+ const modelId = request.model || this.defaultModel;
6599
+ const body = this.buildGeminiBody(request);
6600
+ const url = this.buildUrl(modelId, true);
6601
+ const res = await this.doFetch(url, body);
6602
+ if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
6603
+ const contentType = res.headers.get("content-type") ?? "";
6604
+ if (contentType.includes("text/event-stream")) {
6605
+ for await (const event of parseSSEStream(res.body)) {
6606
+ if (event.data === "[DONE]") break;
6607
+ let chunk;
6608
+ try {
6609
+ chunk = JSON.parse(event.data);
6610
+ } catch {
6611
+ continue;
6612
+ }
6613
+ yield this.parseStreamChunk(chunk, modelId);
6614
+ }
6615
+ } else {
6616
+ const reader = res.body.getReader();
6617
+ const decoder = new TextDecoder();
6618
+ let buffer = "";
6619
+ try {
6620
+ while (true) {
6621
+ const { done, value } = await reader.read();
6622
+ if (done) break;
6623
+ buffer += decoder.decode(value, { stream: true });
6624
+ const lines = buffer.split("\n");
6625
+ buffer = lines.pop() ?? "";
6626
+ for (const line of lines) {
6627
+ const trimmed = line.trim().replace(/^,/, "").replace(/^\[/, "").replace(/\]$/, "");
6628
+ if (!trimmed || trimmed === "[" || trimmed === "]") continue;
6629
+ try {
6630
+ const chunk = JSON.parse(trimmed);
6631
+ yield this.parseStreamChunk(chunk, modelId);
6632
+ } catch {
6633
+ buffer = line + "\n" + buffer;
6634
+ break;
6635
+ }
6636
+ }
6637
+ }
6638
+ const remaining = buffer.trim().replace(/^,/, "").replace(/^\[/, "").replace(/\]$/, "");
6639
+ if (remaining && remaining !== "[" && remaining !== "]") {
6640
+ try {
6641
+ const chunk = JSON.parse(remaining);
6642
+ yield this.parseStreamChunk(chunk, modelId);
6643
+ } catch {
6644
+ }
6645
+ }
6646
+ } finally {
6647
+ reader.releaseLock();
6648
+ }
6649
+ }
6650
+ }
6651
+ // ─── Internals ───────────────────────────────────────────────────────────
6652
+ buildUrl(modelId, stream) {
6653
+ const base = `https://${this.location}-aiplatform.googleapis.com/v1`;
6654
+ const path2 = `projects/${this.projectId}/locations/${this.location}/publishers/google/models/${modelId}`;
6655
+ const action = stream ? "streamGenerateContent?alt=sse" : "generateContent";
6656
+ return `${base}/${path2}:${action}`;
6657
+ }
6658
+ buildGeminiBody(request) {
6659
+ const { system, contents } = this.buildContents(request.messages);
6660
+ const body = { contents };
6661
+ if (system) {
6662
+ body.systemInstruction = { parts: [{ text: system }] };
6663
+ }
6664
+ const generationConfig = {};
6665
+ if (request.maxTokens != null) generationConfig.maxOutputTokens = request.maxTokens;
6666
+ if (request.temperature != null) generationConfig.temperature = request.temperature;
6667
+ if (request.topP != null) generationConfig.topP = request.topP;
6668
+ if (request.stop) generationConfig.stopSequences = request.stop;
6669
+ if (request.responseFormat) {
6670
+ if (request.responseFormat.type === "json_schema" || request.responseFormat.type === "json_object") {
6671
+ generationConfig.responseMimeType = "application/json";
6672
+ if (request.responseFormat.schema) {
6673
+ generationConfig.responseSchema = request.responseFormat.schema;
6674
+ }
6675
+ }
6676
+ }
6677
+ if (Object.keys(generationConfig).length > 0) body.generationConfig = generationConfig;
6678
+ if (request.tools?.length) {
6679
+ body.tools = [
6680
+ {
6681
+ functionDeclarations: request.tools.map((t) => this.toGeminiTool(t))
6682
+ }
6683
+ ];
6684
+ if (request.toolChoice != null) {
6685
+ const mode = request.toolChoice === "auto" ? "AUTO" : request.toolChoice === "required" ? "ANY" : request.toolChoice === "none" ? "NONE" : "AUTO";
6686
+ body.toolConfig = { functionCallingConfig: { mode } };
6687
+ }
6688
+ }
6689
+ if (request.extra) {
6690
+ Object.assign(body, request.extra);
6691
+ }
6692
+ return body;
6693
+ }
6694
+ buildContents(messages) {
6695
+ const systemParts = [];
6696
+ const contents = [];
6697
+ for (const m of messages) {
6698
+ if (m.role === "system") {
6699
+ const text = typeof m.content === "string" ? m.content : m.content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
6700
+ if (text) systemParts.push(text);
6701
+ continue;
6702
+ }
6703
+ contents.push(this.toGeminiContent(m));
6704
+ }
6705
+ return {
6706
+ system: systemParts.length > 0 ? systemParts.join("\n\n") : void 0,
6707
+ contents
6708
+ };
6709
+ }
6710
+ toGeminiContent(m) {
6711
+ if (m.role === "tool" && m.toolCallId) {
6712
+ const text = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
6713
+ let response;
6714
+ try {
6715
+ response = JSON.parse(text);
6716
+ } catch {
6717
+ response = { result: text };
6718
+ }
6719
+ return {
6720
+ role: "user",
6721
+ parts: [
6722
+ {
6723
+ functionResponse: {
6724
+ name: m.name ?? m.toolCallId,
6725
+ response
6726
+ }
6727
+ }
6728
+ ]
6729
+ };
6730
+ }
6731
+ if (m.role === "assistant" && m.toolCalls?.length) {
6732
+ const parts2 = [];
6733
+ if (typeof m.content === "string" && m.content) {
6734
+ parts2.push({ text: m.content });
6735
+ }
6736
+ for (const tc of m.toolCalls) {
6737
+ parts2.push({
6738
+ functionCall: {
6739
+ name: tc.name,
6740
+ args: safeJsonParse3(tc.arguments)
6741
+ }
6742
+ });
6743
+ }
6744
+ return { role: "model", parts: parts2 };
6745
+ }
6746
+ const role = m.role === "assistant" ? "model" : "user";
6747
+ const parts = [];
6748
+ if (typeof m.content === "string") {
6749
+ parts.push({ text: m.content });
6750
+ } else if (Array.isArray(m.content)) {
6751
+ for (const part of m.content) {
6752
+ if (part.type === "text") {
6753
+ parts.push({ text: part.text });
6754
+ } else if (part.type === "image") {
6755
+ const src = part.source;
6756
+ if (src.type === "base64") {
6757
+ parts.push({
6758
+ inlineData: { mimeType: src.mediaType, data: src.data }
6759
+ });
6760
+ } else {
6761
+ parts.push({
6762
+ fileData: { mimeType: "image/jpeg", fileUri: src.url }
6763
+ });
6764
+ }
6765
+ }
6766
+ }
6767
+ }
6768
+ return { role, parts };
6769
+ }
6770
+ toGeminiTool(t) {
6771
+ return {
6772
+ name: t.name,
6773
+ description: t.description,
6774
+ parameters: t.parameters
6775
+ };
6776
+ }
6777
+ async getAccessToken() {
6778
+ if (this.accessToken && Date.now() < this.tokenExpiry) {
6779
+ return this.accessToken;
6780
+ }
6781
+ if (this.serviceAccountJson) {
6782
+ const token = await this.getTokenFromServiceAccount(this.serviceAccountJson);
6783
+ this.accessToken = token;
6784
+ this.tokenExpiry = Date.now() + 35e5;
6785
+ return token;
6786
+ }
6787
+ try {
6788
+ const { execSync } = await import('child_process');
6789
+ const token = execSync("gcloud auth print-access-token", { encoding: "utf8" }).trim();
6790
+ this.accessToken = token;
6791
+ this.tokenExpiry = Date.now() + 35e5;
6792
+ return token;
6793
+ } catch {
6794
+ throw new ProviderError(
6795
+ "No access token available. Provide accessToken, serviceAccountJson, or ensure gcloud CLI is authenticated.",
6796
+ this.providerName
6797
+ );
6798
+ }
6799
+ }
6800
+ async getTokenFromServiceAccount(sa) {
6801
+ const { createSign } = await import('crypto');
6802
+ const now = Math.floor(Date.now() / 1e3);
6803
+ const header = { alg: "RS256", typ: "JWT" };
6804
+ const claims = {
6805
+ iss: sa.client_email,
6806
+ scope: "https://www.googleapis.com/auth/cloud-platform",
6807
+ aud: "https://oauth2.googleapis.com/token",
6808
+ iat: now,
6809
+ exp: now + 3600
6810
+ };
6811
+ const segments = [
6812
+ base64url(JSON.stringify(header)),
6813
+ base64url(JSON.stringify(claims))
6814
+ ];
6815
+ const signingInput = segments.join(".");
6816
+ const sign = createSign("RSA-SHA256");
6817
+ sign.update(signingInput);
6818
+ const signature = sign.sign(sa.private_key, "base64url");
6819
+ const jwt = `${signingInput}.${signature}`;
6820
+ const tokenRes = await fetch("https://oauth2.googleapis.com/token", {
6821
+ method: "POST",
6822
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
6823
+ body: `grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=${jwt}`
6824
+ });
6825
+ if (!tokenRes.ok) {
6826
+ const text = await tokenRes.text().catch(() => "");
6827
+ throw new ProviderError(`Failed to get Vertex access token: ${text}`, this.providerName);
6828
+ }
6829
+ const tokenJson = await tokenRes.json();
6830
+ return tokenJson.access_token;
6831
+ }
6832
+ async doFetch(url, body) {
6833
+ const token = await this.getAccessToken();
6834
+ const res = await fetch(url, {
6835
+ method: "POST",
6836
+ headers: {
6837
+ "Content-Type": "application/json",
6838
+ Authorization: `Bearer ${token}`,
6839
+ ...this.defaultHeaders
6840
+ },
6841
+ body: JSON.stringify(body)
6842
+ });
6843
+ if (!res.ok) {
6844
+ const text = await res.text().catch(() => "");
6845
+ throw new ProviderError(
6846
+ `Vertex AI API error ${res.status}: ${text}`,
6847
+ this.providerName,
6848
+ res.status,
6849
+ text
6850
+ );
6851
+ }
6852
+ return res;
6853
+ }
6854
+ parseResponse(json, modelId) {
6855
+ const candidate = json.candidates?.[0];
6856
+ const parts = candidate?.content?.parts ?? [];
6857
+ const textParts = [];
6858
+ const toolCalls = [];
6859
+ let toolCallIdx = 0;
6860
+ for (const part of parts) {
6861
+ if (part.text) {
6862
+ textParts.push(part.text);
6863
+ } else if (part.functionCall) {
6864
+ toolCalls.push({
6865
+ id: `call_${toolCallIdx++}`,
6866
+ name: part.functionCall.name,
6867
+ arguments: JSON.stringify(part.functionCall.args ?? {})
6868
+ });
6869
+ }
6870
+ }
6871
+ const usageMeta = json.usageMetadata ?? {};
6872
+ const tokenUsage = {
6873
+ inputTokens: usageMeta.promptTokenCount ?? 0,
6874
+ outputTokens: usageMeta.candidatesTokenCount ?? 0,
6875
+ totalTokens: usageMeta.totalTokenCount ?? 0,
6876
+ cachedInputTokens: usageMeta.cachedContentTokenCount ?? 0,
6877
+ cachedWriteTokens: 0,
6878
+ cachedOutputTokens: 0,
6879
+ reasoningTokens: usageMeta.thoughtsTokenCount ?? 0
6880
+ };
6881
+ return {
6882
+ id: json.responseId ?? "",
6883
+ model: modelId,
6884
+ content: textParts.join("") || null,
6885
+ toolCalls,
6886
+ usage: tokenUsage,
6887
+ finishReason: mapGeminiFinishReason(candidate?.finishReason),
6888
+ raw: json
6889
+ };
6890
+ }
6891
+ parseStreamChunk(chunk, modelId) {
6892
+ const candidate = chunk.candidates?.[0];
6893
+ const parts = candidate?.content?.parts ?? [];
6894
+ const delta = {};
6895
+ for (const part of parts) {
6896
+ if (part.text) {
6897
+ delta.content = (delta.content ?? "") + part.text;
6898
+ } else if (part.functionCall) {
6899
+ if (!delta.toolCalls) delta.toolCalls = [];
6900
+ delta.toolCalls.push({
6901
+ id: `call_${delta.toolCalls.length}`,
6902
+ name: part.functionCall.name,
6903
+ arguments: JSON.stringify(part.functionCall.args ?? {})
6904
+ });
6905
+ }
6906
+ }
6907
+ let usage;
6908
+ if (chunk.usageMetadata) {
6909
+ const u = chunk.usageMetadata;
6910
+ usage = {
6911
+ inputTokens: u.promptTokenCount ?? 0,
6912
+ outputTokens: u.candidatesTokenCount ?? 0,
6913
+ totalTokens: u.totalTokenCount ?? 0,
6914
+ cachedInputTokens: u.cachedContentTokenCount ?? 0,
6915
+ cachedWriteTokens: 0,
6916
+ cachedOutputTokens: 0,
6917
+ reasoningTokens: u.thoughtsTokenCount ?? 0
6918
+ };
6919
+ }
6920
+ return {
6921
+ id: chunk.responseId ?? "",
6922
+ model: modelId,
6923
+ delta,
6924
+ usage,
6925
+ finishReason: mapGeminiFinishReason(candidate?.finishReason)
6926
+ };
6927
+ }
6928
+ };
6929
+ function mapGeminiFinishReason(reason) {
6930
+ switch (reason) {
6931
+ case "STOP":
6932
+ return "stop";
6933
+ case "MAX_TOKENS":
6934
+ return "length";
6935
+ case "SAFETY":
6936
+ case "RECITATION":
6937
+ return "content_filter";
6938
+ case "TOOL_CALLS":
6939
+ return "tool_calls";
6940
+ default:
6941
+ return "stop";
6942
+ }
6943
+ }
6944
+ function base64url(str) {
6945
+ return Buffer.from(str).toString("base64url");
6946
+ }
6947
+ function safeJsonParse3(str) {
6948
+ try {
6949
+ return JSON.parse(str);
6950
+ } catch {
6951
+ return {};
6952
+ }
6953
+ }
6954
+ function fromNativeProvider(provider, options) {
6955
+ let boundTools = options?.__tools;
6956
+ const modelName = options?.model ?? provider.defaultModel ?? provider.providerName;
6957
+ const adapted = {
6958
+ modelName,
6959
+ capabilities: resolveCapabilities(provider.providerName),
6960
+ invoke: async (messages, _invokeOptions) => {
6961
+ const request = buildRequest(messages, boundTools, options, _invokeOptions);
6962
+ const response = await provider.complete(request);
6963
+ return toBaseChatMessage(response);
6964
+ },
6965
+ stream: async function* (messages, _invokeOptions) {
6966
+ const request = buildRequest(messages, boundTools, options, _invokeOptions);
6967
+ request.stream = true;
6968
+ let fullContent = "";
6969
+ let allToolCalls = [];
6970
+ let lastUsage;
6971
+ const toolCallBuffers = /* @__PURE__ */ new Map();
6972
+ for await (const chunk of provider.completeStream(request)) {
6973
+ if (chunk.delta.content) {
6974
+ fullContent += chunk.delta.content;
6975
+ yield chunk.delta.content;
6976
+ }
6977
+ if (chunk.delta.toolCalls) {
6978
+ for (const tc of chunk.delta.toolCalls) {
6979
+ if (tc.id) {
6980
+ const existing = toolCallBuffers.get(tc.id);
6981
+ if (existing) {
6982
+ if (tc.arguments) existing.arguments += tc.arguments;
6983
+ } else {
6984
+ toolCallBuffers.set(tc.id, {
6985
+ id: tc.id,
6986
+ name: tc.name ?? "",
6987
+ arguments: tc.arguments ?? ""
6988
+ });
6989
+ }
6990
+ }
6991
+ }
6992
+ }
6993
+ if (chunk.usage) lastUsage = chunk.usage;
6994
+ }
6995
+ allToolCalls = [...toolCallBuffers.values()];
6996
+ const finalMessage = assembleMessage(fullContent, allToolCalls, lastUsage);
6997
+ yield finalMessage;
6998
+ },
6999
+ bindTools: (tools, _bindOptions) => {
7000
+ const strict = _bindOptions?.strict ?? false;
7001
+ const toolDefs = tools.map((t) => toToolDefinition(t, strict));
7002
+ return fromNativeProvider(provider, { ...options, __tools: toolDefs });
7003
+ }
7004
+ };
7005
+ return adapted;
7006
+ function buildRequest(messages, tools, opts, invokeOptions) {
7007
+ const req = {
7008
+ model: opts?.model ?? modelName,
7009
+ messages: messages.map(toUnifiedMessage)
7010
+ };
7011
+ if (opts?.temperature != null) req.temperature = opts.temperature;
7012
+ if (opts?.maxTokens != null) req.maxTokens = opts.maxTokens;
7013
+ if (tools?.length) req.tools = tools;
7014
+ if (opts?.extra) req.extra = opts.extra;
7015
+ const rf = invokeOptions?.response_format;
7016
+ if (rf && typeof rf === "object" && rf.type) {
7017
+ if (rf.type === "json_schema" && rf.json_schema) {
7018
+ req.responseFormat = {
7019
+ type: "json_schema",
7020
+ schema: rf.json_schema.schema,
7021
+ name: rf.json_schema.name
7022
+ };
7023
+ } else if (rf.type === "json_object") {
7024
+ req.responseFormat = { type: "json_object" };
7025
+ } else if (rf.type === "text") {
7026
+ req.responseFormat = { type: "text" };
7027
+ }
7028
+ }
7029
+ if (!req.responseFormat) {
7030
+ const lastMsg = messages[messages.length - 1];
7031
+ if (lastMsg && lastMsg.__responseFormat) {
7032
+ req.responseFormat = lastMsg.__responseFormat;
7033
+ }
7034
+ }
7035
+ return req;
7036
+ }
7037
+ }
7038
+ function toUnifiedMessage(msg) {
7039
+ const unified = {
7040
+ role: msg.role,
7041
+ content: convertContent(msg.content)
7042
+ };
7043
+ if (msg.name) unified.name = msg.name;
7044
+ if (msg.tool_call_id) unified.toolCallId = msg.tool_call_id;
7045
+ if (msg.tool_calls?.length) {
7046
+ unified.toolCalls = msg.tool_calls.map((tc) => ({
7047
+ id: tc.id,
7048
+ name: tc.function?.name ?? tc.name ?? "",
7049
+ arguments: tc.function?.arguments ?? tc.arguments ?? "{}"
7050
+ }));
7051
+ }
7052
+ return unified;
7053
+ }
7054
+ function convertContent(content) {
7055
+ if (typeof content === "string") return content;
7056
+ if (!Array.isArray(content)) return String(content ?? "");
7057
+ return content.map((part) => {
7058
+ if (part.type === "text" || !part.type && part.text) {
7059
+ return { type: "text", text: part.text ?? part.content ?? "" };
7060
+ }
7061
+ if (part.type === "image_url") {
7062
+ const img = part.image_url;
7063
+ if (typeof img === "string" || img?.url?.startsWith("http")) {
7064
+ return {
7065
+ type: "image",
7066
+ source: { type: "url", url: typeof img === "string" ? img : img.url }
7067
+ };
7068
+ }
7069
+ if (img?.url?.startsWith("data:")) {
7070
+ const match = img.url.match(/^data:([^;]+);base64,(.+)$/);
7071
+ if (match) {
7072
+ return {
7073
+ type: "image",
7074
+ source: { type: "base64", mediaType: match[1], data: match[2] }
7075
+ };
7076
+ }
7077
+ }
7078
+ }
7079
+ return { type: "text", text: part.text ?? part.content ?? JSON.stringify(part) };
7080
+ });
7081
+ }
7082
+ function isZodSchema(obj) {
7083
+ return obj != null && typeof obj === "object" && typeof obj.safeParse === "function" && "_def" in obj;
7084
+ }
7085
+ function convertToJsonSchema(schema, strict = false) {
7086
+ if (!schema || typeof schema !== "object") {
7087
+ return { type: "object", properties: {}, required: [], additionalProperties: false };
7088
+ }
7089
+ if (isZodSchema(schema)) {
7090
+ const converted = zodToJsonSchema(schema, {
7091
+ $refStrategy: "none",
7092
+ ...strict ? { openaiStrictMode: true } : {}
7093
+ });
7094
+ delete converted["$schema"];
7095
+ if (!converted.type) converted.type = "object";
7096
+ return converted;
7097
+ }
7098
+ if (!schema.type && schema.properties) {
7099
+ return { type: "object", ...schema };
7100
+ }
7101
+ return schema;
7102
+ }
7103
+ function normalizeStrictSchema(schema) {
7104
+ if (!schema || typeof schema !== "object") return schema;
7105
+ const clone2 = { ...schema };
7106
+ if (clone2.type === "object" || clone2.properties) {
7107
+ if (!clone2.type) clone2.type = "object";
7108
+ if (clone2.properties && typeof clone2.properties === "object") {
7109
+ const normalized = {};
7110
+ for (const [k, v] of Object.entries(clone2.properties)) {
7111
+ normalized[k] = normalizeStrictSchema(v);
7112
+ }
7113
+ clone2.properties = normalized;
7114
+ clone2.required = Object.keys(normalized);
7115
+ } else {
7116
+ clone2.properties = {};
7117
+ clone2.required = [];
7118
+ }
7119
+ clone2.additionalProperties = false;
7120
+ }
7121
+ if (clone2.items) clone2.items = normalizeStrictSchema(clone2.items);
7122
+ for (const key of ["anyOf", "oneOf", "allOf"]) {
7123
+ if (Array.isArray(clone2[key])) {
7124
+ clone2[key] = clone2[key].map((s) => normalizeStrictSchema(s));
7125
+ }
7126
+ }
7127
+ if (clone2.definitions && typeof clone2.definitions === "object") {
7128
+ const nd = {};
7129
+ for (const [k, v] of Object.entries(clone2.definitions)) {
7130
+ nd[k] = normalizeStrictSchema(v);
7131
+ }
7132
+ clone2.definitions = nd;
7133
+ }
7134
+ if (clone2.$defs && typeof clone2.$defs === "object") {
7135
+ const nd = {};
7136
+ for (const [k, v] of Object.entries(clone2.$defs)) {
7137
+ nd[k] = normalizeStrictSchema(v);
7138
+ }
7139
+ clone2.$defs = nd;
7140
+ }
7141
+ return clone2;
7142
+ }
7143
+ function toToolDefinition(tool, strict) {
7144
+ let name;
7145
+ let description;
7146
+ let parameters;
7147
+ if (tool.type === "function" && tool.function) {
7148
+ name = tool.function.name;
7149
+ description = tool.function.description ?? "";
7150
+ parameters = tool.function.parameters ?? { type: "object", properties: {}, additionalProperties: false };
7151
+ } else {
7152
+ name = tool.name ?? "unknown";
7153
+ description = tool.description ?? "";
7154
+ const rawSchema = tool.schema ?? tool.parameters;
7155
+ parameters = convertToJsonSchema(rawSchema, strict);
7156
+ }
7157
+ if (strict) {
7158
+ parameters = normalizeStrictSchema(parameters);
7159
+ }
7160
+ return { name, description, parameters, ...strict ? { strict: true } : {} };
7161
+ }
7162
+ function toBaseChatMessage(response) {
7163
+ const msg = {
7164
+ role: "assistant",
7165
+ content: response.content ?? "",
7166
+ usage: {
7167
+ prompt_tokens: response.usage.inputTokens,
7168
+ completion_tokens: response.usage.outputTokens,
7169
+ total_tokens: response.usage.totalTokens,
7170
+ prompt_tokens_details: {
7171
+ cached_tokens: response.usage.cachedInputTokens
7172
+ },
7173
+ completion_tokens_details: {
7174
+ reasoning_tokens: response.usage.reasoningTokens
7175
+ }
7176
+ },
7177
+ response_metadata: {
7178
+ token_usage: {
7179
+ prompt_tokens: response.usage.inputTokens,
7180
+ completion_tokens: response.usage.outputTokens,
7181
+ total_tokens: response.usage.totalTokens,
7182
+ cached_tokens: response.usage.cachedInputTokens,
7183
+ cache_write_tokens: response.usage.cachedWriteTokens
7184
+ },
7185
+ finish_reason: response.finishReason,
7186
+ model_name: response.model
7187
+ }
7188
+ };
7189
+ if (response.toolCalls.length > 0) {
7190
+ msg.tool_calls = response.toolCalls.map((tc) => ({
7191
+ id: tc.id,
7192
+ type: "function",
7193
+ function: { name: tc.name, arguments: tc.arguments }
7194
+ }));
7195
+ }
7196
+ return msg;
7197
+ }
7198
+ function assembleMessage(content, toolCalls, usage) {
7199
+ const msg = {
7200
+ role: "assistant",
7201
+ content
7202
+ };
7203
+ if (usage) {
7204
+ msg.usage = {
7205
+ prompt_tokens: usage.inputTokens,
7206
+ completion_tokens: usage.outputTokens,
7207
+ total_tokens: usage.totalTokens,
7208
+ prompt_tokens_details: {
7209
+ cached_tokens: usage.cachedInputTokens
7210
+ },
7211
+ completion_tokens_details: {
7212
+ reasoning_tokens: usage.reasoningTokens
7213
+ }
7214
+ };
7215
+ msg.response_metadata = {
7216
+ token_usage: {
7217
+ prompt_tokens: usage.inputTokens,
7218
+ completion_tokens: usage.outputTokens,
7219
+ total_tokens: usage.totalTokens,
7220
+ cached_tokens: usage.cachedInputTokens,
7221
+ cache_write_tokens: usage.cachedWriteTokens
7222
+ }
7223
+ };
7224
+ }
7225
+ if (toolCalls.length > 0) {
7226
+ msg.tool_calls = toolCalls.map((tc) => ({
7227
+ id: tc.id,
7228
+ type: "function",
7229
+ function: { name: tc.name, arguments: tc.arguments }
7230
+ }));
7231
+ }
7232
+ return msg;
7233
+ }
7234
+ function resolveCapabilities(provider) {
7235
+ switch (provider) {
7236
+ case "openai":
7237
+ case "azure":
7238
+ case "openai-compatible":
7239
+ return {
7240
+ structuredOutput: "native",
7241
+ strictToolCalling: true,
7242
+ streaming: true,
7243
+ provider
7244
+ };
7245
+ case "anthropic":
7246
+ return {
7247
+ structuredOutput: "tool_based",
7248
+ strictToolCalling: false,
7249
+ streaming: true,
7250
+ provider: "anthropic"
7251
+ };
7252
+ case "bedrock":
7253
+ return {
7254
+ structuredOutput: "tool_based",
7255
+ strictToolCalling: false,
7256
+ streaming: false,
7257
+ // streaming uses binary protocol
7258
+ provider: "bedrock"
7259
+ };
7260
+ case "vertex":
7261
+ return {
7262
+ structuredOutput: "native",
7263
+ strictToolCalling: false,
7264
+ streaming: true,
7265
+ provider: "google"
7266
+ };
7267
+ default:
7268
+ return { provider };
7269
+ }
7270
+ }
7271
+
7272
+ // src/providers/index.ts
7273
+ function createProvider(config) {
7274
+ switch (config.provider) {
7275
+ case "openai":
7276
+ return new OpenAIProvider(config);
7277
+ case "anthropic":
7278
+ return new AnthropicProvider(config);
7279
+ case "azure":
7280
+ return new AzureProvider(config);
7281
+ case "openai-compatible":
7282
+ return new OpenAICompatibleProvider(config);
7283
+ case "bedrock":
7284
+ return new BedrockProvider(config);
7285
+ case "vertex":
7286
+ return new VertexProvider(config);
7287
+ default:
7288
+ throw new Error(`Unknown provider: ${config.provider}`);
7289
+ }
7290
+ }
7291
+
7292
+ export { AnthropicProvider, AzureProvider, BaseProvider, BedrockProvider, DEFAULT_PROFILE_CONFIGS, GuardrailPhase, InMemoryMemoryStore, NativeJsonSchemaStrategy, OpenAICompatibleProvider, OpenAIProvider, ProviderError, StructuredOutputManager, ToolBasedStrategy, VertexProvider, agentVerdictRule, 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, resolveMemoryStore, resolveStrategy, resolveToolApprovalState, restoreSnapshot, runSmartAgentEvalHarness, startStreamingSession, traceSessionToOtlp, withTools, writeSummaryFactsToMemory };
5155
7293
  //# sourceMappingURL=index.mjs.map
5156
7294
  //# sourceMappingURL=index.mjs.map