@cognipeer/agent-sdk 0.10.3 → 0.10.5

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.cjs CHANGED
@@ -513,7 +513,7 @@ function getModelCapabilities(model) {
513
513
  return {
514
514
  provider,
515
515
  structuredOutput: supportsNativeStructuredOutput(provider) ? "native" : "tool_based",
516
- strictToolCalling: requiresStrictToolCalling(provider),
516
+ strictToolCalling: requiresStrictToolCalling(provider) && isOpenAIHostedEndpoint(model._lc),
517
517
  streaming: true
518
518
  };
519
519
  }
@@ -556,6 +556,17 @@ function supportsNativeStructuredOutput(provider) {
556
556
  function requiresStrictToolCalling(provider) {
557
557
  return ["openai"].includes(provider);
558
558
  }
559
+ function isOpenAIHostedEndpoint(lcModel) {
560
+ if (!lcModel) return true;
561
+ const baseURL = lcModel.clientConfig?.baseURL ?? lcModel.configuration?.baseURL ?? lcModel.baseURL;
562
+ if (typeof baseURL !== "string" || baseURL.trim() === "") return true;
563
+ try {
564
+ const host = new URL(baseURL).hostname.toLowerCase();
565
+ return host === "api.openai.com" || host.endsWith(".openai.azure.com") || host.endsWith(".cognitiveservices.azure.com");
566
+ } catch {
567
+ return false;
568
+ }
569
+ }
559
570
 
560
571
  // src/model.ts
561
572
  function isSmartModel(m) {
@@ -2520,13 +2531,19 @@ function resolveCapabilities(provider) {
2520
2531
  switch (provider) {
2521
2532
  case "openai":
2522
2533
  case "azure":
2523
- case "openai-compatible":
2524
2534
  return {
2525
2535
  structuredOutput: "native",
2526
2536
  strictToolCalling: true,
2527
2537
  streaming: true,
2528
2538
  provider
2529
2539
  };
2540
+ case "openai-compatible":
2541
+ return {
2542
+ structuredOutput: "native",
2543
+ strictToolCalling: false,
2544
+ streaming: true,
2545
+ provider
2546
+ };
2530
2547
  case "anthropic":
2531
2548
  return {
2532
2549
  structuredOutput: "tool_based",
@@ -3855,6 +3872,187 @@ Identical to an earlier ${duplicate.toolName} result. Use get_tool_response with
3855
3872
  }
3856
3873
  return notApplied4(output);
3857
3874
  }
3875
+ var identity = (value) => value;
3876
+ function describeLike(schema, source, suffix) {
3877
+ const description = [source.description, suffix].filter(Boolean).join(" ");
3878
+ return description ? schema.describe(description) : schema;
3879
+ }
3880
+ function jsonString(source) {
3881
+ return {
3882
+ schema: describeLike(zod.z.string(), source, "(a JSON value, encoded as a string)"),
3883
+ restore: (value) => {
3884
+ if (typeof value !== "string") return value;
3885
+ try {
3886
+ return JSON.parse(value);
3887
+ } catch {
3888
+ return value;
3889
+ }
3890
+ }
3891
+ };
3892
+ }
3893
+ function unwrapOptional(schema) {
3894
+ const def = schema._def;
3895
+ if (def?.typeName === "ZodOptional") return { inner: def.innerType, optional: true };
3896
+ if (def?.typeName === "ZodDefault") return { inner: def.innerType, optional: true };
3897
+ return { inner: schema, optional: false };
3898
+ }
3899
+ function isZodSchema2(schema) {
3900
+ return Boolean(schema) && typeof schema.safeParse === "function" && Boolean(schema._def);
3901
+ }
3902
+ function toStrictCompatible(schema) {
3903
+ const def = schema?._def;
3904
+ switch (def?.typeName) {
3905
+ case "ZodObject": {
3906
+ const shape = typeof def.shape === "function" ? def.shape() : def.shape;
3907
+ const keys = Object.keys(shape ?? {});
3908
+ const open = def.unknownKeys === "passthrough" || def.catchall && def.catchall._def?.typeName !== "ZodNever";
3909
+ if (keys.length === 0 && open) return jsonString(schema);
3910
+ const nextShape = {};
3911
+ const restores = {};
3912
+ for (const key of keys) {
3913
+ const field = shape[key];
3914
+ const { inner, optional } = unwrapOptional(field);
3915
+ const child = toStrictCompatible(inner);
3916
+ const childSchema = describeLike(child.schema, field.description ? field : inner);
3917
+ nextShape[key] = optional ? childSchema.nullable() : childSchema;
3918
+ restores[key] = { restore: child.restore, optional };
3919
+ }
3920
+ return {
3921
+ schema: describeLike(zod.z.object(nextShape), schema),
3922
+ restore: (value) => {
3923
+ if (!value || typeof value !== "object" || Array.isArray(value)) return value;
3924
+ const out = {};
3925
+ for (const [key, raw] of Object.entries(value)) {
3926
+ const entry = restores[key];
3927
+ if (raw === null && entry?.optional) continue;
3928
+ out[key] = entry ? entry.restore(raw) : raw;
3929
+ }
3930
+ return out;
3931
+ }
3932
+ };
3933
+ }
3934
+ case "ZodOptional":
3935
+ case "ZodDefault": {
3936
+ const child = toStrictCompatible(def.innerType);
3937
+ return {
3938
+ schema: describeLike(child.schema.nullable(), schema),
3939
+ restore: (value) => value === null ? void 0 : child.restore(value)
3940
+ };
3941
+ }
3942
+ case "ZodNullable": {
3943
+ const child = toStrictCompatible(def.innerType);
3944
+ return {
3945
+ schema: describeLike(child.schema.nullable(), schema),
3946
+ restore: (value) => value === null ? null : child.restore(value)
3947
+ };
3948
+ }
3949
+ case "ZodArray": {
3950
+ const child = toStrictCompatible(def.type);
3951
+ return {
3952
+ schema: describeLike(zod.z.array(child.schema), schema),
3953
+ restore: (value) => Array.isArray(value) ? value.map(child.restore) : value
3954
+ };
3955
+ }
3956
+ case "ZodUnion":
3957
+ case "ZodDiscriminatedUnion": {
3958
+ const rawOptions = def.options instanceof Map ? [...def.options.values()] : def.options;
3959
+ const options = rawOptions.map(toStrictCompatible);
3960
+ if (options.length === 1) return options[0];
3961
+ if (options.every((option, index) => option.schema === rawOptions[index])) {
3962
+ return { schema, restore: identity };
3963
+ }
3964
+ return {
3965
+ schema: describeLike(zod.z.union(options.map((option) => option.schema)), schema),
3966
+ restore: (value) => {
3967
+ const candidates = options.map((option) => option.restore(value));
3968
+ const index = candidates.findIndex((candidate, i) => rawOptions[i].safeParse(candidate).success);
3969
+ return candidates[index >= 0 ? index : 0];
3970
+ }
3971
+ };
3972
+ }
3973
+ case "ZodEffects":
3974
+ return toStrictCompatible(def.schema);
3975
+ case "ZodRecord":
3976
+ case "ZodAny":
3977
+ case "ZodUnknown":
3978
+ return jsonString(schema);
3979
+ default:
3980
+ return { schema, restore: identity };
3981
+ }
3982
+ }
3983
+ function topLevelObject(transform) {
3984
+ if (transform.schema._def?.typeName === "ZodObject") return transform;
3985
+ return {
3986
+ schema: zod.z.object({
3987
+ input: transform.schema.describe(
3988
+ transform.schema.description ?? "The tool arguments as a JSON object, encoded as a string"
3989
+ )
3990
+ }),
3991
+ restore: (value) => {
3992
+ const input = value && typeof value === "object" ? value.input : void 0;
3993
+ const restored = transform.restore(input);
3994
+ return restored && typeof restored === "object" ? restored : {};
3995
+ }
3996
+ };
3997
+ }
3998
+ function toStrictToolSchema(schema) {
3999
+ return topLevelObject(toStrictCompatible(schema));
4000
+ }
4001
+ function restoreToolCalls(message, restorers) {
4002
+ if (!message || typeof message !== "object" || restorers.size === 0) return message;
4003
+ const restoreList = (calls) => {
4004
+ if (!Array.isArray(calls) || calls.length === 0) return calls;
4005
+ return calls.map((call) => {
4006
+ const fn = call?.function;
4007
+ const name = call?.name ?? fn?.name;
4008
+ const restore = name ? restorers.get(name) : void 0;
4009
+ if (!restore) return call;
4010
+ let next = call;
4011
+ if (call.args && typeof call.args === "object") next = { ...next, args: restore(call.args) };
4012
+ if (fn && typeof fn.arguments === "string") {
4013
+ try {
4014
+ next = { ...next, function: { ...fn, arguments: JSON.stringify(restore(JSON.parse(fn.arguments))) } };
4015
+ } catch {
4016
+ }
4017
+ }
4018
+ return next;
4019
+ });
4020
+ };
4021
+ const m = message;
4022
+ const hasDirect = Array.isArray(m.tool_calls) && m.tool_calls.length > 0;
4023
+ const hasKwargs = Array.isArray(m.additional_kwargs?.tool_calls) && m.additional_kwargs.tool_calls.length > 0;
4024
+ if (!hasDirect && !hasKwargs) return message;
4025
+ return {
4026
+ ...message,
4027
+ ...hasDirect ? { tool_calls: restoreList(m.tool_calls) } : {},
4028
+ ...hasKwargs ? { additional_kwargs: { ...m.additional_kwargs, tool_calls: restoreList(m.additional_kwargs.tool_calls) } } : {}
4029
+ };
4030
+ }
4031
+ var strictViews = /* @__PURE__ */ new WeakMap();
4032
+ function prepareStrictToolMenu(tools) {
4033
+ const restorers = /* @__PURE__ */ new Map();
4034
+ const menu = tools.map((tool) => {
4035
+ const t = tool;
4036
+ if (!t || typeof t.name !== "string" || !isZodSchema2(t.schema)) return tool;
4037
+ const cached = strictViews.get(tool);
4038
+ if (cached && cached.schema === t.schema) {
4039
+ restorers.set(t.name, cached.restore);
4040
+ return cached.view;
4041
+ }
4042
+ try {
4043
+ const transform = toStrictToolSchema(t.schema);
4044
+ const view = Object.create(tool);
4045
+ view.schema = transform.schema;
4046
+ view.__lcTool = void 0;
4047
+ strictViews.set(tool, { schema: t.schema, view, restore: transform.restore });
4048
+ restorers.set(t.name, transform.restore);
4049
+ return view;
4050
+ } catch {
4051
+ return tool;
4052
+ }
4053
+ });
4054
+ return { menu, restorers };
4055
+ }
3858
4056
 
3859
4057
  // src/nodes/agentCore.ts
3860
4058
  function createAgentCoreNode(opts) {
@@ -3875,10 +4073,17 @@ function createAgentCoreNode(opts) {
3875
4073
  };
3876
4074
  let tools = runtime.tools ?? [];
3877
4075
  const shouldUseStrictToolCalling = Boolean(
3878
- runtime.responseFormat && runtime.model?.capabilities?.strictToolCalling
4076
+ runtime.model?.capabilities?.strictToolCalling
3879
4077
  );
4078
+ let strictRestorers = /* @__PURE__ */ new Map();
4079
+ const wireMenu = (menu) => {
4080
+ if (!shouldUseStrictToolCalling) return menu;
4081
+ const prepared = prepareStrictToolMenu(menu);
4082
+ strictRestorers = prepared.restorers;
4083
+ return prepared.menu;
4084
+ };
3880
4085
  const bindToolMenu = (menu) => runtime.model?.bindTools ? runtime.model.bindTools(
3881
- menu,
4086
+ wireMenu(menu),
3882
4087
  shouldUseStrictToolCalling ? { strict: true } : void 0
3883
4088
  ) : runtime.model;
3884
4089
  const menuMutationReachesProvider = typeof runtime.model?.bindTools === "function";
@@ -3889,7 +4094,8 @@ function createAgentCoreNode(opts) {
3889
4094
  const computeTraceToolDefinitions = (menu) => {
3890
4095
  if (!traceSession || !traceSession.resolvedConfig.logData || menu.length === 0) return void 0;
3891
4096
  try {
3892
- return menu.map((tool) => {
4097
+ const wire = shouldUseStrictToolCalling ? prepareStrictToolMenu(menu).menu : menu;
4098
+ return wire.map((tool) => {
3893
4099
  const def = toToolDefinition(tool, shouldUseStrictToolCalling ? true : void 0);
3894
4100
  return { name: def.name, description: def.description || void 0, parameters: def.parameters };
3895
4101
  });
@@ -4195,6 +4401,9 @@ ${append}`;
4195
4401
  throw err;
4196
4402
  }
4197
4403
  }
4404
+ if (shortCircuited === void 0 && strictRestorers.size > 0) {
4405
+ response = restoreToolCalls(response, strictRestorers);
4406
+ }
4198
4407
  if (!host?.has("postModelCall")) break;
4199
4408
  const postGate = await host.runGate("postModelCall", {
4200
4409
  message: response,
@@ -8793,6 +9002,21 @@ var todoUpdateItemSchema = zod.z.object({
8793
9002
  status: todoStatusSchema.optional(),
8794
9003
  evidence: zod.z.string().max(300).optional()
8795
9004
  });
9005
+ var todoItemWireSchema = zod.z.object({
9006
+ id: zod.z.number().int().min(1).describe("Sequential id starting from 1 (write) or an existing id (update)"),
9007
+ step: zod.z.string().min(1).optional(),
9008
+ title: zod.z.string().min(1).optional(),
9009
+ description: zod.z.string().min(1).optional(),
9010
+ owner: zod.z.string().min(1).optional(),
9011
+ exitCriteria: zod.z.string().min(1).optional(),
9012
+ status: todoStatusSchema.optional().describe("Required for every item on write"),
9013
+ evidence: zod.z.string().max(300).optional()
9014
+ });
9015
+ function describeTodoIssue(issue) {
9016
+ if (!issue) return void 0;
9017
+ const path2 = issue.path.length > 0 ? `todoList[${issue.path[0]}]${issue.path.slice(1).map((p) => `.${String(p)}`).join("")}` : "todoList";
9018
+ return `${path2}: ${issue.message}`;
9019
+ }
8796
9020
  function normalizeTodoItem(item, existing) {
8797
9021
  const id = item.id ?? existing?.id;
8798
9022
  const step = item.step || item.title || item.description || existing?.step || existing?.title || existing?.description || `Step ${id}`;
@@ -8909,7 +9133,7 @@ function createPlanTool(stateRef, toolName, description) {
8909
9133
  schema: zod.z.object({
8910
9134
  operation: zod.z.enum(["write", "read", "update"]),
8911
9135
  expectedVersion: zod.z.number().int().min(0).optional(),
8912
- todoList: zod.z.array(zod.z.union([todoWriteItemSchema, todoUpdateItemSchema])).optional()
9136
+ todoList: zod.z.array(todoItemWireSchema).optional()
8913
9137
  }),
8914
9138
  func: async ({ operation, expectedVersion, todoList }) => {
8915
9139
  const toolStateRef = manageTodo._stateRef;
@@ -8975,7 +9199,10 @@ function createPlanTool(stateRef, toolName, description) {
8975
9199
  return {
8976
9200
  status: "error",
8977
9201
  operation,
8978
- error: parsed.error.issues[0]?.message || "Invalid todoList for write.",
9202
+ // The wire schema has `status` optional (one shape for write and
9203
+ // update), so a missing one is caught here — named, so the model
9204
+ // can fix the call.
9205
+ error: describeTodoIssue(parsed.error.issues[0]) || "Invalid todoList for write.",
8979
9206
  version: currentVersion,
8980
9207
  adherenceScore: stateRef.adherenceScore || 0
8981
9208
  };
@@ -8998,7 +9225,7 @@ function createPlanTool(stateRef, toolName, description) {
8998
9225
  return {
8999
9226
  status: "error",
9000
9227
  operation,
9001
- error: parsed.error.issues[0]?.message || "Invalid todoList for update.",
9228
+ error: describeTodoIssue(parsed.error.issues[0]) || "Invalid todoList for update.",
9002
9229
  version: currentVersion,
9003
9230
  adherenceScore: stateRef.adherenceScore || 0
9004
9231
  };
@@ -14764,8 +14991,127 @@ function sanitizeBedrockDocName(name, fallback = "document") {
14764
14991
  return cleaned || fallback;
14765
14992
  }
14766
14993
 
14994
+ // src/providers/utils/jsonSchemaRefs.ts
14995
+ var DEFINITION_KEYS = ["definitions", "$defs"];
14996
+ function inlineJsonSchemaRefs(schema) {
14997
+ if (!isObject(schema) || !containsRef(schema)) return schema;
14998
+ const root = structuredClone(schema);
14999
+ const selfReaching = /* @__PURE__ */ new Map();
15000
+ const targetOf = (ref) => {
15001
+ const target = resolvePointer(root, ref);
15002
+ return isObject(target) ? target : void 0;
15003
+ };
15004
+ const reachesItself = (target) => {
15005
+ const known = selfReaching.get(target);
15006
+ if (known !== void 0) return known;
15007
+ const seen = /* @__PURE__ */ new Set();
15008
+ const walk = (node) => {
15009
+ if (Array.isArray(node)) return node.some(walk);
15010
+ if (!isObject(node)) return false;
15011
+ if (typeof node.$ref === "string") {
15012
+ const next = targetOf(node.$ref);
15013
+ if (next === target) return true;
15014
+ if (next && !seen.has(next)) {
15015
+ seen.add(next);
15016
+ if (walk(next)) return true;
15017
+ }
15018
+ }
15019
+ return Object.entries(node).some(([key, value]) => key !== "$ref" && walk(value));
15020
+ };
15021
+ const result2 = walk(target);
15022
+ selfReaching.set(target, result2);
15023
+ return result2;
15024
+ };
15025
+ const keptRefs = /* @__PURE__ */ new Set();
15026
+ const expand = (node) => {
15027
+ if (Array.isArray(node)) return node.map(expand);
15028
+ if (!isObject(node)) return node;
15029
+ if (typeof node.$ref === "string") {
15030
+ const target = targetOf(node.$ref);
15031
+ if (target && !reachesItself(target)) {
15032
+ const { $ref: _inlined, ...siblings } = node;
15033
+ return { ...expand(target), ...expandEntries(siblings) };
15034
+ }
15035
+ keptRefs.add(node.$ref);
15036
+ return expandEntries(node);
15037
+ }
15038
+ return expandEntries(node);
15039
+ };
15040
+ const expandEntries = (node) => {
15041
+ const out = {};
15042
+ for (const [key, value] of Object.entries(node)) out[key] = key === "$ref" ? value : expand(value);
15043
+ return out;
15044
+ };
15045
+ const { definitions: _definitions, $defs: _defs, ...rootBody } = root;
15046
+ const result = typeof root.$ref === "string" ? expand(rootBody) : expandEntries(rootBody);
15047
+ for (const key of DEFINITION_KEYS) delete result[key];
15048
+ for (const key of DEFINITION_KEYS) {
15049
+ const block = root[key];
15050
+ if (!isObject(block)) continue;
15051
+ const needed = neededEntries(key, block, keptRefs);
15052
+ if (needed.length === 0) continue;
15053
+ const kept = {};
15054
+ for (const name of Object.keys(block)) if (needed.includes(name)) kept[name] = block[name];
15055
+ result[key] = kept;
15056
+ }
15057
+ return result;
15058
+ }
15059
+ function neededEntries(key, block, keptRefs) {
15060
+ const needed = [];
15061
+ const queue = [...keptRefs];
15062
+ while (queue.length > 0) {
15063
+ const ref = queue.shift();
15064
+ const segments = pointerSegments(ref);
15065
+ if (!segments || segments[0] !== key || segments.length < 2) continue;
15066
+ const name = segments[1];
15067
+ if (needed.includes(name) || !(name in block)) continue;
15068
+ needed.push(name);
15069
+ collectRefs(block[name], queue);
15070
+ }
15071
+ return needed;
15072
+ }
15073
+ function collectRefs(node, into) {
15074
+ if (Array.isArray(node)) {
15075
+ node.forEach((child) => collectRefs(child, into));
15076
+ return;
15077
+ }
15078
+ if (!isObject(node)) return;
15079
+ if (typeof node.$ref === "string") into.push(node.$ref);
15080
+ for (const [k, value] of Object.entries(node)) if (k !== "$ref") collectRefs(value, into);
15081
+ }
15082
+ function containsRef(node) {
15083
+ if (Array.isArray(node)) return node.some(containsRef);
15084
+ if (!isObject(node)) return false;
15085
+ if (typeof node.$ref === "string") return true;
15086
+ return Object.values(node).some(containsRef);
15087
+ }
15088
+ function pointerSegments(ref) {
15089
+ if (ref === "#") return [];
15090
+ if (!ref.startsWith("#/")) return null;
15091
+ return ref.slice(2).split("/").map((segment) => segment.replace(/~1/g, "/").replace(/~0/g, "~"));
15092
+ }
15093
+ function resolvePointer(document, ref) {
15094
+ const segments = pointerSegments(ref);
15095
+ if (!segments) return void 0;
15096
+ let current = document;
15097
+ for (const segment of segments) {
15098
+ if (Array.isArray(current)) {
15099
+ if (!/^\d+$/.test(segment)) return void 0;
15100
+ current = current[Number(segment)];
15101
+ } else if (isObject(current) && Object.prototype.hasOwnProperty.call(current, segment)) {
15102
+ current = current[segment];
15103
+ } else {
15104
+ return void 0;
15105
+ }
15106
+ }
15107
+ return current;
15108
+ }
15109
+ function isObject(value) {
15110
+ return typeof value === "object" && value !== null && !Array.isArray(value);
15111
+ }
15112
+
14767
15113
  // src/providers/openai.ts
14768
- var OpenAIProvider = class extends BaseProvider {
15114
+ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
14769
15115
  constructor(config) {
14770
15116
  super();
14771
15117
  this.providerName = "openai";
@@ -14777,8 +15123,21 @@ var OpenAIProvider = class extends BaseProvider {
14777
15123
  ...config.organization ? { "OpenAI-Organization": config.organization } : {}
14778
15124
  };
14779
15125
  this.responsesApi = config.responsesApi ?? "auto";
15126
+ const ownInstance = new.target === _OpenAIProvider;
15127
+ this.responsesApiModels = ownInstance ? [...config.responsesApiModels ?? []] : [];
15128
+ this.responsesStreaming = ownInstance && config.responsesStreaming === true;
14780
15129
  if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
14781
15130
  }
15131
+ /** True when the caller listed this request's model in `responsesApiModels`. */
15132
+ isResponsesApiModel(request) {
15133
+ if (this.responsesApiModels.length === 0) return false;
15134
+ const model = request.model || this.defaultModel;
15135
+ return this.responsesApiModels.some((entry) => {
15136
+ if (typeof entry === "string") return entry === model;
15137
+ entry.lastIndex = 0;
15138
+ return entry.test(model);
15139
+ });
15140
+ }
14782
15141
  /** True when this request should go to the Responses API rather than Chat
14783
15142
  * Completions — which exposes `reasoning.effort` and reasoning summaries that
14784
15143
  * Chat Completions silently drops.
@@ -14787,10 +15146,14 @@ var OpenAIProvider = class extends BaseProvider {
14787
15146
  * carries a reasoning config AND the model name looks like an o-series/gpt-5
14788
15147
  * model. `never`/`always` let a caller state the answer instead, because the
14789
15148
  * name check is a guess and the two APIs differ in far more than reasoning —
14790
- * see `responsesApi` in OpenAIProviderConfig. */
15149
+ * see `responsesApi` in OpenAIProviderConfig.
15150
+ *
15151
+ * A model listed in `responsesApiModels` is the caller stating the answer
15152
+ * for that model alone; `never` still overrides it. */
14791
15153
  useResponsesApi(request) {
14792
15154
  if (this.responsesApi === "never") return false;
14793
15155
  if (this.responsesApi === "always") return true;
15156
+ if (this.isResponsesApiModel(request)) return true;
14794
15157
  if (!request.reasoning) return false;
14795
15158
  return isReasoningModel(request.model || this.defaultModel);
14796
15159
  }
@@ -14805,6 +15168,10 @@ var OpenAIProvider = class extends BaseProvider {
14805
15168
  }
14806
15169
  async *completeStream(request) {
14807
15170
  if (this.useResponsesApi(request)) {
15171
+ if (this.responsesStreaming && this.isResponsesApiModel(request)) {
15172
+ yield* this.streamResponses(request);
15173
+ return;
15174
+ }
14808
15175
  const response = await this.completeResponses(request);
14809
15176
  yield {
14810
15177
  id: response.id,
@@ -14920,10 +15287,101 @@ var OpenAIProvider = class extends BaseProvider {
14920
15287
  // ─── Responses API (reasoning models) ──────────────────────────────────────
14921
15288
  async completeResponses(request) {
14922
15289
  const body = this.buildResponsesBody(request);
15290
+ if (this.isResponsesApiModel(request)) adaptChatExtrasForResponses(body, request.extra);
14923
15291
  const res = await this.responsesFetch(body);
14924
15292
  const json = await res.json();
14925
15293
  return this.parseResponsesResponse(json, request.model || this.defaultModel);
14926
15294
  }
15295
+ /** Real Responses SSE streaming, for models opted in through
15296
+ * `responsesApiModels` with `responsesStreaming` on. Emits the same chunk
15297
+ * shapes as the Chat Completions stream: text deltas as they arrive, a tool
15298
+ * call's id + name when it opens and its argument fragments under that id,
15299
+ * then one final chunk with usage, finish reason and any reasoning summary
15300
+ * (taken from the completed response, exactly as the non-streaming parse
15301
+ * reads it). */
15302
+ async *streamResponses(request) {
15303
+ const modelId = request.model || this.defaultModel;
15304
+ const body = this.buildResponsesBody(request);
15305
+ adaptChatExtrasForResponses(body, request.extra);
15306
+ body.stream = true;
15307
+ const res = await this.responsesFetch(body);
15308
+ if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
15309
+ let id = "";
15310
+ let model = modelId;
15311
+ const calls = /* @__PURE__ */ new Map();
15312
+ let final;
15313
+ for await (const event of parseSSEStream(res.body)) {
15314
+ if (event.data === "[DONE]") break;
15315
+ let data;
15316
+ try {
15317
+ data = JSON.parse(event.data);
15318
+ } catch {
15319
+ continue;
15320
+ }
15321
+ switch (data.type ?? event.event) {
15322
+ case "response.created":
15323
+ case "response.in_progress":
15324
+ id = data.response?.id ?? id;
15325
+ model = data.response?.model ?? model;
15326
+ break;
15327
+ case "response.output_text.delta":
15328
+ if (data.delta) yield { id, model, delta: { content: data.delta } };
15329
+ break;
15330
+ case "response.output_item.added": {
15331
+ const item = data.item;
15332
+ if (item?.type !== "function_call") break;
15333
+ const call = { callId: item.call_id ?? item.id ?? "", name: item.name ?? "", arguments: item.arguments ?? "" };
15334
+ calls.set(item.id ?? call.callId, call);
15335
+ yield { id, model, delta: { toolCalls: [{ id: call.callId, name: call.name, arguments: call.arguments }] } };
15336
+ break;
15337
+ }
15338
+ case "response.function_call_arguments.delta": {
15339
+ const call = calls.get(data.item_id);
15340
+ if (!call || !data.delta) break;
15341
+ call.arguments += data.delta;
15342
+ yield { id, model, delta: { toolCalls: [{ id: call.callId, arguments: data.delta }] } };
15343
+ break;
15344
+ }
15345
+ case "response.output_item.done": {
15346
+ const item = data.item;
15347
+ if (item?.type !== "function_call") break;
15348
+ const full = typeof item.arguments === "string" ? item.arguments : "";
15349
+ const call = calls.get(item.id ?? item.call_id);
15350
+ if (!call) {
15351
+ const added = { callId: item.call_id ?? item.id ?? "", name: item.name ?? "", arguments: full };
15352
+ calls.set(item.id ?? added.callId, added);
15353
+ yield { id, model, delta: { toolCalls: [{ id: added.callId, name: added.name, arguments: full }] } };
15354
+ } else if (full.length > call.arguments.length && full.startsWith(call.arguments)) {
15355
+ const rest = full.slice(call.arguments.length);
15356
+ call.arguments = full;
15357
+ yield { id, model, delta: { toolCalls: [{ id: call.callId, arguments: rest }] } };
15358
+ }
15359
+ break;
15360
+ }
15361
+ case "response.completed":
15362
+ case "response.incomplete":
15363
+ final = this.parseResponsesResponse(data.response ?? {}, modelId);
15364
+ break;
15365
+ case "response.failed":
15366
+ case "error": {
15367
+ const err = data.response?.error ?? data.error ?? data;
15368
+ throw new ProviderError(
15369
+ `OpenAI Responses API stream error: ${err?.message ?? "unknown error"}`,
15370
+ this.providerName,
15371
+ void 0,
15372
+ data
15373
+ );
15374
+ }
15375
+ }
15376
+ }
15377
+ yield {
15378
+ id: final?.id || id,
15379
+ model: final?.model || model,
15380
+ delta: final?.reasoning ? { reasoning: final.reasoning } : {},
15381
+ usage: final?.usage,
15382
+ finishReason: final?.finishReason ?? (calls.size > 0 ? "tool_calls" : "stop")
15383
+ };
15384
+ }
14927
15385
  buildResponsesBody(request) {
14928
15386
  const body = {
14929
15387
  model: request.model || this.defaultModel,
@@ -14953,7 +15411,7 @@ var OpenAIProvider = class extends BaseProvider {
14953
15411
  format: {
14954
15412
  type: "json_schema",
14955
15413
  name: request.responseFormat.name ?? "response",
14956
- schema: request.responseFormat.schema,
15414
+ schema: inlineJsonSchemaRefs(request.responseFormat.schema),
14957
15415
  strict: true
14958
15416
  }
14959
15417
  };
@@ -15208,6 +15666,48 @@ function isReasoningModel(model) {
15208
15666
  const m = model.toLowerCase();
15209
15667
  return /(^|[/_-])o[0-9]/.test(m) || m.startsWith("o1") || m.startsWith("o3") || m.startsWith("o4") || m.includes("gpt-5") || m.includes("gpt5");
15210
15668
  }
15669
+ function adaptChatExtrasForResponses(body, extra) {
15670
+ const native = extra ?? {};
15671
+ if ("reasoning_effort" in body) {
15672
+ const effort = body.reasoning_effort;
15673
+ delete body.reasoning_effort;
15674
+ if (effort != null) {
15675
+ const reasoning = body.reasoning && typeof body.reasoning === "object" ? body.reasoning : {};
15676
+ if (reasoning.effort == null) body.reasoning = { ...reasoning, effort };
15677
+ }
15678
+ }
15679
+ for (const key of ["max_tokens", "max_completion_tokens"]) {
15680
+ if (!(key in body)) continue;
15681
+ const value = body[key];
15682
+ delete body[key];
15683
+ if (value != null && !("max_output_tokens" in native)) body.max_output_tokens = value;
15684
+ }
15685
+ if ("response_format" in body) {
15686
+ const format = toResponsesTextFormat(body.response_format);
15687
+ delete body.response_format;
15688
+ if (format && !("text" in native)) body.text = { ...body.text, format };
15689
+ }
15690
+ if (body.stream_options && typeof body.stream_options === "object" && "include_usage" in body.stream_options) {
15691
+ const { include_usage: _dropped, ...rest } = body.stream_options;
15692
+ if (Object.keys(rest).length > 0) body.stream_options = rest;
15693
+ else delete body.stream_options;
15694
+ }
15695
+ }
15696
+ function toResponsesTextFormat(rf) {
15697
+ if (!rf || typeof rf !== "object") return void 0;
15698
+ if (rf.type === "json_schema") {
15699
+ const js = rf.json_schema ?? {};
15700
+ return {
15701
+ type: "json_schema",
15702
+ name: js.name ?? "response",
15703
+ schema: inlineJsonSchemaRefs(js.schema),
15704
+ ...js.description != null ? { description: js.description } : {},
15705
+ ...js.strict != null ? { strict: js.strict } : {}
15706
+ };
15707
+ }
15708
+ if (rf.type === "json_object" || rf.type === "text") return { type: rf.type };
15709
+ return void 0;
15710
+ }
15211
15711
  function expandResponsesInput(input) {
15212
15712
  const out = [];
15213
15713
  for (const item of input) {
@@ -16658,6 +17158,7 @@ exports.httpSink = httpSink;
16658
17158
  exports.inMemoryCheckpointStore = inMemoryCheckpointStore;
16659
17159
  exports.inMemoryConversationStore = inMemoryConversationStore;
16660
17160
  exports.isHookImplemented = isHookImplemented;
17161
+ exports.isOpenAIHostedEndpoint = isOpenAIHostedEndpoint;
16661
17162
  exports.isSmartModel = isSmartModel;
16662
17163
  exports.isValidIban = isValidIban;
16663
17164
  exports.isValidLuhn = isValidLuhn;
@@ -16686,6 +17187,7 @@ exports.portkeyGateway = portkeyGateway;
16686
17187
  exports.portkeyGuardrail = portkeyGuardrail;
16687
17188
  exports.preopenSkills = preopenSkills;
16688
17189
  exports.preopenToolCallId = preopenToolCallId;
17190
+ exports.prepareStrictToolMenu = prepareStrictToolMenu;
16689
17191
  exports.promptInjectionGuard = promptInjectionGuard;
16690
17192
  exports.rateLimit = rateLimit;
16691
17193
  exports.readMemoryFacts = readMemoryFacts;
@@ -16705,6 +17207,7 @@ exports.resolveToolApprovalState = resolveToolApprovalState;
16705
17207
  exports.resolveUserQuestionState = resolveUserQuestionState;
16706
17208
  exports.responseCache = responseCache;
16707
17209
  exports.restoreSnapshot = restoreSnapshot;
17210
+ exports.restoreToolCalls = restoreToolCalls;
16708
17211
  exports.runSmartAgentEvalHarness = runSmartAgentEvalHarness;
16709
17212
  exports.scoreItemsByRelevance = scoreItemsByRelevance;
16710
17213
  exports.searchSkills = searchSkills;
@@ -16715,6 +17218,8 @@ exports.setTokenCounter = setTokenCounter;
16715
17218
  exports.startStreamingSession = startStreamingSession;
16716
17219
  exports.summarizeObject = summarizeObject;
16717
17220
  exports.textFromContent = textFromContent;
17221
+ exports.toStrictCompatible = toStrictCompatible;
17222
+ exports.toStrictToolSchema = toStrictToolSchema;
16718
17223
  exports.tokenize = tokenize;
16719
17224
  exports.toolPolicy = toolPolicy;
16720
17225
  exports.traceSessionToOtlp = traceSessionToOtlp;