openfox 2.0.76 → 2.0.78

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.
Files changed (76) hide show
  1. package/dist/{chat-handler-3BZ4PDL2.js → chat-handler-5ZHPYU24.js} +21 -20
  2. package/dist/{chunk-MF3UW36X.js → chunk-37WF2Z3T.js} +84 -17
  3. package/dist/{chunk-OBQPXUXI.js → chunk-5CQRB7AD.js} +2 -2
  4. package/dist/chunk-64TKWWNF.js +7 -0
  5. package/dist/{chunk-LXFLRM7M.js → chunk-6AE3JPDJ.js} +2 -2
  6. package/dist/chunk-AANVMTYU.js +327 -0
  7. package/dist/{chunk-YMFCFMMB.js → chunk-AFJLY43Z.js} +8 -3
  8. package/dist/{chunk-OCVRIPQP.js → chunk-CJDYSUU5.js} +3 -3
  9. package/dist/{chunk-OMWVB2XC.js → chunk-DI4SIPFT.js} +46 -27
  10. package/dist/{chunk-7772ENZN.js → chunk-EHVDAVYG.js} +4 -3
  11. package/dist/{chunk-DH3SSBZN.js → chunk-EYIWUONA.js} +2 -2
  12. package/dist/{chunk-NGJ5XJ7D.js → chunk-M7EFXRDS.js} +57 -290
  13. package/dist/{chunk-4QHDQP2N.js → chunk-MTUYDBSL.js} +2 -2
  14. package/dist/{chunk-HHUIXHHX.js → chunk-NUR2B4RU.js} +7 -7
  15. package/dist/{chunk-46RORSKU.js → chunk-NZIYELSQ.js} +9 -7
  16. package/dist/{chunk-7QXRQ5ZL.js → chunk-RGUUZD5B.js} +5 -1
  17. package/dist/{chunk-EZK7CGJ7.js → chunk-RMXPVQP4.js} +315 -45
  18. package/dist/{chunk-D6CI2B7B.js → chunk-SGBXBP3S.js} +253 -110
  19. package/dist/{chunk-O3NUNU4H.js → chunk-SJTG56CH.js} +5 -4
  20. package/dist/{chunk-DJASQ2ZB.js → chunk-TSVFRWSG.js} +24 -5
  21. package/dist/{chunk-XTK6E2QR.js → chunk-UIU7OGHP.js} +26 -10
  22. package/dist/{chunk-J7VCI5EX.js → chunk-UMX7MMNW.js} +652 -248
  23. package/dist/{chunk-HLHGNHMV.js → chunk-VGWJFXPR.js} +23 -11
  24. package/dist/{chunk-KVRSAC4Z.js → chunk-VPFUXGMM.js} +6 -1
  25. package/dist/{chunk-W4ZEG3LX.js → chunk-WL2WEJAK.js} +2 -2
  26. package/dist/{chunk-HAQV6AJP.js → chunk-WUSWNLTV.js} +3 -3
  27. package/dist/{chunk-5GOOSZX7.js → chunk-X6BXZFO7.js} +1 -1
  28. package/dist/{chunk-WGAAQASA.js → chunk-YFVSSMA3.js} +3 -2
  29. package/dist/cli/dev.js +1 -1
  30. package/dist/cli/index.js +1 -1
  31. package/dist/{client-TDPQVBQ7.js → client-V5DZRO2D.js} +3 -3
  32. package/dist/{client-pure-NMLDKRDN.js → client-pure-5YE5WFJP.js} +2 -2
  33. package/dist/compactor-G7B3V24F.js +24 -0
  34. package/dist/{config-EUMVEFHU.js → config-MD74ETY5.js} +2 -2
  35. package/dist/{events-FLWRLM6V.js → events-KNOZX7YG.js} +7 -4
  36. package/dist/{folding-ONH3D4OK.js → folding-OPP2BVUB.js} +2 -2
  37. package/dist/{inspect-proxy-JHZV34QR.js → inspect-proxy-DRYIIH4O.js} +4 -4
  38. package/dist/{manager-OPMCSYN2.js → manager-ATWZUL3O.js} +5 -5
  39. package/dist/{orchestrator-5IMCWX5P.js → orchestrator-L6LX5BV7.js} +20 -19
  40. package/dist/package.json +6 -1
  41. package/dist/{path-security-ZROWNL22.js → path-security-HUXQHEQ5.js} +10 -7
  42. package/dist/{platform-QGXO3CNR.js → platform-VBNNMTR5.js} +4 -4
  43. package/dist/{processor-3R4GS6VB.js → processor-UQX5KL56.js} +21 -20
  44. package/dist/{project-creator-C22J43BC.js → project-creator-NNUITNUI.js} +3 -3
  45. package/dist/{projects-WSVINK2C.js → projects-ZGJGICGT.js} +3 -3
  46. package/dist/{protocol-Sr6Jr4ZS.d.ts → protocol-BXxJmsOW.d.ts} +2 -1
  47. package/dist/{protocol-EWEVW65B.js → protocol-Q7X4NEME.js} +3 -3
  48. package/dist/provider/index.d.ts +4 -4
  49. package/dist/{provider-G2YC2IC4.js → provider-PI3RDDE4.js} +6 -6
  50. package/dist/{provider-manager-SQIELYLK.js → provider-manager-PD3BXUHY.js} +4 -4
  51. package/dist/{serve-MXKMKZYY.js → serve-QWC3U5MB.js} +26 -25
  52. package/dist/server/index.d.ts +27 -4
  53. package/dist/server/index.js +25 -24
  54. package/dist/server-3XUD2NB7.js +40 -0
  55. package/dist/sessions-SFG42M4W.js +42 -0
  56. package/dist/{settings-7XDMSPIC.js → settings-6M55N4Q2.js} +5 -3
  57. package/dist/shared/index.d.ts +3 -3
  58. package/dist/shared/index.js +1 -1
  59. package/dist/{tools-JRCUWPJO.js → tools-HGKUD327.js} +26 -23
  60. package/dist/{types-C4cG9CtC.d.ts → types-C308SU4B.d.ts} +3 -0
  61. package/dist/{types-DSZhFA8O.d.ts → types-o07LJcLI.d.ts} +1 -1
  62. package/dist/{update-OXDQ75M7.js → update-NWZALB5M.js} +2 -2
  63. package/dist/{update-server-4MTM7M5P.js → update-server-V6GJ43WH.js} +2 -2
  64. package/dist/web/assets/index-7_xKqeJc.js +305 -0
  65. package/dist/web/assets/index-DRkTjPo3.css +32 -0
  66. package/dist/web/index.html +4 -4
  67. package/dist/web/manifest.webmanifest +1 -1
  68. package/dist/web/registerSW.js +1 -1
  69. package/dist/web/sw.js +1 -1
  70. package/dist/{workspace-6QTTUMMW.js → workspace-J74ZMSOJ.js} +14 -2
  71. package/package.json +6 -1
  72. package/dist/chunk-KQEVAO2Q.js +0 -7
  73. package/dist/compactor-BB3YYVRP.js +0 -23
  74. package/dist/server-SPYFMFQE.js +0 -39
  75. package/dist/web/assets/index-BNVBxKnB.css +0 -32
  76. package/dist/web/assets/index-ghDkIdlf.js +0 -305
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  getEventStore
3
- } from "./chunk-NGJ5XJ7D.js";
3
+ } from "./chunk-M7EFXRDS.js";
4
4
  import {
5
5
  createChatPathConfirmationMessage
6
- } from "./chunk-O3NUNU4H.js";
6
+ } from "./chunk-SJTG56CH.js";
7
7
 
8
8
  // src/server/tools/path-security.ts
9
9
  import { realpath } from "fs/promises";
@@ -416,10 +416,8 @@ function providePathConfirmation(callId, approved, alwaysAllow) {
416
416
  });
417
417
  } catch {
418
418
  }
419
- if (approved) {
420
- if (alwaysAllow) {
421
- addAllowedPaths(pending.sessionId, pending.paths);
422
- } else {
419
+ if (approved && alwaysAllow) {
420
+ if (pending.reason !== "dangerous_command" && pending.reason !== "git_no_verify") {
423
421
  addAllowedPaths(pending.sessionId, pending.paths);
424
422
  }
425
423
  }
@@ -451,6 +449,9 @@ function cancelPathConfirmationsForSession(sessionId, reason) {
451
449
  function hasPendingPathConfirmation(callId) {
452
450
  return pendingConfirmations.has(callId);
453
451
  }
452
+ function getConfirmationSessionId(callId) {
453
+ return pendingConfirmations.get(callId)?.sessionId;
454
+ }
454
455
  function getPendingConfirmationsBySession() {
455
456
  const bySession = {};
456
457
  for (const [_callId, pending] of pendingConfirmations.entries()) {
@@ -490,6 +491,7 @@ export {
490
491
  cancelPathConfirmation,
491
492
  cancelPathConfirmationsForSession,
492
493
  hasPendingPathConfirmation,
494
+ getConfirmationSessionId,
493
495
  getPendingConfirmationsBySession
494
496
  };
495
- //# sourceMappingURL=chunk-46RORSKU.js.map
497
+ //# sourceMappingURL=chunk-NZIYELSQ.js.map
@@ -181,6 +181,10 @@ function runMigrations(db2) {
181
181
  logger.info("Migrating sessions table: adding workspace column");
182
182
  db2.exec(`ALTER TABLE sessions ADD COLUMN workspace TEXT`);
183
183
  }
184
+ if (!columnNames.includes("branch")) {
185
+ logger.info("Migrating sessions table: adding branch column");
186
+ db2.exec(`ALTER TABLE sessions ADD COLUMN branch TEXT`);
187
+ }
184
188
  logger.info("Database migrations completed");
185
189
  }
186
190
 
@@ -188,4 +192,4 @@ export {
188
192
  initDatabase,
189
193
  getDatabase
190
194
  };
191
- //# sourceMappingURL=chunk-7QXRQ5ZL.js.map
195
+ //# sourceMappingURL=chunk-RGUUZD5B.js.map
@@ -19,6 +19,7 @@ var OUTPUT_LIMITS = {
19
19
  maxImageBytes: 2097152,
20
20
  // 2MB for images
21
21
  maxPdfPages: 50,
22
+ maxPdfImages: 20,
22
23
  maxFileBytes: 20971520
23
24
  // 20MB general file safety limit
24
25
  },
@@ -38,7 +39,8 @@ var OUTPUT_LIMITS = {
38
39
  };
39
40
 
40
41
  // src/server/tools/pdf-utils.ts
41
- import { getDocument } from "pdfjs-dist/legacy/build/pdf.mjs";
42
+ import { getDocument, OPS } from "pdfjs-dist/legacy/build/pdf.mjs";
43
+ import { PNG } from "pngjs";
42
44
  var PDF_HEADER = Buffer.from("%PDF");
43
45
  function isPdfBuffer(buffer) {
44
46
  return buffer.length > 4 && buffer.subarray(0, 4).equals(PDF_HEADER);
@@ -63,47 +65,278 @@ function processPdfContent(text, maxBytes) {
63
65
  const isScanned = output.replace(/\[Page \d+\/\d+\]\n/g, "").trim().length === 0;
64
66
  return { output, truncated, isScanned };
65
67
  }
66
- async function extractPdfText(buffer) {
68
+ var IMAGE_KIND_GRAYSCALE_1BPP = 1;
69
+ var IMAGE_KIND_RGB_24BPP = 2;
70
+ var IMAGE_KIND_RGBA_32BPP = 3;
71
+ function getRgbaPixel(data, width, x, y, kind) {
72
+ if (kind === IMAGE_KIND_RGBA_32BPP) {
73
+ const i = (y * width + x) * 4;
74
+ return [data[i] ?? 0, data[i + 1] ?? 0, data[i + 2] ?? 0, data[i + 3] ?? 255];
75
+ }
76
+ if (kind === IMAGE_KIND_RGB_24BPP) {
77
+ const i = (y * width + x) * 3;
78
+ return [data[i] ?? 0, data[i + 1] ?? 0, data[i + 2] ?? 0, 255];
79
+ }
80
+ if (kind === IMAGE_KIND_GRAYSCALE_1BPP) {
81
+ const bytesPerRow = Math.ceil(width / 8);
82
+ const byteIdx = y * bytesPerRow + Math.floor(x / 8);
83
+ const bitIdx = 7 - x % 8;
84
+ const val = (data[byteIdx] ?? 0) >> bitIdx & 1 ? 255 : 0;
85
+ return [val, val, val, 255];
86
+ }
87
+ return [0, 0, 0, 255];
88
+ }
89
+ function toRgbaBuffer(data, width, height, kind) {
90
+ const rgba = new Uint8Array(width * height * 4);
91
+ for (let y = 0; y < height; y++) {
92
+ for (let x = 0; x < width; x++) {
93
+ const [r, g, b, a] = getRgbaPixel(data, width, x, y, kind);
94
+ const i = (y * width + x) * 4;
95
+ rgba[i] = r;
96
+ rgba[i + 1] = g;
97
+ rgba[i + 2] = b;
98
+ rgba[i + 3] = a;
99
+ }
100
+ }
101
+ return rgba;
102
+ }
103
+ function extractBitmapToRawImageData(bitmap) {
104
+ const canvas = new globalThis.OffscreenCanvas(bitmap.width, bitmap.height);
105
+ const ctx = canvas.getContext("2d");
106
+ ctx.drawImage(bitmap, 0, 0);
107
+ const imageData = ctx.getImageData(0, 0, bitmap.width, bitmap.height);
108
+ return {
109
+ width: bitmap.width,
110
+ height: bitmap.height,
111
+ data: new Uint8Array(imageData.data.buffer),
112
+ kind: IMAGE_KIND_RGBA_32BPP
113
+ };
114
+ }
115
+ function encodeImageToDataUrl(imgData, maxDimension = 1024) {
116
+ try {
117
+ let { width, height } = imgData;
118
+ let { data, kind } = imgData;
119
+ if (imgData.bitmap && !data) {
120
+ const converted = extractBitmapToRawImageData(imgData.bitmap);
121
+ data = converted.data;
122
+ kind = converted.kind;
123
+ }
124
+ if (!data) return null;
125
+ if (width * height * 4 > 20971520) return null;
126
+ if (maxDimension > 0 && (width > maxDimension || height > maxDimension)) {
127
+ const scale = maxDimension / Math.max(width, height);
128
+ const newWidth = Math.round(width * scale);
129
+ const newHeight = Math.round(height * scale);
130
+ const scaled = new Uint8Array(newWidth * newHeight * 4);
131
+ for (let y = 0; y < newHeight; y++) {
132
+ for (let x = 0; x < newWidth; x++) {
133
+ const srcX = Math.floor(x / scale);
134
+ const srcY = Math.floor(y / scale);
135
+ const dstI = (y * newWidth + x) * 4;
136
+ const [r, g, b, a] = getRgbaPixel(data, width, srcX, srcY, kind);
137
+ scaled[dstI] = r;
138
+ scaled[dstI + 1] = g;
139
+ scaled[dstI + 2] = b;
140
+ scaled[dstI + 3] = a;
141
+ }
142
+ }
143
+ width = newWidth;
144
+ height = newHeight;
145
+ const png2 = new PNG({ width, height });
146
+ Buffer.from(scaled.buffer, scaled.byteOffset, scaled.byteLength).copy(png2.data);
147
+ const pngBuffer2 = PNG.sync.write(png2);
148
+ return `data:image/png;base64,${pngBuffer2.toString("base64")}`;
149
+ }
150
+ let rgba;
151
+ if (kind === IMAGE_KIND_RGBA_32BPP) {
152
+ rgba = Uint8Array.from(
153
+ data instanceof Uint8ClampedArray ? new Uint8Array(data.buffer, data.byteOffset, data.byteLength) : data
154
+ );
155
+ } else {
156
+ rgba = toRgbaBuffer(data, width, height, kind);
157
+ }
158
+ const png = new PNG({ width, height });
159
+ Buffer.from(rgba.buffer, rgba.byteOffset, rgba.byteLength).copy(png.data);
160
+ const pngBuffer = PNG.sync.write(png);
161
+ return `data:image/png;base64,${pngBuffer.toString("base64")}`;
162
+ } catch {
163
+ return null;
164
+ }
165
+ }
166
+ async function extractPageBlocks(page, pageIndex, pageCount, imageCounter, maxImages) {
167
+ const [textContent, opList] = await Promise.all([page.getTextContent(), page.getOperatorList()]);
168
+ const textItems = textContent.items.filter(
169
+ (item) => "str" in item
170
+ );
171
+ const textStr = textItems.map((t) => t.str).join(" ").trim();
172
+ const imageOpIndices = [];
173
+ for (let i = 0; i < opList.fnArray.length; i++) {
174
+ const op = opList.fnArray[i];
175
+ if (op === OPS.paintImageXObject || op === OPS.paintInlineImageXObject || op === OPS.paintImageMaskXObject || op === OPS.paintImageMaskXObjectGroup || op === OPS.paintImageXObjectRepeat || op === OPS.paintImageMaskXObjectRepeat || op === OPS.paintInlineImageXObjectGroup || op === OPS.paintSolidColorImageMask) {
176
+ imageOpIndices.push(i);
177
+ }
178
+ }
179
+ const blocks = [];
180
+ if (textStr) {
181
+ blocks.push({ type: "text", content: `[Page ${pageIndex}/${pageCount}]
182
+ ${textStr}` });
183
+ }
184
+ for (let imageOpIdx = 0; imageOpIdx < imageOpIndices.length; imageOpIdx++) {
185
+ if (imageCounter.count >= maxImages) {
186
+ imageCounter.limitReached = true;
187
+ break;
188
+ }
189
+ const opIdx = imageOpIndices[imageOpIdx];
190
+ if (opIdx === void 0) break;
191
+ const imgBlock = await extractImageBlock(page, opList, opIdx);
192
+ if (imgBlock) {
193
+ blocks.push(imgBlock);
194
+ imageCounter.count++;
195
+ }
196
+ }
197
+ return blocks;
198
+ }
199
+ function isValidPixelData(data) {
200
+ return data instanceof Uint8Array || data instanceof Uint8ClampedArray;
201
+ }
202
+ var IMAGE_RESOLVE_TIMEOUT_MS = 5e3;
203
+ async function getObjectWithFallback(objs, objId) {
204
+ try {
205
+ return objs.get(objId);
206
+ } catch {
207
+ }
208
+ return new Promise((resolve, reject) => {
209
+ const timeoutId = setTimeout(() => reject(new Error("timeout")), IMAGE_RESOLVE_TIMEOUT_MS);
210
+ try {
211
+ ;
212
+ objs.get(objId, (data) => {
213
+ clearTimeout(timeoutId);
214
+ resolve(data);
215
+ });
216
+ } catch (e) {
217
+ clearTimeout(timeoutId);
218
+ reject(e);
219
+ }
220
+ });
221
+ }
222
+ async function extractImageBlock(page, opList, opIdx) {
223
+ const args = opList.argsArray[opIdx];
224
+ const objId = Array.isArray(args) ? args[0] : void 0;
225
+ let imgData = null;
226
+ if (objId && typeof objId === "string") {
227
+ try {
228
+ const store = objId.startsWith("g_") ? page.commonObjs : page.objs;
229
+ const raw = await getObjectWithFallback(store, objId);
230
+ if (raw && typeof raw === "object" && "width" in raw && "height" in raw) {
231
+ const r = raw;
232
+ if (isValidPixelData(r["data"])) {
233
+ imgData = raw;
234
+ } else if (r["bitmap"] && !r["data"]) {
235
+ imgData = raw;
236
+ }
237
+ }
238
+ } catch {
239
+ }
240
+ } else if (Array.isArray(args) && args[0] && typeof args[0] === "object" && "width" in args[0]) {
241
+ const inline = args[0];
242
+ if (isValidPixelData(inline["data"])) {
243
+ imgData = args[0];
244
+ } else if (inline["bitmap"] && !inline["data"]) {
245
+ imgData = args[0];
246
+ }
247
+ }
248
+ if (imgData) {
249
+ const dataUrl = encodeImageToDataUrl(imgData);
250
+ if (dataUrl) {
251
+ return { type: "image", dataUrl };
252
+ }
253
+ }
254
+ return null;
255
+ }
256
+ async function extractPdfContent(buffer) {
67
257
  const doc = await getDocument({ data: Uint8Array.from(buffer) }).promise;
68
258
  const pageCount = doc.numPages;
69
- const rawMeta = await doc.getMetadata();
70
- const info = rawMeta.info;
71
- const title = info?.["Title"] || null;
72
- const author = info?.["Author"] || null;
73
- const pages = [];
259
+ const allBlocks = [];
74
260
  const limitedPageCount = Math.min(pageCount, OUTPUT_LIMITS.read_file.maxPdfPages);
261
+ const maxImages = OUTPUT_LIMITS.read_file.maxPdfImages;
262
+ const imageCounter = { count: 0, limitReached: false };
263
+ let title = null;
264
+ let author = null;
265
+ try {
266
+ const rawMeta = await doc.getMetadata();
267
+ const info = rawMeta.info;
268
+ title = info?.["Title"] || null;
269
+ author = info?.["Author"] || null;
270
+ } catch {
271
+ }
75
272
  try {
76
273
  for (let i = 1; i <= limitedPageCount; i++) {
77
274
  const page = await doc.getPage(i);
78
275
  try {
79
- const content = await page.getTextContent();
80
- const pageText = content.items.map((item) => "str" in item ? item.str : "").join(" ");
81
- pages.push(`[Page ${i}/${pageCount}]
82
- ${pageText}`);
276
+ const blocks = await extractPageBlocks(page, i, pageCount, imageCounter, maxImages);
277
+ allBlocks.push(...blocks);
83
278
  } finally {
84
279
  page.cleanup();
85
280
  }
86
281
  }
282
+ } catch {
87
283
  } finally {
88
284
  doc.cleanup();
89
285
  }
90
- let text = pages.join("\n\n");
91
286
  if (pageCount > limitedPageCount) {
92
- text += `
287
+ allBlocks.push({
288
+ type: "text",
289
+ content: `[PDF has ${pageCount} pages, showing first ${limitedPageCount}. Use a shell command to process more.]`
290
+ });
291
+ }
292
+ if (imageCounter.limitReached) {
293
+ allBlocks.push({
294
+ type: "text",
295
+ content: `[Image limit reached: showing first ${maxImages} images out of more in this document.]`
296
+ });
297
+ }
298
+ return {
299
+ blocks: allBlocks,
300
+ pageCount,
301
+ title,
302
+ author,
303
+ imageCount: imageCounter.count,
304
+ imageLimitReached: imageCounter.limitReached
305
+ };
306
+ }
307
+ async function extractPdfText(buffer) {
308
+ const result = await extractPdfContent(buffer);
309
+ const text = result.blocks.filter((b) => b.type === "text").map((b) => b.content ?? "").join("\n\n");
310
+ return { text, pageCount: result.pageCount, title: result.title, author: result.author };
311
+ }
93
312
 
94
- [PDF has ${pageCount} pages, showing first ${limitedPageCount}. Use a shell command to process more.]`;
313
+ // src/server/utils/cache.ts
314
+ import { createHash } from "crypto";
315
+ function contentHash(data) {
316
+ return createHash("sha256").update(data).digest("hex").slice(0, 16);
317
+ }
318
+ function cacheSet(cache, key, value) {
319
+ const max = 50;
320
+ if (cache.size >= max) {
321
+ const first = cache.keys().next().value;
322
+ if (first !== void 0) cache.delete(first);
95
323
  }
96
- return { text, pageCount, title, author };
324
+ cache.set(key, value);
97
325
  }
98
326
 
99
- // src/server/llm/resolve-attachments.ts
100
- function decodeDataUrlToText(data) {
327
+ // src/server/utils/data-url.ts
328
+ function decodeDataUrl(data) {
101
329
  const match = data.match(/^data:.*?;base64,(.+)$/);
102
- if (match?.[1]) {
103
- return Buffer.from(match[1], "base64").toString("utf8");
330
+ if (!match?.[1]) return null;
331
+ try {
332
+ return Buffer.from(match[1], "base64");
333
+ } catch {
334
+ return null;
104
335
  }
105
- return data;
106
336
  }
337
+
338
+ // src/server/llm/resolve-attachments.ts
339
+ var pdfBlockCache = /* @__PURE__ */ new Map();
107
340
  async function extractPdfFromDataUrl(data, filename) {
108
341
  const base64Match = data.match(/^data:.*?;base64,(.+)$/);
109
342
  if (base64Match?.[1]) {
@@ -118,14 +351,50 @@ ${result.text}`;
118
351
  }
119
352
  return `[PDF: ${filename}] (could not extract text)`;
120
353
  }
354
+ async function extractPdfBlocksFromDataUrl(data, filename) {
355
+ const cacheKey = contentHash(data);
356
+ const cached = pdfBlockCache.get(cacheKey);
357
+ if (cached) {
358
+ const result = [{ type: "text", text: `[PDF: ${filename}]` }];
359
+ result.push(...cached);
360
+ return result;
361
+ }
362
+ const base64Match = data.match(/^data:.*?;base64,(.+)$/);
363
+ if (!base64Match?.[1]) {
364
+ cacheSet(pdfBlockCache, cacheKey, []);
365
+ return [{ type: "text", text: `[PDF: ${filename}] (could not extract content)` }];
366
+ }
367
+ try {
368
+ const buffer = Buffer.from(base64Match[1], "base64");
369
+ const result = await extractPdfContent(buffer);
370
+ const body = [];
371
+ for (const block of result.blocks) {
372
+ if (block.type === "text" && block.content) {
373
+ body.push({ type: "text", text: block.content });
374
+ } else if (block.type === "image" && block.dataUrl) {
375
+ body.push({ type: "image_url", image_url: { url: block.dataUrl } });
376
+ }
377
+ }
378
+ cacheSet(pdfBlockCache, cacheKey, body);
379
+ const parts = [{ type: "text", text: `[PDF: ${filename}]` }];
380
+ parts.push(...body);
381
+ return parts;
382
+ } catch {
383
+ cacheSet(pdfBlockCache, cacheKey, []);
384
+ return [{ type: "text", text: `[PDF: ${filename}] (could not extract content)` }];
385
+ }
386
+ }
121
387
  async function resolveAttachmentToText(attachment, supportsVision) {
122
388
  const mimeType = attachment.mimeType;
123
389
  if (TEXT_MIME_EXACT.includes(mimeType) || TEXT_MIME_PREFIXES.some((p) => mimeType.startsWith(p))) {
124
- const text = decodeDataUrlToText(attachment.data);
390
+ const text = decodeDataUrl(attachment.data)?.toString("utf8") ?? attachment.data;
125
391
  return `[File: ${attachment.filename || "file"}]
126
392
  ${text}`;
127
393
  }
128
394
  if (mimeType === "application/pdf") {
395
+ if (attachment.pdfContent) {
396
+ return attachment.pdfContent;
397
+ }
129
398
  return extractPdfFromDataUrl(attachment.data, attachment.filename || "document.pdf");
130
399
  }
131
400
  if (supportsVision) {
@@ -136,6 +405,7 @@ ${text}`;
136
405
  }
137
406
  return `[Image: ${attachment.filename || "image"}] (vision not supported, cannot describe)`;
138
407
  }
408
+ var isVisionAttachment = (attachment) => attachment.mimeType.startsWith("image/") || attachment.mimeType === "application/pdf";
139
409
  async function resolveAttachmentsInMessages(messages, supportsVision) {
140
410
  const result = [];
141
411
  for (const msg of messages) {
@@ -143,7 +413,7 @@ async function resolveAttachmentsInMessages(messages, supportsVision) {
143
413
  result.push(msg);
144
414
  continue;
145
415
  }
146
- if (supportsVision && msg.attachments.every((a) => a.mimeType.startsWith("image/"))) {
416
+ if (supportsVision && msg.attachments.every(isVisionAttachment)) {
147
417
  result.push(msg);
148
418
  continue;
149
419
  }
@@ -151,7 +421,7 @@ async function resolveAttachmentsInMessages(messages, supportsVision) {
151
421
  if (msg.content?.trim()) parts.push(msg.content);
152
422
  const remainingImageAttachments = [];
153
423
  for (const attachment of msg.attachments) {
154
- if (supportsVision && attachment.mimeType.startsWith("image/")) {
424
+ if (supportsVision && isVisionAttachment(attachment)) {
155
425
  remainingImageAttachments.push(attachment);
156
426
  continue;
157
427
  }
@@ -197,8 +467,8 @@ async function buildAttachmentContent(msgContent, attachments, modelSupportsVisi
197
467
  content.push({ type: "text", text: msgContent });
198
468
  }
199
469
  for (const attachment of attachments) {
200
- const converted = await convertAttachment(attachment, modelSupportsVision);
201
- content.push(converted);
470
+ const parts = await convertAttachment(attachment, modelSupportsVision);
471
+ content.push(...parts);
202
472
  }
203
473
  return content;
204
474
  }
@@ -222,7 +492,7 @@ function getThinking(msg, override) {
222
492
  function buildAssistantMessage(msg, thinkingField) {
223
493
  const result = {
224
494
  role: "assistant",
225
- content: msg.content || null
495
+ content: msg.toolCalls?.length ? msg.content || "" : msg.content || null
226
496
  };
227
497
  if (msg.toolCalls?.length) {
228
498
  result["tool_calls"] = convertToolCalls(msg.toolCalls);
@@ -235,32 +505,28 @@ function buildAssistantMessage(msg, thinkingField) {
235
505
  async function convertAttachment(attachment, modelSupportsVision) {
236
506
  const mimeType = attachment.mimeType;
237
507
  if (TEXT_MIME_EXACT.includes(mimeType) || TEXT_MIME_PREFIXES.some((p) => mimeType.startsWith(p))) {
238
- return {
239
- type: "text",
240
- text: `[File: ${attachment.filename || "file"}]
241
- ${attachment.data}`
242
- };
508
+ return [{ type: "text", text: `[File: ${attachment.filename || "file"}]
509
+ ${attachment.data}` }];
243
510
  }
244
511
  if (mimeType === "application/pdf") {
512
+ if (modelSupportsVision) {
513
+ return extractPdfBlocksFromDataUrl(attachment.data, attachment.filename || "document.pdf");
514
+ }
515
+ if (attachment.pdfContent) {
516
+ return [{ type: "text", text: attachment.pdfContent }];
517
+ }
245
518
  const text = await extractPdfFromDataUrl(attachment.data, attachment.filename || "document.pdf");
246
- return { type: "text", text };
519
+ return [{ type: "text", text }];
247
520
  }
248
521
  if (modelSupportsVision) {
249
- return {
250
- type: "image_url",
251
- image_url: { url: attachment.data }
252
- };
522
+ return [{ type: "image_url", image_url: { url: attachment.data } }];
253
523
  }
254
524
  if (attachment.description) {
255
- return {
256
- type: "text",
257
- text: `[Image: ${attachment.filename || "image"} - description: ${attachment.description}]`
258
- };
525
+ return [
526
+ { type: "text", text: `[Image: ${attachment.filename || "image"} - description: ${attachment.description}]` }
527
+ ];
259
528
  }
260
- return {
261
- type: "text",
262
- text: `[Image: ${attachment.filename || "image"}] (vision not supported, cannot describe)`
263
- };
529
+ return [{ type: "text", text: `[Image: ${attachment.filename || "image"}] (vision not supported, cannot describe)` }];
264
530
  }
265
531
  async function convertMessages(messages, modelSupportsVision, thinkingField) {
266
532
  const filtered = messages.filter((msg) => {
@@ -398,7 +664,11 @@ export {
398
664
  isPdfBuffer,
399
665
  formatPdfErrorMessage,
400
666
  processPdfContent,
667
+ extractPdfContent,
401
668
  extractPdfText,
669
+ contentHash,
670
+ cacheSet,
671
+ decodeDataUrl,
402
672
  resolveAttachmentsInMessages,
403
673
  parseToolArguments,
404
674
  buildModelParams,
@@ -409,4 +679,4 @@ export {
409
679
  buildStreamingCreateParams,
410
680
  mapFinishReason
411
681
  };
412
- //# sourceMappingURL=chunk-EZK7CGJ7.js.map
682
+ //# sourceMappingURL=chunk-RMXPVQP4.js.map