@yeaft/webchat-agent 0.1.821 → 0.1.823
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/package.json +1 -1
- package/unify/attachments.js +49 -2
- package/unify/web-bridge.js +13 -3
package/package.json
CHANGED
package/unify/attachments.js
CHANGED
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
* than swallowing it in a console.warn.
|
|
41
41
|
*/
|
|
42
42
|
|
|
43
|
-
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
44
|
-
import { basename, extname, join } from 'node:path';
|
|
43
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, statSync } from 'node:fs';
|
|
44
|
+
import { basename, extname, join, resolve, relative, isAbsolute } from 'node:path';
|
|
45
45
|
import { randomBytes } from 'node:crypto';
|
|
46
46
|
|
|
47
47
|
// Same dir name Chat mode uses, so ".gitignore" rules and tool-side
|
|
@@ -54,6 +54,7 @@ const TEMP_UPLOAD_DIR = '.claude-tmp-attachments';
|
|
|
54
54
|
// - MAX_TOTAL_BYTES: 50 MiB across all files in one turn.
|
|
55
55
|
export const MAX_FILES_PER_TURN = 16;
|
|
56
56
|
export const MAX_TOTAL_BYTES = 50 * 1024 * 1024;
|
|
57
|
+
export const MAX_PREVIEW_BYTES = 10 * 1024 * 1024;
|
|
57
58
|
|
|
58
59
|
/**
|
|
59
60
|
* Sanitize a user-supplied filename's basename for use as an on-disk
|
|
@@ -228,3 +229,49 @@ export function attachmentsForPersistence(promptAttachments) {
|
|
|
228
229
|
isImage: !!f.isImage,
|
|
229
230
|
}));
|
|
230
231
|
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Resolve a persisted Unify attachment path to an on-disk file. The persisted
|
|
235
|
+
* path is intentionally relative (for tool use), so preview hydration must
|
|
236
|
+
* keep it inside the upload root instead of serving arbitrary files.
|
|
237
|
+
*
|
|
238
|
+
* @param {string} attachmentPath
|
|
239
|
+
* @param {{ cwd?: string }} [opts]
|
|
240
|
+
* @returns {string|null}
|
|
241
|
+
*/
|
|
242
|
+
export function resolvePersistedAttachmentPath(attachmentPath, opts = {}) {
|
|
243
|
+
if (!attachmentPath || typeof attachmentPath !== 'string') return null;
|
|
244
|
+
if (isAbsolute(attachmentPath)) return null;
|
|
245
|
+
const cwd = resolve(opts.cwd || process.cwd());
|
|
246
|
+
const uploadRoot = resolve(cwd, TEMP_UPLOAD_DIR);
|
|
247
|
+
const absPath = resolve(cwd, attachmentPath);
|
|
248
|
+
const rel = relative(uploadRoot, absPath);
|
|
249
|
+
if (rel === '' || rel.startsWith('..') || isAbsolute(rel)) return null;
|
|
250
|
+
return absPath;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Read a persisted image attachment into a short-lived preview payload for the
|
|
255
|
+
* web server. Returns null for non-images, bad paths, missing files, or files
|
|
256
|
+
* too large to cache as previews.
|
|
257
|
+
*
|
|
258
|
+
* @param {{name?:string, path?:string, mimeType?:string, isImage?:boolean}} att
|
|
259
|
+
* @param {{ cwd?: string }} [opts]
|
|
260
|
+
* @returns {{data:string,mimeType:string,filename:string}|null}
|
|
261
|
+
*/
|
|
262
|
+
export function persistedAttachmentPreviewPayload(att, opts = {}) {
|
|
263
|
+
if (!att || !att.isImage || !att.path) return null;
|
|
264
|
+
const absPath = resolvePersistedAttachmentPath(att.path, opts);
|
|
265
|
+
if (!absPath) return null;
|
|
266
|
+
try {
|
|
267
|
+
const st = statSync(absPath);
|
|
268
|
+
if (!st.isFile() || st.size > MAX_PREVIEW_BYTES) return null;
|
|
269
|
+
return {
|
|
270
|
+
data: readFileSync(absPath).toString('base64'),
|
|
271
|
+
mimeType: att.mimeType || 'application/octet-stream',
|
|
272
|
+
filename: att.name || basename(absPath),
|
|
273
|
+
};
|
|
274
|
+
} catch {
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
}
|
package/unify/web-bridge.js
CHANGED
|
@@ -52,7 +52,7 @@ import { seedDefaultGroup } from './groups/seed-default.js';
|
|
|
52
52
|
import {
|
|
53
53
|
trimSnapshotForBudget,
|
|
54
54
|
} from './history-compact.js';
|
|
55
|
-
import { persistUnifyAttachments, attachmentsForPersistence } from './attachments.js';
|
|
55
|
+
import { persistUnifyAttachments, attachmentsForPersistence, persistedAttachmentPreviewPayload } from './attachments.js';
|
|
56
56
|
import { parseSeqFromId } from './conversation/persist.js';
|
|
57
57
|
import { sliceLastNTurns } from './turn-utils.js';
|
|
58
58
|
import { createVpStatusBroker } from './vp-status-broker.js';
|
|
@@ -584,6 +584,16 @@ function projectPersistedToVisibleHistoryEntry(m) {
|
|
|
584
584
|
return entry && (entry.role === 'user' || entry.role === 'assistant') ? entry : null;
|
|
585
585
|
}
|
|
586
586
|
|
|
587
|
+
function hydrateHistoryAttachmentPreviews(attachments) {
|
|
588
|
+
if (!Array.isArray(attachments) || attachments.length === 0) return [];
|
|
589
|
+
return attachments.map((att) => {
|
|
590
|
+
if (!att || typeof att !== 'object') return att;
|
|
591
|
+
if (!att.isImage || att.preview || att.previewData) return att;
|
|
592
|
+
const payload = persistedAttachmentPreviewPayload(att);
|
|
593
|
+
return payload ? { ...att, previewData: payload } : att;
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
|
|
587
597
|
function loadVisibleGroupHistoryPage(store, groupId, limit, beforeSeq = null) {
|
|
588
598
|
if (!store || !groupId || !(limit > 0)) return { messages: [], oldestSeq: null, hasMore: false };
|
|
589
599
|
|
|
@@ -3561,7 +3571,7 @@ export async function handleUnifyLoadHistory(msg) {
|
|
|
3561
3571
|
message: {
|
|
3562
3572
|
content: entry.content,
|
|
3563
3573
|
id: entry.id || null,
|
|
3564
|
-
...(Array.isArray(entry.attachments) && entry.attachments.length > 0 ? { attachments: entry.attachments } : {}),
|
|
3574
|
+
...(Array.isArray(entry.attachments) && entry.attachments.length > 0 ? { attachments: hydrateHistoryAttachmentPreviews(entry.attachments) } : {}),
|
|
3565
3575
|
},
|
|
3566
3576
|
ts: entry.ts || null,
|
|
3567
3577
|
}, { groupId: entry.groupId || null });
|
|
@@ -3655,7 +3665,7 @@ export async function handleUnifyLoadMoreHistory(msg) {
|
|
|
3655
3665
|
role: m.role,
|
|
3656
3666
|
content: m.content,
|
|
3657
3667
|
groupId: m.groupId || null,
|
|
3658
|
-
...(Array.isArray(m.attachments) && m.attachments.length > 0 ? { attachments: m.attachments } : {}),
|
|
3668
|
+
...(Array.isArray(m.attachments) && m.attachments.length > 0 ? { attachments: hydrateHistoryAttachmentPreviews(m.attachments) } : {}),
|
|
3659
3669
|
...(m.speakerVpId ? { speakerVpId: m.speakerVpId } : {}),
|
|
3660
3670
|
}));
|
|
3661
3671
|
|