@dexto/core 1.8.11 → 1.8.12

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.
@@ -41,6 +41,70 @@ function normalizeToolMediaData(data) {
41
41
  }
42
42
  return data;
43
43
  }
44
+ function isTextLikeMimeType(mimeType) {
45
+ const normalized = mimeType.toLowerCase().split(";", 1)[0]?.trim() ?? "";
46
+ if (normalized.startsWith("text/")) return true;
47
+ if (normalized.endsWith("+json") || normalized.endsWith("+xml")) return true;
48
+ return [
49
+ "application/json",
50
+ "application/ld+json",
51
+ "application/xml",
52
+ "application/yaml",
53
+ "application/x-yaml",
54
+ "application/toml",
55
+ "application/x-toml",
56
+ "application/javascript",
57
+ "application/typescript",
58
+ "application/x-sh",
59
+ "application/sql"
60
+ ].includes(normalized);
61
+ }
62
+ function isRemoteFileData(data) {
63
+ return data instanceof URL || typeof data === "string" && (/^https?:\/\//i.test(data) || data.startsWith("blob:") || data.startsWith("@blob:"));
64
+ }
65
+ function decodeLikelyBase64Text(value) {
66
+ const normalized = value.replace(/\s/g, "");
67
+ if (normalized.length === 0 || normalized.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/.test(normalized)) {
68
+ return null;
69
+ }
70
+ const decoded = Buffer.from(normalized, "base64");
71
+ const canonical = decoded.toString("base64").replace(/=+$/, "");
72
+ if (canonical !== normalized.replace(/=+$/, "")) return null;
73
+ const text = decoded.toString("utf8");
74
+ const hasInvalidControlCharacter = [...text].some((char) => {
75
+ const code = char.charCodeAt(0);
76
+ return code < 32 && code !== 9 && code !== 10 && code !== 13;
77
+ });
78
+ if (text.includes("\uFFFD") || hasInvalidControlCharacter) {
79
+ return null;
80
+ }
81
+ return text;
82
+ }
83
+ function decodeBase64Text(value) {
84
+ return Buffer.from(value.replace(/\s/g, ""), "base64").toString("utf8");
85
+ }
86
+ function decodeTextFileData(data) {
87
+ if (data instanceof URL) return null;
88
+ if (typeof data === "string") {
89
+ const dataUri = (0, import_utils.parseDataUri)(data);
90
+ if (dataUri) return decodeBase64Text(dataUri.base64);
91
+ if (isRemoteFileData(data)) return null;
92
+ return decodeLikelyBase64Text(data) ?? data;
93
+ }
94
+ return Buffer.from(data instanceof ArrayBuffer ? new Uint8Array(data) : data).toString("utf8");
95
+ }
96
+ function filePartToText(part) {
97
+ if (!isTextLikeMimeType(part.mimeType) || isRemoteFileData(part.data)) return null;
98
+ const text = decodeTextFileData(part.data);
99
+ if (text === null) return null;
100
+ const filename = part.filename ?? "attachment";
101
+ return {
102
+ type: "text",
103
+ text: `Attached file "${filename}" (${part.mimeType}):
104
+
105
+ ${text}`
106
+ };
107
+ }
44
108
  class VercelMessageFormatter {
45
109
  logger;
46
110
  constructor(logger) {
@@ -89,6 +153,8 @@ class VercelMessageFormatter {
89
153
  (part) => part.type !== "ui-resource" && part.type !== "resource"
90
154
  ).map((part) => {
91
155
  if (part.type === "file") {
156
+ const textPart = filePartToText(part);
157
+ if (textPart) return textPart;
92
158
  return {
93
159
  type: "file",
94
160
  data: toUrlIfString(part.data),
@@ -1 +1 @@
1
- {"version":3,"file":"vercel.d.ts","sourceRoot":"","sources":["../../../src/llm/formatters/vercel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAiD,MAAM,IAAI,CAAC;AACtF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,eAAe,EAAiC,MAAM,wBAAwB,CAAC;AAO7F,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AA8BvD;;;;;;;;;;GAUG;AACH,qBAAa,sBAAsB;IAC/B,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,EAAE,MAAM;IAG1B;;;;;;OAMG;IACH,MAAM,CACF,OAAO,EAAE,QAAQ,CAAC,eAAe,EAAE,CAAC,EACpC,OAAO,EAAE,UAAU,EACnB,YAAY,EAAE,MAAM,GAAG,IAAI,GAC5B,YAAY,EAAE;IAkKjB;;;;;;OAMG;IACH,kBAAkB,IAAI,IAAI;IAI1B,OAAO,CAAC,wBAAwB;IAUhC,OAAO,CAAC,sBAAsB;IAmG9B,OAAO,CAAC,iBAAiB;CA6E5B"}
1
+ {"version":3,"file":"vercel.d.ts","sourceRoot":"","sources":["../../../src/llm/formatters/vercel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAiD,MAAM,IAAI,CAAC;AACtF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EACR,eAAe,EAIlB,MAAM,wBAAwB,CAAC;AAOhC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAqHvD;;;;;;;;;;GAUG;AACH,qBAAa,sBAAsB;IAC/B,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,EAAE,MAAM;IAG1B;;;;;;OAMG;IACH,MAAM,CACF,OAAO,EAAE,QAAQ,CAAC,eAAe,EAAE,CAAC,EACpC,OAAO,EAAE,UAAU,EACnB,YAAY,EAAE,MAAM,GAAG,IAAI,GAC5B,YAAY,EAAE;IAqKjB;;;;;;OAMG;IACH,kBAAkB,IAAI,IAAI;IAI1B,OAAO,CAAC,wBAAwB;IAUhC,OAAO,CAAC,sBAAsB;IAmG9B,OAAO,CAAC,iBAAiB;CA6E5B"}
@@ -24,6 +24,70 @@ function normalizeToolMediaData(data) {
24
24
  }
25
25
  return data;
26
26
  }
27
+ function isTextLikeMimeType(mimeType) {
28
+ const normalized = mimeType.toLowerCase().split(";", 1)[0]?.trim() ?? "";
29
+ if (normalized.startsWith("text/")) return true;
30
+ if (normalized.endsWith("+json") || normalized.endsWith("+xml")) return true;
31
+ return [
32
+ "application/json",
33
+ "application/ld+json",
34
+ "application/xml",
35
+ "application/yaml",
36
+ "application/x-yaml",
37
+ "application/toml",
38
+ "application/x-toml",
39
+ "application/javascript",
40
+ "application/typescript",
41
+ "application/x-sh",
42
+ "application/sql"
43
+ ].includes(normalized);
44
+ }
45
+ function isRemoteFileData(data) {
46
+ return data instanceof URL || typeof data === "string" && (/^https?:\/\//i.test(data) || data.startsWith("blob:") || data.startsWith("@blob:"));
47
+ }
48
+ function decodeLikelyBase64Text(value) {
49
+ const normalized = value.replace(/\s/g, "");
50
+ if (normalized.length === 0 || normalized.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/.test(normalized)) {
51
+ return null;
52
+ }
53
+ const decoded = Buffer.from(normalized, "base64");
54
+ const canonical = decoded.toString("base64").replace(/=+$/, "");
55
+ if (canonical !== normalized.replace(/=+$/, "")) return null;
56
+ const text = decoded.toString("utf8");
57
+ const hasInvalidControlCharacter = [...text].some((char) => {
58
+ const code = char.charCodeAt(0);
59
+ return code < 32 && code !== 9 && code !== 10 && code !== 13;
60
+ });
61
+ if (text.includes("\uFFFD") || hasInvalidControlCharacter) {
62
+ return null;
63
+ }
64
+ return text;
65
+ }
66
+ function decodeBase64Text(value) {
67
+ return Buffer.from(value.replace(/\s/g, ""), "base64").toString("utf8");
68
+ }
69
+ function decodeTextFileData(data) {
70
+ if (data instanceof URL) return null;
71
+ if (typeof data === "string") {
72
+ const dataUri = parseDataUri(data);
73
+ if (dataUri) return decodeBase64Text(dataUri.base64);
74
+ if (isRemoteFileData(data)) return null;
75
+ return decodeLikelyBase64Text(data) ?? data;
76
+ }
77
+ return Buffer.from(data instanceof ArrayBuffer ? new Uint8Array(data) : data).toString("utf8");
78
+ }
79
+ function filePartToText(part) {
80
+ if (!isTextLikeMimeType(part.mimeType) || isRemoteFileData(part.data)) return null;
81
+ const text = decodeTextFileData(part.data);
82
+ if (text === null) return null;
83
+ const filename = part.filename ?? "attachment";
84
+ return {
85
+ type: "text",
86
+ text: `Attached file "${filename}" (${part.mimeType}):
87
+
88
+ ${text}`
89
+ };
90
+ }
27
91
  class VercelMessageFormatter {
28
92
  logger;
29
93
  constructor(logger) {
@@ -72,6 +136,8 @@ class VercelMessageFormatter {
72
136
  (part) => part.type !== "ui-resource" && part.type !== "resource"
73
137
  ).map((part) => {
74
138
  if (part.type === "file") {
139
+ const textPart = filePartToText(part);
140
+ if (textPart) return textPart;
75
141
  return {
76
142
  type: "file",
77
143
  data: toUrlIfString(part.data),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dexto/core",
3
- "version": "1.8.11",
3
+ "version": "1.8.12",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -157,7 +157,7 @@
157
157
  "nanoid": "^5.1.6",
158
158
  "winston": "^3.17.0",
159
159
  "yaml": "^2.8.3",
160
- "@dexto/llm": "1.8.11"
160
+ "@dexto/llm": "1.8.12"
161
161
  },
162
162
  "devDependencies": {
163
163
  "@opentelemetry/context-async-hooks": "^1.28.0",