Files
memind/health-report-finish-guard.mjs
T
john 2baf29b3ae feat(health): complete P0 health channel — baseline engine, page-data, MindSpace UI
Deliver encrypted health zone, observation API, baseline maturity pipeline,
page-data bindings, and H5/WeChat channel integration for health P0.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-09 18:04:57 +08:00

128 lines
4.4 KiB
JavaScript

import path from 'node:path';
import { extractStaticPageLinks } from './mindspace-chat-save.mjs';
import { HEALTH_ASSISTANT_SKILL_NAME } from './health-feature.mjs';
import {
HEALTH_REPORT_FILENAME_PATTERN,
healthReportPageExists,
} from './health-report-page.mjs';
const MARKDOWN_LINK_PATTERN = /\[([^\]]+)\]\((https?:\/\/[^)]+|\/MindSpace\/[^)]+)\)/g;
const BARE_MINDSPACE_HTML_PATTERN = /https?:\/\/[^\s)]+?\/MindSpace\/[^\s)]+?\.html/gi;
function messageText(message) {
const content = message?.content;
if (typeof content === 'string') return content;
if (!Array.isArray(content)) return '';
return content
.filter((item) => item?.type === 'text')
.map((item) => String(item.text ?? ''))
.join('\n');
}
function setMessageText(message, text) {
if (!message || typeof message !== 'object') return message;
const next = { ...message };
if (Array.isArray(next.content)) {
next.content = next.content.map((item) => {
if (item?.type !== 'text') return item;
return { ...item, text };
});
} else {
next.content = [{ type: 'text', text }];
}
if (next.metadata && typeof next.metadata === 'object') {
next.metadata = {
...next.metadata,
displayText: text,
};
}
return next;
}
export function readSelectedChatSkill(messages) {
for (let index = (Array.isArray(messages) ? messages.length : 0) - 1; index >= 0; index -= 1) {
const message = messages[index];
if (message?.role !== 'user') continue;
const skill = message?.metadata?.memindRun?.selectedChatSkill
?? message?.metadata?.selectedChatSkill;
if (typeof skill === 'string' && skill.trim()) return skill.trim();
}
return null;
}
export function isHealthReportArtifactPath(relativePath) {
const basename = path.posix.basename(String(relativePath ?? '').replace(/\\/g, '/'));
return HEALTH_REPORT_FILENAME_PATTERN.test(basename);
}
export function healthReportLinkIsVerified(link, { h5Root, userId }) {
const relativePath = String(link?.relativePath ?? '');
if (!isHealthReportArtifactPath(relativePath)) return true;
return healthReportPageExists(h5Root, userId, relativePath);
}
export function sanitizeHealthReportDeliveryText(
text,
{ userId, username = null, h5Root = process.cwd() } = {},
) {
const original = String(text ?? '');
if (!original.trim()) return { text: original, stripped: false, removed: [] };
const removed = [];
let next = original;
for (const match of original.matchAll(MARKDOWN_LINK_PATTERN)) {
const full = match[0];
const url = match[2];
const links = extractStaticPageLinks(url, { userId, username });
const healthLinks = links.filter((link) => isHealthReportArtifactPath(link.relativePath));
if (healthLinks.length === 0) continue;
const invalid = healthLinks.some((link) => !healthReportLinkIsVerified(link, { h5Root, userId }));
if (!invalid) continue;
next = next.replace(full, '(报告页链接已移除:文件尚未落盘,请重新生成或回复「生成健康报告」)');
removed.push(full);
}
for (const match of original.matchAll(BARE_MINDSPACE_HTML_PATTERN)) {
const url = match[0];
const links = extractStaticPageLinks(url, { userId, username });
const healthLinks = links.filter((link) => isHealthReportArtifactPath(link.relativePath));
if (healthLinks.length === 0) continue;
const invalid = healthLinks.some((link) => !healthReportLinkIsVerified(link, { h5Root, userId }));
if (!invalid) continue;
next = next.replace(url, '(报告页链接已移除:文件尚未落盘)');
removed.push(url);
}
return {
text: next,
stripped: removed.length > 0,
removed,
};
}
export function sanitizeHealthAssistantReportDelivery(messages, {
userId,
username = null,
h5Root = process.cwd(),
} = {}) {
const list = Array.isArray(messages) ? messages : [];
if (readSelectedChatSkill(list) !== HEALTH_ASSISTANT_SKILL_NAME) {
return { messages: list, changed: false, removed: [] };
}
const removed = [];
let changed = false;
const sanitized = list.map((message) => {
if (message?.role !== 'assistant') return message;
const text = messageText(message);
const result = sanitizeHealthReportDeliveryText(text, { userId, username, h5Root });
if (!result.stripped) return message;
changed = true;
removed.push(...result.removed);
return setMessageText(message, result.text);
});
return { messages: sanitized, changed, removed };
}