Files
memind/conversation-display.mjs
T
john 6acd79e4fa
Memind CI / Test, build, and release guards (push) Failing after 3s
Fix MindSpace deliverable link redaction and release gate impact mapping.
Preserve local MindSpace URLs in assistant replies during architecture term redaction, and map db.mjs and skills-registry.mjs for impact selection.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-31 08:22:54 +08:00

134 lines
5.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { stripKnownChatSkillPrompt } from './chat-skills.mjs';
/**
* Strip agent-only prefixes from persisted user messages for UI display.
* Regression guard — see docs/regression-guards/mindspace-publish-and-chat-finish.md
*/
export const TASK_ROUTING_HINT_RE = /^【TKMind 路由提示】[\s\S]*?\n\n/;
export const MEMIND_TASK_ORCHESTRATION_RE = /^【Memind 任务编排】[\s\S]*?(?:用户任务:|用户任务:)\s*/u;
export const MINDSPACE_CONTEXT_RE = /^\[MindSpace 上下文\][\s\S]*?\n\n/;
export const IMAGE_TURN_SCOPE_SUFFIX_RE =
/\n*【本轮图片主题(硬性)】[\s\S]*$/u;
const USER_IDENTITY_BLOCK_RE = /^\[用户身份\][\s\S]*?\n\n/;
export function stripUserIdentityPrefix(text) {
return String(text ?? '').replace(USER_IDENTITY_BLOCK_RE, '').trimStart();
}
export function stripTaskRoutingHint(text) {
return String(text ?? '').replace(TASK_ROUTING_HINT_RE, '').trimStart();
}
export function stripMemindTaskOrchestrationPrefix(text) {
return String(text ?? '').replace(MEMIND_TASK_ORCHESTRATION_RE, '').trimStart();
}
export function stripMindSpaceContextPrefix(text) {
let next = String(text ?? '');
while (MINDSPACE_CONTEXT_RE.test(next)) {
next = next.replace(MINDSPACE_CONTEXT_RE, '').trimStart();
}
return next;
}
/** MindSpace 交付链接:生产域名与本地 dev127.0.0.1 / localhost)都必须可点击,不可被架构脱敏替换。 */
export const MINDSPACE_DELIVERABLE_ORIGIN_RE =
'(?:127\\.0\\.0\\.1|localhost):\\d+|(?:m\\.)?[^/\\s]*tkmind\\.(?:cn|ai)';
const ASSISTANT_DELIVERABLE_RE = new RegExp(
[
'\\[[^\\]]+\\]\\(https?:\\/\\/[^)]+\\)',
`https?:\\/\\/${MINDSPACE_DELIVERABLE_ORIGIN_RE}\\/MindSpace\\/`,
].join('|'),
'i',
);
const INTERNAL_ASSISTANT_MARKERS = [
/data-mindspace-page-tag/i,
/mindspace-cover/i,
/\bload_skill\b/i,
/static-page-publish/i,
/\.agents\/skills/i,
/SKILL\.md/i,
/注意到技能/u,
/技能更新/u,
/页脚要用.*platform-brand/u,
];
/** Replace internal architecture / vendor names before showing assistant text to users. */
export const USER_FACING_ARCHITECTURE_TERM_REPLACEMENTS = Object.freeze([
[/duck\s*duck\s*go|\bduckduckgo\b|\bddg\b/gi, '联网搜索'],
[/\bsearxng\b|\bsearx\b/gi, '联网搜索'],
[/\bgoosed\b|\bgoose\b/gi, '助手'],
[/\bcolima\b|\bdocker\b|\bkubernetes\b|\bk8s\b/gi, '运行环境'],
[/\b(?:stdio\s*)?mcp\b|\btkmind-search\b|\bsandbox-fs\b/gi, '工具扩展'],
[/\bopenhands\b|\baider\b|\blitellm\b/gi, '代码助手'],
[/\borchestrator\b|\bmemindadm\b|\bportal\b(?=\s*(?:api|server|runtime|8081|8085))/gi, '后台服务'],
[/host\.docker\.internal|127\.0\.0\.1:\d{4,5}/gi, '本地服务'],
[/platform\/web|web_search|fetch_url|tkmind_search|tkmind_read/gi, '联网搜索'],
[/\bpostgres(?:ql)?\b|\bredis\b|\bmysql\b|\bweaviate\b/gi, '数据服务'],
]);
export const USER_FACING_ARCHITECTURE_LANGUAGE_RULE =
'- 向用户回复时禁止出现内部实现、架构、供应商或运行时名称(搜索引擎品牌、中间件、容器平台、MCP、编排/代理/代码执行器名称等);统一说「联网搜索」「搜索服务」「助手」「数据服务」';
/** Strip or neutralize architecture terms from assistant replies shown in chat UI. */
export function redactUserFacingArchitectureTerms(text) {
let next = String(text ?? '');
const protectedSegments = [];
const protect = (match) => {
const token = `__MINDSPACE_DELIVERABLE_${protectedSegments.length}__`;
protectedSegments.push(match);
return token;
};
next = next.replace(
new RegExp(
`\\[[^\\]]+\\]\\(https?:\\/\\/${MINDSPACE_DELIVERABLE_ORIGIN_RE}\\/MindSpace\\/[^)]+\\)`,
'gi',
),
protect,
);
next = next.replace(
new RegExp(
`https?:\\/\\/${MINDSPACE_DELIVERABLE_ORIGIN_RE}\\/MindSpace\\/[^\\s)]+`,
'gi',
),
protect,
);
for (const [pattern, replacement] of USER_FACING_ARCHITECTURE_TERM_REPLACEMENTS) {
next = next.replace(pattern, replacement);
}
for (const [index, segment] of protectedSegments.entries()) {
next = next.replace(`__MINDSPACE_DELIVERABLE_${index}__`, segment);
}
return next.replace(/[ \t]{2,}/g, ' ').replace(/\n{3,}/g, '\n\n').trim();
}
/** Agent-only process narration that should not appear in the chat UI. */
export function isInternalAssistantProcessNarration(text) {
const trimmed = String(text ?? '').trim();
if (!trimmed) return false;
if (ASSISTANT_DELIVERABLE_RE.test(trimmed)) return false;
return INTERNAL_ASSISTANT_MARKERS.some((pattern) => pattern.test(trimmed));
}
export function deriveAssistantFacingText(text) {
const trimmed = String(text ?? '').trim();
if (!trimmed) return '';
if (isInternalAssistantProcessNarration(trimmed)) return '';
return redactUserFacingArchitectureTerms(trimmed);
}
/** Strip agent-only prefixes from persisted user message content for UI display. */
export function deriveUserFacingText(text) {
let next = String(text ?? '');
next = next.replace(IMAGE_TURN_SCOPE_SUFFIX_RE, '');
next = stripUserIdentityPrefix(next);
next = stripTaskRoutingHint(next);
next = stripMemindTaskOrchestrationPrefix(next);
next = stripMindSpaceContextPrefix(next);
next = stripKnownChatSkillPrompt(next);
return next.trim();
}