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:
john
2026-07-11 00:23:01 +08:00
parent e3063ea806
commit 32fb2cdeaf
51 changed files with 4588 additions and 186 deletions
+46 -5
View File
@@ -8,6 +8,11 @@ import { pageInternals, normalizeWorkspaceRelativePath } from './mindspace-pages
import { readWorkspacePublishHtml } from './mindspace-workspace-path.mjs';
import { loadMindSpaceConfig } from './mindspace-config.mjs';
import { buildPublicUrl, resolvePublicBaseUrl, resolvePublishDir } from './user-publish.mjs';
import {
isPublicWorkspaceHtmlRelativePath,
resolvePageWorkspaceRelativePath,
resolveWorkspaceMindSpacePublicUrl,
} from './mindspace-runtime-config.mjs';
import {
isRelativeDownloadReference,
loadWorkspaceDownloadLinkIndex,
@@ -161,6 +166,27 @@ function buildOnlinePublicationPublicUrl(ownerSlug, urlSlug) {
return `${publicBaseUrl}/u/${encodeURIComponent(ownerSlug)}/pages/${urlSlug}`;
}
function resolvePublicationDeliveryPublicUrl({
h5Root,
userId,
ownerSlug,
urlSlug,
accessMode,
workspaceRelativePath,
privateToken = null,
} = {}) {
if (privateToken) return `/s/${privateToken}`;
if (accessMode === 'public') {
const mindSpaceUrl = resolveWorkspaceMindSpacePublicUrl({
h5Root,
userId,
relativePath: workspaceRelativePath,
});
if (mindSpaceUrl) return mindSpaceUrl;
}
return buildOnlinePublicationPublicUrl(ownerSlug, urlSlug);
}
function publicHomepageResponse(owner, pages) {
const totalViews = pages.reduce(
(sum, page) => sum + Number(page.viewCount ?? page.view_count ?? 0),
@@ -767,10 +793,16 @@ export function createPublicationService(pool, options = {}) {
await fs.mkdir(path.dirname(writtenPath), { recursive: true });
await fs.writeFile(writtenPath, html, { flag: 'wx' });
const checksum = crypto.createHash('sha256').update(html).digest('hex');
const publicBaseUrl = resolvePublicBaseUrl();
const publicUrl = privateToken
? `/s/${privateToken}`
: `${publicBaseUrl}/u/${encodeURIComponent(ownerSlug)}/pages/${result.urlSlug}`;
const workspaceRelativePath = resolvePageWorkspaceRelativePath(page);
const publicUrl = resolvePublicationDeliveryPublicUrl({
h5Root,
userId,
ownerSlug,
urlSlug: result.urlSlug,
accessMode: result.accessMode,
workspaceRelativePath,
privateToken,
});
await conn.query(
`INSERT INTO h5_assets
@@ -1005,7 +1037,15 @@ export function createPublicationService(pool, options = {}) {
);
let publicUrl = publication.public_url;
if (publication.access_mode === 'public') {
publicUrl = buildOnlinePublicationPublicUrl(ownerSlug, publication.url_slug);
const workspaceRelativePath = resolvePageWorkspaceRelativePath(page);
publicUrl = resolvePublicationDeliveryPublicUrl({
h5Root,
userId,
ownerSlug,
urlSlug: publication.url_slug,
accessMode: publication.access_mode,
workspaceRelativePath,
});
await pool.query(
`UPDATE h5_publish_records SET public_url = ?, updated_at = ? WHERE id = ? AND user_id = ?`,
[publicUrl, now, publication.id, userId],
@@ -1179,6 +1219,7 @@ export function createPublicationService(pool, options = {}) {
html: await refreshPublishedHtmlDownloadLinks(row, html),
publication: publicationResponse({ ...row, view_count: Number(row.view_count) + 1 }),
ownerId: row.owner_id,
workspaceRelativePath: resolvePageWorkspaceRelativePath(row),
pageSource: {
pageId: row.page_id,
title: row.title,