feat(mindspace): enforce PostgreSQL user data delivery
This commit is contained in:
@@ -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 建表和注册 dataset,HTML 只能通过 Page Data API 读写。禁止 SQLite 或任何本地 fallback。',
|
||||
);
|
||||
}
|
||||
lines.push('', '写完后请 list_dir public/assets 或对应目录,确认文件真实存在。');
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user