feat: chat uploads, vision turn isolation, and MindSpace agent improvements
Add chat file/image upload UX, attachment proxying, vision thumbnails, and per-turn image scoping so agents only use the current upload. Extend MindSpace asset context, billing token state, OA/scenario verify scripts, and related runtime config. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -67,8 +67,17 @@ function resolvePageRecord(selectedPageId, pages, pageLive) {
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeWorkspaceRelativePath(value) {
|
||||
const trimmed = String(value ?? '').trim().replace(/^\.?\//, '');
|
||||
if (!trimmed || trimmed.includes('..')) return null;
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
function normalizeSelectedAsset(asset) {
|
||||
if (!asset?.id) return null;
|
||||
const workspaceRelativePath = normalizeWorkspaceRelativePath(
|
||||
asset.workspaceRelativePath ?? asset.workspace_relative_path,
|
||||
);
|
||||
return {
|
||||
id: asset.id,
|
||||
displayName: asset.displayName ?? asset.filename ?? asset.id,
|
||||
@@ -77,9 +86,18 @@ function normalizeSelectedAsset(asset) {
|
||||
...(typeof asset.sizeBytes === 'number' ? { sizeBytes: asset.sizeBytes } : {}),
|
||||
...(asset.categoryCode ? { categoryCode: asset.categoryCode } : {}),
|
||||
...(asset.assetType ? { assetType: asset.assetType } : {}),
|
||||
...(workspaceRelativePath ? { workspaceRelativePath } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
function resolveCategoryItemCount(category, view, assets) {
|
||||
const listed = typeof category?.itemCount === 'number' ? category.itemCount : 0;
|
||||
if (view !== 'category' || !Array.isArray(assets) || assets.length === 0) {
|
||||
return category?.itemCount;
|
||||
}
|
||||
return Math.max(listed, assets.length);
|
||||
}
|
||||
|
||||
function inferAssetKindLabel(asset) {
|
||||
const mime = String(asset?.mimeType ?? '').toLowerCase();
|
||||
const name = String(asset?.displayName ?? asset?.filename ?? '').toLowerCase();
|
||||
@@ -107,10 +125,15 @@ function buildSelectedAssetInstructions(context) {
|
||||
if (selected.length === 1) {
|
||||
const asset = selected[0];
|
||||
const kind = inferAssetKindLabel(asset);
|
||||
const workspacePath = normalizeWorkspaceRelativePath(
|
||||
asset.workspaceRelativePath ?? asset.workspace_relative_path,
|
||||
);
|
||||
lines.push(
|
||||
`- 用户在界面勾选了 1 份${kind}:「${asset.displayName}」(id: ${asset.id},${asset.mimeType})`,
|
||||
'- 用户说「这个 / 这份 / 这篇 / 这份报告 / 这个数据 / 帮我分析 / 帮我删除」等指代词时,**必须**指这份勾选资料,不得猜测其它文件。',
|
||||
'- 读取/分析:优先 `list_dir oa` / `read_file oa/文件名`;若工作区尚无副本,见下方「勾选资料读取」用 Agent 代理下载落盘。',
|
||||
workspacePath
|
||||
? `- 读取/分析:文件已落盘 \`${workspacePath}\`,直接 read/脚本读取;见下方「勾选资料读取」。`
|
||||
: '- 读取/分析:优先 `list_dir oa` / `read_file oa/文件名`;若工作区尚无副本,见下方「勾选资料读取」用 Agent 代理下载落盘。',
|
||||
'- 删除:须先与用户确认,确认后由你直接执行删除(见下方「勾选资料删除」);你对该用户空间有完整读写删改权限,**禁止**推给用户自行去界面删除。',
|
||||
);
|
||||
if (context.h5ApiBase) {
|
||||
@@ -184,7 +207,7 @@ export function buildMindSpaceChatContext(input) {
|
||||
category: {
|
||||
code: category.code,
|
||||
name: category.name,
|
||||
itemCount: category.itemCount,
|
||||
itemCount: resolveCategoryItemCount(category, view, assets),
|
||||
...(view === 'category' && assets.length
|
||||
? {
|
||||
assets: assets.slice(0, ASSET_PREVIEW_LIMIT).map((asset) => ({
|
||||
@@ -344,7 +367,9 @@ export function buildContextPrefix(context) {
|
||||
if (context.page.summary?.trim()) {
|
||||
lines.push(`- 页面摘要:${context.page.summary.trim()}`);
|
||||
}
|
||||
if (context.page.publicationUrl) {
|
||||
if (context.page.workspacePublicUrl) {
|
||||
lines.push(`- 公开地址(MindSpace):${context.page.workspacePublicUrl}`);
|
||||
} else if (context.page.publicationUrl) {
|
||||
lines.push(`- 公开地址:${context.page.publicationUrl}`);
|
||||
}
|
||||
if (context.page.contentExcerpt) {
|
||||
|
||||
Reference in New Issue
Block a user