Separate WeChat chat, page refine, and task flows with explicit routing.
Memind CI / Test, build, and release guards (push) Successful in 4m40s
Memind CI / Test, build, and release guards (push) Successful in 4m40s
Add channel prefixes, pin edit targets in Cursor prompts, and force fresh sessions for page refine URLs so DeepSeek/Goose chat history does not pollute page tasks. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
/** Explicit WeChat channel prefixes so users can separate chat, page, and scheduled tasks. */
|
||||
|
||||
export const WECHAT_CHANNEL_PREFIX = Object.freeze({
|
||||
CHAT: 'chat',
|
||||
PAGE: 'page',
|
||||
SCHEDULED_TASK: 'scheduled_task',
|
||||
});
|
||||
|
||||
const PREFIX_ENTRIES = Object.freeze([
|
||||
{
|
||||
channel: WECHAT_CHANNEL_PREFIX.CHAT,
|
||||
pattern: /^【?\s*聊天\s*】?[::\s]*/iu,
|
||||
},
|
||||
{
|
||||
channel: WECHAT_CHANNEL_PREFIX.PAGE,
|
||||
pattern: /^【?\s*(?:改页|做页|页面)\s*】?[::\s]*/iu,
|
||||
},
|
||||
{
|
||||
channel: WECHAT_CHANNEL_PREFIX.SCHEDULED_TASK,
|
||||
pattern: /^【?\s*定时任务\s*】?[::\s]*/iu,
|
||||
},
|
||||
]);
|
||||
|
||||
/**
|
||||
* @returns {{ channel: string|null, body: string, hadPrefix: boolean }}
|
||||
*/
|
||||
export function parseWechatChannelPrefix(text) {
|
||||
const normalized = String(text ?? '').trim();
|
||||
if (!normalized) {
|
||||
return { channel: null, body: '', hadPrefix: false };
|
||||
}
|
||||
for (const entry of PREFIX_ENTRIES) {
|
||||
if (!entry.pattern.test(normalized)) continue;
|
||||
return {
|
||||
channel: entry.channel,
|
||||
body: normalized.replace(entry.pattern, '').trim(),
|
||||
hadPrefix: true,
|
||||
};
|
||||
}
|
||||
return { channel: null, body: normalized, hadPrefix: false };
|
||||
}
|
||||
|
||||
export function stripWechatChannelPrefix(text) {
|
||||
return parseWechatChannelPrefix(text).body;
|
||||
}
|
||||
|
||||
export function hasWechatPageChannelPrefix(text) {
|
||||
return parseWechatChannelPrefix(text).channel === WECHAT_CHANNEL_PREFIX.PAGE;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {
|
||||
parseWechatChannelPrefix,
|
||||
stripWechatChannelPrefix,
|
||||
WECHAT_CHANNEL_PREFIX,
|
||||
} from './channel-prefix.mjs';
|
||||
|
||||
test('parseWechatChannelPrefix strips chat prefix', () => {
|
||||
const parsed = parseWechatChannelPrefix('【聊天】上海明天天气怎么样');
|
||||
assert.equal(parsed.channel, WECHAT_CHANNEL_PREFIX.CHAT);
|
||||
assert.equal(parsed.body, '上海明天天气怎么样');
|
||||
});
|
||||
|
||||
test('parseWechatChannelPrefix strips page refine prefix', () => {
|
||||
const parsed = parseWechatChannelPrefix('【改页】public/demo.html 补充时间节点');
|
||||
assert.equal(parsed.channel, WECHAT_CHANNEL_PREFIX.PAGE);
|
||||
assert.match(parsed.body, /public\/demo\.html/);
|
||||
});
|
||||
|
||||
test('parseWechatChannelPrefix strips scheduled task prefix', () => {
|
||||
const parsed = parseWechatChannelPrefix('【定时任务】列出我进行中的任务');
|
||||
assert.equal(parsed.channel, WECHAT_CHANNEL_PREFIX.SCHEDULED_TASK);
|
||||
assert.equal(parsed.body, '列出我进行中的任务');
|
||||
});
|
||||
|
||||
test('stripWechatChannelPrefix leaves unprefixed text unchanged', () => {
|
||||
assert.equal(stripWechatChannelPrefix('帮我做嵊泗攻略'), '帮我做嵊泗攻略');
|
||||
});
|
||||
@@ -1,3 +1,4 @@
|
||||
import { parseWechatChannelPrefix, WECHAT_CHANNEL_PREFIX } from './channel-prefix.mjs';
|
||||
import {
|
||||
CONNECTIVITY_TEST_PATTERN,
|
||||
GREETING_PATTERN,
|
||||
@@ -43,27 +44,50 @@ import { WECHAT_SESSION_ACTION } from './session-action.mjs';
|
||||
* @param {{ msgType?: string, agentText?: string, displayText?: string }} intent
|
||||
* @returns {WechatIntent}
|
||||
*/
|
||||
function withChannelMeta(result, channelPrefix) {
|
||||
if (!channelPrefix) return result;
|
||||
return { ...result, channelPrefix };
|
||||
}
|
||||
|
||||
export function classifyWechatIntent(intent) {
|
||||
const msgType = String(intent?.msgType ?? 'text').toLowerCase();
|
||||
const text = String(intent?.agentText ?? intent?.displayText ?? '').trim();
|
||||
const rawText = String(intent?.agentText ?? intent?.displayText ?? '').trim();
|
||||
const { channel: channelPrefix, body: text } = parseWechatChannelPrefix(rawText);
|
||||
|
||||
if (msgType === 'text' || msgType === 'voice') {
|
||||
if (isTopicResetText(text)) {
|
||||
return { kind: 'session.reset', sessionAction: WECHAT_SESSION_ACTION.RESET };
|
||||
return withChannelMeta(
|
||||
{ kind: 'session.reset', sessionAction: WECHAT_SESSION_ACTION.RESET },
|
||||
channelPrefix,
|
||||
);
|
||||
}
|
||||
if (STATUS_PROBE_PATTERN.test(text)) {
|
||||
return withChannelMeta({ kind: 'status.probe' }, channelPrefix);
|
||||
}
|
||||
if (GREETING_PATTERN.test(text.replace(/[!!。.\s]+$/g, ''))) {
|
||||
return withChannelMeta({ kind: 'greeting' }, channelPrefix);
|
||||
}
|
||||
if (STATUS_PROBE_PATTERN.test(text)) return { kind: 'status.probe' };
|
||||
if (GREETING_PATTERN.test(text.replace(/[!!。.\s]+$/g, ''))) return { kind: 'greeting' };
|
||||
if (CONNECTIVITY_TEST_PATTERN.test(text.replace(/[!!。.\s]+$/g, ''))) {
|
||||
return { kind: 'connectivity.test' };
|
||||
return withChannelMeta({ kind: 'connectivity.test' }, channelPrefix);
|
||||
}
|
||||
if (isPageGenerateText(text)) {
|
||||
return {
|
||||
if (channelPrefix === WECHAT_CHANNEL_PREFIX.CHAT) {
|
||||
return withChannelMeta({ kind: 'chat.general', text }, channelPrefix);
|
||||
}
|
||||
if (channelPrefix === WECHAT_CHANNEL_PREFIX.PAGE) {
|
||||
return withChannelMeta({
|
||||
kind: 'page.generate',
|
||||
topic: text,
|
||||
wantsDocx: wantsDocxDownload(text),
|
||||
};
|
||||
}, channelPrefix);
|
||||
}
|
||||
return { kind: 'chat.general', text };
|
||||
if (isPageGenerateText(text)) {
|
||||
return withChannelMeta({
|
||||
kind: 'page.generate',
|
||||
topic: text,
|
||||
wantsDocx: wantsDocxDownload(text),
|
||||
}, channelPrefix);
|
||||
}
|
||||
return withChannelMeta({ kind: 'chat.general', text }, channelPrefix);
|
||||
}
|
||||
|
||||
if (msgType === 'event') {
|
||||
|
||||
Reference in New Issue
Block a user