@cognipeer/agent-sdk 0.10.5 → 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;
@@ -2553,157 +2947,28 @@ function resolveCapabilities(provider) {
2553
2947
  };
2554
2948
  case "bedrock":
2555
2949
  return {
2556
- structuredOutput: "tool_based",
2557
- strictToolCalling: false,
2558
- streaming: false,
2559
- // streaming uses binary protocol
2560
- provider: "bedrock"
2561
- };
2562
- case "vertex":
2563
- return {
2564
- structuredOutput: "native",
2565
- strictToolCalling: false,
2566
- streaming: true,
2567
- provider: "google"
2568
- };
2569
- default:
2570
- return { provider };
2571
- }
2572
- }
2573
-
2574
- // src/smart/toolResponses.ts
2575
- 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
- };
2950
+ structuredOutput: "tool_based",
2951
+ strictToolCalling: false,
2952
+ streaming: false,
2953
+ // streaming uses binary protocol
2954
+ provider: "bedrock"
2955
+ };
2956
+ case "vertex":
2957
+ return {
2958
+ structuredOutput: "native",
2959
+ strictToolCalling: false,
2960
+ streaming: true,
2961
+ provider: "google"
2962
+ };
2963
+ default:
2964
+ return { provider };
2665
2965
  }
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
2966
  }
2706
2967
 
2968
+ // src/smart/toolResponses.ts
2969
+ init_utilTokens();
2970
+ init_content();
2971
+
2707
2972
  // src/smart/toolArgCoercion.ts
2708
2973
  var PLACEHOLDER_TEXT = /* @__PURE__ */ new Set(["", "null", "undefined", "none", "nil", "n/a", "na"]);
2709
2974
  var ENVELOPE_KEYS = ["input", "args", "arguments", "params", "parameters", "payload"];
@@ -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 || {};
@@ -4334,6 +4603,29 @@ ${append}`;
4334
4603
  let response;
4335
4604
  let modelAttempt = 0;
4336
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
  }
@@ -14994,12 +15433,12 @@ function sanitizeBedrockDocName(name, fallback = "document") {
14994
15433
  // src/providers/utils/jsonSchemaRefs.ts
14995
15434
  var DEFINITION_KEYS = ["definitions", "$defs"];
14996
15435
  function inlineJsonSchemaRefs(schema) {
14997
- if (!isObject(schema) || !containsRef(schema)) return schema;
15436
+ if (!isObject2(schema) || !containsRef(schema)) return schema;
14998
15437
  const root = structuredClone(schema);
14999
15438
  const selfReaching = /* @__PURE__ */ new Map();
15000
15439
  const targetOf = (ref) => {
15001
15440
  const target = resolvePointer(root, ref);
15002
- return isObject(target) ? target : void 0;
15441
+ return isObject2(target) ? target : void 0;
15003
15442
  };
15004
15443
  const reachesItself = (target) => {
15005
15444
  const known = selfReaching.get(target);
@@ -15007,7 +15446,7 @@ function inlineJsonSchemaRefs(schema) {
15007
15446
  const seen = /* @__PURE__ */ new Set();
15008
15447
  const walk = (node) => {
15009
15448
  if (Array.isArray(node)) return node.some(walk);
15010
- if (!isObject(node)) return false;
15449
+ if (!isObject2(node)) return false;
15011
15450
  if (typeof node.$ref === "string") {
15012
15451
  const next = targetOf(node.$ref);
15013
15452
  if (next === target) return true;
@@ -15025,7 +15464,7 @@ function inlineJsonSchemaRefs(schema) {
15025
15464
  const keptRefs = /* @__PURE__ */ new Set();
15026
15465
  const expand = (node) => {
15027
15466
  if (Array.isArray(node)) return node.map(expand);
15028
- if (!isObject(node)) return node;
15467
+ if (!isObject2(node)) return node;
15029
15468
  if (typeof node.$ref === "string") {
15030
15469
  const target = targetOf(node.$ref);
15031
15470
  if (target && !reachesItself(target)) {
@@ -15047,7 +15486,7 @@ function inlineJsonSchemaRefs(schema) {
15047
15486
  for (const key of DEFINITION_KEYS) delete result[key];
15048
15487
  for (const key of DEFINITION_KEYS) {
15049
15488
  const block = root[key];
15050
- if (!isObject(block)) continue;
15489
+ if (!isObject2(block)) continue;
15051
15490
  const needed = neededEntries(key, block, keptRefs);
15052
15491
  if (needed.length === 0) continue;
15053
15492
  const kept = {};
@@ -15075,13 +15514,13 @@ function collectRefs(node, into) {
15075
15514
  node.forEach((child) => collectRefs(child, into));
15076
15515
  return;
15077
15516
  }
15078
- if (!isObject(node)) return;
15517
+ if (!isObject2(node)) return;
15079
15518
  if (typeof node.$ref === "string") into.push(node.$ref);
15080
15519
  for (const [k, value] of Object.entries(node)) if (k !== "$ref") collectRefs(value, into);
15081
15520
  }
15082
15521
  function containsRef(node) {
15083
15522
  if (Array.isArray(node)) return node.some(containsRef);
15084
- if (!isObject(node)) return false;
15523
+ if (!isObject2(node)) return false;
15085
15524
  if (typeof node.$ref === "string") return true;
15086
15525
  return Object.values(node).some(containsRef);
15087
15526
  }
@@ -15098,7 +15537,7 @@ function resolvePointer(document, ref) {
15098
15537
  if (Array.isArray(current)) {
15099
15538
  if (!/^\d+$/.test(segment)) return void 0;
15100
15539
  current = current[Number(segment)];
15101
- } else if (isObject(current) && Object.prototype.hasOwnProperty.call(current, segment)) {
15540
+ } else if (isObject2(current) && Object.prototype.hasOwnProperty.call(current, segment)) {
15102
15541
  current = current[segment];
15103
15542
  } else {
15104
15543
  return void 0;
@@ -15106,7 +15545,7 @@ function resolvePointer(document, ref) {
15106
15545
  }
15107
15546
  return current;
15108
15547
  }
15109
- function isObject(value) {
15548
+ function isObject2(value) {
15110
15549
  return typeof value === "object" && value !== null && !Array.isArray(value);
15111
15550
  }
15112
15551
 
@@ -15162,7 +15601,7 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
15162
15601
  return this.completeResponses(request);
15163
15602
  }
15164
15603
  const body = this.buildRequestBody(request, false);
15165
- const res = await this.doFetch(body);
15604
+ const res = await this.doFetch(body, request.signal);
15166
15605
  const json = await res.json();
15167
15606
  return this.parseResponse(json);
15168
15607
  }
@@ -15187,13 +15626,13 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
15187
15626
  return;
15188
15627
  }
15189
15628
  const body = this.buildRequestBody(request, true);
15190
- const res = await this.doFetch(body);
15629
+ const res = await this.doFetch(body, request.signal);
15191
15630
  if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
15192
15631
  let id = "";
15193
15632
  let model = "";
15194
15633
  const toolCallBuffers = /* @__PURE__ */ new Map();
15195
15634
  let usage;
15196
- for await (const event of parseSSEStream(res.body)) {
15635
+ for await (const event of parseSSEStream(res.body, request.signal)) {
15197
15636
  if (event.data === "[DONE]") break;
15198
15637
  let chunk;
15199
15638
  try {
@@ -15288,7 +15727,7 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
15288
15727
  async completeResponses(request) {
15289
15728
  const body = this.buildResponsesBody(request);
15290
15729
  if (this.isResponsesApiModel(request)) adaptChatExtrasForResponses(body, request.extra);
15291
- const res = await this.responsesFetch(body);
15730
+ const res = await this.responsesFetch(body, request.signal);
15292
15731
  const json = await res.json();
15293
15732
  return this.parseResponsesResponse(json, request.model || this.defaultModel);
15294
15733
  }
@@ -15304,13 +15743,13 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
15304
15743
  const body = this.buildResponsesBody(request);
15305
15744
  adaptChatExtrasForResponses(body, request.extra);
15306
15745
  body.stream = true;
15307
- const res = await this.responsesFetch(body);
15746
+ const res = await this.responsesFetch(body, request.signal);
15308
15747
  if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
15309
15748
  let id = "";
15310
15749
  let model = modelId;
15311
15750
  const calls = /* @__PURE__ */ new Map();
15312
15751
  let final;
15313
- for await (const event of parseSSEStream(res.body)) {
15752
+ for await (const event of parseSSEStream(res.body, request.signal)) {
15314
15753
  if (event.data === "[DONE]") break;
15315
15754
  let data;
15316
15755
  try {
@@ -15478,7 +15917,7 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
15478
15917
  });
15479
15918
  return { role, content };
15480
15919
  }
15481
- async responsesFetch(body) {
15920
+ async responsesFetch(body, signal) {
15482
15921
  body.input = expandResponsesInput(body.input);
15483
15922
  const url = `${this.baseURL}/responses`;
15484
15923
  const headers = {
@@ -15487,7 +15926,7 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
15487
15926
  ...this.defaultHeaders
15488
15927
  };
15489
15928
  const payload = JSON.stringify(body);
15490
- 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);
15491
15930
  if (!res.ok) {
15492
15931
  const text = await res.text().catch(() => "");
15493
15932
  throw new ProviderError(
@@ -15608,7 +16047,7 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
15608
16047
  }
15609
16048
  return { type: "function", function: fn };
15610
16049
  }
15611
- async doFetch(body) {
16050
+ async doFetch(body, signal) {
15612
16051
  const url = `${this.baseURL}/chat/completions`;
15613
16052
  const headers = {
15614
16053
  "Content-Type": "application/json",
@@ -15616,7 +16055,7 @@ var OpenAIProvider = class _OpenAIProvider extends BaseProvider {
15616
16055
  ...this.defaultHeaders
15617
16056
  };
15618
16057
  const payload = JSON.stringify(body);
15619
- 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);
15620
16059
  if (!res.ok) {
15621
16060
  const text = await res.text().catch(() => "");
15622
16061
  throw new ProviderError(
@@ -15752,13 +16191,13 @@ var AnthropicProvider = class extends BaseProvider {
15752
16191
  }
15753
16192
  async complete(request) {
15754
16193
  const body = this.buildRequestBody(request, false);
15755
- const res = await this.doFetch(body);
16194
+ const res = await this.doFetch(body, request.signal);
15756
16195
  const json = await res.json();
15757
16196
  return this.parseResponse(json);
15758
16197
  }
15759
16198
  async *completeStream(request) {
15760
16199
  const body = this.buildRequestBody(request, true);
15761
- const res = await this.doFetch(body);
16200
+ const res = await this.doFetch(body, request.signal);
15762
16201
  if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
15763
16202
  let id = "";
15764
16203
  let model = "";
@@ -15768,7 +16207,7 @@ var AnthropicProvider = class extends BaseProvider {
15768
16207
  let cacheCreationInputTokens = 0;
15769
16208
  const currentToolCalls = /* @__PURE__ */ new Map();
15770
16209
  const reasoningBlocks = /* @__PURE__ */ new Map();
15771
- for await (const event of parseSSEStream(res.body)) {
16210
+ for await (const event of parseSSEStream(res.body, request.signal)) {
15772
16211
  let data;
15773
16212
  try {
15774
16213
  data = JSON.parse(event.data);
@@ -16048,7 +16487,7 @@ var AnthropicProvider = class extends BaseProvider {
16048
16487
  input_schema: t.parameters
16049
16488
  };
16050
16489
  }
16051
- async doFetch(body) {
16490
+ async doFetch(body, signal) {
16052
16491
  const url = `${this.baseURL}/v1/messages`;
16053
16492
  const headers = {
16054
16493
  "Content-Type": "application/json",
@@ -16057,7 +16496,7 @@ var AnthropicProvider = class extends BaseProvider {
16057
16496
  ...this.defaultHeaders
16058
16497
  };
16059
16498
  const payload = JSON.stringify(body);
16060
- 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);
16061
16500
  if (!res.ok) {
16062
16501
  const text = await res.text().catch(() => "");
16063
16502
  throw new ProviderError(
@@ -16167,7 +16606,7 @@ var AzureProvider = class extends OpenAIProvider {
16167
16606
  this.deploymentName = config.deploymentName;
16168
16607
  this.azureApiKey = config.apiKey;
16169
16608
  }
16170
- async doFetch(body) {
16609
+ async doFetch(body, signal) {
16171
16610
  const deployment = this.deploymentName ?? body.model;
16172
16611
  if (!deployment) {
16173
16612
  throw new ProviderError("Azure requires a deploymentName or model", this.providerName);
@@ -16181,7 +16620,8 @@ var AzureProvider = class extends OpenAIProvider {
16181
16620
  "api-key": this.azureApiKey,
16182
16621
  ...this.defaultHeaders
16183
16622
  },
16184
- body: JSON.stringify(bodyWithoutModel)
16623
+ body: JSON.stringify(bodyWithoutModel),
16624
+ signal
16185
16625
  });
16186
16626
  if (!res.ok) {
16187
16627
  const text = await res.text().catch(() => "");
@@ -16197,7 +16637,7 @@ var AzureProvider = class extends OpenAIProvider {
16197
16637
  /** Azure exposes the Responses API at /openai/responses with api-key auth.
16198
16638
  * Reasoning summaries require a preview api-version; callers can override via
16199
16639
  * the `apiVersion` config. */
16200
- async responsesFetch(body) {
16640
+ async responsesFetch(body, signal) {
16201
16641
  body.input = expandAzureResponsesInput(body.input);
16202
16642
  const deployment = this.deploymentName ?? body.model;
16203
16643
  if (!deployment) {
@@ -16212,7 +16652,8 @@ var AzureProvider = class extends OpenAIProvider {
16212
16652
  "api-key": this.azureApiKey,
16213
16653
  ...this.defaultHeaders
16214
16654
  },
16215
- body: payload
16655
+ body: payload,
16656
+ signal
16216
16657
  });
16217
16658
  if (!res.ok) {
16218
16659
  const text = await res.text().catch(() => "");
@@ -16280,7 +16721,7 @@ var BedrockProvider = class extends BaseProvider {
16280
16721
  async complete(request) {
16281
16722
  const modelId = request.model || this.defaultModel;
16282
16723
  const body = this.buildConverseBody(request);
16283
- const json = await this.doFetch(modelId, body);
16724
+ const json = await this.doFetch(modelId, body, request.signal);
16284
16725
  return this.parseResponse(json, modelId);
16285
16726
  }
16286
16727
  async *completeStream(request) {
@@ -16457,7 +16898,7 @@ var BedrockProvider = class extends BaseProvider {
16457
16898
  }
16458
16899
  };
16459
16900
  }
16460
- async doFetch(modelId, body) {
16901
+ async doFetch(modelId, body, signal) {
16461
16902
  const host = `bedrock-runtime.${this.region}.amazonaws.com`;
16462
16903
  const url = `https://${host}/model/${encodeURIComponent(modelId)}/converse`;
16463
16904
  const bodyStr = JSON.stringify(body);
@@ -16475,8 +16916,8 @@ var BedrockProvider = class extends BaseProvider {
16475
16916
  service: "bedrock",
16476
16917
  credentials: this.credentials
16477
16918
  });
16478
- return fetch(url, { method: "POST", headers, body: bodyStr });
16479
- });
16919
+ return fetch(url, { method: "POST", headers, body: bodyStr, signal });
16920
+ }, signal);
16480
16921
  if (!res.ok) {
16481
16922
  const text = await res.text().catch(() => "");
16482
16923
  throw new ProviderError(
@@ -16609,7 +17050,7 @@ var VertexProvider = class extends BaseProvider {
16609
17050
  const modelId = request.model || this.defaultModel;
16610
17051
  const body = this.buildGeminiBody(request);
16611
17052
  const url = this.buildUrl(modelId, false);
16612
- const res = await this.doFetch(url, body);
17053
+ const res = await this.doFetch(url, body, request.signal);
16613
17054
  const json = await res.json();
16614
17055
  return this.parseResponse(json, modelId);
16615
17056
  }
@@ -16617,11 +17058,11 @@ var VertexProvider = class extends BaseProvider {
16617
17058
  const modelId = request.model || this.defaultModel;
16618
17059
  const body = this.buildGeminiBody(request);
16619
17060
  const url = this.buildUrl(modelId, true);
16620
- const res = await this.doFetch(url, body);
17061
+ const res = await this.doFetch(url, body, request.signal);
16621
17062
  if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
16622
17063
  const contentType = res.headers.get("content-type") ?? "";
16623
17064
  if (contentType.includes("text/event-stream")) {
16624
- for await (const event of parseSSEStream(res.body)) {
17065
+ for await (const event of parseSSEStream(res.body, request.signal)) {
16625
17066
  if (event.data === "[DONE]") break;
16626
17067
  let chunk;
16627
17068
  try {
@@ -16663,6 +17104,7 @@ var VertexProvider = class extends BaseProvider {
16663
17104
  }
16664
17105
  }
16665
17106
  } finally {
17107
+ reader.cancel().catch(() => void 0);
16666
17108
  reader.releaseLock();
16667
17109
  }
16668
17110
  }
@@ -16874,7 +17316,7 @@ var VertexProvider = class extends BaseProvider {
16874
17316
  const tokenJson = await tokenRes.json();
16875
17317
  return tokenJson.access_token;
16876
17318
  }
16877
- async doFetch(url, body) {
17319
+ async doFetch(url, body, signal) {
16878
17320
  const token = await this.getAccessToken();
16879
17321
  const res = await fetch(url, {
16880
17322
  method: "POST",
@@ -16883,7 +17325,8 @@ var VertexProvider = class extends BaseProvider {
16883
17325
  Authorization: `Bearer ${token}`,
16884
17326
  ...this.defaultHeaders
16885
17327
  },
16886
- body: JSON.stringify(body)
17328
+ body: JSON.stringify(body),
17329
+ signal
16887
17330
  });
16888
17331
  if (!res.ok) {
16889
17332
  const text = await res.text().catch(() => "");
@@ -17022,6 +17465,8 @@ function createProvider(config) {
17022
17465
  return new BedrockProvider(config);
17023
17466
  case "vertex":
17024
17467
  return new VertexProvider(config);
17468
+ case "jev":
17469
+ return new JevProvider(config);
17025
17470
  default:
17026
17471
  throw new Error(`Unknown provider: ${config.provider}`);
17027
17472
  }
@@ -17049,6 +17494,7 @@ exports.HOOK_CONTRACT_VERSION = HOOK_CONTRACT_VERSION;
17049
17494
  exports.HOOK_NAMES = HOOK_NAMES;
17050
17495
  exports.HookTimeoutError = HookTimeoutError;
17051
17496
  exports.InMemoryMemoryStore = InMemoryMemoryStore;
17497
+ exports.JevProvider = JevProvider;
17052
17498
  exports.NativeJsonSchemaStrategy = NativeJsonSchemaStrategy;
17053
17499
  exports.OpenAICompatibleProvider = OpenAICompatibleProvider;
17054
17500
  exports.OpenAIProvider = OpenAIProvider;
@@ -17088,6 +17534,7 @@ exports.compressLogOutput = compressLogOutput;
17088
17534
  exports.compressSearchOutput = compressSearchOutput;
17089
17535
  exports.compressTextOutput = compressTextOutput;
17090
17536
  exports.compressToolOutput = compressToolOutput;
17537
+ exports.contentPartToText = contentPartToText;
17091
17538
  exports.contentToString = contentToString;
17092
17539
  exports.conversationHistory = conversationHistory;
17093
17540
  exports.countApproxTokens = countApproxTokens;
@@ -17130,6 +17577,7 @@ exports.dedupeToolsByName = dedupeToolsByName;
17130
17577
  exports.defaultTokenCounter = defaultTokenCounter;
17131
17578
  exports.defineHook = defineHook;
17132
17579
  exports.definePlugin = definePlugin;
17580
+ exports.describeContentPart = describeContentPart;
17133
17581
  exports.detectLanguage = detectLanguage;
17134
17582
  exports.detectTextFormat = detectTextFormat;
17135
17583
  exports.detectVolatileContent = detectVolatileContent;
@@ -17138,7 +17586,7 @@ exports.digestToolInputValue = digestToolInputValue;
17138
17586
  exports.emptyUsage = emptyUsage;
17139
17587
  exports.evaluateGuardrails = evaluateGuardrails;
17140
17588
  exports.extractLatestUserQuery = extractLatestUserQuery;
17141
- exports.extractMessageText = extractMessageText2;
17589
+ exports.extractMessageText = extractMessageText;
17142
17590
  exports.fileCheckpointStore = fileCheckpointStore;
17143
17591
  exports.fileSink = fileSink;
17144
17592
  exports.findSubagent = findSubagent;
@@ -17152,6 +17600,7 @@ exports.getModelCapabilities = getModelCapabilities;
17152
17600
  exports.getNestedValue = getNestedValue;
17153
17601
  exports.getResolvedSmartConfig = getResolvedSmartConfig;
17154
17602
  exports.getTokenCounter = getTokenCounter;
17603
+ exports.hasMediaPart = hasMediaPart;
17155
17604
  exports.hasToolResponseRecoveryReference = hasToolResponseRecoveryReference;
17156
17605
  exports.httpGuardrail = httpGuardrail;
17157
17606
  exports.httpSink = httpSink;
@@ -17171,6 +17620,7 @@ exports.loadSkillsFromDisk = loadSkillsFromDisk;
17171
17620
  exports.mapConsoleHookVerdict = mapConsoleHookVerdict;
17172
17621
  exports.mapTextParts = mapTextParts;
17173
17622
  exports.mcp = mcp;
17623
+ exports.mediaKindOf = mediaKindOf;
17174
17624
  exports.mediaPolicy = mediaPolicy;
17175
17625
  exports.normalizeSmartAgentOptions = normalizeSmartAgentOptions;
17176
17626
  exports.normalizeVerdicts = normalizeVerdicts;