@cognipeer/agent-sdk 0.10.4 → 0.10.6

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
@@ -28,6 +28,238 @@ var __export = (target, all) => {
28
28
  __defProp(target, name, { get: all[name], enumerable: true });
29
29
  };
30
30
 
31
+ // src/utils/content.ts
32
+ function isRecord(value) {
33
+ return value !== null && typeof value === "object" && !Array.isArray(value);
34
+ }
35
+ function nonEmpty(value) {
36
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
37
+ }
38
+ function kindFromMediaType(mediaType) {
39
+ const top = mediaType?.split("/")[0];
40
+ return top === "image" || top === "audio" || top === "video" ? top : "file";
41
+ }
42
+ function inlineDataOf(part) {
43
+ const inline = part.inlineData ?? part.inline_data ?? part.fileData ?? part.file_data;
44
+ return isRecord(inline) ? inline : void 0;
45
+ }
46
+ function mediaKindOf(part) {
47
+ if (!isRecord(part)) return void 0;
48
+ const type = nonEmpty(part.type);
49
+ if (type) return MEDIA_PART_KINDS[type];
50
+ const inline = inlineDataOf(part);
51
+ if (inline) return kindFromMediaType(nonEmpty(inline.mimeType) ?? nonEmpty(inline.mime_type));
52
+ return void 0;
53
+ }
54
+ function hasMediaPart(content) {
55
+ return Array.isArray(content) && content.some((part) => mediaKindOf(part) !== void 0);
56
+ }
57
+ function urlOfPart(part) {
58
+ const image = part.image_url;
59
+ const video = part.video_url;
60
+ return nonEmpty(typeof image === "string" ? image : image?.url) ?? nonEmpty(typeof video === "string" ? video : video?.url) ?? nonEmpty(part.url) ?? nonEmpty(part.source?.url) ?? nonEmpty(inlineDataOf(part)?.fileUri) ?? nonEmpty(inlineDataOf(part)?.file_uri);
61
+ }
62
+ function mediaTypeOfPart(part) {
63
+ const image = isRecord(part.image_url) ? part.image_url : void 0;
64
+ const source = isRecord(part.source) ? part.source : void 0;
65
+ const inline = inlineDataOf(part);
66
+ const audioFormat = nonEmpty(part.input_audio?.format);
67
+ const fromDataUrl = /^data:([^;,]+)/i.exec(urlOfPart(part) ?? "")?.[1];
68
+ return nonEmpty(part.mediaType) ?? nonEmpty(part.media_type) ?? nonEmpty(part.mimeType) ?? nonEmpty(part.mime_type) ?? nonEmpty(source?.mediaType) ?? nonEmpty(source?.media_type) ?? nonEmpty(image?.mediaType) ?? nonEmpty(image?.media_type) ?? nonEmpty(inline?.mimeType) ?? nonEmpty(inline?.mime_type) ?? nonEmpty(fromDataUrl) ?? (audioFormat ? `audio/${audioFormat}` : void 0);
69
+ }
70
+ function nameOfPart(part) {
71
+ const file = isRecord(part.file) ? part.file : void 0;
72
+ const image = isRecord(part.image_url) ? part.image_url : void 0;
73
+ const source = isRecord(part.source) ? part.source : void 0;
74
+ return nonEmpty(part.name) ?? nonEmpty(part.fileName) ?? nonEmpty(part.filename) ?? nonEmpty(part.file_name) ?? nonEmpty(file?.filename) ?? nonEmpty(file?.fileName) ?? nonEmpty(image?.name) ?? nonEmpty(source?.fileName) ?? nonEmpty(source?.filename) ?? nonEmpty(part.fileId) ?? nonEmpty(part.file_id) ?? nonEmpty(file?.file_id) ?? nonEmpty(file?.fileId) ?? nonEmpty(image?.fileId) ?? nonEmpty(image?.file_id) ?? nonEmpty(part.image_file?.file_id);
75
+ }
76
+ function labelFromUrl(url) {
77
+ if (!url || url.startsWith("data:")) return void 0;
78
+ try {
79
+ const parsed = new URL(url);
80
+ if (parsed.protocol === "session-file:") return nonEmpty(parsed.hostname) ?? nonEmpty(parsed.pathname.replace(/^\/+/, ""));
81
+ if (parsed.protocol === "http:" || parsed.protocol === "https:") {
82
+ return nonEmpty(decodeURIComponent(parsed.pathname.split("/").filter(Boolean).pop() ?? ""));
83
+ }
84
+ } catch {
85
+ }
86
+ return void 0;
87
+ }
88
+ function describeContentPart(part) {
89
+ const kind = mediaKindOf(part);
90
+ if (!kind || !isRecord(part)) return void 0;
91
+ const label = nameOfPart(part) ?? mediaTypeOfPart(part) ?? labelFromUrl(urlOfPart(part));
92
+ if (!label) return `[${kind}]`;
93
+ const clean = label.replace(/[\r\n\]]+/g, " ").slice(0, PLACEHOLDER_LABEL_MAX);
94
+ return `[${kind}: ${clean}]`;
95
+ }
96
+ function contentPartToText(part, fallback) {
97
+ if (typeof part === "string") return part;
98
+ if (!isRecord(part)) return part == null ? "" : String(part);
99
+ const media = describeContentPart(part);
100
+ if (media) return media;
101
+ if (typeof part.text === "string") return part.text;
102
+ if (typeof part.content === "string") return part.content;
103
+ if (fallback) return fallback(part);
104
+ try {
105
+ return JSON.stringify(part);
106
+ } catch {
107
+ return String(part);
108
+ }
109
+ }
110
+ function contentToString(content) {
111
+ try {
112
+ if (content == null) return "";
113
+ if (typeof content === "string") return content;
114
+ if (Array.isArray(content)) {
115
+ return content.map((p) => {
116
+ if (!p || typeof p !== "object") return String(p);
117
+ if (p.type === "text") return String(p.text ?? "");
118
+ if (p.type === "image_url") {
119
+ const img = p.image_url;
120
+ const url = typeof img === "string" ? img : img?.url;
121
+ if (typeof url === "string" && /^https?:\/\//i.test(url)) {
122
+ const detail = typeof img === "string" ? void 0 : img?.detail;
123
+ return `[image:${url}${detail ? ` detail=${detail}` : ""}]`;
124
+ }
125
+ return describeContentPart(p) ?? "[image]";
126
+ }
127
+ const media = describeContentPart(p);
128
+ if (media) return media;
129
+ return JSON.stringify(p);
130
+ }).join("\n");
131
+ }
132
+ return describeContentPart(content) ?? JSON.stringify(content);
133
+ } catch {
134
+ return String(content);
135
+ }
136
+ }
137
+ function mergeContentsToString(contents) {
138
+ return contents.map((c) => contentToString(c)).join("\n");
139
+ }
140
+ function safeStringify(value, pretty = false) {
141
+ if (value == null) return "";
142
+ if (typeof value === "string") return value;
143
+ try {
144
+ return JSON.stringify(value, null, pretty ? 2 : void 0) ?? String(value);
145
+ } catch {
146
+ return String(value);
147
+ }
148
+ }
149
+ function extractMessageText(message) {
150
+ if (!message) return "";
151
+ const content = message.content;
152
+ if (typeof content === "string") return content;
153
+ if (Array.isArray(content)) {
154
+ return content.map((part) => typeof part === "string" ? part : part?.text ?? part?.content ?? "").join("");
155
+ }
156
+ return "";
157
+ }
158
+ function textFromContent(content) {
159
+ if (typeof content === "string") return content;
160
+ if (!Array.isArray(content)) return "";
161
+ return content.filter((part) => isTextPart(part)).map((part) => part.text ?? "").join("\n");
162
+ }
163
+ function isTextPart(part) {
164
+ return !!part && typeof part === "object" && part.type === "text";
165
+ }
166
+ function mapTextParts(content, fn) {
167
+ if (typeof content === "string") return fn(content);
168
+ if (!Array.isArray(content)) return content;
169
+ return content.map((part) => isTextPart(part) ? { ...part, text: fn(part.text) } : part);
170
+ }
171
+ function replaceTextContent(content, text) {
172
+ if (typeof content === "string" || content == null) return text;
173
+ if (!Array.isArray(content)) return text;
174
+ const textIndexes = content.reduce((acc, part, index) => {
175
+ if (isTextPart(part)) acc.push(index);
176
+ return acc;
177
+ }, []);
178
+ if (textIndexes.length === 0) {
179
+ return text.length > 0 ? [{ type: "text", text }, ...content] : content;
180
+ }
181
+ const [first, ...rest] = textIndexes;
182
+ const dropped = new Set(rest);
183
+ return content.map((part, index) => index === first ? { ...part, text } : part).filter((_part, index) => !dropped.has(index));
184
+ }
185
+ function normalizeSource(source) {
186
+ if (!source || typeof source !== "object") return {};
187
+ const src = source;
188
+ if (src.type === "base64" && typeof src.data === "string") {
189
+ return {
190
+ sourceType: "base64",
191
+ mediaType: typeof src.mediaType === "string" ? src.mediaType : void 0,
192
+ // base64 encodes 3 bytes per 4 characters; padding makes this an upper bound.
193
+ approxBytes: Math.floor(src.data.length * 3 / 4)
194
+ };
195
+ }
196
+ if (src.type === "url" && typeof src.url === "string") {
197
+ return {
198
+ sourceType: "url",
199
+ url: src.url,
200
+ mediaType: typeof src.mediaType === "string" ? src.mediaType : void 0
201
+ };
202
+ }
203
+ return {};
204
+ }
205
+ function collectAttachments(content) {
206
+ if (!Array.isArray(content)) return [];
207
+ const attachments = [];
208
+ content.forEach((part, index) => {
209
+ if (!part || typeof part !== "object") return;
210
+ const type = part.type;
211
+ if (!type || type === "text") return;
212
+ if (type === "image" || type === "audio" || type === "video" || type === "file") {
213
+ attachments.push({
214
+ index,
215
+ kind: type,
216
+ fileName: typeof part.fileName === "string" ? part.fileName : void 0,
217
+ ...normalizeSource(part.source)
218
+ });
219
+ return;
220
+ }
221
+ if (type === "image_url") {
222
+ const image = part.image_url;
223
+ const url = typeof image === "string" ? image : image?.url;
224
+ if (typeof url === "string" && url.startsWith("data:")) {
225
+ const comma = url.indexOf(",");
226
+ const header = url.slice(5, comma);
227
+ attachments.push({
228
+ index,
229
+ kind: "image",
230
+ sourceType: "base64",
231
+ mediaType: header.split(";")[0] || void 0,
232
+ approxBytes: Math.floor((url.length - comma - 1) * 3 / 4)
233
+ });
234
+ } else {
235
+ attachments.push({ index, kind: "image", sourceType: "url", url });
236
+ }
237
+ return;
238
+ }
239
+ attachments.push({ index, kind: "unknown" });
240
+ });
241
+ return attachments;
242
+ }
243
+ var MEDIA_PART_KINDS, PLACEHOLDER_LABEL_MAX;
244
+ var init_content = __esm({
245
+ "src/utils/content.ts"() {
246
+ MEDIA_PART_KINDS = {
247
+ image: "image",
248
+ image_url: "image",
249
+ input_image: "image",
250
+ image_file: "image",
251
+ audio: "audio",
252
+ input_audio: "audio",
253
+ video: "video",
254
+ video_url: "video",
255
+ file: "file",
256
+ input_file: "file",
257
+ document: "file"
258
+ };
259
+ PLACEHOLDER_LABEL_MAX = 80;
260
+ }
261
+ });
262
+
31
263
  // src/utils/utilTokens.ts
32
264
  var utilTokens_exports = {};
33
265
  __export(utilTokens_exports, {
@@ -52,7 +284,7 @@ function getTokenCounter() {
52
284
  function countApproxTokens(text) {
53
285
  return activeTokenCounter(text || "");
54
286
  }
55
- function extractMessageText(m) {
287
+ function extractMessageText2(m) {
56
288
  if (!m) return "";
57
289
  const parts = [];
58
290
  parts.push(m.role || "");
@@ -62,6 +294,7 @@ function extractMessageText(m) {
62
294
  } else if (Array.isArray(m.content)) {
63
295
  for (const c of m.content) {
64
296
  if (typeof c === "string") parts.push(c);
297
+ else if (describeContentPart(c)) parts.push(describeContentPart(c));
65
298
  else if (c?.text) parts.push(c.text);
66
299
  else if (c?.content) parts.push(c.content);
67
300
  else if (c?.type === "tool_use" && c?.input) {
@@ -72,7 +305,7 @@ function extractMessageText(m) {
72
305
  }
73
306
  }
74
307
  } else if (m.content != null && typeof m.content === "object") {
75
- parts.push(JSON.stringify(m.content));
308
+ parts.push(describeContentPart(m.content) ?? JSON.stringify(m.content));
76
309
  }
77
310
  if (Array.isArray(m.tool_calls)) {
78
311
  for (const tc of m.tool_calls) {
@@ -102,12 +335,13 @@ function extractMessageText(m) {
102
335
  }
103
336
  function countMessagesTokens(messages) {
104
337
  if (!Array.isArray(messages) || messages.length === 0) return 0;
105
- const allText = messages.map(extractMessageText).join("\n");
338
+ const allText = messages.map(extractMessageText2).join("\n");
106
339
  return countApproxTokens(allText) + messages.length * 4;
107
340
  }
108
341
  var activeTokenCounter;
109
342
  var init_utilTokens = __esm({
110
343
  "src/utils/utilTokens.ts"() {
344
+ init_content();
111
345
  activeTokenCounter = defaultTokenCounter;
112
346
  }
113
347
  });
@@ -568,6 +802,125 @@ function isOpenAIHostedEndpoint(lcModel) {
568
802
  }
569
803
  }
570
804
 
805
+ // src/utils/abort.ts
806
+ var POLL_INTERVAL_MS = 100;
807
+ var NOOP = () => void 0;
808
+ function isObject(value) {
809
+ return value !== null && typeof value === "object";
810
+ }
811
+ function isRealSignal(value) {
812
+ return typeof AbortSignal !== "undefined" && value instanceof AbortSignal;
813
+ }
814
+ function isCancellationRequested(source) {
815
+ if (!source) return false;
816
+ for (const candidate of [source.signal, source.cancellationToken]) {
817
+ if (!isObject(candidate)) continue;
818
+ if (candidate.aborted === true || candidate.isCancellationRequested === true) return true;
819
+ }
820
+ return false;
821
+ }
822
+ function linkAbortSignal(source) {
823
+ if (!source) return { signal: void 0, dispose: NOOP };
824
+ const candidates = [];
825
+ for (const candidate of [source.signal, source.cancellationToken]) {
826
+ if (isObject(candidate) && !candidates.includes(candidate)) candidates.push(candidate);
827
+ }
828
+ if (candidates.length === 0) return { signal: void 0, dispose: NOOP };
829
+ const real = candidates.filter(isRealSignal);
830
+ if (candidates.length === 1 && real.length === 1) {
831
+ return { signal: real[0], dispose: NOOP };
832
+ }
833
+ const controller = new AbortController();
834
+ const cleanups = [];
835
+ let polled = [];
836
+ let timer;
837
+ const fire = (reason) => {
838
+ if (controller.signal.aborted) return;
839
+ try {
840
+ controller.abort(reason);
841
+ } catch {
842
+ controller.abort();
843
+ }
844
+ };
845
+ const dispose = () => {
846
+ if (timer !== void 0) {
847
+ clearInterval(timer);
848
+ timer = void 0;
849
+ }
850
+ polled = [];
851
+ for (const cleanup of cleanups.splice(0)) {
852
+ try {
853
+ cleanup();
854
+ } catch {
855
+ }
856
+ }
857
+ };
858
+ for (const candidate of candidates) {
859
+ if (candidate.aborted === true || candidate.isCancellationRequested === true) {
860
+ fire(isRealSignal(candidate) ? candidate.reason : void 0);
861
+ return { signal: controller.signal, dispose };
862
+ }
863
+ }
864
+ for (const candidate of candidates) {
865
+ if (typeof candidate.addEventListener === "function") {
866
+ const onAbort = () => fire(isRealSignal(candidate) ? candidate.reason : void 0);
867
+ candidate.addEventListener("abort", onAbort, { once: true });
868
+ cleanups.push(() => candidate.removeEventListener?.("abort", onAbort));
869
+ continue;
870
+ }
871
+ if (typeof candidate.onCancellationRequested === "function") {
872
+ let handle;
873
+ try {
874
+ handle = candidate.onCancellationRequested(() => fire());
875
+ } catch {
876
+ handle = void 0;
877
+ }
878
+ if (isObject(handle) && typeof handle.dispose === "function") {
879
+ cleanups.push(() => handle.dispose());
880
+ }
881
+ polled.push(candidate);
882
+ continue;
883
+ }
884
+ if ("isCancellationRequested" in candidate || "aborted" in candidate) {
885
+ polled.push(candidate);
886
+ }
887
+ }
888
+ if (polled.length > 0) {
889
+ timer = setInterval(() => {
890
+ for (const candidate of polled) {
891
+ if (candidate.aborted === true || candidate.isCancellationRequested === true) {
892
+ fire();
893
+ dispose();
894
+ return;
895
+ }
896
+ }
897
+ }, POLL_INTERVAL_MS);
898
+ timer.unref?.();
899
+ }
900
+ return { signal: controller.signal, dispose };
901
+ }
902
+ function abortableSleep(ms, signal) {
903
+ if (!signal) return new Promise((resolve) => setTimeout(resolve, ms));
904
+ if (signal.aborted) return Promise.reject(abortReason(signal));
905
+ return new Promise((resolve, reject) => {
906
+ const onAbort = () => {
907
+ clearTimeout(timer);
908
+ reject(abortReason(signal));
909
+ };
910
+ const timer = setTimeout(() => {
911
+ signal.removeEventListener("abort", onAbort);
912
+ resolve();
913
+ }, ms);
914
+ signal.addEventListener("abort", onAbort, { once: true });
915
+ });
916
+ }
917
+ function abortReason(signal) {
918
+ if (signal.reason !== void 0) return signal.reason;
919
+ const error = new Error("This operation was aborted");
920
+ error.name = "AbortError";
921
+ return error;
922
+ }
923
+
571
924
  // src/model.ts
572
925
  function isSmartModel(m) {
573
926
  return !!m && typeof m === "object" && typeof m.invoke === "function";
@@ -576,6 +929,10 @@ function withTools(model, tools, options) {
576
929
  if (model?.bindTools) return model.bindTools(tools, options);
577
930
  return model;
578
931
  }
932
+ function withLinkedSignal(options, signal) {
933
+ if (!signal || options?.signal === signal) return options;
934
+ return { ...options || {}, signal };
935
+ }
579
936
  function fromLangchainModel(lcModel) {
580
937
  if (!lcModel) throw new Error("fromLangchainModel: model is undefined/null");
581
938
  const normalizeContent = (content) => {
@@ -611,7 +968,13 @@ function fromLangchainModel(lcModel) {
611
968
  const adapted = {
612
969
  invoke: async (messages, options) => {
613
970
  const lcMessages = messages.map(toLC);
614
- const response = await lcModel.invoke(lcMessages, options);
971
+ const link = linkAbortSignal(options);
972
+ let response;
973
+ try {
974
+ response = await lcModel.invoke(lcMessages, withLinkedSignal(options, link.signal));
975
+ } finally {
976
+ link.dispose();
977
+ }
615
978
  if (response && typeof response === "object") {
616
979
  const content = response.content ?? response.text ?? "";
617
980
  return {
@@ -629,10 +992,15 @@ function fromLangchainModel(lcModel) {
629
992
  stream: async function* (messages, options) {
630
993
  if (typeof lcModel.stream !== "function") return;
631
994
  const lcMessages = messages.map(toLC);
632
- const streamResult = lcModel.stream(lcMessages, options);
633
- const stream = typeof streamResult?.then === "function" ? await streamResult : streamResult;
634
- for await (const chunk of stream) {
635
- yield chunk;
995
+ const link = linkAbortSignal(options);
996
+ try {
997
+ const streamResult = lcModel.stream(lcMessages, withLinkedSignal(options, link.signal));
998
+ const stream = typeof streamResult?.then === "function" ? await streamResult : streamResult;
999
+ for await (const chunk of stream) {
1000
+ yield chunk;
1001
+ }
1002
+ } finally {
1003
+ link.dispose();
636
1004
  }
637
1005
  },
638
1006
  bindTools: (tools, options) => {
@@ -811,6 +1179,9 @@ function safe(v) {
811
1179
  const n = Number(v);
812
1180
  return Number.isFinite(n) && n >= 0 ? n : 0;
813
1181
  }
1182
+
1183
+ // src/utils/tracing.ts
1184
+ init_content();
814
1185
  var DEFAULT_COGNIPEER_URL = "https://console.cognipeer.com/api/client/v1/tracing/sessions";
815
1186
  function fileSink(path2) {
816
1187
  return { type: "file", path: path2 };
@@ -1555,6 +1926,12 @@ function buildMessageLabel(role, index) {
1555
1926
  const base = `${normalized.charAt(0).toUpperCase()}${normalized.slice(1)} Message`;
1556
1927
  return index > 1 ? `${base} #${index}` : base;
1557
1928
  }
1929
+ function structuredMessageParts(content) {
1930
+ if (!hasMediaPart(content)) return void 0;
1931
+ const cloned = sanitizeTracePayload(content);
1932
+ if (!Array.isArray(cloned)) return void 0;
1933
+ return cloned.map((part) => typeof part === "string" ? { type: "text", text: part } : part);
1934
+ }
1558
1935
  function toDisplayString(value) {
1559
1936
  const sanitized = sanitizeTracePayload(value);
1560
1937
  if (sanitized == null) return "";
@@ -1563,7 +1940,9 @@ function toDisplayString(value) {
1563
1940
  return sanitized.map((item) => toDisplayString(item)).filter((part) => part && part.trim().length > 0).join("\n");
1564
1941
  }
1565
1942
  if (typeof sanitized === "object") {
1566
- if (typeof sanitized.text === "string" && Object.keys(sanitized).length === 1) {
1943
+ const media = describeContentPart(sanitized);
1944
+ if (media) return media;
1945
+ if (typeof sanitized.text === "string" && (Object.keys(sanitized).length === 1 || sanitized.type === "text")) {
1567
1946
  return sanitized.text;
1568
1947
  }
1569
1948
  if (typeof sanitized.content === "string") {
@@ -1635,11 +2014,13 @@ function convertMessageListToSections(messageList) {
1635
2014
  if (!content || !content.trim().length) continue;
1636
2015
  }
1637
2016
  }
2017
+ const parts = structuredMessageParts(message?.content);
1638
2018
  sections.push({
1639
2019
  kind: "message",
1640
2020
  label: buildMessageLabel(role, next),
1641
2021
  role,
1642
2022
  content,
2023
+ ...parts ? { parts } : {},
1643
2024
  metadata: typeof message?.name === "string" ? { name: message.name } : void 0
1644
2025
  });
1645
2026
  }
@@ -2067,50 +2448,63 @@ function fromNativeProvider(provider, options) {
2067
2448
  modelName,
2068
2449
  capabilities: resolveCapabilities(provider.providerName),
2069
2450
  invoke: async (messages, _invokeOptions) => {
2070
- const request = buildRequest(messages, boundTools, options, _invokeOptions);
2071
- const response = await provider.complete(request);
2072
- return toBaseChatMessage(response);
2451
+ const link = linkAbortSignal(_invokeOptions);
2452
+ try {
2453
+ const request = buildRequest(messages, boundTools, options, _invokeOptions);
2454
+ if (link.signal) request.signal = link.signal;
2455
+ const response = await provider.complete(request);
2456
+ return toBaseChatMessage(response);
2457
+ } finally {
2458
+ link.dispose();
2459
+ }
2073
2460
  },
2074
2461
  stream: async function* (messages, _invokeOptions) {
2075
- const request = buildRequest(messages, boundTools, options, _invokeOptions);
2076
- request.stream = true;
2077
- let fullContent = "";
2078
- let allToolCalls = [];
2079
- let lastUsage;
2080
- let reasoningBlocks;
2081
- const toolCallBuffers = /* @__PURE__ */ new Map();
2082
- for await (const chunk of provider.completeStream(request)) {
2083
- if (chunk.delta.content) {
2084
- fullContent += chunk.delta.content;
2085
- yield chunk.delta.content;
2086
- }
2087
- if (chunk.delta.reasoning?.blocks?.length) {
2088
- reasoningBlocks = chunk.delta.reasoning.blocks;
2089
- }
2090
- if (chunk.delta.toolCalls) {
2091
- for (const tc of chunk.delta.toolCalls) {
2092
- if (tc.id) {
2093
- const existing = toolCallBuffers.get(tc.id);
2094
- if (existing) {
2095
- if (tc.arguments) existing.arguments += tc.arguments;
2096
- } else {
2097
- toolCallBuffers.set(tc.id, {
2098
- id: tc.id,
2099
- name: tc.name ?? "",
2100
- arguments: tc.arguments ?? ""
2101
- });
2462
+ const link = linkAbortSignal(_invokeOptions);
2463
+ try {
2464
+ const request = buildRequest(messages, boundTools, options, _invokeOptions);
2465
+ request.stream = true;
2466
+ if (link.signal) request.signal = link.signal;
2467
+ let fullContent = "";
2468
+ let allToolCalls = [];
2469
+ let lastUsage;
2470
+ let reasoningBlocks;
2471
+ const toolCallBuffers = /* @__PURE__ */ new Map();
2472
+ for await (const chunk of provider.completeStream(request)) {
2473
+ if (chunk.delta.content) {
2474
+ fullContent += chunk.delta.content;
2475
+ yield chunk.delta.content;
2476
+ }
2477
+ if (chunk.delta.reasoning?.blocks?.length) {
2478
+ reasoningBlocks = chunk.delta.reasoning.blocks;
2479
+ }
2480
+ if (chunk.delta.toolCalls) {
2481
+ for (const tc of chunk.delta.toolCalls) {
2482
+ if (tc.id) {
2483
+ const existing = toolCallBuffers.get(tc.id);
2484
+ if (existing) {
2485
+ if (tc.arguments) existing.arguments += tc.arguments;
2486
+ } else {
2487
+ toolCallBuffers.set(tc.id, {
2488
+ id: tc.id,
2489
+ name: tc.name ?? "",
2490
+ arguments: tc.arguments ?? ""
2491
+ });
2492
+ }
2102
2493
  }
2103
2494
  }
2104
2495
  }
2496
+ if (chunk.usage) lastUsage = chunk.usage;
2105
2497
  }
2106
- if (chunk.usage) lastUsage = chunk.usage;
2107
- }
2108
- allToolCalls = [...toolCallBuffers.values()];
2109
- const finalMessage = assembleMessage(fullContent, allToolCalls, lastUsage);
2110
- if (reasoningBlocks?.length) {
2111
- finalMessage.reasoning = { blocks: reasoningBlocks };
2498
+ if (link.signal?.aborted) return;
2499
+ allToolCalls = [...toolCallBuffers.values()];
2500
+ const finalMessage = assembleMessage(fullContent, allToolCalls, lastUsage);
2501
+ if (reasoningBlocks?.length) {
2502
+ finalMessage.reasoning = { blocks: reasoningBlocks };
2503
+ }
2504
+ yield finalMessage;
2505
+ } finally {
2506
+ link.dispose();
2112
2507
  }
2113
- yield finalMessage;
2114
2508
  },
2115
2509
  bindTools: (tools, _bindOptions) => {
2116
2510
  const strict = _bindOptions?.strict ?? false;
@@ -2573,136 +2967,7 @@ function resolveCapabilities(provider) {
2573
2967
 
2574
2968
  // src/smart/toolResponses.ts
2575
2969
  init_utilTokens();
2576
-
2577
- // src/utils/content.ts
2578
- function contentToString(content) {
2579
- try {
2580
- if (content == null) return "";
2581
- if (typeof content === "string") return content;
2582
- if (Array.isArray(content)) {
2583
- return content.map((p) => {
2584
- if (!p || typeof p !== "object") return String(p);
2585
- if (p.type === "text") return String(p.text ?? "");
2586
- if (p.type === "image_url") {
2587
- const img = p.image_url;
2588
- const url = typeof img === "string" ? img : img?.url;
2589
- const detail = typeof img === "string" ? void 0 : img?.detail;
2590
- return `[image:${url || "unknown"}${detail ? ` detail=${detail}` : ""}]`;
2591
- }
2592
- return JSON.stringify(p);
2593
- }).join("\n");
2594
- }
2595
- return JSON.stringify(content);
2596
- } catch {
2597
- return String(content);
2598
- }
2599
- }
2600
- function mergeContentsToString(contents) {
2601
- return contents.map((c) => contentToString(c)).join("\n");
2602
- }
2603
- function safeStringify(value, pretty = false) {
2604
- if (value == null) return "";
2605
- if (typeof value === "string") return value;
2606
- try {
2607
- return JSON.stringify(value, null, pretty ? 2 : void 0) ?? String(value);
2608
- } catch {
2609
- return String(value);
2610
- }
2611
- }
2612
- function extractMessageText2(message) {
2613
- if (!message) return "";
2614
- const content = message.content;
2615
- if (typeof content === "string") return content;
2616
- if (Array.isArray(content)) {
2617
- return content.map((part) => typeof part === "string" ? part : part?.text ?? part?.content ?? "").join("");
2618
- }
2619
- return "";
2620
- }
2621
- function textFromContent(content) {
2622
- if (typeof content === "string") return content;
2623
- if (!Array.isArray(content)) return "";
2624
- return content.filter((part) => isTextPart(part)).map((part) => part.text ?? "").join("\n");
2625
- }
2626
- function isTextPart(part) {
2627
- return !!part && typeof part === "object" && part.type === "text";
2628
- }
2629
- function mapTextParts(content, fn) {
2630
- if (typeof content === "string") return fn(content);
2631
- if (!Array.isArray(content)) return content;
2632
- return content.map((part) => isTextPart(part) ? { ...part, text: fn(part.text) } : part);
2633
- }
2634
- function replaceTextContent(content, text) {
2635
- if (typeof content === "string" || content == null) return text;
2636
- if (!Array.isArray(content)) return text;
2637
- const textIndexes = content.reduce((acc, part, index) => {
2638
- if (isTextPart(part)) acc.push(index);
2639
- return acc;
2640
- }, []);
2641
- if (textIndexes.length === 0) {
2642
- return text.length > 0 ? [{ type: "text", text }, ...content] : content;
2643
- }
2644
- const [first, ...rest] = textIndexes;
2645
- const dropped = new Set(rest);
2646
- return content.map((part, index) => index === first ? { ...part, text } : part).filter((_part, index) => !dropped.has(index));
2647
- }
2648
- function normalizeSource(source) {
2649
- if (!source || typeof source !== "object") return {};
2650
- const src = source;
2651
- if (src.type === "base64" && typeof src.data === "string") {
2652
- return {
2653
- sourceType: "base64",
2654
- mediaType: typeof src.mediaType === "string" ? src.mediaType : void 0,
2655
- // base64 encodes 3 bytes per 4 characters; padding makes this an upper bound.
2656
- approxBytes: Math.floor(src.data.length * 3 / 4)
2657
- };
2658
- }
2659
- if (src.type === "url" && typeof src.url === "string") {
2660
- return {
2661
- sourceType: "url",
2662
- url: src.url,
2663
- mediaType: typeof src.mediaType === "string" ? src.mediaType : void 0
2664
- };
2665
- }
2666
- return {};
2667
- }
2668
- function collectAttachments(content) {
2669
- if (!Array.isArray(content)) return [];
2670
- const attachments = [];
2671
- content.forEach((part, index) => {
2672
- if (!part || typeof part !== "object") return;
2673
- const type = part.type;
2674
- if (!type || type === "text") return;
2675
- if (type === "image" || type === "audio" || type === "video" || type === "file") {
2676
- attachments.push({
2677
- index,
2678
- kind: type,
2679
- fileName: typeof part.fileName === "string" ? part.fileName : void 0,
2680
- ...normalizeSource(part.source)
2681
- });
2682
- return;
2683
- }
2684
- if (type === "image_url") {
2685
- const image = part.image_url;
2686
- const url = typeof image === "string" ? image : image?.url;
2687
- if (typeof url === "string" && url.startsWith("data:")) {
2688
- const comma = url.indexOf(",");
2689
- const header = url.slice(5, comma);
2690
- attachments.push({
2691
- index,
2692
- kind: "image",
2693
- sourceType: "base64",
2694
- mediaType: header.split(";")[0] || void 0,
2695
- approxBytes: Math.floor((url.length - comma - 1) * 3 / 4)
2696
- });
2697
- } else {
2698
- attachments.push({ index, kind: "image", sourceType: "url", url });
2699
- }
2700
- return;
2701
- }
2702
- attachments.push({ index, kind: "unknown" });
2703
- });
2704
- return attachments;
2705
- }
2970
+ init_content();
2706
2971
 
2707
2972
  // src/smart/toolArgCoercion.ts
2708
2973
  var PLACEHOLDER_TEXT = /* @__PURE__ */ new Set(["", "null", "undefined", "none", "nil", "n/a", "na"]);
@@ -3591,6 +3856,7 @@ function detectVolatileContent(text, limit = 20) {
3591
3856
 
3592
3857
  // src/smart/contextPilot/jsonCrusher.ts
3593
3858
  init_utilTokens();
3859
+ init_content();
3594
3860
  var CONTEXT_PILOT_JSON_MARKER_KEY = "__contextPilotDropped";
3595
3861
  function findCompressibleArray(output, minItems) {
3596
3862
  if (Array.isArray(output)) {
@@ -3803,6 +4069,7 @@ function compressSearchOutput(text, config, ccrStore) {
3803
4069
 
3804
4070
  // src/smart/contextPilot/pipeline.ts
3805
4071
  init_utilTokens();
4072
+ init_content();
3806
4073
  function createContextPilotRuntime(config) {
3807
4074
  return {
3808
4075
  ccrStore: new CCRStore({ ttlMs: config.ccr.ttlMs, maxEntries: config.ccr.maxEntries }),
@@ -4176,6 +4443,8 @@ function createAgentCoreNode(opts) {
4176
4443
  const streamingEnabled = Boolean(state.ctx?.__streaming);
4177
4444
  const cancellationToken = state.ctx?.__cancellationToken;
4178
4445
  const abortSignal = state.ctx?.__abortSignal;
4446
+ const onProgress = state.ctx?.__onProgress;
4447
+ const cancelRequested = () => isCancellationRequested({ signal: abortSignal, cancellationToken });
4179
4448
  const resolvedForCachePilot = getResolvedSmartConfig(opts, runtime);
4180
4449
  if (resolvedForCachePilot.contextPilot.enabled && resolvedForCachePilot.contextPilot.cacheAlignment.enabled) {
4181
4450
  const ctxForCacheWarning = state.ctx = state.ctx || {};
@@ -4331,9 +4600,32 @@ ${append}`;
4331
4600
  }
4332
4601
  return merged;
4333
4602
  };
4334
- let response;
4335
- let modelAttempt = 0;
4336
- const maxModelRetries = host?.maxModelRetries ?? 0;
4603
+ let response;
4604
+ let modelAttempt = 0;
4605
+ const maxModelRetries = host?.maxModelRetries ?? 0;
4606
+ const finishCancelledTurn = (partialText) => {
4607
+ const reason = abortSignal?.aborted ? "aborted" : "cancelled";
4608
+ const ctx = state.ctx = state.ctx || {};
4609
+ ctx.__cancelled = { stage: "agent", reason, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
4610
+ recordTraceEvent(traceSession, {
4611
+ type: "ai_call",
4612
+ label: "Assistant Cancelled",
4613
+ actor: { scope: "agent", name: actorName, role: "assistant", version: actorVersion },
4614
+ // "skipped", not "error": the call did not fail, the caller ended it —
4615
+ // and an "error" would flip the whole session to error.
4616
+ status: "skipped",
4617
+ durationMs: Date.now() - start,
4618
+ requestBytes: promptBytes,
4619
+ model: getModelName(runtime.model || opts.model),
4620
+ provider: getProviderName(runtime.model || opts.model),
4621
+ messageList: state.messages,
4622
+ toolDefinitions: traceToolDefinitions
4623
+ });
4624
+ onEvent?.({ type: "cancelled", stage: "agent", reason });
4625
+ onProgress?.({ stage: "agent", message: "Cancelled", detail: { reason } });
4626
+ const partial = partialText.trim().length > 0 ? [{ role: "assistant", content: partialText }] : [];
4627
+ return { messages: [...state.messages, ...partial], usage: state.usage };
4628
+ };
4337
4629
  while (true) {
4338
4630
  start = Date.now();
4339
4631
  if (shortCircuited !== void 0) {
@@ -4349,14 +4641,16 @@ ${append}`;
4349
4641
  toolDefinitions: traceToolDefinitions
4350
4642
  });
4351
4643
  } else {
4644
+ let streamedText = "";
4352
4645
  try {
4353
4646
  const normalizedMessages = normalizeBedrockToolPairing([...wireMessages]);
4354
4647
  debugToolPairing(normalizedMessages);
4355
4648
  if (streamingEnabled && typeof modelWithTools.stream === "function") {
4356
- let streamedText = "";
4357
4649
  let streamedMessage;
4650
+ let cutByCancel = false;
4358
4651
  for await (const chunk of modelWithTools.stream(normalizedMessages, invokeParams)) {
4359
- if (cancellationToken && cancellationToken.isCancellationRequested || abortSignal?.aborted) {
4652
+ if (cancelRequested()) {
4653
+ cutByCancel = true;
4360
4654
  break;
4361
4655
  }
4362
4656
  if (chunk && typeof chunk === "object" && chunk.role) {
@@ -4370,6 +4664,7 @@ ${append}`;
4370
4664
  onEvent?.({ type: "stream", text });
4371
4665
  }
4372
4666
  }
4667
+ if (cutByCancel) return finishCancelledTurn(streamedText);
4373
4668
  if (streamedMessage) {
4374
4669
  response = { ...streamedMessage };
4375
4670
  if (response.content == null || response.content === "") {
@@ -4382,6 +4677,7 @@ ${append}`;
4382
4677
  response = await modelWithTools.invoke(normalizedMessages, invokeParams);
4383
4678
  }
4384
4679
  } catch (err) {
4680
+ if (cancelRequested()) return finishCancelledTurn(streamedText);
4385
4681
  const durationMs2 = Date.now() - start;
4386
4682
  recordTraceEvent(traceSession, {
4387
4683
  type: "ai_call",
@@ -4479,6 +4775,7 @@ ${append}`;
4479
4775
  }
4480
4776
 
4481
4777
  // src/utils/traceSections.ts
4778
+ init_content();
4482
4779
  function truncate(text, max = 500) {
4483
4780
  if (!text) return "";
4484
4781
  return text.length > max ? `${text.slice(0, max)}\u2026` : text;
@@ -6369,6 +6666,9 @@ function isCompactedToolContent(content) {
6369
6666
  return typeof content === "string" && (content === "SUMMARIZED" || content.startsWith("SUMMARIZED_TOOL_RESPONSE") || content.startsWith("ARCHIVED_TOOL_RESPONSE") || content.startsWith("STRUCTURED_TOOL_RESPONSE") || content.startsWith("DROPPED_TOOL_RESPONSE"));
6370
6667
  }
6371
6668
 
6669
+ // src/agent.ts
6670
+ init_content();
6671
+
6372
6672
  // src/structuredOutput/manager.ts
6373
6673
  var StructuredOutputManager = class {
6374
6674
  constructor(schema, strategy, config) {
@@ -6601,6 +6901,7 @@ function buildSubagentCatalogBlock(subagents, policy, toolNames) {
6601
6901
  }
6602
6902
 
6603
6903
  // src/plugins/host.ts
6904
+ init_content();
6604
6905
  var DEFAULT_PRIORITY = 100;
6605
6906
  var DEFAULT_TIMEOUT_MS = 1e4;
6606
6907
  var HOOK_SPECS = {
@@ -7137,6 +7438,7 @@ function performanceNow() {
7137
7438
  }
7138
7439
 
7139
7440
  // src/plugins/session.ts
7441
+ init_content();
7140
7442
  async function openPluginSession(runHost, params) {
7141
7443
  let messages = params.messages;
7142
7444
  const startGate = await runHost.runGate("sessionStart", {
@@ -7606,6 +7908,7 @@ function createAgent(opts) {
7606
7908
  onProgress?.({ stage: "agent", message: "Invoking model" });
7607
7909
  if (cancelIfRequested("before_agent")) break;
7608
7910
  state = publish({ ...state, ...await agentCore(state) });
7911
+ if (state.ctx?.__cancelled) break;
7609
7912
  onProgress?.({ stage: "agent", message: "Model response received" });
7610
7913
  if (checkpointIfRequested("after_agent")) break;
7611
7914
  const postGuardrails = getGuardrailConfig(state);
@@ -7676,7 +7979,7 @@ function createAgent(opts) {
7676
7979
  if (toolCalls.length === 0) {
7677
7980
  if (soManager && !state.ctx?.__finalizedDueToStructuredOutput && !state.ctx?.__structuredOutputForceFinalize && !state.ctx?.__guardrailBlocked) {
7678
7981
  if (soManager.strategy.kind === "native") {
7679
- const assistantText = extractMessageText2(lastMsg);
7982
+ const assistantText = extractMessageText(lastMsg);
7680
7983
  if (assistantText) {
7681
7984
  const parsed = soManager.parseFromContent(assistantText);
7682
7985
  if (parsed.success) {
@@ -7732,7 +8035,7 @@ function createAgent(opts) {
7732
8035
  if (soManager && !state.ctx?.__finalizedDueToStructuredOutput && !structuredFinalizeBlocked) {
7733
8036
  const lastForParse = state.messages[state.messages.length - 1];
7734
8037
  if (lastForParse?.role === "assistant") {
7735
- const assistantText = extractMessageText2(lastForParse);
8038
+ const assistantText = extractMessageText(lastForParse);
7736
8039
  if (assistantText) {
7737
8040
  const parsed = soManager.parseFromContent(assistantText);
7738
8041
  if (parsed.success) {
@@ -7994,7 +8297,7 @@ function createAgent(opts) {
7994
8297
  const isTransientSummarizationExit = !!res.ctx?.__needsSummarization;
7995
8298
  const finalAssistantMsg = isTransientSummarizationExit ? void 0 : getLastAssistantMessage(res.messages);
7996
8299
  const finalIsSyntheticSummary = !isTransientSummarizationExit && finalAssistantMsg && isSyntheticSummaryMessage(finalAssistantMsg);
7997
- let content = isTransientSummarizationExit || finalIsSyntheticSummary ? "" : extractMessageText2(finalAssistantMsg);
8300
+ let content = isTransientSummarizationExit || finalIsSyntheticSummary ? "" : extractMessageText(finalAssistantMsg);
7998
8301
  const finalAnswerBlocked = Boolean(
7999
8302
  isTransientSummarizationExit || res.ctx?.__awaitingApproval || res.ctx?.__awaitingUserQuestion || res.ctx?.__cancelled || res.ctx?.__paused || res.ctx?.__limitBreached
8000
8303
  );
@@ -8253,9 +8556,11 @@ function createAgentNode(opts) {
8253
8556
 
8254
8557
  // src/nodes/contextSummarize.ts
8255
8558
  init_utilTokens();
8559
+ init_content();
8256
8560
 
8257
8561
  // src/smart/contextPolicy.ts
8258
8562
  init_utilTokens();
8563
+ init_content();
8259
8564
  function collectRecentTurns(messages, lastTurnsToKeep) {
8260
8565
  if (lastTurnsToKeep <= 0) return [];
8261
8566
  const systemPrefix = messages.filter((message, index) => index === 0 && message.role === "system");
@@ -8317,7 +8622,7 @@ function isClampProtected(message, index, firstUserIndex) {
8317
8622
  function clampToBudget(messages, maxContextTokens) {
8318
8623
  let working = [...messages];
8319
8624
  while (working.length > 2) {
8320
- const tokenCount = countApproxTokens(working.map(extractMessageText2).join("\n"));
8625
+ const tokenCount = countApproxTokens(working.map(extractMessageText).join("\n"));
8321
8626
  if (tokenCount <= maxContextTokens) return working;
8322
8627
  const firstUserIndex = working.findIndex((message) => message.role === "user");
8323
8628
  const firstNonSystem = working.findIndex((message, index) => !isClampProtected(message, index, firstUserIndex));
@@ -8657,7 +8962,7 @@ function createContextSummarizeNode(opts) {
8657
8962
  let content = "";
8658
8963
  if (typeof m.content === "string") content = m.content;
8659
8964
  else if (Array.isArray(m.content)) {
8660
- content = m.content.map((c) => c.text || JSON.stringify(c)).join(" ");
8965
+ content = m.content.map((c) => contentPartToText(c, (part) => JSON.stringify(part))).join(" ");
8661
8966
  }
8662
8967
  if (m.role === "tool" && isSummarizedToolPlaceholder(content)) {
8663
8968
  const historyEntry = findToolHistoryEntry(state, m.tool_call_id, m.name);
@@ -9502,6 +9807,9 @@ async function writeSummaryFactsToMemory(store, config, summary, sourceTurn) {
9502
9807
  return store.get(config.memory.scope, { limit: config.context.lastTurnsToKeep + summary.stable_facts.length });
9503
9808
  }
9504
9809
 
9810
+ // src/smart/index.ts
9811
+ init_content();
9812
+
9505
9813
  // src/smart/skills/types.ts
9506
9814
  var DEFAULT_SKILL_POLICY = {
9507
9815
  maxOpenSkills: 4,
@@ -10516,7 +10824,7 @@ function createSmartAgent(opts) {
10516
10824
  ].join("\n");
10517
10825
  function latestUserPrompt(messages) {
10518
10826
  const latest = [...messages].reverse().find((message) => message.role === "user");
10519
- return latest ? extractMessageText2(latest) : "";
10827
+ return latest ? extractMessageText(latest) : "";
10520
10828
  }
10521
10829
  async function syncMemory(state) {
10522
10830
  const query = latestUserPrompt(state.messages || []);
@@ -11227,6 +11535,132 @@ async function runSmartAgentEvalHarness(params) {
11227
11535
  }
11228
11536
  return results;
11229
11537
  }
11538
+
11539
+ // src/providers/base.ts
11540
+ var DEFAULT_RETRY = {
11541
+ maxRetries: 3,
11542
+ baseDelayMs: 500,
11543
+ maxDelayMs: 8e3
11544
+ };
11545
+ var BaseProvider = class {
11546
+ constructor() {
11547
+ this.retryPolicy = DEFAULT_RETRY;
11548
+ }
11549
+ /**
11550
+ * Helper concrete providers can wrap their fetch with. Retries 429/5xx
11551
+ * with exponential backoff and respects Retry-After if the response
11552
+ * carries one.
11553
+ *
11554
+ * `signal` is the call's cancellation: a run that is cancelled while the
11555
+ * provider is backing off must not sit out the delay, nor start another attempt.
11556
+ */
11557
+ async doFetchWithRetry(execute, signal) {
11558
+ const policy = this.retryPolicy;
11559
+ const maxRetries = Math.max(0, policy.maxRetries ?? DEFAULT_RETRY.maxRetries);
11560
+ const baseDelay = Math.max(0, policy.baseDelayMs ?? DEFAULT_RETRY.baseDelayMs);
11561
+ const maxDelay = Math.max(baseDelay, policy.maxDelayMs ?? DEFAULT_RETRY.maxDelayMs);
11562
+ let lastResp;
11563
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
11564
+ if (signal?.aborted) throw abortReason(signal);
11565
+ const res = await execute();
11566
+ const isRetryable = res.status === 429 || res.status >= 500 && res.status < 600;
11567
+ if (!isRetryable) return res;
11568
+ const predicate = policy.shouldRetry;
11569
+ if (predicate && !predicate({ status: res.status }, attempt)) return res;
11570
+ if (attempt === maxRetries) {
11571
+ lastResp = res;
11572
+ break;
11573
+ }
11574
+ const retryAfter = res.headers.get("retry-after");
11575
+ let delay2 = Math.min(maxDelay, baseDelay * Math.pow(2, attempt));
11576
+ if (retryAfter) {
11577
+ const asNumber = Number(retryAfter);
11578
+ if (!Number.isNaN(asNumber)) delay2 = Math.min(maxDelay, Math.max(delay2, asNumber * 1e3));
11579
+ }
11580
+ try {
11581
+ await res.text();
11582
+ } catch {
11583
+ }
11584
+ await abortableSleep(delay2, signal);
11585
+ }
11586
+ return lastResp;
11587
+ }
11588
+ };
11589
+
11590
+ // src/providers/types.ts
11591
+ function emptyUsage() {
11592
+ return {
11593
+ inputTokens: 0,
11594
+ outputTokens: 0,
11595
+ totalTokens: 0,
11596
+ cachedInputTokens: 0,
11597
+ cachedWriteTokens: 0,
11598
+ cachedOutputTokens: 0,
11599
+ reasoningTokens: 0
11600
+ };
11601
+ }
11602
+ var ProviderError = class extends Error {
11603
+ constructor(message, provider, statusCode, responseBody) {
11604
+ super(message);
11605
+ this.provider = provider;
11606
+ this.statusCode = statusCode;
11607
+ this.responseBody = responseBody;
11608
+ this.name = "ProviderError";
11609
+ }
11610
+ };
11611
+
11612
+ // src/providers/jev.ts
11613
+ var JEV_DEFAULT_BASE_URL = "https://api.typesafe.ai";
11614
+ var JevProvider = class extends BaseProvider {
11615
+ constructor(config) {
11616
+ super();
11617
+ this.providerName = "jev";
11618
+ this.apiKey = config.apiKey;
11619
+ this.baseURL = (config.baseURL ?? JEV_DEFAULT_BASE_URL).replace(/\/$/, "");
11620
+ this.defaultModel = config.defaultModel;
11621
+ this.defaultHeaders = config.defaultHeaders ?? {};
11622
+ if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
11623
+ }
11624
+ /** Ask System One questions about `state`. */
11625
+ async systemOne(request, options) {
11626
+ const model = request.model ?? this.defaultModel;
11627
+ const body = {
11628
+ ...model ? { model } : {},
11629
+ state: request.state,
11630
+ questions: request.questions
11631
+ };
11632
+ const res = await this.doFetchWithRetry(
11633
+ () => fetch(`${this.baseURL}/v1/systemone`, {
11634
+ method: "POST",
11635
+ headers: {
11636
+ "Content-Type": "application/json",
11637
+ Authorization: `Bearer ${this.apiKey}`,
11638
+ ...this.defaultHeaders
11639
+ },
11640
+ body: JSON.stringify(body),
11641
+ signal: options?.signal
11642
+ })
11643
+ );
11644
+ if (!res.ok) {
11645
+ const text = await res.text().catch(() => "");
11646
+ throw new ProviderError(`Jev API error ${res.status}: ${text}`, this.providerName, res.status, text);
11647
+ }
11648
+ return await res.json();
11649
+ }
11650
+ async complete(_request) {
11651
+ throw new ProviderError(
11652
+ 'Jev is a decision model and has no chat completion; use systemOne() or agentVerdictRule({ engine: "jev" })',
11653
+ this.providerName
11654
+ );
11655
+ }
11656
+ // eslint-disable-next-line require-yield
11657
+ async *completeStream(_request) {
11658
+ throw new ProviderError("Jev does not stream; use systemOne()", this.providerName);
11659
+ }
11660
+ };
11661
+
11662
+ // src/guardrails/checks.ts
11663
+ init_content();
11230
11664
  var Ajv = AjvModule__default.default?.default ?? AjvModule__default.default;
11231
11665
  var defaultAjv = new Ajv({ allErrors: true });
11232
11666
  defaultAjv.opts.strict = false;
@@ -11376,14 +11810,82 @@ function defaultPayload(context) {
11376
11810
  2
11377
11811
  );
11378
11812
  }
11813
+ var DEFAULT_JEV_THRESHOLD = 0.5;
11814
+ var JEV_QUESTION_KEY = "shouldBlock";
11815
+ var DEFAULT_JEV_QUESTION = "Does this conversation payload pose a safety, security, or policy risk that should stop the agent from continuing? Answer yes when the latest message or action should be blocked, and no when it is safe to allow.";
11816
+ var DEFAULT_JEV_CRITERIA = {
11817
+ true: "The payload is unsafe, non-compliant, or otherwise warrants blocking.",
11818
+ false: "The payload is benign and the agent should be allowed to continue."
11819
+ };
11820
+ function resolveJevClient(config) {
11821
+ if (config.client) return config.client;
11822
+ const apiKey = config.apiKey ?? process.env.TYPESAFE_API_KEY;
11823
+ if (!apiKey) {
11824
+ throw new Error(
11825
+ "Jev engine requires an API key: pass `jev.apiKey`, set TYPESAFE_API_KEY, or pass a `jev.client`."
11826
+ );
11827
+ }
11828
+ return new JevProvider({
11829
+ provider: "jev",
11830
+ apiKey,
11831
+ baseURL: config.baseURL ?? process.env.TYPESAFE_BASE_URL
11832
+ });
11833
+ }
11379
11834
  function agentVerdictRule(options) {
11380
11835
  const payloadBuilder = options.payloadBuilder || defaultPayload;
11381
11836
  const fallbackDisposition = options.dispositionFallback || "block";
11837
+ const engine = options.engine || "llm";
11838
+ const jevOptions = options.jev || {};
11839
+ const jevThreshold = typeof jevOptions.threshold === "number" ? jevOptions.threshold : DEFAULT_JEV_THRESHOLD;
11840
+ let jevClient;
11841
+ async function evaluateWithJev(context) {
11842
+ const client = jevClient ?? (jevClient = resolveJevClient(jevOptions));
11843
+ const instructions = jevOptions.question ? jevOptions.question(context) : DEFAULT_JEV_QUESTION;
11844
+ const result = await client.systemOne({
11845
+ state: payloadBuilder(context),
11846
+ questions: {
11847
+ [JEV_QUESTION_KEY]: {
11848
+ type: "noul",
11849
+ instructions,
11850
+ criteria: jevOptions.criteria ?? DEFAULT_JEV_CRITERIA
11851
+ }
11852
+ },
11853
+ ...jevOptions.model ? { model: jevOptions.model } : {}
11854
+ });
11855
+ const answer = result?.answers?.[JEV_QUESTION_KEY];
11856
+ const probability = typeof answer?.noul === "number" ? answer.noul : void 0;
11857
+ if (probability === void 0) {
11858
+ return {
11859
+ passed: false,
11860
+ disposition: fallbackDisposition,
11861
+ reason: "Jev engine did not return a noul probability.",
11862
+ details: { jevModel: result?.model, jevUsage: result?.usage }
11863
+ };
11864
+ }
11865
+ const passed = probability < jevThreshold;
11866
+ return {
11867
+ passed,
11868
+ disposition: passed ? "allow" : fallbackDisposition,
11869
+ reason: passed ? void 0 : `Jev risk probability ${probability.toFixed(3)} met or exceeded threshold ${jevThreshold}.`,
11870
+ details: {
11871
+ jevProbability: probability,
11872
+ jevThreshold,
11873
+ jevModel: result?.model,
11874
+ jevUsage: result?.usage
11875
+ }
11876
+ };
11877
+ }
11382
11878
  return {
11383
11879
  id: options.id,
11384
11880
  title: options.title || "Agent-based guardrail",
11385
11881
  description: options.description,
11386
11882
  async evaluate(context) {
11883
+ if (engine === "jev") {
11884
+ return evaluateWithJev(context);
11885
+ }
11886
+ if (!options.agent) {
11887
+ throw new Error("agentVerdictRule requires an `agent` when using the default 'llm' engine.");
11888
+ }
11387
11889
  const payload = payloadBuilder(context);
11388
11890
  const systemPrompt = options.systemPrompt || "You are a safety auditor. Review the provided conversation payload and decide whether it should continue. Return JSON with keys allow (boolean), optional disposition ('allow' | 'warn' | 'block'), optional reason (string), and optional details (object).";
11389
11891
  const review = await options.agent.invoke({
@@ -11599,6 +12101,7 @@ function isHookImplemented(hook) {
11599
12101
  }
11600
12102
 
11601
12103
  // src/plugins/builtin/guardrail.ts
12104
+ init_content();
11602
12105
  var ALLOW = { action: "allow" };
11603
12106
  var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 425, 429, 500, 502, 503, 504]);
11604
12107
  var RetryableGuardrailError = class extends Error {
@@ -11885,7 +12388,7 @@ function createGuardrailPlugin(options) {
11885
12388
  return { decision: "deny", reason: verdict.message ?? "Request blocked by guardrail policy." };
11886
12389
  },
11887
12390
  postModelCall: async ({ message }, ctx) => {
11888
- const text = extractMessageText2(message);
12391
+ const text = extractMessageText(message);
11889
12392
  if (!text) return void 0;
11890
12393
  const verdict = await check({ phase: "output", content: text, hook: "postModelCall" }, ctx);
11891
12394
  if (!raiseIfFailed(verdict, ctx) || verdict.action === "allow") return void 0;
@@ -12155,6 +12658,7 @@ function portkeyGuardrail(config = {}) {
12155
12658
  }
12156
12659
 
12157
12660
  // src/plugins/builtin/piiRedaction.ts
12661
+ init_content();
12158
12662
  function isValidTckn(value) {
12159
12663
  const digits = value.replace(/\D/g, "");
12160
12664
  if (digits.length !== 11 || digits[0] === "0") return false;
@@ -12274,7 +12778,7 @@ function piiRedaction(config = {}) {
12274
12778
  },
12275
12779
  postModelCall: ({ message }, ctx) => {
12276
12780
  if (!applied.has("modelOutput")) return void 0;
12277
- const text = extractMessageText2(message);
12781
+ const text = extractMessageText(message);
12278
12782
  if (!text) return void 0;
12279
12783
  const result = redactText(text, detectors, mask);
12280
12784
  if (result.findings.length === 0) return void 0;
@@ -14681,74 +15185,8 @@ function languageGuard(config) {
14681
15185
  };
14682
15186
  }
14683
15187
 
14684
- // src/providers/types.ts
14685
- function emptyUsage() {
14686
- return {
14687
- inputTokens: 0,
14688
- outputTokens: 0,
14689
- totalTokens: 0,
14690
- cachedInputTokens: 0,
14691
- cachedWriteTokens: 0,
14692
- cachedOutputTokens: 0,
14693
- reasoningTokens: 0
14694
- };
14695
- }
14696
- var ProviderError = class extends Error {
14697
- constructor(message, provider, statusCode, responseBody) {
14698
- super(message);
14699
- this.provider = provider;
14700
- this.statusCode = statusCode;
14701
- this.responseBody = responseBody;
14702
- this.name = "ProviderError";
14703
- }
14704
- };
14705
-
14706
- // src/providers/base.ts
14707
- var DEFAULT_RETRY = {
14708
- maxRetries: 3,
14709
- baseDelayMs: 500,
14710
- maxDelayMs: 8e3
14711
- };
14712
- var BaseProvider = class {
14713
- constructor() {
14714
- this.retryPolicy = DEFAULT_RETRY;
14715
- }
14716
- /**
14717
- * Helper concrete providers can wrap their fetch with. Retries 429/5xx
14718
- * with exponential backoff and respects Retry-After if the response
14719
- * carries one.
14720
- */
14721
- async doFetchWithRetry(execute) {
14722
- const policy = this.retryPolicy;
14723
- const maxRetries = Math.max(0, policy.maxRetries ?? DEFAULT_RETRY.maxRetries);
14724
- const baseDelay = Math.max(0, policy.baseDelayMs ?? DEFAULT_RETRY.baseDelayMs);
14725
- const maxDelay = Math.max(baseDelay, policy.maxDelayMs ?? DEFAULT_RETRY.maxDelayMs);
14726
- let lastResp;
14727
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
14728
- const res = await execute();
14729
- const isRetryable = res.status === 429 || res.status >= 500 && res.status < 600;
14730
- if (!isRetryable) return res;
14731
- const predicate = policy.shouldRetry;
14732
- if (predicate && !predicate({ status: res.status }, attempt)) return res;
14733
- if (attempt === maxRetries) {
14734
- lastResp = res;
14735
- break;
14736
- }
14737
- const retryAfter = res.headers.get("retry-after");
14738
- let delay2 = Math.min(maxDelay, baseDelay * Math.pow(2, attempt));
14739
- if (retryAfter) {
14740
- const asNumber = Number(retryAfter);
14741
- if (!Number.isNaN(asNumber)) delay2 = Math.min(maxDelay, Math.max(delay2, asNumber * 1e3));
14742
- }
14743
- try {
14744
- await res.text();
14745
- } catch {
14746
- }
14747
- await new Promise((r) => setTimeout(r, delay2));
14748
- }
14749
- return lastResp;
14750
- }
14751
- };
15188
+ // src/index.ts
15189
+ init_content();
14752
15190
 
14753
15191
  // src/providers/utils/sse.ts
14754
15192
  async function* parseSSEStream(body, signal) {
@@ -14757,7 +15195,7 @@ async function* parseSSEStream(body, signal) {
14757
15195
  let buffer = "";
14758
15196
  try {
14759
15197
  while (true) {
14760
- if (signal?.aborted) ;
15198
+ if (signal?.aborted) throw abortReason(signal);
14761
15199
  const { done, value } = await reader.read();
14762
15200
  if (done) break;
14763
15201
  buffer += decoder.decode(value, { stream: true });
@@ -14774,6 +15212,7 @@ async function* parseSSEStream(body, signal) {
14774
15212
  if (event) yield event;
14775
15213
  }
14776
15214
  } finally {
15215
+ reader.cancel().catch(() => void 0);
14777
15216
  reader.releaseLock();
14778
15217
  }
14779
15218
  }
@@ -14991,8 +15430,127 @@ function sanitizeBedrockDocName(name, fallback = "document") {
14991
15430
  return cleaned || fallback;
14992
15431
  }
14993
15432
 
15433
+ // src/providers/utils/jsonSchemaRefs.ts
15434
+ var DEFINITION_KEYS = ["definitions", "$defs"];
15435
+ function inlineJsonSchemaRefs(schema) {
15436
+ if (!isObject2(schema) || !containsRef(schema)) return schema;
15437
+ const root = structuredClone(schema);
15438
+ const selfReaching = /* @__PURE__ */ new Map();
15439
+ const targetOf = (ref) => {
15440
+ const target = resolvePointer(root, ref);
15441
+ return isObject2(target) ? target : void 0;
15442
+ };
15443
+ const reachesItself = (target) => {
15444
+ const known = selfReaching.get(target);
15445
+ if (known !== void 0) return known;
15446
+ const seen = /* @__PURE__ */ new Set();
15447
+ const walk = (node) => {
15448
+ if (Array.isArray(node)) return node.some(walk);
15449
+ if (!isObject2(node)) return false;
15450
+ if (typeof node.$ref === "string") {
15451
+ const next = targetOf(node.$ref);
15452
+ if (next === target) return true;
15453
+ if (next && !seen.has(next)) {
15454
+ seen.add(next);
15455
+ if (walk(next)) return true;
15456
+ }
15457
+ }
15458
+ return Object.entries(node).some(([key, value]) => key !== "$ref" && walk(value));
15459
+ };
15460
+ const result2 = walk(target);
15461
+ selfReaching.set(target, result2);
15462
+ return result2;
15463
+ };
15464
+ const keptRefs = /* @__PURE__ */ new Set();
15465
+ const expand = (node) => {
15466
+ if (Array.isArray(node)) return node.map(expand);
15467
+ if (!isObject2(node)) return node;
15468
+ if (typeof node.$ref === "string") {
15469
+ const target = targetOf(node.$ref);
15470
+ if (target && !reachesItself(target)) {
15471
+ const { $ref: _inlined, ...siblings } = node;
15472
+ return { ...expand(target), ...expandEntries(siblings) };
15473
+ }
15474
+ keptRefs.add(node.$ref);
15475
+ return expandEntries(node);
15476
+ }
15477
+ return expandEntries(node);
15478
+ };
15479
+ const expandEntries = (node) => {
15480
+ const out = {};
15481
+ for (const [key, value] of Object.entries(node)) out[key] = key === "$ref" ? value : expand(value);
15482
+ return out;
15483
+ };
15484
+ const { definitions: _definitions, $defs: _defs, ...rootBody } = root;
15485
+ const result = typeof root.$ref === "string" ? expand(rootBody) : expandEntries(rootBody);
15486
+ for (const key of DEFINITION_KEYS) delete result[key];
15487
+ for (const key of DEFINITION_KEYS) {
15488
+ const block = root[key];
15489
+ if (!isObject2(block)) continue;
15490
+ const needed = neededEntries(key, block, keptRefs);
15491
+ if (needed.length === 0) continue;
15492
+ const kept = {};
15493
+ for (const name of Object.keys(block)) if (needed.includes(name)) kept[name] = block[name];
15494
+ result[key] = kept;
15495
+ }
15496
+ return result;
15497
+ }
15498
+ function neededEntries(key, block, keptRefs) {
15499
+ const needed = [];
15500
+ const queue = [...keptRefs];
15501
+ while (queue.length > 0) {
15502
+ const ref = queue.shift();
15503
+ const segments = pointerSegments(ref);
15504
+ if (!segments || segments[0] !== key || segments.length < 2) continue;
15505
+ const name = segments[1];
15506
+ if (needed.includes(name) || !(name in block)) continue;
15507
+ needed.push(name);
15508
+ collectRefs(block[name], queue);
15509
+ }
15510
+ return needed;
15511
+ }
15512
+ function collectRefs(node, into) {
15513
+ if (Array.isArray(node)) {
15514
+ node.forEach((child) => collectRefs(child, into));
15515
+ return;
15516
+ }
15517
+ if (!isObject2(node)) return;
15518
+ if (typeof node.$ref === "string") into.push(node.$ref);
15519
+ for (const [k, value] of Object.entries(node)) if (k !== "$ref") collectRefs(value, into);
15520
+ }
15521
+ function containsRef(node) {
15522
+ if (Array.isArray(node)) return node.some(containsRef);
15523
+ if (!isObject2(node)) return false;
15524
+ if (typeof node.$ref === "string") return true;
15525
+ return Object.values(node).some(containsRef);
15526
+ }
15527
+ function pointerSegments(ref) {
15528
+ if (ref === "#") return [];
15529
+ if (!ref.startsWith("#/")) return null;
15530
+ return ref.slice(2).split("/").map((segment) => segment.replace(/~1/g, "/").replace(/~0/g, "~"));
15531
+ }
15532
+ function resolvePointer(document, ref) {
15533
+ const segments = pointerSegments(ref);
15534
+ if (!segments) return void 0;
15535
+ let current = document;
15536
+ for (const segment of segments) {
15537
+ if (Array.isArray(current)) {
15538
+ if (!/^\d+$/.test(segment)) return void 0;
15539
+ current = current[Number(segment)];
15540
+ } else if (isObject2(current) && Object.prototype.hasOwnProperty.call(current, segment)) {
15541
+ current = current[segment];
15542
+ } else {
15543
+ return void 0;
15544
+ }
15545
+ }
15546
+ return current;
15547
+ }
15548
+ function isObject2(value) {
15549
+ return typeof value === "object" && value !== null && !Array.isArray(value);
15550
+ }
15551
+
14994
15552
  // src/providers/openai.ts
14995
- var OpenAIProvider = class extends BaseProvider {
15553
+ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
14996
15554
  constructor(config) {
14997
15555
  super();
14998
15556
  this.providerName = "openai";
@@ -15004,8 +15562,21 @@ var OpenAIProvider = class extends BaseProvider {
15004
15562
  ...config.organization ? { "OpenAI-Organization": config.organization } : {}
15005
15563
  };
15006
15564
  this.responsesApi = config.responsesApi ?? "auto";
15565
+ const ownInstance = new.target === _OpenAIProvider;
15566
+ this.responsesApiModels = ownInstance ? [...config.responsesApiModels ?? []] : [];
15567
+ this.responsesStreaming = ownInstance && config.responsesStreaming === true;
15007
15568
  if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
15008
15569
  }
15570
+ /** True when the caller listed this request's model in `responsesApiModels`. */
15571
+ isResponsesApiModel(request) {
15572
+ if (this.responsesApiModels.length === 0) return false;
15573
+ const model = request.model || this.defaultModel;
15574
+ return this.responsesApiModels.some((entry) => {
15575
+ if (typeof entry === "string") return entry === model;
15576
+ entry.lastIndex = 0;
15577
+ return entry.test(model);
15578
+ });
15579
+ }
15009
15580
  /** True when this request should go to the Responses API rather than Chat
15010
15581
  * Completions — which exposes `reasoning.effort` and reasoning summaries that
15011
15582
  * Chat Completions silently drops.
@@ -15014,10 +15585,14 @@ var OpenAIProvider = class extends BaseProvider {
15014
15585
  * carries a reasoning config AND the model name looks like an o-series/gpt-5
15015
15586
  * model. `never`/`always` let a caller state the answer instead, because the
15016
15587
  * name check is a guess and the two APIs differ in far more than reasoning —
15017
- * see `responsesApi` in OpenAIProviderConfig. */
15588
+ * see `responsesApi` in OpenAIProviderConfig.
15589
+ *
15590
+ * A model listed in `responsesApiModels` is the caller stating the answer
15591
+ * for that model alone; `never` still overrides it. */
15018
15592
  useResponsesApi(request) {
15019
15593
  if (this.responsesApi === "never") return false;
15020
15594
  if (this.responsesApi === "always") return true;
15595
+ if (this.isResponsesApiModel(request)) return true;
15021
15596
  if (!request.reasoning) return false;
15022
15597
  return isReasoningModel(request.model || this.defaultModel);
15023
15598
  }
@@ -15026,12 +15601,16 @@ var OpenAIProvider = class extends BaseProvider {
15026
15601
  return this.completeResponses(request);
15027
15602
  }
15028
15603
  const body = this.buildRequestBody(request, false);
15029
- const res = await this.doFetch(body);
15604
+ const res = await this.doFetch(body, request.signal);
15030
15605
  const json = await res.json();
15031
15606
  return this.parseResponse(json);
15032
15607
  }
15033
15608
  async *completeStream(request) {
15034
15609
  if (this.useResponsesApi(request)) {
15610
+ if (this.responsesStreaming && this.isResponsesApiModel(request)) {
15611
+ yield* this.streamResponses(request);
15612
+ return;
15613
+ }
15035
15614
  const response = await this.completeResponses(request);
15036
15615
  yield {
15037
15616
  id: response.id,
@@ -15047,13 +15626,13 @@ var OpenAIProvider = class extends BaseProvider {
15047
15626
  return;
15048
15627
  }
15049
15628
  const body = this.buildRequestBody(request, true);
15050
- const res = await this.doFetch(body);
15629
+ const res = await this.doFetch(body, request.signal);
15051
15630
  if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
15052
15631
  let id = "";
15053
15632
  let model = "";
15054
15633
  const toolCallBuffers = /* @__PURE__ */ new Map();
15055
15634
  let usage;
15056
- for await (const event of parseSSEStream(res.body)) {
15635
+ for await (const event of parseSSEStream(res.body, request.signal)) {
15057
15636
  if (event.data === "[DONE]") break;
15058
15637
  let chunk;
15059
15638
  try {
@@ -15147,10 +15726,101 @@ var OpenAIProvider = class extends BaseProvider {
15147
15726
  // ─── Responses API (reasoning models) ──────────────────────────────────────
15148
15727
  async completeResponses(request) {
15149
15728
  const body = this.buildResponsesBody(request);
15150
- const res = await this.responsesFetch(body);
15729
+ if (this.isResponsesApiModel(request)) adaptChatExtrasForResponses(body, request.extra);
15730
+ const res = await this.responsesFetch(body, request.signal);
15151
15731
  const json = await res.json();
15152
15732
  return this.parseResponsesResponse(json, request.model || this.defaultModel);
15153
15733
  }
15734
+ /** Real Responses SSE streaming, for models opted in through
15735
+ * `responsesApiModels` with `responsesStreaming` on. Emits the same chunk
15736
+ * shapes as the Chat Completions stream: text deltas as they arrive, a tool
15737
+ * call's id + name when it opens and its argument fragments under that id,
15738
+ * then one final chunk with usage, finish reason and any reasoning summary
15739
+ * (taken from the completed response, exactly as the non-streaming parse
15740
+ * reads it). */
15741
+ async *streamResponses(request) {
15742
+ const modelId = request.model || this.defaultModel;
15743
+ const body = this.buildResponsesBody(request);
15744
+ adaptChatExtrasForResponses(body, request.extra);
15745
+ body.stream = true;
15746
+ const res = await this.responsesFetch(body, request.signal);
15747
+ if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
15748
+ let id = "";
15749
+ let model = modelId;
15750
+ const calls = /* @__PURE__ */ new Map();
15751
+ let final;
15752
+ for await (const event of parseSSEStream(res.body, request.signal)) {
15753
+ if (event.data === "[DONE]") break;
15754
+ let data;
15755
+ try {
15756
+ data = JSON.parse(event.data);
15757
+ } catch {
15758
+ continue;
15759
+ }
15760
+ switch (data.type ?? event.event) {
15761
+ case "response.created":
15762
+ case "response.in_progress":
15763
+ id = data.response?.id ?? id;
15764
+ model = data.response?.model ?? model;
15765
+ break;
15766
+ case "response.output_text.delta":
15767
+ if (data.delta) yield { id, model, delta: { content: data.delta } };
15768
+ break;
15769
+ case "response.output_item.added": {
15770
+ const item = data.item;
15771
+ if (item?.type !== "function_call") break;
15772
+ const call = { callId: item.call_id ?? item.id ?? "", name: item.name ?? "", arguments: item.arguments ?? "" };
15773
+ calls.set(item.id ?? call.callId, call);
15774
+ yield { id, model, delta: { toolCalls: [{ id: call.callId, name: call.name, arguments: call.arguments }] } };
15775
+ break;
15776
+ }
15777
+ case "response.function_call_arguments.delta": {
15778
+ const call = calls.get(data.item_id);
15779
+ if (!call || !data.delta) break;
15780
+ call.arguments += data.delta;
15781
+ yield { id, model, delta: { toolCalls: [{ id: call.callId, arguments: data.delta }] } };
15782
+ break;
15783
+ }
15784
+ case "response.output_item.done": {
15785
+ const item = data.item;
15786
+ if (item?.type !== "function_call") break;
15787
+ const full = typeof item.arguments === "string" ? item.arguments : "";
15788
+ const call = calls.get(item.id ?? item.call_id);
15789
+ if (!call) {
15790
+ const added = { callId: item.call_id ?? item.id ?? "", name: item.name ?? "", arguments: full };
15791
+ calls.set(item.id ?? added.callId, added);
15792
+ yield { id, model, delta: { toolCalls: [{ id: added.callId, name: added.name, arguments: full }] } };
15793
+ } else if (full.length > call.arguments.length && full.startsWith(call.arguments)) {
15794
+ const rest = full.slice(call.arguments.length);
15795
+ call.arguments = full;
15796
+ yield { id, model, delta: { toolCalls: [{ id: call.callId, arguments: rest }] } };
15797
+ }
15798
+ break;
15799
+ }
15800
+ case "response.completed":
15801
+ case "response.incomplete":
15802
+ final = this.parseResponsesResponse(data.response ?? {}, modelId);
15803
+ break;
15804
+ case "response.failed":
15805
+ case "error": {
15806
+ const err = data.response?.error ?? data.error ?? data;
15807
+ throw new ProviderError(
15808
+ `OpenAI Responses API stream error: ${err?.message ?? "unknown error"}`,
15809
+ this.providerName,
15810
+ void 0,
15811
+ data
15812
+ );
15813
+ }
15814
+ }
15815
+ }
15816
+ yield {
15817
+ id: final?.id || id,
15818
+ model: final?.model || model,
15819
+ delta: final?.reasoning ? { reasoning: final.reasoning } : {},
15820
+ usage: final?.usage,
15821
+ finishReason: final?.finishReason ?? (calls.size > 0 ? "tool_calls" : "stop")
15822
+ };
15823
+ }
15154
15824
  buildResponsesBody(request) {
15155
15825
  const body = {
15156
15826
  model: request.model || this.defaultModel,
@@ -15180,7 +15850,7 @@ var OpenAIProvider = class extends BaseProvider {
15180
15850
  format: {
15181
15851
  type: "json_schema",
15182
15852
  name: request.responseFormat.name ?? "response",
15183
- schema: request.responseFormat.schema,
15853
+ schema: inlineJsonSchemaRefs(request.responseFormat.schema),
15184
15854
  strict: true
15185
15855
  }
15186
15856
  };
@@ -15247,7 +15917,7 @@ var OpenAIProvider = class extends BaseProvider {
15247
15917
  });
15248
15918
  return { role, content };
15249
15919
  }
15250
- async responsesFetch(body) {
15920
+ async responsesFetch(body, signal) {
15251
15921
  body.input = expandResponsesInput(body.input);
15252
15922
  const url = `${this.baseURL}/responses`;
15253
15923
  const headers = {
@@ -15256,7 +15926,7 @@ var OpenAIProvider = class extends BaseProvider {
15256
15926
  ...this.defaultHeaders
15257
15927
  };
15258
15928
  const payload = JSON.stringify(body);
15259
- const res = await this.doFetchWithRetry(() => fetch(url, { method: "POST", headers, body: payload }));
15929
+ const res = await this.doFetchWithRetry(() => fetch(url, { method: "POST", headers, body: payload, signal }), signal);
15260
15930
  if (!res.ok) {
15261
15931
  const text = await res.text().catch(() => "");
15262
15932
  throw new ProviderError(
@@ -15377,7 +16047,7 @@ var OpenAIProvider = class extends BaseProvider {
15377
16047
  }
15378
16048
  return { type: "function", function: fn };
15379
16049
  }
15380
- async doFetch(body) {
16050
+ async doFetch(body, signal) {
15381
16051
  const url = `${this.baseURL}/chat/completions`;
15382
16052
  const headers = {
15383
16053
  "Content-Type": "application/json",
@@ -15385,7 +16055,7 @@ var OpenAIProvider = class extends BaseProvider {
15385
16055
  ...this.defaultHeaders
15386
16056
  };
15387
16057
  const payload = JSON.stringify(body);
15388
- const res = await this.doFetchWithRetry(() => fetch(url, { method: "POST", headers, body: payload }));
16058
+ const res = await this.doFetchWithRetry(() => fetch(url, { method: "POST", headers, body: payload, signal }), signal);
15389
16059
  if (!res.ok) {
15390
16060
  const text = await res.text().catch(() => "");
15391
16061
  throw new ProviderError(
@@ -15435,6 +16105,48 @@ function isReasoningModel(model) {
15435
16105
  const m = model.toLowerCase();
15436
16106
  return /(^|[/_-])o[0-9]/.test(m) || m.startsWith("o1") || m.startsWith("o3") || m.startsWith("o4") || m.includes("gpt-5") || m.includes("gpt5");
15437
16107
  }
16108
+ function adaptChatExtrasForResponses(body, extra) {
16109
+ const native = extra ?? {};
16110
+ if ("reasoning_effort" in body) {
16111
+ const effort = body.reasoning_effort;
16112
+ delete body.reasoning_effort;
16113
+ if (effort != null) {
16114
+ const reasoning = body.reasoning && typeof body.reasoning === "object" ? body.reasoning : {};
16115
+ if (reasoning.effort == null) body.reasoning = { ...reasoning, effort };
16116
+ }
16117
+ }
16118
+ for (const key of ["max_tokens", "max_completion_tokens"]) {
16119
+ if (!(key in body)) continue;
16120
+ const value = body[key];
16121
+ delete body[key];
16122
+ if (value != null && !("max_output_tokens" in native)) body.max_output_tokens = value;
16123
+ }
16124
+ if ("response_format" in body) {
16125
+ const format = toResponsesTextFormat(body.response_format);
16126
+ delete body.response_format;
16127
+ if (format && !("text" in native)) body.text = { ...body.text, format };
16128
+ }
16129
+ if (body.stream_options && typeof body.stream_options === "object" && "include_usage" in body.stream_options) {
16130
+ const { include_usage: _dropped, ...rest } = body.stream_options;
16131
+ if (Object.keys(rest).length > 0) body.stream_options = rest;
16132
+ else delete body.stream_options;
16133
+ }
16134
+ }
16135
+ function toResponsesTextFormat(rf) {
16136
+ if (!rf || typeof rf !== "object") return void 0;
16137
+ if (rf.type === "json_schema") {
16138
+ const js = rf.json_schema ?? {};
16139
+ return {
16140
+ type: "json_schema",
16141
+ name: js.name ?? "response",
16142
+ schema: inlineJsonSchemaRefs(js.schema),
16143
+ ...js.description != null ? { description: js.description } : {},
16144
+ ...js.strict != null ? { strict: js.strict } : {}
16145
+ };
16146
+ }
16147
+ if (rf.type === "json_object" || rf.type === "text") return { type: rf.type };
16148
+ return void 0;
16149
+ }
15438
16150
  function expandResponsesInput(input) {
15439
16151
  const out = [];
15440
16152
  for (const item of input) {
@@ -15479,13 +16191,13 @@ var AnthropicProvider = class extends BaseProvider {
15479
16191
  }
15480
16192
  async complete(request) {
15481
16193
  const body = this.buildRequestBody(request, false);
15482
- const res = await this.doFetch(body);
16194
+ const res = await this.doFetch(body, request.signal);
15483
16195
  const json = await res.json();
15484
16196
  return this.parseResponse(json);
15485
16197
  }
15486
16198
  async *completeStream(request) {
15487
16199
  const body = this.buildRequestBody(request, true);
15488
- const res = await this.doFetch(body);
16200
+ const res = await this.doFetch(body, request.signal);
15489
16201
  if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
15490
16202
  let id = "";
15491
16203
  let model = "";
@@ -15495,7 +16207,7 @@ var AnthropicProvider = class extends BaseProvider {
15495
16207
  let cacheCreationInputTokens = 0;
15496
16208
  const currentToolCalls = /* @__PURE__ */ new Map();
15497
16209
  const reasoningBlocks = /* @__PURE__ */ new Map();
15498
- for await (const event of parseSSEStream(res.body)) {
16210
+ for await (const event of parseSSEStream(res.body, request.signal)) {
15499
16211
  let data;
15500
16212
  try {
15501
16213
  data = JSON.parse(event.data);
@@ -15775,7 +16487,7 @@ var AnthropicProvider = class extends BaseProvider {
15775
16487
  input_schema: t.parameters
15776
16488
  };
15777
16489
  }
15778
- async doFetch(body) {
16490
+ async doFetch(body, signal) {
15779
16491
  const url = `${this.baseURL}/v1/messages`;
15780
16492
  const headers = {
15781
16493
  "Content-Type": "application/json",
@@ -15784,7 +16496,7 @@ var AnthropicProvider = class extends BaseProvider {
15784
16496
  ...this.defaultHeaders
15785
16497
  };
15786
16498
  const payload = JSON.stringify(body);
15787
- const res = await this.doFetchWithRetry(() => fetch(url, { method: "POST", headers, body: payload }));
16499
+ const res = await this.doFetchWithRetry(() => fetch(url, { method: "POST", headers, body: payload, signal }), signal);
15788
16500
  if (!res.ok) {
15789
16501
  const text = await res.text().catch(() => "");
15790
16502
  throw new ProviderError(
@@ -15894,7 +16606,7 @@ var AzureProvider = class extends OpenAIProvider {
15894
16606
  this.deploymentName = config.deploymentName;
15895
16607
  this.azureApiKey = config.apiKey;
15896
16608
  }
15897
- async doFetch(body) {
16609
+ async doFetch(body, signal) {
15898
16610
  const deployment = this.deploymentName ?? body.model;
15899
16611
  if (!deployment) {
15900
16612
  throw new ProviderError("Azure requires a deploymentName or model", this.providerName);
@@ -15908,7 +16620,8 @@ var AzureProvider = class extends OpenAIProvider {
15908
16620
  "api-key": this.azureApiKey,
15909
16621
  ...this.defaultHeaders
15910
16622
  },
15911
- body: JSON.stringify(bodyWithoutModel)
16623
+ body: JSON.stringify(bodyWithoutModel),
16624
+ signal
15912
16625
  });
15913
16626
  if (!res.ok) {
15914
16627
  const text = await res.text().catch(() => "");
@@ -15924,7 +16637,7 @@ var AzureProvider = class extends OpenAIProvider {
15924
16637
  /** Azure exposes the Responses API at /openai/responses with api-key auth.
15925
16638
  * Reasoning summaries require a preview api-version; callers can override via
15926
16639
  * the `apiVersion` config. */
15927
- async responsesFetch(body) {
16640
+ async responsesFetch(body, signal) {
15928
16641
  body.input = expandAzureResponsesInput(body.input);
15929
16642
  const deployment = this.deploymentName ?? body.model;
15930
16643
  if (!deployment) {
@@ -15939,7 +16652,8 @@ var AzureProvider = class extends OpenAIProvider {
15939
16652
  "api-key": this.azureApiKey,
15940
16653
  ...this.defaultHeaders
15941
16654
  },
15942
- body: payload
16655
+ body: payload,
16656
+ signal
15943
16657
  });
15944
16658
  if (!res.ok) {
15945
16659
  const text = await res.text().catch(() => "");
@@ -16007,7 +16721,7 @@ var BedrockProvider = class extends BaseProvider {
16007
16721
  async complete(request) {
16008
16722
  const modelId = request.model || this.defaultModel;
16009
16723
  const body = this.buildConverseBody(request);
16010
- const json = await this.doFetch(modelId, body);
16724
+ const json = await this.doFetch(modelId, body, request.signal);
16011
16725
  return this.parseResponse(json, modelId);
16012
16726
  }
16013
16727
  async *completeStream(request) {
@@ -16184,7 +16898,7 @@ var BedrockProvider = class extends BaseProvider {
16184
16898
  }
16185
16899
  };
16186
16900
  }
16187
- async doFetch(modelId, body) {
16901
+ async doFetch(modelId, body, signal) {
16188
16902
  const host = `bedrock-runtime.${this.region}.amazonaws.com`;
16189
16903
  const url = `https://${host}/model/${encodeURIComponent(modelId)}/converse`;
16190
16904
  const bodyStr = JSON.stringify(body);
@@ -16202,8 +16916,8 @@ var BedrockProvider = class extends BaseProvider {
16202
16916
  service: "bedrock",
16203
16917
  credentials: this.credentials
16204
16918
  });
16205
- return fetch(url, { method: "POST", headers, body: bodyStr });
16206
- });
16919
+ return fetch(url, { method: "POST", headers, body: bodyStr, signal });
16920
+ }, signal);
16207
16921
  if (!res.ok) {
16208
16922
  const text = await res.text().catch(() => "");
16209
16923
  throw new ProviderError(
@@ -16336,7 +17050,7 @@ var VertexProvider = class extends BaseProvider {
16336
17050
  const modelId = request.model || this.defaultModel;
16337
17051
  const body = this.buildGeminiBody(request);
16338
17052
  const url = this.buildUrl(modelId, false);
16339
- const res = await this.doFetch(url, body);
17053
+ const res = await this.doFetch(url, body, request.signal);
16340
17054
  const json = await res.json();
16341
17055
  return this.parseResponse(json, modelId);
16342
17056
  }
@@ -16344,11 +17058,11 @@ var VertexProvider = class extends BaseProvider {
16344
17058
  const modelId = request.model || this.defaultModel;
16345
17059
  const body = this.buildGeminiBody(request);
16346
17060
  const url = this.buildUrl(modelId, true);
16347
- const res = await this.doFetch(url, body);
17061
+ const res = await this.doFetch(url, body, request.signal);
16348
17062
  if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
16349
17063
  const contentType = res.headers.get("content-type") ?? "";
16350
17064
  if (contentType.includes("text/event-stream")) {
16351
- for await (const event of parseSSEStream(res.body)) {
17065
+ for await (const event of parseSSEStream(res.body, request.signal)) {
16352
17066
  if (event.data === "[DONE]") break;
16353
17067
  let chunk;
16354
17068
  try {
@@ -16390,6 +17104,7 @@ var VertexProvider = class extends BaseProvider {
16390
17104
  }
16391
17105
  }
16392
17106
  } finally {
17107
+ reader.cancel().catch(() => void 0);
16393
17108
  reader.releaseLock();
16394
17109
  }
16395
17110
  }
@@ -16601,7 +17316,7 @@ var VertexProvider = class extends BaseProvider {
16601
17316
  const tokenJson = await tokenRes.json();
16602
17317
  return tokenJson.access_token;
16603
17318
  }
16604
- async doFetch(url, body) {
17319
+ async doFetch(url, body, signal) {
16605
17320
  const token = await this.getAccessToken();
16606
17321
  const res = await fetch(url, {
16607
17322
  method: "POST",
@@ -16610,7 +17325,8 @@ var VertexProvider = class extends BaseProvider {
16610
17325
  Authorization: `Bearer ${token}`,
16611
17326
  ...this.defaultHeaders
16612
17327
  },
16613
- body: JSON.stringify(body)
17328
+ body: JSON.stringify(body),
17329
+ signal
16614
17330
  });
16615
17331
  if (!res.ok) {
16616
17332
  const text = await res.text().catch(() => "");
@@ -16749,6 +17465,8 @@ function createProvider(config) {
16749
17465
  return new BedrockProvider(config);
16750
17466
  case "vertex":
16751
17467
  return new VertexProvider(config);
17468
+ case "jev":
17469
+ return new JevProvider(config);
16752
17470
  default:
16753
17471
  throw new Error(`Unknown provider: ${config.provider}`);
16754
17472
  }
@@ -16776,6 +17494,7 @@ exports.HOOK_CONTRACT_VERSION = HOOK_CONTRACT_VERSION;
16776
17494
  exports.HOOK_NAMES = HOOK_NAMES;
16777
17495
  exports.HookTimeoutError = HookTimeoutError;
16778
17496
  exports.InMemoryMemoryStore = InMemoryMemoryStore;
17497
+ exports.JevProvider = JevProvider;
16779
17498
  exports.NativeJsonSchemaStrategy = NativeJsonSchemaStrategy;
16780
17499
  exports.OpenAICompatibleProvider = OpenAICompatibleProvider;
16781
17500
  exports.OpenAIProvider = OpenAIProvider;
@@ -16815,6 +17534,7 @@ exports.compressLogOutput = compressLogOutput;
16815
17534
  exports.compressSearchOutput = compressSearchOutput;
16816
17535
  exports.compressTextOutput = compressTextOutput;
16817
17536
  exports.compressToolOutput = compressToolOutput;
17537
+ exports.contentPartToText = contentPartToText;
16818
17538
  exports.contentToString = contentToString;
16819
17539
  exports.conversationHistory = conversationHistory;
16820
17540
  exports.countApproxTokens = countApproxTokens;
@@ -16857,6 +17577,7 @@ exports.dedupeToolsByName = dedupeToolsByName;
16857
17577
  exports.defaultTokenCounter = defaultTokenCounter;
16858
17578
  exports.defineHook = defineHook;
16859
17579
  exports.definePlugin = definePlugin;
17580
+ exports.describeContentPart = describeContentPart;
16860
17581
  exports.detectLanguage = detectLanguage;
16861
17582
  exports.detectTextFormat = detectTextFormat;
16862
17583
  exports.detectVolatileContent = detectVolatileContent;
@@ -16865,7 +17586,7 @@ exports.digestToolInputValue = digestToolInputValue;
16865
17586
  exports.emptyUsage = emptyUsage;
16866
17587
  exports.evaluateGuardrails = evaluateGuardrails;
16867
17588
  exports.extractLatestUserQuery = extractLatestUserQuery;
16868
- exports.extractMessageText = extractMessageText2;
17589
+ exports.extractMessageText = extractMessageText;
16869
17590
  exports.fileCheckpointStore = fileCheckpointStore;
16870
17591
  exports.fileSink = fileSink;
16871
17592
  exports.findSubagent = findSubagent;
@@ -16879,6 +17600,7 @@ exports.getModelCapabilities = getModelCapabilities;
16879
17600
  exports.getNestedValue = getNestedValue;
16880
17601
  exports.getResolvedSmartConfig = getResolvedSmartConfig;
16881
17602
  exports.getTokenCounter = getTokenCounter;
17603
+ exports.hasMediaPart = hasMediaPart;
16882
17604
  exports.hasToolResponseRecoveryReference = hasToolResponseRecoveryReference;
16883
17605
  exports.httpGuardrail = httpGuardrail;
16884
17606
  exports.httpSink = httpSink;
@@ -16898,6 +17620,7 @@ exports.loadSkillsFromDisk = loadSkillsFromDisk;
16898
17620
  exports.mapConsoleHookVerdict = mapConsoleHookVerdict;
16899
17621
  exports.mapTextParts = mapTextParts;
16900
17622
  exports.mcp = mcp;
17623
+ exports.mediaKindOf = mediaKindOf;
16901
17624
  exports.mediaPolicy = mediaPolicy;
16902
17625
  exports.normalizeSmartAgentOptions = normalizeSmartAgentOptions;
16903
17626
  exports.normalizeVerdicts = normalizeVerdicts;