Add smart ACK provider for WeChat MP replies

Replace fixed ackText with a rule-based AckProvider that picks
response templates by message type and intent (translate, summary,
rewrite, poster, ppt, mindmap, code, search, schedule). Pure sync,
zero I/O, auto-falls back to config.ackText on any error.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
john
2026-06-26 15:19:03 +08:00
parent 9ed4fd48d7
commit 9b4a25799f
162 changed files with 17276 additions and 2054 deletions
+45 -4
View File
@@ -13,6 +13,7 @@ import {
extractStaticPageLinks,
findPublishHtml,
injectHtmlBaseHref,
resolveClosestHtmlRelativePath,
resolveChatSaveAnalysis,
sanitizeMessageContentForSave,
} from './mindspace-chat-save.mjs';
@@ -22,16 +23,17 @@ const USER_ID = 'a6fb1e97-2b0f-447b-b138-4561d8e5c53e';
test('extractStaticPageLinks finds user-owned MindSpace html links', () => {
const content = `
页面已完成!
https://g2.tkmind.cn/MindSpace/${USER_ID}/mapo-tofu.html
https://m.tkmind.cn/MindSpace/${USER_ID}/mapo-tofu.html
`;
const links = extractStaticPageLinks(content, { userId: USER_ID });
assert.equal(links.length, 1);
assert.equal(links[0].filename, 'mapo-tofu.html');
assert.equal(links[0].relativePath, 'mapo-tofu.html');
assert.equal(links[0].relativePath, 'public/mapo-tofu.html');
assert.equal(links[0].publicUrl, `https://m.tkmind.cn/MindSpace/${USER_ID}/public/mapo-tofu.html`);
});
test('extractStaticPageLinks ignores other users', () => {
const content = 'https://g2.tkmind.cn/MindSpace/other-id/report.html';
const content = 'https://m.tkmind.cn/MindSpace/other-id/report.html';
const links = extractStaticPageLinks(content, { userId: USER_ID });
assert.equal(links.length, 0);
});
@@ -40,11 +42,27 @@ test('extractStaticPageLinks accepts legacy username urls when username provided
const content = 'https://goo.tkmind.cn/MindSpace/john/report.html';
const links = extractStaticPageLinks(content, { username: 'john' });
assert.equal(links.length, 1);
assert.equal(links[0].relativePath, 'public/report.html');
assert.equal(links[0].publicUrl, 'https://goo.tkmind.cn/MindSpace/john/public/report.html');
});
test('extractStaticPageLinks accepts uuid links when both user id and username are provided', () => {
const content = `https://goo.tkmind.cn/MindSpace/${USER_ID}/public/report.html`;
const links = extractStaticPageLinks(content, { userId: USER_ID, username: 'john' });
assert.equal(links.length, 1);
assert.equal(links[0].relativePath, 'public/report.html');
});
test('extractStaticPageLinks accepts legacy username links when user id is available', () => {
const content = 'https://goo.tkmind.cn/MindSpace/john/report.html';
const links = extractStaticPageLinks(content, { userId: USER_ID, username: 'john' });
assert.equal(links.length, 1);
assert.equal(links[0].publicUrl, 'https://goo.tkmind.cn/MindSpace/john/public/report.html');
});
test('analyzeChatMessageForSave prefers static html mode', () => {
const analysis = analyzeChatMessageForSave({
content: `链接 https://g2.tkmind.cn/MindSpace/${USER_ID}/report.html`,
content: `链接 https://m.tkmind.cn/MindSpace/${USER_ID}/report.html`,
userId: USER_ID,
username: 'john',
h5Root: '/tmp/h5',
@@ -110,6 +128,29 @@ test('findPublishHtml resolves nested public html by basename', async () => {
assert.match(loaded.content, /Welcome/);
});
test('findPublishHtml resolves close html filename typos when match is unique', async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'memind-chat-save-typo-'));
const publishDir = path.join(root, 'MindSpace', USER_ID, 'public');
await fs.mkdir(publishDir, { recursive: true });
await fs.writeFile(
path.join(publishDir, 'memind-deep-analysis.html'),
'<!DOCTYPE html><html><head><title>Deep Analysis</title></head><body>ok</body></html>',
'utf8',
);
const loaded = await findPublishHtml(root, USER_ID, 'public/memind-dep-analysis.html');
assert.equal(loaded.relativePath, 'public/memind-deep-analysis.html');
assert.match(loaded.content, /Deep Analysis/);
});
test('resolveClosestHtmlRelativePath ignores ambiguous typo matches', async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'memind-chat-save-ambiguous-'));
await fs.mkdir(path.join(root, 'public'), { recursive: true });
await fs.writeFile(path.join(root, 'public', 'mindmap-plan.html'), '<html></html>', 'utf8');
await fs.writeFile(path.join(root, 'public', 'mindmap-play.html'), '<html></html>', 'utf8');
const resolved = await resolveClosestHtmlRelativePath(root, 'public/mindmap-plab.html');
assert.equal(resolved, null);
});
test('injectHtmlBaseHref adds base tag for relative assets', () => {
const html = '<html><head><title>x</title></head><body></body></html>';
const next = injectHtmlBaseHref(html, buildWorkspaceBaseHref(USER_ID, 'public/page.html'));