@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,
|
|
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.
|
|
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.
|
|
160
|
+
"@dexto/llm": "1.8.12"
|
|
161
161
|
},
|
|
162
162
|
"devDependencies": {
|
|
163
163
|
"@opentelemetry/context-async-hooks": "^1.28.0",
|