feat(mindspace): enforce PostgreSQL user data delivery

This commit is contained in:
john
2026-07-13 15:29:29 +08:00
parent b33c943b69
commit a6620fb719
57 changed files with 2779 additions and 164 deletions
+41 -1
View File
@@ -10,6 +10,7 @@ import {
isStubPublicHtmlContent,
materializeMissingPublicHtmlWrites,
} from './mindspace-public-finish-sync.mjs';
import { scanWorkspaceFilesForProhibitedBrowserStorage } from './mindspace-browser-storage-policy.mjs';
const PUBLIC_HTML_PATH_PATTERN_GLOBAL = /(?:^|[^a-z0-9_./-])(public\/[a-z0-9][a-z0-9._/-]{0,255}\.html)\b/gi;
const PUBLIC_ASSET_ATTR_PATTERN = /(?:href|src)=["']([^"'#?\s]+)["']/gi;
@@ -106,6 +107,26 @@ export function collectMissingPublicHtmlPaths(messages, currentUser, publishDir)
);
}
export function collectTouchedPublicCodePaths(messages, publishDir, syncResult = null) {
const paths = new Set(
(syncResult?.publicHtmlRelativePaths ?? [])
.map((item) => normalizePublicRelativePath(item, { publishDir }))
.filter(Boolean),
);
for (const message of Array.isArray(messages) ? messages : []) {
for (const item of message?.content ?? []) {
if (item?.type !== 'toolRequest') continue;
const toolCall = item.toolCall?.value;
const name = String(toolCall?.name ?? '').trim().split('__').at(-1);
if (!['write_file', 'edit_file', 'write', 'edit'].includes(name)) continue;
const args = toolCall?.arguments ?? {};
const relativePath = normalizePublicRelativePath(args.path ?? args.file_path, { publishDir });
if (relativePath) paths.add(relativePath);
}
}
return [...paths];
}
export function collectMissingPublicAssetReferences(publishDir) {
const root = path.resolve(String(publishDir ?? ''));
const publicDir = path.join(root, 'public');
@@ -135,6 +156,10 @@ export function evaluateH5HtmlFinishGuard({
materializeMissingPublicHtmlWrites({ messages, publishDir });
const missingHtml = collectMissingPublicHtmlPaths(messages, currentUser, publishDir);
const missingAssets = collectMissingPublicAssetReferences(publishDir);
const browserStorageViolations = scanWorkspaceFilesForProhibitedBrowserStorage({
publishDir,
relativePaths: collectTouchedPublicCodePaths(messages, publishDir, syncResult),
});
const lastAssistant = [...(Array.isArray(messages) ? messages : [])]
.reverse()
@@ -159,18 +184,24 @@ export function evaluateH5HtmlFinishGuard({
const needsRepair =
missingHtml.length > 0 ||
missingAssets.length > 0 ||
browserStorageViolations.length > 0 ||
htmlGenerationNeedsRetry;
return {
needsRepair,
missingHtml,
missingAssets,
browserStorageViolations,
htmlGenerationNeedsRetry,
linkExists,
};
}
export function buildH5HtmlRepairPrompt({ missingHtml = [], missingAssets = [] } = {}) {
export function buildH5HtmlRepairPrompt({
missingHtml = [],
missingAssets = [],
browserStorageViolations = [],
} = {}) {
const lines = [
'【系统补盘请求】检测到 public 页面交付不完整。请立即使用 write_file 写入缺失文件(完整内容),并在完成后确认磁盘存在。',
'要求:先 load_skill static-page-publish,再逐个 write_file;不要用空回复或仅 edit_file 改 HTML 代替资源落盘。',
@@ -185,6 +216,15 @@ export function buildH5HtmlRepairPrompt({ missingHtml = [], missingAssets = [] }
lines.push(`- ${item.relativePath}(来自 ${item.htmlRelativePath}`);
}
}
if (browserStorageViolations.length) {
lines.push(
'',
'以下页面违反数据存储硬约束:',
...browserStorageViolations.map((item) => `- ${item.relativePath}:禁止 ${item.apis.join(', ')}`),
'',
'必须删除浏览器存储代码,load_skill → page-data-collect,使用 private_data_* 在当前用户专属 PostgreSQL schema 建表和注册 datasetHTML 只能通过 Page Data API 读写。禁止 SQLite 或任何本地 fallback。',
);
}
lines.push('', '写完后请 list_dir public/assets 或对应目录,确认文件真实存在。');
return lines.join('\n');
}