Files
memind/wechat-mp.test.mjs
T
john 7b3acb6813
Memind CI / Test, build, and release guards (pull_request) Successful in 4m1s
fix(wechat): retry missing page thumbnails in fresh sessions
2026-07-22 09:38:13 +08:00

5464 lines
204 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 assert from 'node:assert/strict';
import crypto from 'node:crypto';
import fs from 'node:fs';
import path from 'node:path';
import test from 'node:test';
import sharp from 'sharp';
import {
buildWechatAgentPrompt,
buildWechatTextReply,
createWechatMpService,
guardMissingPublicHtmlLinks,
assertWechatAgentReplyIsSendable,
findRecoverableWechatAgentErrorInReply,
isRecoverableWechatAgentSessionError,
isWechatAgentApiErrorText,
sanitizeWechatAgentOutboundText,
loadWechatMpConfig,
maybeAttachPublishedHtmlLink,
shouldRetryHtmlGenerationReply,
shouldForceNewWechatAgentSession,
splitWechatText,
verifyWechatMpSignature,
verifyWechatMpUrlChallenge,
decryptWechatMpPayload,
WECHAT_CUSTOMER_TEXT_MAX_BYTES,
} from './wechat-mp.mjs';
function signatureFor(token, timestamp, nonce) {
return crypto
.createHash('sha1')
.update([token, timestamp, nonce].sort().join(''))
.digest('hex');
}
function inboundXml({
toUserName = 'gh_test',
fromUserName = 'openid-1',
msgType = 'text',
content = 'hello',
event = '',
extraFields = {},
} = {}) {
const renderedExtras = Object.entries(extraFields)
.map(([key, value]) => {
if (value === '' || value === null || value === undefined) return '';
return typeof value === 'number'
? `<${key}>${value}</${key}>`
: `<${key}><![CDATA[${value}]]></${key}>`;
})
.filter(Boolean)
.join('');
return [
'<xml>',
`<ToUserName><![CDATA[${toUserName}]]></ToUserName>`,
`<FromUserName><![CDATA[${fromUserName}]]></FromUserName>`,
'<CreateTime>1710000000</CreateTime>',
`<MsgType><![CDATA[${msgType}]]></MsgType>`,
content ? `<Content><![CDATA[${content}]]></Content>` : '',
event ? `<Event><![CDATA[${event}]]></Event>` : '',
renderedExtras,
'<MsgId>10001</MsgId>',
'</xml>',
].join('');
}
function previewReadyPageHtml({ title = 'Page', subtitle = '测试页面', cover = '' } = {}) {
const coverField = cover ? `,"cover":"${cover}"` : '';
return `<!doctype html><html><head><meta name="description" content="${subtitle}"><meta name="mindspace-cover" content='{"tag":"页面","accent":"#3366cc","accent2":"#112233","subtitle":"${subtitle}"${coverField}}'><title>${title}</title></head><body><main>${'x'.repeat(600)}</main><p data-mindspace-page-tag="platform-brand">TKMind · 智趣</p></body></html>`;
}
function jsonEscapedPreviewReadyPageHtml(options = {}) {
return previewReadyPageHtml(options).replace(/\\/g, '\\\\').replace(/"/g, '\\"');
}
function createBoundWechatService({
token = 'token',
wechatFetch,
sessionApiFetch,
startAgentSession = null,
userAuth = {},
config = {},
scheduleService = null,
applySessionLlmProvider = null,
submitSessionReply = null,
}) {
return createWechatMpService({
config: {
enabled: true,
appId: 'wx123',
appSecret: 'secret',
token,
publicBaseUrl: 'https://example.com',
mediaPublicBaseUrl: 'https://example.com',
bindPath: '/auth/wechat/authorize?intent=login',
ackText: 'ack',
unsupportedText: 'unsupported',
unboundTextPrefix: '请先绑定',
progressDelayMs: 0,
maxImageBytes: 1024 * 1024,
requireFreshPageThumbnail: false,
...config,
},
userAuth: {
async findWechatUserByOpenid() {
return { userId: 'user-1', status: 'active', nickname: '毕升' };
},
async getWechatAgentRoute() {
return { agentSessionId: 'session-1' };
},
async clearWechatAgentRoute() {},
async canUseChat() {
return { ok: true };
},
async resolveWorkingDir() {
return '/tmp/user-1';
},
async getAgentSessionPolicy() {
return { enableContextMemory: false, extensionOverrides: [], unrestricted: true };
},
async getUserPublishLayout() {
return { displayName: 'John', username: 'john', slug: 'john', constraints: null };
},
async registerAgentSession() {},
async upsertWechatAgentRoute() {},
async billSessionUsage() {},
async recordWechatMpMessage() {
return { inserted: true };
},
async finishWechatMpMessage() {},
async insertWechatMpMessageDetail() {},
...userAuth,
},
startAgentSession,
sessionApiFetch,
submitSessionReply,
scheduleService,
applySessionLlmProvider,
wechatFetch,
apiFetch: async () => new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } }),
});
}
test('splitWechatText respects WeChat 2048-byte customer service limit', () => {
const longChinese = '中'.repeat(900);
const chunks = splitWechatText(longChinese);
assert.ok(chunks.length > 1);
for (const chunk of chunks) {
assert.ok(Buffer.byteLength(chunk, 'utf8') <= WECHAT_CUSTOMER_TEXT_MAX_BYTES);
}
assert.equal(chunks.join(''), longChinese);
});
test('Page Data requests always rotate away from an existing WeChat route', () => {
assert.equal(
shouldForceNewWechatAgentSession({ kind: 'chat.general' }, '做一个问卷,后台可以查看提交数据'),
true,
);
assert.equal(shouldForceNewWechatAgentSession({ kind: 'chat.general' }, '继续聊德川家康'), false);
assert.equal(shouldForceNewWechatAgentSession({ kind: 'session.reset' }, '继续聊德川家康'), true);
});
test('buildWechatAgentPrompt requires docx generation before html when Word download is requested', () => {
const prompt = buildWechatAgentPrompt({
msgType: 'text',
content: '帮我生成一个行业分布报告页面,提供链接word给我来下载',
});
assert.match(prompt, /docx-generate/);
assert.match(prompt, /public\/\*\.docx/);
assert.match(prompt, /static-page-publish/);
});
test('buildWechatAgentPrompt keeps normal html guidance without docx requirement for plain page requests', () => {
const prompt = buildWechatAgentPrompt({
msgType: 'text',
content: '帮我生成一个简单页面',
});
assert.match(prompt, /static-page-publish/);
assert.doesNotMatch(prompt, /docx-generate/);
});
test('guardMissingPublicHtmlLinks blocks missing MindSpace public html links', async () => {
const text =
'页面已创建:\nhttps://m.tkmind.cn/MindSpace/a70ff537-8908-486e-9b6c-042e07cc25db/public/missing.html';
const guarded = await guardMissingPublicHtmlLinks(text, {
linkExists: async () => false,
});
assert.doesNotMatch(guarded, /https:\/\/g2\.tkmind\.cn\/MindSpace/);
assert.match(guarded, /页面生成未完成/);
assert.match(guarded, /missing\.html/);
});
test('shouldRetryHtmlGenerationReply retries when reply link is missing on disk', () => {
const intent = { agentText: '帮我生成一个 html 页面 public/test.html' };
const reply = {
text: '[Test](https://m.tkmind.cn/MindSpace/user-1/public/test.html)',
messages: [
{
content: [
{
type: 'toolRequest',
toolCall: { value: { name: 'load_skill', arguments: { name: 'static-page-publish' } } },
},
],
},
],
};
assert.equal(
shouldRetryHtmlGenerationReply({
reply,
intent,
confirmedArtifacts: [],
hasValidLinkInReply: false,
}),
true,
);
});
test('shouldRetryHtmlGenerationReply does not skip retry because unrelated recent html exists', () => {
const intent = { agentText: '帮我做一个小页面吧' };
const reply = {
text: '[Test](https://m.tkmind.cn/MindSpace/user-1/public/test.html)',
messages: [
{
content: [
{
type: 'toolRequest',
toolCall: { value: { name: 'load_skill', arguments: { name: 'static-page-publish' } } },
},
{
type: 'toolRequest',
toolCall: {
value: {
name: 'write_file',
arguments: { path: 'public/other-page.html', content: '<html></html>' },
},
},
},
],
},
],
};
assert.equal(
shouldRetryHtmlGenerationReply({
reply,
intent,
confirmedArtifacts: [
{
localPath: '/tmp/user-1/public/other-page.html',
relativePath: 'public/other-page.html',
url: 'https://m.tkmind.cn/MindSpace/user-1/public/other-page.html',
},
],
hasValidLinkInReply: false,
}),
true,
);
});
test('shouldRetryHtmlGenerationReply allows normal H5 agent text without fake page links', () => {
const intent = { agentText: '帮我生成页面吧' };
const reply = {
text: '请告诉我要生成什么主题和内容,我再继续。',
messages: [
{
content: [
{
type: 'text',
text: '请告诉我要生成什么主题和内容,我再继续。',
},
],
},
],
};
assert.equal(
shouldRetryHtmlGenerationReply({
reply,
intent,
confirmedArtifacts: [],
hasValidLinkInReply: false,
}),
false,
);
});
test('wechat mp service rejects blocked publish claims that did not use the H5 page skill', async () => {
const wechatCalls = [];
const service = createBoundWechatService({
config: {
publicBaseUrl: 'https://m.tkmind.cn',
},
sessionApiFetch: async (sessionId, pathname) => {
assert.equal(sessionId, 'session-1');
if (pathname === '/sessions/session-1/events') {
return new Response(
[
'data: {"type":"Message","request_id":"req-1","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"好消息!m.tkmind.cn 其实是可以访问的,页面都已经成功发布了!以下是您的夏日主题页面:\\n\\n🌴 夏日主题页面已发布\\n\\nhttps://m.tkmind.cn/MindSpace/john/public/summer-breeze-journal.html"}]}}\n\n',
'data: {"type":"Finish","request_id":"req-1","token_state":{"inputTokens":2,"outputTokens":2}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === '/sessions/session-1/reply') {
return new Response(JSON.stringify({ status: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response(JSON.stringify({ ok: true }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected session api path: ${sessionId} ${pathname}`);
},
wechatFetch: async (url, init = {}) => {
wechatCalls.push([url, init.method ?? 'GET', init.body ?? null]);
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
userAuth: {
async resolveWorkingDir() {
return '/tmp/user-1';
},
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = () => 'req-1';
try {
const result = await service.handleInboundMessage(inboundXml({ content: '帮我生成一个夏日页面' }), {
timestamp: '1710000000',
nonce: 'nonce',
signature: signatureFor('token', '1710000000', 'nonce'),
});
assert.equal(result.status, 200);
await result.task;
} finally {
crypto.randomUUID = originalRandomUuid;
}
const sendCall = wechatCalls.find(([url]) => String(url).includes('/cgi-bin/message/custom/send'));
const payload = JSON.parse(sendCall[2]);
assert.doesNotMatch(payload.text.content, /成功发布|主题页面已发布/);
assert.match(payload.text.content, /服务号页面技能|没有按 H5 里的页面技能/);
assert.doesNotMatch(payload.text.content, /summer-breeze-journal\.html/);
});
test('maybeAttachPublishedHtmlLink copies generated root html into public and returns link', async (t) => {
const workspaceRoot = fs.mkdtempSync('/tmp/wechat-mp-public-');
const htmlPath = `${workspaceRoot}/hello.html`;
fs.writeFileSync(htmlPath, '<!doctype html><title>Hello</title>');
const text = await maybeAttachPublishedHtmlLink(
{
text: '已完成',
messages: [
{
role: 'assistant',
content: [
{
type: 'toolRequest',
toolCall: {
value: {
name: 'developer',
arguments: {
action: 'write',
path: htmlPath,
content: '<!doctype html><title>Hello</title>',
},
},
},
},
],
},
],
},
{
workingDir: workspaceRoot,
publicBaseUrl: 'https://m.tkmind.cn',
},
);
assert.equal(fs.existsSync(`${workspaceRoot}/public/hello.html`), true);
assert.match(
text,
/https:\/\/m\.tkmind\.cn\/MindSpace\/.+\/public\/hello\.html/,
);
});
test('maybeAttachPublishedHtmlLink recognizes sandbox-fs write_file html outputs', async () => {
const workspaceRoot = fs.mkdtempSync('/tmp/wechat-mp-sandbox-fs-');
const htmlPath = `${workspaceRoot}/public/codex-verify.html`;
fs.mkdirSync(`${workspaceRoot}/public`, { recursive: true });
fs.writeFileSync(htmlPath, '<!doctype html><title>Codex Verify</title>');
const text = await maybeAttachPublishedHtmlLink(
{
text: '页面已生成 ✅',
messages: [
{
role: 'assistant',
content: [
{
type: 'toolRequest',
toolCall: {
value: {
name: 'sandbox-fs__write_file',
arguments: {
path: 'public/codex-verify.html',
content: '<!doctype html><title>Codex Verify</title>',
},
},
},
},
],
},
],
},
{
workingDir: workspaceRoot,
publicBaseUrl: 'https://m.tkmind.cn',
},
);
assert.match(
text,
/https:\/\/m\.tkmind\.cn\/MindSpace\/.+\/public\/codex-verify\.html/,
);
});
test('maybeAttachPublishedHtmlLink rewrites wrong public html links to the canonical published url', async () => {
const workspaceRoot = fs.mkdtempSync('/tmp/wechat-mp-rewrite-link-');
const htmlPath = `${workspaceRoot}/public/summer-breeze-journal.html`;
fs.mkdirSync(`${workspaceRoot}/public`, { recursive: true });
fs.writeFileSync(htmlPath, '<!doctype html><title>Summer</title>');
const text = await maybeAttachPublishedHtmlLink(
{
text: '页面已发布:https://m.tkmind.cn/public/summer-breeze-journal.html',
messages: [
{
role: 'assistant',
content: [
{
type: 'toolRequest',
toolCall: {
value: {
name: 'sandbox-fs__write_file',
arguments: {
path: 'public/summer-breeze-journal.html',
content: '<!doctype html><title>Summer</title>',
},
},
},
},
],
},
],
},
{
workingDir: workspaceRoot,
publicBaseUrl: 'https://m.tkmind.cn',
},
);
assert.doesNotMatch(text, /https:\/\/m\.tkmind\.cn\/public\/summer-breeze-journal\.html/);
assert.match(
text,
/https:\/\/m\.tkmind\.cn\/MindSpace\/.+\/public\/summer-breeze-journal\.html/,
);
});
test('maybeAttachPublishedHtmlLink rewrites tkmind domain variants to the canonical published url', async () => {
const workspaceRoot = fs.mkdtempSync('/tmp/wechat-mp-rewrite-domain-');
const htmlPath = `${workspaceRoot}/public/codex-prod-check-v6.html`;
fs.mkdirSync(`${workspaceRoot}/public`, { recursive: true });
fs.writeFileSync(htmlPath, '<!doctype html><title>Prod Check</title>');
const text = await maybeAttachPublishedHtmlLink(
{
text: '唯一可访问链接:https://mindspace.tkmind.cn/a70ff537-8908-486e-9b6c-042e07cc25db/codex-prod-check-v6.html',
messages: [
{
role: 'assistant',
content: [
{
type: 'toolRequest',
toolCall: {
value: {
name: 'sandbox-fs__write_file',
arguments: {
path: 'public/codex-prod-check-v6.html',
content: '<!doctype html><title>Prod Check</title>',
},
},
},
},
],
},
],
},
{
workingDir: workspaceRoot,
publicBaseUrl: 'https://m.tkmind.cn',
},
);
assert.doesNotMatch(text, /https:\/\/mindspace\.tkmind\.cn\/.+\/codex-prod-check-v6\.html/);
assert.match(
text,
/https:\/\/m\.tkmind\.cn\/MindSpace\/.+\/public\/codex-prod-check-v6\.html/,
);
});
test('maybeAttachPublishedHtmlLink can attach a verified existing public html link from provided artifacts', async () => {
const workspaceRoot = fs.mkdtempSync('/tmp/wechat-mp-existing-public-');
const htmlPath = `${workspaceRoot}/public/thailand-guide.html`;
fs.mkdirSync(`${workspaceRoot}/public`, { recursive: true });
fs.writeFileSync(htmlPath, previewReadyPageHtml({ title: 'Thailand', subtitle: '泰国旅行攻略' }));
const text = await maybeAttachPublishedHtmlLink(
{
text: '页面已生成!点击下方链接查看:',
messages: [],
},
{
workingDir: workspaceRoot,
publicBaseUrl: 'https://m.tkmind.cn',
artifacts: [
{
localPath: htmlPath,
relativePath: 'public/thailand-guide.html',
url: `https://m.tkmind.cn/MindSpace/${path.basename(workspaceRoot)}/public/thailand-guide.html`,
},
],
},
);
assert.match(text, /https:\/\/m\.tkmind\.cn\/MindSpace\/.+\/public\/thailand-guide\.html/);
});
test('wechat mp service splits long agent replies into multiple customer messages', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const longReply = '答'.repeat(900);
const wechatCalls = [];
const service = createWechatMpService({
config: {
enabled: true,
appId: 'wx123',
appSecret: 'secret',
token,
publicBaseUrl: 'https://example.com',
bindPath: '/auth/wechat/authorize?intent=login',
ackText: 'ack',
progressDelayMs: 0,
unsupportedText: 'unsupported',
unboundTextPrefix: '请先绑定',
},
userAuth: {
async findWechatUserByOpenid() {
return { userId: 'user-1', status: 'active', nickname: '毕升' };
},
async getWechatAgentRoute() {
return { agentSessionId: 'session-1' };
},
async clearWechatAgentRoute() {},
async canUseChat() {
return { ok: true };
},
async resolveWorkingDir() {
return '/tmp/user-1';
},
async getAgentSessionPolicy() {
return { enableContextMemory: false, extensionOverrides: [], unrestricted: true };
},
async getUserPublishLayout() {
return { displayName: 'John', username: 'john', slug: 'john', constraints: null };
},
async registerAgentSession() {},
async upsertWechatAgentRoute() {},
async billSessionUsage() {},
},
sessionApiFetch: async (sessionId, pathname, init = {}) => {
assert.equal(sessionId, 'session-1');
if (pathname === '/sessions/session-1/events') {
return new Response(
[
`data: {"type":"Message","request_id":"req-long","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":${JSON.stringify(longReply)}}]}}\n\n`,
'data: {"type":"Finish","request_id":"req-long","token_state":{"inputTokens":1,"outputTokens":2}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === '/sessions/session-1/reply') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
throw new Error(`unexpected api path: ${pathname}`);
},
wechatFetch: async (url, init = {}) => {
wechatCalls.push([url, init.method ?? 'GET', init.body ?? null]);
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
const payload = JSON.parse(init.body);
assert.ok(Buffer.byteLength(payload.text.content, 'utf8') <= WECHAT_CUSTOMER_TEXT_MAX_BYTES);
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = () => 'req-long';
try {
const result = await service.handleInboundMessage(inboundXml({ content: '给我长回复' }), {
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
});
assert.equal(result.status, 200);
await result.task;
} finally {
crypto.randomUUID = originalRandomUuid;
}
const sendCalls = wechatCalls.filter(([url]) => String(url).includes('/cgi-bin/message/custom/send'));
assert.ok(sendCalls.length > 1);
const combined = sendCalls
.map(([, , body]) => JSON.parse(body).text.content)
.join('');
assert.equal(combined, longReply);
});
test('wechat mp service surfaces wechat customer-service errcode and errmsg', async () => {
const service = createBoundWechatService({
userAuth: {
async getWechatOpenidForUser() {
return 'openid-1';
},
},
wechatFetch: async (url) => {
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
return new Response(JSON.stringify({ errcode: 45047, errmsg: 'out of response count limit rid: test-rid' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
},
});
await assert.rejects(
service.sendTextToUser('user-1', 'hello'),
/errcode=45047.*out of response count limit/,
);
});
test('wechat mp service strips markdown emphasis around outbound links', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const wechatCalls = [];
const service = createWechatMpService({
config: {
enabled: true,
appId: 'wx123',
appSecret: 'secret',
token,
publicBaseUrl: 'https://example.com',
bindPath: '/auth/wechat/authorize?intent=login',
ackText: 'ack',
progressDelayMs: 0,
unsupportedText: 'unsupported',
unboundTextPrefix: '请先绑定',
},
userAuth: {
async findWechatUserByOpenid() {
return { userId: 'user-1', status: 'active', nickname: '毕升' };
},
async getWechatAgentRoute() {
return { agentSessionId: 'session-1' };
},
async clearWechatAgentRoute() {},
async canUseChat() {
return { ok: true };
},
async resolveWorkingDir() {
return '/tmp/user-1';
},
async getAgentSessionPolicy() {
return { enableContextMemory: false, extensionOverrides: [], unrestricted: true };
},
async getUserPublishLayout() {
return { displayName: 'John', username: 'john', slug: 'john', constraints: null };
},
async registerAgentSession() {},
async upsertWechatAgentRoute() {},
async billSessionUsage() {},
},
sessionApiFetch: async (sessionId, pathname) => {
assert.equal(sessionId, 'session-1');
if (pathname === '/sessions/session-1/events') {
return new Response(
[
`data: {"type":"Message","request_id":"req-link-md","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"👉 **跑步路书**\\nhttps://example.com/running-route.html**"}]}}\n\n`,
'data: {"type":"Finish","request_id":"req-link-md","token_state":{"inputTokens":1,"outputTokens":2}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === '/sessions/session-1/reply') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
throw new Error(`unexpected api path: ${pathname}`);
},
wechatFetch: async (url, init = {}) => {
wechatCalls.push([url, init.method ?? 'GET', init.body ?? null]);
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
linkExists: async (urlText) => {
if (!String(urlText).includes('/summer-breeze-journal.html')) return false;
return fs.existsSync(htmlPath);
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = () => 'req-link-md';
try {
const result = await service.handleInboundMessage(inboundXml({ content: '给我链接' }), {
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
});
assert.equal(result.status, 200);
await result.task;
} finally {
crypto.randomUUID = originalRandomUuid;
}
const sendCall = wechatCalls.find(([url]) => String(url).includes('/cgi-bin/message/custom/send'));
const payload = JSON.parse(sendCall[2]);
assert.equal(payload.text.content.includes('**'), false);
assert.equal(payload.text.content.includes('https://example.com/running-route.html**'), false);
assert.match(payload.text.content, /https:\/\/example\.com\/running-route\.html/);
});
test('wechat mp service does not send stale page links from unscoped conversation snapshots', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const wechatCalls = [];
const service = createBoundWechatService({
sessionApiFetch: async (sessionId, pathname) => {
assert.equal(sessionId, 'session-1');
if (pathname === '/sessions/session-1/events') {
return new Response(
[
'data: {"type":"UpdateConversation","conversation":[{"id":"user-old","role":"user","metadata":{"userVisible":true},"content":[{"type":"text","text":"帮我做餐厅推荐"}]},{"id":"assistant-old","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"页面已生成!餐厅推荐\\nhttps://g2.tkmind.cn/MindSpace/old/public/restaurant.html"}]}]}\n\n',
'data: {"type":"Finish","request_id":"req-stale","token_state":{"inputTokens":1,"outputTokens":2}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === '/sessions/session-1/reply') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
throw new Error(`unexpected api path: ${pathname}`);
},
wechatFetch: async (url, init = {}) => {
wechatCalls.push([url, init.method ?? 'GET', init.body ?? null]);
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
linkExists: async (urlText) => {
if (!String(urlText).includes('/summer-breeze-journal.html')) return false;
return fs.existsSync(htmlPath);
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = () => 'req-stale';
try {
const result = await service.handleInboundMessage(inboundXml({ content: '帮我做上马计划' }), {
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
});
assert.equal(result.status, 200);
await result.task;
} finally {
crypto.randomUUID = originalRandomUuid;
}
const sendCalls = wechatCalls.filter(([url]) => String(url).includes('/cgi-bin/message/custom/send'));
assert.equal(sendCalls.length, 1);
const payload = JSON.parse(sendCalls[0][2]);
assert.doesNotMatch(payload.text.content, /restaurant\.html/);
assert.match(payload.text.content, /本轮未收到可发送的新回复/);
});
test('loadWechatMpConfig requires full config and enable flag', () => {
const config = loadWechatMpConfig({
H5_WECHAT_MP_ENABLED: '1',
H5_WECHAT_MP_APP_ID: 'wx123',
H5_WECHAT_MP_APP_SECRET: 'secret',
H5_WECHAT_MP_TOKEN: 'token',
H5_PUBLIC_BASE_URL: 'https://example.com',
IMGPROXY_BASE_URL: 'https://img.example.com',
});
assert.equal(config.enabled, true);
assert.equal(config.bindPath, '/auth/wechat/authorize?intent=login');
assert.equal(config.requireFreshPageThumbnail, true);
assert.deepEqual(config.generatedImagePublicBaseUrls, [
'https://example.com',
'https://img.example.com',
]);
assert.equal(loadWechatMpConfig({ H5_WECHAT_MP_FRESH_PAGE_THUMBNAILS: '0' }).requireFreshPageThumbnail, false);
});
test('verifyWechatMpSignature accepts valid signature', () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
assert.equal(
verifyWechatMpSignature({
token,
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
}),
true,
);
});
test('verifyWechatMpUrlChallenge supports aes mode echostr', () => {
const token = 'memindprodmp';
const appId = 'wxaa5f3cbb9ee009c2';
const encodingAesKey = 'oovBs3npy6NdflERl0LObynzVwH8UgwtCKrfngwfBod';
const timestamp = '1710000000';
const nonce = 'nonce123';
const plainEcho = '77889900';
const key = Buffer.from(`${encodingAesKey}=`, 'base64');
const iv = key.subarray(0, 16);
const random16 = Buffer.alloc(16, 0x61);
const msgBuf = Buffer.from(plainEcho, 'utf8');
const appIdBuf = Buffer.from(appId, 'utf8');
const lenBuf = Buffer.alloc(4);
lenBuf.writeUInt32BE(msgBuf.length, 0);
const raw = Buffer.concat([random16, lenBuf, msgBuf, appIdBuf]);
const pad = 32 - (raw.length % 32);
const padded = Buffer.concat([raw, Buffer.alloc(pad, pad)]);
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
cipher.setAutoPadding(false);
const encrypted = Buffer.concat([cipher.update(padded), cipher.final()]).toString('base64');
const msgSignature = crypto
.createHash('sha1')
.update([token, timestamp, nonce, encrypted].sort().join(''))
.digest('hex');
const result = verifyWechatMpUrlChallenge(
{
encrypt_type: 'aes',
msg_signature: msgSignature,
timestamp,
nonce,
echostr: encrypted,
},
{ token, appId, encodingAesKey },
);
assert.equal(result.ok, true);
assert.equal(result.body, plainEcho);
});
test('buildWechatTextReply includes both parties and content', () => {
const xml = buildWechatTextReply({
toUserName: 'openid-1',
fromUserName: 'gh_test',
content: '收到',
createTime: 123,
});
assert.match(xml, /<ToUserName><!\[CDATA\[openid-1\]\]><\/ToUserName>/);
assert.match(xml, /<Content><!\[CDATA\[收到\]\]><\/Content>/);
});
test('wechat mp service prompts binding for unknown openid', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const service = createWechatMpService({
config: {
enabled: true,
appId: 'wx123',
appSecret: 'secret',
token,
publicBaseUrl: 'https://example.com',
bindPath: '/auth/wechat/authorize?intent=login',
ackText: 'ack',
unsupportedText: 'unsupported',
unboundTextPrefix: '请先绑定',
},
userAuth: {
async findWechatUserByOpenid() {
return null;
},
},
apiFetch: async () => new Response('{}', { status: 200 }),
});
const result = await service.handleInboundMessage(inboundXml(), {
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
});
assert.equal(result.status, 200);
assert.match(result.body, /请先绑定/);
assert.match(result.body, /https:\/\/example.com\/auth\/wechat\/authorize\?intent=login/);
});
test('wechat mp service routes text to dedicated session and sends customer service reply', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const apiCalls = [];
const wechatCalls = [];
const service = createWechatMpService({
config: {
enabled: true,
appId: 'wx123',
appSecret: 'secret',
token,
publicBaseUrl: 'https://example.com',
bindPath: '/auth/wechat/authorize?intent=login',
ackText: 'ack',
unsupportedText: 'unsupported',
unboundTextPrefix: '请先绑定',
},
userAuth: {
async findWechatUserByOpenid() {
return { userId: 'user-1', status: 'active', nickname: '毕升' };
},
async getWechatAgentRoute() {
return { agentSessionId: 'session-1' };
},
async clearWechatAgentRoute() {},
async canUseChat() {
return { ok: true };
},
async resolveWorkingDir() {
return '/tmp/user-1';
},
async getAgentSessionPolicy() {
return { enableContextMemory: false, extensionOverrides: [], unrestricted: true };
},
async getUserPublishLayout() {
return { displayName: 'John', username: 'john', slug: 'john', constraints: null };
},
async registerAgentSession() {},
async upsertWechatAgentRoute() {},
async billSessionUsage(userId, sessionId, tokenState, requestId) {
apiCalls.push(['bill', userId, sessionId, tokenState, requestId]);
},
},
apiFetch: async (pathname, init = {}) => {
apiCalls.push(['fallback', pathname, init.method ?? 'GET']);
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
},
sessionApiFetch: async (sessionId, pathname, init = {}) => {
assert.equal(sessionId, 'session-1');
apiCalls.push([pathname, init.method ?? 'GET']);
if (pathname === '/sessions/session-1/events') {
return new Response(
[
'data: {"type":"Message","request_id":"req-1","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"wx_abcd1234,页面已创建完毕!\\n[标题](https://example.com/page.html)\\n\\n| 项 | 值 |\\n| --- | --- |\\n| A | B |"}]}}\n\n',
'data: {"type":"Finish","request_id":"req-1","token_state":{"inputTokens":1,"outputTokens":2}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === '/sessions/session-1/reply') {
const body = JSON.parse(init.body);
assert.match(body.user_message.content[0].text, /用户消息:帮我做个页面/);
assert.match(body.user_message.content[0].text, /static-page-publish/);
body.request_id && (apiCalls[apiCalls.length - 1][2] = body.request_id);
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_remember') {
const body = JSON.parse(init.body);
assert.equal(body.sessionId, 'session-1');
assert.match(body.content, /当前服务号用户名称/);
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/session/reconcile') {
const body = JSON.parse(init.body);
assert.equal(body.user_context.displayName, '毕升');
assert.equal(body.user_context.username, '毕升');
assert.equal(body.user_context.slug, null);
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_bootstrap') {
const body = JSON.parse(init.body);
assert.equal(body.sessionId, 'session-1');
assert.equal(body.force, false);
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
throw new Error(`unexpected api path: ${pathname}`);
},
wechatFetch: async (url, init = {}) => {
wechatCalls.push([url, init.method ?? 'GET', init.body ?? null]);
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
const payload = JSON.parse(init.body);
assert.equal(payload.touser, 'openid-1');
assert.match(payload.text.content, /页面已创建完毕/);
assert.doesNotMatch(payload.text.content, /^wx_/);
assert.match(payload.text.content, /^毕升,页面已创建完毕/);
assert.doesNotMatch(payload.text.content, /\[标题\]\(/);
assert.match(payload.text.content, /标题\nhttps:\/\/example.com\/page.html/);
assert.match(payload.text.content, /项:A;值:B/);
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
linkExists: async (urlText) => {
if (!String(urlText).includes('/summer-breeze-journal.html')) return false;
return fs.existsSync(htmlPath);
},
});
const originalRandomUuid = crypto.randomUUID;
let uuidCalls = 0;
crypto.randomUUID = () => {
uuidCalls += 1;
return uuidCalls === 1 ? 'req-1' : `uuid-${uuidCalls}`;
};
try {
const result = await service.handleInboundMessage(inboundXml({ content: '帮我做个页面' }), {
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
});
assert.equal(result.status, 200);
assert.match(result.body, /<Content>/);
await result.task;
} finally {
crypto.randomUUID = originalRandomUuid;
}
assert.equal(
apiCalls.some(([pathname]) => pathname === '/sessions/session-1/reply'),
true,
);
assert.equal(
apiCalls.some(([kind]) => kind === 'fallback'),
false,
);
assert.equal(
wechatCalls.some(([url]) => String(url).includes('/cgi-bin/message/custom/send')),
true,
);
});
test('wechat mp service rewrites internal wx username mentions that appear mid-message', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const sentPayloads = [];
const service = createWechatMpService({
config: {
enabled: true,
appId: 'wx123',
appSecret: 'secret',
token,
publicBaseUrl: 'https://example.com',
bindPath: '/auth/wechat/authorize?intent=login',
ackText: 'ack',
unsupportedText: 'unsupported',
unboundTextPrefix: '请先绑定',
progressDelayMs: 0,
},
userAuth: {
async findWechatUserByOpenid() {
return { userId: 'user-1', status: 'active', nickname: 'Lumi' };
},
async getWechatAgentRoute() {
return { agentSessionId: 'session-1' };
},
async clearWechatAgentRoute() {},
async canUseChat() {
return { ok: true };
},
async resolveWorkingDir() {
return '/tmp/user-1';
},
async getAgentSessionPolicy() {
return { enableContextMemory: false, extensionOverrides: [], unrestricted: true };
},
async getUserPublishLayout() {
return { displayName: 'Lumi', username: 'wx_ul610et8', slug: 'wx_ul610et8', constraints: null };
},
async registerAgentSession() {},
async upsertWechatAgentRoute() {},
async billSessionUsage() {},
},
sessionApiFetch: async (sessionId, pathname, init = {}) => {
assert.equal(sessionId, 'session-1');
if (pathname === '/sessions/session-1/events') {
return new Response(
[
'data: {"type":"Message","request_id":"req-mid","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"LumiMind 这个感觉很好。\\n\\nwx_ul610et8,这个感觉对了。\\n好的,wx_ul610et8,为你写了一首。"}]}}\n\n',
'data: {"type":"Finish","request_id":"req-mid","token_state":{"inputTokens":1,"outputTokens":2}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === '/sessions/session-1/reply') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
throw new Error(`unexpected api path: ${pathname}`);
},
wechatFetch: async (url, init = {}) => {
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
sentPayloads.push(JSON.parse(init.body));
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
linkExists: async (urlText) => {
if (!String(urlText).includes('/summer-breeze-journal.html')) return false;
return fs.existsSync(htmlPath);
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = () => 'req-mid';
try {
const result = await service.handleInboundMessage(
inboundXml({ msgType: 'text', content: '这个名字怎么样' }),
{
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
},
);
await result.task;
} finally {
crypto.randomUUID = originalRandomUuid;
}
assert.equal(sentPayloads.length, 1);
assert.doesNotMatch(sentPayloads[0].text.content, /wx_ul610et8/);
assert.match(sentPayloads[0].text.content, /\nLumi,这个感觉对了。/);
assert.match(sentPayloads[0].text.content, /好的,Lumi,为你写了一首。/);
});
test('wechat mp service applies provider before forwarding to dedicated session', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const appliedSessions = [];
const service = createBoundWechatService({
token,
applySessionLlmProvider: async (sessionId) => {
appliedSessions.push(sessionId);
return { ok: true, providerId: 'custom_tkmind_relay_deepseek', model: 'deepseek-chat' };
},
sessionApiFetch: async (sessionId, pathname) => {
assert.equal(sessionId, 'session-1');
if (pathname === '/sessions/session-1/events') {
return new Response(
[
'data: {"type":"Message","request_id":"req-provider","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"收到。"}]}}\n\n',
'data: {"type":"Finish","request_id":"req-provider","token_state":{"inputTokens":1,"outputTokens":2}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === '/sessions/session-1/reply') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
throw new Error(`unexpected api path: ${pathname}`);
},
wechatFetch: async (url) => {
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = () => 'req-provider';
try {
const result = await service.handleInboundMessage(inboundXml({ content: '帮我推荐一个跑步路线' }), {
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
});
assert.equal(result.status, 200);
await result.task;
} finally {
crypto.randomUUID = originalRandomUuid;
}
assert.deepEqual(appliedSessions, ['session-1']);
});
test('wechat mp service rotates an idle dedicated session before replying', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const clearedRoutes = [];
const upsertedSessions = [];
const sessionApiCalls = [];
let routeCleared = false;
const service = createBoundWechatService({
token,
config: {
sessionIdleRotateMs: 1000,
},
startAgentSession: async () => ({ id: 'session-2' }),
sessionApiFetch: async (sessionId, pathname, init = {}) => {
sessionApiCalls.push([sessionId, pathname, init.body ?? null]);
assert.equal(sessionId, 'session-2');
if (pathname === '/sessions/session-2/events') {
return new Response(
[
'data: {"type":"Message","request_id":"req-rotate","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"收到,新的会话已接上。"}]}}\n\n',
'data: {"type":"Finish","request_id":"req-rotate","token_state":{"inputTokens":1,"outputTokens":2}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === '/sessions/session-2/reply') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
throw new Error(`unexpected api path: ${pathname}`);
},
wechatFetch: async (url) => {
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
userAuth: {
async getWechatAgentRoute() {
if (routeCleared) return null;
return {
agentSessionId: 'session-1',
status: 'active',
updatedAt: Date.now() - 60_000,
};
},
async clearWechatAgentRoute(appId, openid) {
routeCleared = true;
clearedRoutes.push([appId, openid]);
},
async upsertWechatAgentRoute({ agentSessionId }) {
upsertedSessions.push(agentSessionId);
},
async finishWechatMpMessage() {},
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = () => 'req-rotate';
try {
const result = await service.handleInboundMessage(inboundXml({ content: '继续' }), {
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
});
assert.equal(result.status, 200);
await result.task;
} finally {
crypto.randomUUID = originalRandomUuid;
}
assert.deepEqual(clearedRoutes, [['wx123', 'openid-1']]);
assert.deepEqual(upsertedSessions, ['session-2']);
assert.ok(sessionApiCalls.some(([sessionId, pathname]) => sessionId === 'session-2' && pathname === '/sessions/session-2/reply'));
});
test('wechat mp service rotates a dedicated session after the message-count threshold', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
let routeCleared = false;
const countedSessions = [];
const service = createBoundWechatService({
token,
config: {
sessionIdleRotateMs: 0,
sessionMessageRotateCount: 2,
},
startAgentSession: async () => ({ id: 'session-2' }),
sessionApiFetch: async (sessionId, pathname) => {
assert.equal(sessionId, 'session-2');
if (pathname === '/sessions/session-2/events') {
return new Response(
[
'data: {"type":"Message","request_id":"req-count-rotate","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"已换新会话。"}]}}\n\n',
'data: {"type":"Finish","request_id":"req-count-rotate","token_state":{"inputTokens":1,"outputTokens":2}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === '/sessions/session-2/reply') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
throw new Error(`unexpected api path: ${pathname}`);
},
wechatFetch: async (url) => {
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
userAuth: {
async getWechatAgentRoute() {
if (routeCleared) return null;
return {
agentSessionId: 'session-1',
status: 'active',
updatedAt: Date.now(),
};
},
async countWechatAgentSessionMessages({ agentSessionId }) {
countedSessions.push(agentSessionId);
return 2;
},
async clearWechatAgentRoute() {
routeCleared = true;
},
async upsertWechatAgentRoute() {},
async finishWechatMpMessage() {},
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = () => 'req-count-rotate';
try {
const result = await service.handleInboundMessage(inboundXml({ content: '继续' }), {
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
});
assert.equal(result.status, 200);
await result.task;
} finally {
crypto.randomUUID = originalRandomUuid;
}
assert.deepEqual(countedSessions, ['session-1']);
assert.equal(routeCleared, true);
});
test('wechat mp service rotates a dedicated session when snapshot message count exceeds threshold', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
let routeCleared = false;
const service = createBoundWechatService({
token,
config: {
sessionIdleRotateMs: 0,
sessionMessageRotateCount: 200,
},
startAgentSession: async () => ({ id: 'session-2' }),
sessionApiFetch: async (sessionId, pathname) => {
assert.equal(sessionId, 'session-2');
if (pathname === '/sessions/session-2/events') {
return new Response(
[
'data: {"type":"Message","request_id":"req-snapshot-rotate","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"已换新会话。"}]}}\n\n',
'data: {"type":"Finish","request_id":"req-snapshot-rotate","token_state":{"inputTokens":1,"outputTokens":2}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === '/sessions/session-2/reply') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
throw new Error(`unexpected api path: ${pathname}`);
},
wechatFetch: async (url) => {
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
userAuth: {
async getWechatAgentRoute() {
if (routeCleared) return null;
return {
agentSessionId: 'session-1',
status: 'active',
updatedAt: Date.now(),
};
},
async countWechatAgentSessionMessages() {
return 3;
},
async getWechatAgentSessionSnapshotMessageCount() {
return 616;
},
async clearWechatAgentRoute() {
routeCleared = true;
},
async upsertWechatAgentRoute() {},
async finishWechatMpMessage() {},
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = () => 'req-snapshot-rotate';
try {
const result = await service.handleInboundMessage(inboundXml({ content: '继续' }), {
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
});
assert.equal(result.status, 200);
await result.task;
} finally {
crypto.randomUUID = originalRandomUuid;
}
assert.equal(routeCleared, true);
});
test('wechat mp service skips repeated user-context bootstrap for the same session', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const bootstrapBodies = [];
const service = createBoundWechatService({
token,
sessionApiFetch: async (sessionId, pathname, init = {}) => {
assert.equal(sessionId, 'session-1');
if (pathname === '/sessions/session-1/events') {
return new Response(
[
'data: {"type":"Message","request_id":"req-bootstrap","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"收到。"}]}}\n\n',
'data: {"type":"Finish","request_id":"req-bootstrap","token_state":{"inputTokens":1,"outputTokens":2}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === '/sessions/session-1/reply') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_remember') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_bootstrap') {
bootstrapBodies.push(JSON.parse(String(init.body ?? '{}')));
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
throw new Error(`unexpected api path: ${pathname}`);
},
wechatFetch: async (url) => {
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = () => 'req-bootstrap';
try {
for (const content of ['第一条', '第二条']) {
const result = await service.handleInboundMessage(inboundXml({ content }), {
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
});
assert.equal(result.status, 200);
await result.task;
}
} finally {
crypto.randomUUID = originalRandomUuid;
}
assert.deepEqual(bootstrapBodies, [{ sessionId: 'session-1', force: false }]);
});
test('wechat mp service injects static page publish instructions for html generation requests', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const service = createBoundWechatService({
token,
userAuth: {
async findWechatUserByOpenid() {
return { userId: 'user-1', status: 'active', nickname: '毕升' };
},
async recordWechatMpMessage() {
return { inserted: true };
},
async insertWechatMpMessageDetail() {},
async finishWechatMpMessage() {},
async resolveWorkingDir() {
return '/tmp/user-1';
},
async getAgentSessionPolicy() {
return { enableContextMemory: false, extensionOverrides: [], unrestricted: true };
},
async getUserPublishLayout() {
return { displayName: 'John', username: 'john', slug: 'john', constraints: null };
},
async registerAgentSession() {},
async upsertWechatAgentRoute() {},
async billSessionUsage() {},
},
sessionApiFetch: async (sessionId, pathname, init = {}) => {
assert.equal(sessionId, 'session-1');
if (pathname === '/sessions/session-1/events') {
return new Response(
[
'data: {"type":"Message","request_id":"req-page","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"收到,我去生成页面。"}]}}\n\n',
'data: {"type":"Finish","request_id":"req-page","token_state":{"inputTokens":1,"outputTokens":2}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === '/sessions/session-1/reply') {
const body = JSON.parse(init.body);
assert.match(body.user_message.content[0].text, /static-page-publish/);
assert.match(body.user_message.content[0].text, /必须先调用 `load_skill`/);
assert.match(body.user_message.content[0].text, /写入 `public\/\*\.html`/);
assert.match(body.user_message.content[0].text, /唯一正确链接/);
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
throw new Error(`unexpected api path: ${pathname}`);
},
wechatFetch: async (url) => {
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = () => 'req-page';
try {
const result = await service.handleInboundMessage(inboundXml({ content: '请生成一个 html 页面,文件名 public/hello-world.html' }), {
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
});
assert.equal(result.status, 200);
await result.task;
} finally {
crypto.randomUUID = originalRandomUuid;
}
});
test('wechat mp service injects schedule skill instructions for reminder-like requests', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const service = createBoundWechatService({
token,
userAuth: {
async findWechatUserByOpenid() {
return { userId: 'user-1', status: 'active', nickname: '毕升' };
},
async recordWechatMpMessage() {
return { inserted: true };
},
async insertWechatMpMessageDetail() {},
async finishWechatMpMessage() {},
async resolveWorkingDir() {
return '/tmp/user-1';
},
async getAgentSessionPolicy() {
return { enableContextMemory: false, extensionOverrides: [], unrestricted: true };
},
async getUserPublishLayout() {
return { displayName: 'John', username: 'john', slug: 'john', constraints: null };
},
async registerAgentSession() {},
async upsertWechatAgentRoute() {},
async billSessionUsage() {},
},
sessionApiFetch: async (sessionId, pathname, init = {}) => {
assert.equal(sessionId, 'session-1');
if (pathname === '/sessions/session-1/events') {
return new Response(
[
'data: {"type":"Message","request_id":"req-2","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"收到,我先帮你处理。"}]}}\n\n',
'data: {"type":"Finish","request_id":"req-2","token_state":{"inputTokens":1,"outputTokens":2}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === '/sessions/session-1/reply') {
const body = JSON.parse(init.body);
assert.match(body.user_message.content[0].text, /加载 `schedule-assistant` skill/);
assert.match(body.user_message.content[0].text, /schedule_create_item/);
assert.match(body.user_message.content[0].text, /sourceMessageId: 10001/);
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
throw new Error(`unexpected api path: ${pathname}`);
},
wechatFetch: async (url) => {
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = () => 'req-2';
try {
const result = await service.handleInboundMessage(
inboundXml({ content: '明天早上六点去跑步,五点半提醒我' }),
{
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
},
);
assert.equal(result.status, 200);
await result.task;
} finally {
crypto.randomUUID = originalRandomUuid;
}
});
test('wechat mp service reconciles existing dedicated session before reply', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const calls = [];
let extensionsReadCount = 0;
const workingDir = '/Users/john/Project/Memind/MindSpace/user-1';
const extensionConfig = {
type: 'stdio',
name: 'sandbox-fs',
cmd: '/usr/local/bin/node',
args: ['/Users/john/Project/Memind/mindspace-sandbox-mcp.mjs', workingDir],
envs: { SANDBOX_ROOT: workingDir },
available_tools: ['list_dir'],
};
const service = createWechatMpService({
config: {
enabled: true,
appId: 'wx123',
appSecret: 'secret',
token,
publicBaseUrl: 'https://example.com',
bindPath: '/auth/wechat/authorize?intent=login',
ackText: 'ack',
unsupportedText: 'unsupported',
unboundTextPrefix: '请先绑定',
},
userAuth: {
async findWechatUserByOpenid() {
return { userId: 'user-1', status: 'active', nickname: '毕升' };
},
async getWechatAgentRoute() {
return { agentSessionId: 'session-1' };
},
async clearWechatAgentRoute() {},
async canUseChat() {
return { ok: true };
},
async resolveWorkingDir() {
return workingDir;
},
async getAgentSessionPolicy() {
return {
enableContextMemory: false,
extensionOverrides: [extensionConfig],
unrestricted: false,
gooseMode: 'auto',
};
},
async getUserPublishLayout() {
return { displayName: 'John', username: 'john', slug: 'john', constraints: 'base' };
},
async registerAgentSession() {},
async upsertWechatAgentRoute() {},
async billSessionUsage() {},
},
apiFetch: async (pathname, init = {}) => {
calls.push(['fallback', pathname, init.method ?? 'GET']);
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
},
sessionApiFetch: async (sessionId, pathname, init = {}) => {
assert.equal(sessionId, 'session-1');
calls.push([pathname, init.method ?? 'GET']);
if (pathname === '/sessions/session-1') {
return new Response(JSON.stringify({ working_dir: '/root/tkmind_go/ui/h5/MindSpace/user-1' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (pathname === '/agent/update_working_dir') {
const body = JSON.parse(init.body);
assert.equal(body.session_id, 'session-1');
assert.equal(body.working_dir, workingDir);
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/update_session') {
const body = JSON.parse(init.body);
assert.equal(body.session_id, 'session-1');
assert.equal(body.goose_mode, 'auto');
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/sessions/session-1/extensions') {
extensionsReadCount += 1;
const extensions =
extensionsReadCount > 1
? [{ name: 'sandbox-fs', available_tools: ['list_dir'] }]
: [];
return new Response(JSON.stringify({ extensions }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (pathname === '/agent/add_extension') {
const body = JSON.parse(init.body);
assert.equal(body.session_id, 'session-1');
assert.equal(body.config.name, 'sandbox-fs');
assert.equal(body.config.args[1], workingDir);
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/restart') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_remember') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/sessions/session-1/events') {
return new Response(
[
'data: {"type":"Message","request_id":"req-1","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"收到"}]}}\n\n',
'data: {"type":"Finish","request_id":"req-1","token_state":{"inputTokens":1,"outputTokens":1}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === '/sessions/session-1/reply') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
throw new Error(`unexpected api path: ${pathname}`);
},
wechatFetch: async (url, init = {}) => {
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = () => 'req-1';
try {
const result = await service.handleInboundMessage(inboundXml({ content: '继续' }), {
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
});
assert.equal(result.status, 200);
await result.task;
assert.equal(calls.some(([pathname]) => pathname === '/agent/update_working_dir'), true);
assert.equal(calls.some(([pathname]) => pathname === '/agent/add_extension'), true);
assert.equal(calls.some(([pathname]) => pathname === '/sessions/session-1/reply'), true);
} finally {
crypto.randomUUID = originalRandomUuid;
}
});
test('wechat mp service recreates poisoned dedicated session after bare completion on html generation', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const wechatCalls = [];
const workspaceRoot = fs.mkdtempSync('/tmp/wechat-mp-poisoned-session-');
const htmlPath = `${workspaceRoot}/hello.html`;
let routeCleared = false;
let started = false;
const service = createWechatMpService({
config: {
enabled: true,
appId: 'wx123',
appSecret: 'secret',
token,
publicBaseUrl: 'https://m.tkmind.cn',
bindPath: '/auth/wechat/authorize?intent=login',
ackText: 'ack',
unsupportedText: 'unsupported',
unboundTextPrefix: '请先绑定',
progressDelayMs: 0,
},
userAuth: {
async findWechatUserByOpenid() {
return { userId: 'user-1', status: 'active', nickname: '毕升' };
},
async getWechatAgentRoute() {
return routeCleared ? null : { agentSessionId: 'session-1' };
},
async clearWechatAgentRoute() {
routeCleared = true;
},
async canUseChat() {
return { ok: true };
},
async resolveWorkingDir() {
return workspaceRoot;
},
async getAgentSessionPolicy() {
return {
enableContextMemory: false,
extensionOverrides: [
{
type: 'platform',
name: 'developer',
available_tools: ['write'],
},
],
unrestricted: false,
};
},
async getUserPublishLayout() {
return { publishDir: workspaceRoot, displayName: 'John', username: 'john', slug: 'john', constraints: null };
},
async registerAgentSession() {},
async upsertWechatAgentRoute({ appId, openid, userId, agentSessionId }) {
assert.equal(appId, 'wx123');
assert.equal(openid, 'openid-1');
assert.equal(userId, 'user-1');
assert.equal(agentSessionId, 'session-2');
},
async billSessionUsage() {},
async recordWechatMpMessage() {
return { inserted: true };
},
async finishWechatMpMessage() {},
async insertWechatMpMessageDetail() {},
},
apiFetch: async (pathname, init = {}) => {
if (pathname === '/agent/start') {
started = true;
const body = JSON.parse(init.body);
assert.equal(body.working_dir, workspaceRoot);
return new Response(JSON.stringify({ id: 'session-2' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected api path: ${pathname}`);
},
sessionApiFetch: async (sessionId, pathname, init = {}) => {
if (pathname === `/sessions/${sessionId}/extensions`) {
return new Response(JSON.stringify({ extensions: [{ name: 'developer', available_tools: ['write'] }] }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (pathname === '/agent/update_working_dir' || pathname === '/agent/update_session' || pathname === '/agent/restart') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/add_extension') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === `/sessions/${sessionId}`) {
return new Response(JSON.stringify({ working_dir: workspaceRoot }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (pathname === `/sessions/${sessionId}/reply`) {
if (sessionId === 'session-2') {
fs.writeFileSync(htmlPath, previewReadyPageHtml({ title: 'Hello', subtitle: 'Hello 页面' }));
}
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === `/sessions/${sessionId}/events`) {
if (sessionId === 'session-1') {
return new Response(
[
'data: {"type":"Message","request_id":"req-poisoned","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"已完成"}]}}\n\n',
'data: {"type":"Finish","request_id":"req-poisoned","token_state":{"inputTokens":1,"outputTokens":1}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
return new Response(
[
'data: {"type":"Message","request_id":"req-poisoned-retry","message":{"id":"assistant-skill","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"toolRequest","toolCall":{"value":{"name":"load_skill","arguments":{"name":"static-page-publish"}}}}]}}\n\n',
`data: {"type":"Message","request_id":"req-poisoned-retry","message":{"id":"assistant-tool","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"toolRequest","toolCall":{"value":{"name":"developer","arguments":{"action":"write","path":"${htmlPath.replace(/\\/g, '\\\\')}","content":"${jsonEscapedPreviewReadyPageHtml({ title: 'Hello', subtitle: 'Hello 页面' })}"}}}}]}}\n\n`,
'data: {"type":"Message","request_id":"req-poisoned-retry","message":{"id":"assistant-2","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"已完成"}]}}\n\n',
'data: {"type":"Finish","request_id":"req-poisoned-retry","token_state":{"inputTokens":2,"outputTokens":2}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
throw new Error(`unexpected session api path: ${sessionId} ${pathname}`);
},
wechatFetch: async (url, init = {}) => {
wechatCalls.push([url, init.method ?? 'GET', init.body ?? null]);
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
linkExists: async (urlText) => {
if (!String(urlText).includes('/summer-breeze-journal.html')) return false;
return fs.existsSync(htmlPath);
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = (() => {
const ids = ['req-poisoned', 'req-poisoned-retry'];
return () => ids.shift() ?? 'req-poisoned-retry';
})();
try {
const result = await service.handleInboundMessage(
inboundXml({ content: '帮我生成一个简单的 hello 页面吧' }),
{
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
},
);
assert.equal(result.status, 200);
await result.task;
} finally {
crypto.randomUUID = originalRandomUuid;
}
assert.equal(routeCleared, true);
assert.equal(started, true);
const sendCall = wechatCalls.find(([url]) => String(url).includes('/cgi-bin/message/custom/send'));
const payload = JSON.parse(sendCall[2]);
assert.match(payload.text.content, /已完成/);
assert.match(payload.text.content, /页面生成未完成|hello\.html/);
});
test('wechat mp service forwards html link when write_file created a real page without static-page-publish skill', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const wechatCalls = [];
const workspaceRoot = fs.mkdtempSync('/tmp/wechat-mp-missing-skill-');
const htmlPath = path.join(workspaceRoot, 'public', 'hello-world.html');
let routeCleared = false;
let started = false;
const service = createWechatMpService({
config: {
enabled: true,
appId: 'wx123',
appSecret: 'secret',
token,
publicBaseUrl: 'https://m.tkmind.cn',
bindPath: '/auth/wechat/authorize?intent=login',
ackText: 'ack',
unsupportedText: 'unsupported',
unboundTextPrefix: '请先绑定',
progressDelayMs: 0,
},
userAuth: {
async findWechatUserByOpenid() {
return { userId: 'user-1', status: 'active', nickname: '毕升' };
},
async getWechatAgentRoute() {
return routeCleared ? null : { agentSessionId: 'session-1' };
},
async clearWechatAgentRoute() {
routeCleared = true;
},
async canUseChat() {
return { ok: true };
},
async resolveWorkingDir() {
return workspaceRoot;
},
async getAgentSessionPolicy() {
return {
enableContextMemory: false,
extensionOverrides: [
{
type: 'platform',
name: 'developer',
available_tools: ['write'],
},
],
unrestricted: false,
};
},
async getUserPublishLayout() {
return { publishDir: workspaceRoot, displayName: 'John', username: 'john', slug: 'john', constraints: null };
},
async registerAgentSession() {},
async upsertWechatAgentRoute({ agentSessionId }) {
assert.equal(agentSessionId, 'session-2');
},
async billSessionUsage() {},
async recordWechatMpMessage() {
return { inserted: true };
},
async finishWechatMpMessage() {},
async insertWechatMpMessageDetail() {},
},
apiFetch: async (pathname, init = {}) => {
if (pathname === '/agent/start') {
started = true;
const body = JSON.parse(init.body);
assert.equal(body.working_dir, workspaceRoot);
return new Response(JSON.stringify({ id: 'session-2' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected api path: ${pathname}`);
},
sessionApiFetch: async (sessionId, pathname) => {
if (pathname === `/sessions/${sessionId}/extensions`) {
return new Response(JSON.stringify({ extensions: [{ name: 'developer', available_tools: ['write'] }] }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (pathname === '/agent/update_working_dir' || pathname === '/agent/update_session' || pathname === '/agent/restart') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/add_extension') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === `/sessions/${sessionId}`) {
return new Response(JSON.stringify({ working_dir: workspaceRoot }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (pathname === `/sessions/${sessionId}/reply`) {
if (sessionId === 'session-2') {
fs.mkdirSync(path.dirname(htmlPath), { recursive: true });
fs.writeFileSync(htmlPath, previewReadyPageHtml({ title: 'Hello', subtitle: 'Hello 页面' }));
}
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === `/sessions/${sessionId}/events`) {
if (sessionId === 'session-1') {
return new Response(
[
`data: {"type":"Message","request_id":"req-skill-miss","message":{"id":"assistant-tool","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"toolRequest","toolCall":{"value":{"name":"sandbox-fs__write_file","arguments":{"path":"public/hello-world.html","content":"${jsonEscapedPreviewReadyPageHtml({ title: 'Hello World', subtitle: 'Hello World 页面' })}"}}}}]}}\n\n`,
'data: {"type":"Message","request_id":"req-skill-miss","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"页面已生成:https://m.tkmind.cn/MindSpace/john/public/hello-world.html"}]}}\n\n',
'data: {"type":"Finish","request_id":"req-skill-miss","token_state":{"inputTokens":1,"outputTokens":1}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
return new Response(
[
'data: {"type":"Message","request_id":"req-skill-retry","message":{"id":"assistant-skill","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"toolRequest","toolCall":{"value":{"name":"load_skill","arguments":{"name":"static-page-publish"}}}}]}}\n\n',
`data: {"type":"Message","request_id":"req-skill-retry","message":{"id":"assistant-tool","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"toolRequest","toolCall":{"value":{"name":"sandbox-fs__write_file","arguments":{"path":"public/hello-world.html","content":"${jsonEscapedPreviewReadyPageHtml({ title: 'Hello World', subtitle: 'Hello World 页面' })}"}}}}]}}\n\n`,
'data: {"type":"Message","request_id":"req-skill-retry","message":{"id":"assistant-2","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"[Hello World](https://m.tkmind.cn/MindSpace/user-1/public/hello-world.html)"}]}}\n\n',
'data: {"type":"Finish","request_id":"req-skill-retry","token_state":{"inputTokens":2,"outputTokens":2}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
throw new Error(`unexpected session api path: ${sessionId} ${pathname}`);
},
wechatFetch: async (url, init = {}) => {
wechatCalls.push([url, init.method ?? 'GET', init.body ?? null]);
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = (() => {
const ids = ['req-skill-miss', 'req-skill-retry'];
return () => ids.shift() ?? 'req-skill-retry';
})();
try {
const result = await service.handleInboundMessage(inboundXml({ content: '请生成一个页面 public/hello-world.html' }), {
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
});
assert.equal(result.status, 200);
await result.task;
} finally {
crypto.randomUUID = originalRandomUuid;
}
assert.equal(routeCleared, false);
assert.equal(started, false);
const sendCall = wechatCalls.find(([url]) => String(url).includes('/cgi-bin/message/custom/send'));
const payload = JSON.parse(sendCall[2]);
assert.match(payload.text.content, /hello-world\.html/);
assert.match(payload.text.content, /https:\/\/m\.tkmind\.cn\/MindSpace\/.+\/public\/hello-world\.html/);
});
test('wechat mp service trusts an existing requested public html file when the assistant only returns a title', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const wechatCalls = [];
const workspaceRoot = fs.mkdtempSync('/tmp/wechat-mp-existing-requested-');
const htmlPath = path.join(workspaceRoot, 'public', 'thailand-guide.html');
fs.mkdirSync(path.dirname(htmlPath), { recursive: true });
fs.writeFileSync(htmlPath, previewReadyPageHtml({ title: 'Thailand', subtitle: '泰国旅行攻略' }));
const service = createWechatMpService({
config: {
enabled: true,
appId: 'wx123',
appSecret: 'secret',
token,
publicBaseUrl: 'https://m.tkmind.cn',
bindPath: '/auth/wechat/authorize?intent=login',
ackText: 'ack',
unsupportedText: 'unsupported',
unboundTextPrefix: '请先绑定',
progressDelayMs: 0,
},
userAuth: {
async findWechatUserByOpenid() {
return { userId: 'user-1', status: 'active', nickname: '毕升' };
},
async getWechatAgentRoute() {
return { agentSessionId: 'session-1' };
},
async clearWechatAgentRoute() {},
async canUseChat() {
return { ok: true };
},
async resolveWorkingDir() {
return workspaceRoot;
},
async getAgentSessionPolicy() {
return { enableContextMemory: false, extensionOverrides: [], unrestricted: true };
},
async getUserPublishLayout() {
return { publishDir: workspaceRoot, displayName: 'John', username: 'john', slug: 'john', constraints: null };
},
async billSessionUsage() {},
async recordWechatMpMessage() {
return { inserted: true };
},
async finishWechatMpMessage() {},
async insertWechatMpMessageDetail() {},
},
sessionApiFetch: async (sessionId, pathname) => {
if (pathname === `/sessions/${sessionId}/events`) {
return new Response(
[
'data: {"type":"Message","request_id":"req-existing","message":{"id":"assistant-skill","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"toolRequest","toolCall":{"value":{"name":"load_skill","arguments":{"name":"static-page-publish"}}}}]}}\n\n',
'data: {"type":"Message","request_id":"req-existing","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"页面已生成!点击下方链接查看:\\n\\n🌴 泰国旅行攻略 · 说走就走"}]}}\n\n',
'data: {"type":"Finish","request_id":"req-existing","token_state":{"inputTokens":1,"outputTokens":1}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === `/sessions/${sessionId}/reply`) {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
throw new Error(`unexpected session api path: ${sessionId} ${pathname}`);
},
wechatFetch: async (url, init = {}) => {
wechatCalls.push([url, init.method ?? 'GET', init.body ?? null]);
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = () => 'req-existing';
try {
const result = await service.handleInboundMessage(inboundXml({ content: '请生成 thailand-guide.html 页面' }), {
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
});
assert.equal(result.status, 200);
await result.task;
} finally {
crypto.randomUUID = originalRandomUuid;
}
const sendCall = wechatCalls.find(([url]) => String(url).includes('/cgi-bin/message/custom/send'));
const payload = JSON.parse(sendCall[2]);
assert.match(payload.text.content, /https:\/\/m\.tkmind\.cn\/MindSpace\/.+\/public\/thailand-guide\.html/);
assert.doesNotMatch(payload.text.content, /页面生成未完成/);
});
test('wechat mp service attaches the newly generated page link even when no html tool call is streamed', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const wechatCalls = [];
const workspaceRoot = fs.mkdtempSync('/tmp/wechat-mp-recent-public-');
const htmlPath = path.join(workspaceRoot, 'public', 'summer-breeze-journal.html');
const service = createWechatMpService({
config: {
enabled: true,
appId: 'wx123',
appSecret: 'secret',
token,
publicBaseUrl: 'https://m.tkmind.cn',
bindPath: '/auth/wechat/authorize?intent=login',
ackText: 'ack',
unsupportedText: 'unsupported',
unboundTextPrefix: '请先绑定',
progressDelayMs: 0,
},
userAuth: {
async findWechatUserByOpenid() {
return { userId: 'user-1', status: 'active', nickname: '毕升' };
},
async getWechatAgentRoute() {
return { agentSessionId: 'session-1' };
},
async clearWechatAgentRoute() {},
async canUseChat() {
return { ok: true };
},
async resolveWorkingDir() {
return workspaceRoot;
},
async getAgentSessionPolicy() {
return { enableContextMemory: false, extensionOverrides: [], unrestricted: true };
},
async getUserPublishLayout() {
return { publishDir: workspaceRoot, displayName: 'John', username: 'john', slug: 'john', constraints: null };
},
async billSessionUsage() {},
async recordWechatMpMessage() {
return { inserted: true };
},
async finishWechatMpMessage() {},
async insertWechatMpMessageDetail() {},
},
sessionApiFetch: async (sessionId, pathname) => {
if (pathname === `/sessions/${sessionId}/events`) {
return new Response(
[
'data: {"type":"Message","request_id":"req-recent","message":{"id":"assistant-skill","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"toolRequest","toolCall":{"value":{"name":"load_skill","arguments":{"name":"static-page-publish"}}}}]}}\n\n',
'data: {"type":"Message","request_id":"req-recent","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"页面已生成!点击下方链接查看:\\n\\n🌴 夏日主题页面"}]}}\n\n',
'data: {"type":"Finish","request_id":"req-recent","token_state":{"inputTokens":1,"outputTokens":1}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === `/sessions/${sessionId}/reply`) {
fs.mkdirSync(path.dirname(htmlPath), { recursive: true });
fs.writeFileSync(htmlPath, previewReadyPageHtml({ title: 'Summer', subtitle: '夏日主题页面' }));
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
throw new Error(`unexpected session api path: ${sessionId} ${pathname}`);
},
wechatFetch: async (url, init = {}) => {
wechatCalls.push([url, init.method ?? 'GET', init.body ?? null]);
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = () => 'req-recent';
try {
const result = await service.handleInboundMessage(inboundXml({ content: '帮我生成一个夏日页面' }), {
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
});
assert.equal(result.status, 200);
await result.task;
} finally {
crypto.randomUUID = originalRandomUuid;
}
const sendCall = wechatCalls.find(([url]) => String(url).includes('/cgi-bin/message/custom/send'));
const payload = JSON.parse(sendCall[2]);
assert.match(payload.text.content, /https:\/\/m\.tkmind\.cn\/MindSpace\/.+\/public\/summer-breeze-journal\.html/);
assert.doesNotMatch(payload.text.content, /页面生成未完成|旧指令污染|稍后重试/);
});
test('wechat mp service does not trust unrelated recent html artifacts when reply links a missing file', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const wechatCalls = [];
const workspaceRoot = fs.mkdtempSync('/tmp/wechat-mp-recent-mismatch-');
const unrelatedHtmlPath = path.join(workspaceRoot, 'public', 'summer-breeze-journal.html');
const service = createWechatMpService({
config: {
enabled: true,
appId: 'wx123',
appSecret: 'secret',
token,
publicBaseUrl: 'https://m.tkmind.cn',
bindPath: '/auth/wechat/authorize?intent=login',
ackText: 'ack',
unsupportedText: 'unsupported',
unboundTextPrefix: '请先绑定',
progressDelayMs: 0,
},
userAuth: {
async findWechatUserByOpenid() {
return { userId: 'user-1', status: 'active', nickname: '毕升' };
},
async getWechatAgentRoute() {
return { agentSessionId: 'session-1' };
},
async clearWechatAgentRoute() {},
async canUseChat() {
return { ok: true };
},
async resolveWorkingDir() {
return workspaceRoot;
},
async getAgentSessionPolicy() {
return { enableContextMemory: false, extensionOverrides: [], unrestricted: true };
},
async getUserPublishLayout() {
return { publishDir: workspaceRoot, displayName: 'John', username: 'john', slug: 'john', constraints: null };
},
async billSessionUsage() {},
async recordWechatMpMessage() {
return { inserted: true };
},
async finishWechatMpMessage() {},
async insertWechatMpMessageDetail() {},
},
sessionApiFetch: async (sessionId, pathname) => {
if (pathname === `/sessions/${sessionId}/events`) {
return new Response(
[
'data: {"type":"Message","request_id":"req-recent-mismatch","message":{"id":"assistant-skill","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"toolRequest","toolCall":{"value":{"name":"load_skill","arguments":{"name":"static-page-publish"}}}}]}}\n\n',
'data: {"type":"Message","request_id":"req-recent-mismatch","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"[Hello](https://m.tkmind.cn/MindSpace/user-1/public/hello.html)"}]}}\n\n',
'data: {"type":"Finish","request_id":"req-recent-mismatch","token_state":{"inputTokens":1,"outputTokens":1}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === `/sessions/${sessionId}/reply`) {
fs.mkdirSync(path.dirname(unrelatedHtmlPath), { recursive: true });
fs.writeFileSync(unrelatedHtmlPath, '<!doctype html><title>Summer</title>');
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
throw new Error(`unexpected session api path: ${sessionId} ${pathname}`);
},
wechatFetch: async (url, init = {}) => {
wechatCalls.push([url, init.method ?? 'GET', init.body ?? null]);
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = () => 'req-recent-mismatch';
try {
const result = await service.handleInboundMessage(inboundXml({ content: '帮我生成一个简单的 hello 页面吧' }), {
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
});
assert.equal(result.status, 200);
await result.task;
} finally {
crypto.randomUUID = originalRandomUuid;
}
const sendCall = wechatCalls.find(([url]) => String(url).includes('/cgi-bin/message/custom/send'));
const payload = JSON.parse(sendCall[2]);
assert.doesNotMatch(payload.text.content, /https:\/\/m\.tkmind\.cn\/MindSpace\/.+\/public\/hello\.html/);
assert.doesNotMatch(payload.text.content, /summer-breeze-journal\.html/);
assert.match(payload.text.content, /服务号页面技能|没有按 H5 里的页面技能|页面生成未完成/);
});
test('wechat mp service forwards H5 agent text when page generation produced no file or fake link', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const wechatCalls = [];
const workspaceRoot = fs.mkdtempSync('/tmp/wechat-mp-fallback-page-');
const service = createWechatMpService({
config: {
enabled: true,
appId: 'wx123',
appSecret: 'secret',
token,
publicBaseUrl: 'https://m.tkmind.cn',
bindPath: '/auth/wechat/authorize?intent=login',
ackText: 'ack',
unsupportedText: 'unsupported',
unboundTextPrefix: '请先绑定',
progressDelayMs: 0,
},
userAuth: {
async findWechatUserByOpenid() {
return { userId: 'user-1', status: 'active', nickname: '毕升' };
},
async getWechatAgentRoute() {
return { agentSessionId: 'session-1' };
},
async clearWechatAgentRoute() {},
async canUseChat() {
return { ok: true };
},
async resolveWorkingDir() {
return workspaceRoot;
},
async getAgentSessionPolicy() {
return { enableContextMemory: false, extensionOverrides: [], unrestricted: true };
},
async getUserPublishLayout() {
return { publishDir: workspaceRoot, displayName: 'John', username: 'john', slug: 'john', constraints: null };
},
async billSessionUsage() {},
async recordWechatMpMessage() {
return { inserted: true };
},
async finishWechatMpMessage() {},
async insertWechatMpMessageDetail() {},
},
sessionApiFetch: async (sessionId, pathname) => {
if (pathname === `/sessions/${sessionId}/events`) {
return new Response(
[
'data: {"type":"Message","request_id":"req-fallback","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"🌴 泰国简易攻略"}]}}\n\n',
'data: {"type":"Finish","request_id":"req-fallback","token_state":{"inputTokens":1,"outputTokens":1}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === `/sessions/${sessionId}/reply`) {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
throw new Error(`unexpected session api path: ${sessionId} ${pathname}`);
},
wechatFetch: async (url, init = {}) => {
wechatCalls.push([url, init.method ?? 'GET', init.body ?? null]);
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = () => 'req-fallback';
try {
const result = await service.handleInboundMessage(inboundXml({ content: '帮我做一个泰国攻略页面,稍微简单一点的' }), {
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
});
assert.equal(result.status, 200);
await result.task;
} finally {
crypto.randomUUID = originalRandomUuid;
}
const htmlPath = path.join(workspaceRoot, 'public', 'thailand-guide.html');
assert.equal(fs.existsSync(htmlPath), false);
const sendCall = wechatCalls.find(([url]) => String(url).includes('/cgi-bin/message/custom/send'));
const payload = JSON.parse(sendCall[2]);
assert.match(payload.text.content, /🌴 泰国简易攻略/);
assert.doesNotMatch(payload.text.content, /没有按 H5 里的页面技能真正生成成功/);
assert.doesNotMatch(payload.text.content, /https:\/\/m\.tkmind\.cn\/MindSpace\/.+\/public\/thailand-guide\.html/);
});
test('isRecoverableWechatAgentSessionError detects poisoned tool_calls history', () => {
assert.equal(
isRecoverableWechatAgentSessionError(
"Request failed: Bad request (400): An assistant message with 'tool_calls' must be followed by tool messages responding to each 'tool_call_id'. (insufficient tool messages following tool_calls message).",
),
true,
);
assert.equal(isRecoverableWechatAgentSessionError('stale_session_poisoned_completion'), true);
assert.equal(
isRecoverableWechatAgentSessionError(
'wechat_page_fresh_thumbnail_required:fresh_image_not_generated',
),
true,
);
assert.equal(isRecoverableWechatAgentSessionError('无权访问该会话'), true);
assert.equal(
isRecoverableWechatAgentSessionError('Session already has an active request. Cancel it first.'),
true,
);
assert.equal(isRecoverableWechatAgentSessionError('network timeout'), false);
});
test('findRecoverableWechatAgentErrorInReply scans all assistant messages', () => {
const toolCallsError =
"Request failed: Bad request (400): An assistant message with 'tool_calls' must be followed by tool messages responding to each 'tool_call_id'. (insufficient tool messages following tool_calls message).";
const reply = {
text: '唐,好的,请稍等片刻。',
messages: [
{
id: 'assistant-1',
role: 'assistant',
content: [{ type: 'text', text: '唐,好的,请稍等片刻。' }],
},
{
id: 'assistant-2',
role: 'assistant',
content: [{ type: 'text', text: `Ran into this error:\n${toolCallsError}` }],
},
],
};
assert.match(findRecoverableWechatAgentErrorInReply(reply), /tool_calls/);
assert.throws(() => assertWechatAgentReplyIsSendable(reply), /tool_calls/);
});
test('sanitizeWechatAgentOutboundText replaces raw api errors with friendly text', () => {
const raw =
"Ran into this error:\nRequest failed: Bad request (400): An assistant message with 'tool_calls' must be followed by tool messages responding to each 'tool_call_id'.";
assert.match(sanitizeWechatAgentOutboundText(raw), /切换到新会话/);
assert.equal(isWechatAgentApiErrorText(raw), true);
assert.match(
sanitizeWechatAgentOutboundText('唐,好的。\n\nRan into this error:\nRequest failed: Bad request (400): tool_calls'),
/唐,好的/,
);
assert.doesNotMatch(
sanitizeWechatAgentOutboundText('唐,好的。\n\nRan into this error:\nRequest failed: Bad request (400): tool_calls'),
/Bad request/,
);
});
test('assertWechatAgentReplyIsSendable rejects image inspection placeholders', () => {
assert.throws(
() => assertWechatAgentReplyIsSendable({ text: 'Let me look at the image first.' }),
/wechat_agent_incomplete_reply/,
);
assert.throws(
() => assertWechatAgentReplyIsSendable({ text: '我先看一下这张图片。' }),
/wechat_agent_incomplete_reply/,
);
});
test('wechat mp service recreates dedicated session when tool_calls error arrives via Finish assistant text', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const wechatCalls = [];
let routeCleared = false;
let started = false;
const workspaceRoot = fs.mkdtempSync('/tmp/wechat-mp-tool-calls-finish-');
const toolCallsError =
"Request failed: Bad request (400): An assistant message with 'tool_calls' must be followed by tool messages responding to each 'tool_call_id'. (insufficient tool messages following tool_calls message).";
const service = createWechatMpService({
config: {
enabled: true,
appId: 'wx123',
appSecret: 'secret',
token,
publicBaseUrl: 'https://m.tkmind.cn',
bindPath: '/auth/wechat/authorize?intent=login',
ackText: 'ack',
unsupportedText: 'unsupported',
unboundTextPrefix: '请先绑定',
progressDelayMs: 0,
},
userAuth: {
async findWechatUserByOpenid() {
return { userId: 'user-1', status: 'active', nickname: '唐' };
},
async getWechatAgentRoute() {
return routeCleared ? null : { agentSessionId: 'session-1' };
},
async clearWechatAgentRoute() {
routeCleared = true;
},
async canUseChat() {
return { ok: true };
},
async resolveWorkingDir() {
return workspaceRoot;
},
async getAgentSessionPolicy() {
return {
enableContextMemory: false,
extensionOverrides: [
{
type: 'platform',
name: 'developer',
available_tools: ['write'],
},
],
unrestricted: false,
};
},
async getUserPublishLayout() {
return { publishDir: workspaceRoot, displayName: '唐', username: 'wx_ul610et8', slug: 'wx_ul610et8', constraints: null };
},
async registerAgentSession() {},
async upsertWechatAgentRoute({ agentSessionId }) {
assert.equal(agentSessionId, 'session-2');
},
async billSessionUsage() {},
async recordWechatMpMessage() {
return { inserted: true };
},
async finishWechatMpMessage() {},
async insertWechatMpMessageDetail() {},
},
apiFetch: async (pathname, init = {}) => {
if (pathname === '/agent/start') {
started = true;
return new Response(JSON.stringify({ id: 'session-2' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected api path: ${pathname}`);
},
sessionApiFetch: async (sessionId, pathname, init = {}) => {
if (pathname === `/sessions/${sessionId}/extensions`) {
return new Response(JSON.stringify({ extensions: [{ name: 'developer', available_tools: ['write'] }] }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (
pathname === '/agent/update_working_dir' ||
pathname === '/agent/update_session' ||
pathname === '/agent/restart' ||
pathname === '/agent/add_extension' ||
pathname === '/agent/harness_remember' ||
pathname === '/agent/harness_bootstrap'
) {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === `/sessions/${sessionId}`) {
return new Response(JSON.stringify({ working_dir: workspaceRoot }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (pathname === `/sessions/${sessionId}/reply`) {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === `/sessions/${sessionId}/events`) {
if (sessionId === 'session-1') {
return new Response(
[
'data: {"type":"Message","request_id":"req-tool-finish","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"唐,好的,请稍等片刻。"}]}}\n\n',
`data: {"type":"Message","request_id":"req-tool-finish","message":{"id":"assistant-2","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"Ran into this error:\\n${toolCallsError.replace(/"/g, '\\"')}"}]}}\n\n`,
'data: {"type":"Finish","request_id":"req-tool-finish","token_state":{"inputTokens":1,"outputTokens":1}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
return new Response(
[
'data: {"type":"Message","request_id":"req-tool-retry","message":{"id":"assistant-3","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"已恢复,可以继续对话。"}]}}\n\n',
'data: {"type":"Finish","request_id":"req-tool-retry","token_state":{"inputTokens":1,"outputTokens":1}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
throw new Error(`unexpected session api path: ${sessionId} ${pathname}`);
},
wechatFetch: async (url, init = {}) => {
wechatCalls.push([url, init.method ?? 'GET', init.body ?? null]);
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = (() => {
const ids = ['req-tool-finish', 'req-tool-retry'];
return () => ids.shift() ?? 'req-tool-retry';
})();
try {
const result = await service.handleInboundMessage(
inboundXml({ content: '帮我生成页面查看吧' }),
{
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
},
);
assert.equal(result.status, 200);
await result.task;
} finally {
crypto.randomUUID = originalRandomUuid;
}
assert.equal(routeCleared, true);
assert.equal(started, true);
const sendCall = wechatCalls.find(([url]) => String(url).includes('/cgi-bin/message/custom/send'));
const payload = JSON.parse(sendCall[2]);
assert.match(payload.text.content, /已恢复,可以继续对话/);
assert.doesNotMatch(payload.text.content, /Bad request/);
assert.doesNotMatch(payload.text.content, /tool_calls/);
});
test('wechat mp service recreates dedicated session after poisoned tool_calls history', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const wechatCalls = [];
let routeCleared = false;
let started = false;
const workspaceRoot = fs.mkdtempSync('/tmp/wechat-mp-tool-calls-poison-');
const service = createWechatMpService({
config: {
enabled: true,
appId: 'wx123',
appSecret: 'secret',
token,
publicBaseUrl: 'https://m.tkmind.cn',
bindPath: '/auth/wechat/authorize?intent=login',
ackText: 'ack',
unsupportedText: 'unsupported',
unboundTextPrefix: '请先绑定',
progressDelayMs: 0,
},
userAuth: {
async findWechatUserByOpenid() {
return { userId: 'user-1', status: 'active', nickname: '唐' };
},
async getWechatAgentRoute() {
return routeCleared ? null : { agentSessionId: 'session-1' };
},
async clearWechatAgentRoute() {
routeCleared = true;
},
async canUseChat() {
return { ok: true };
},
async resolveWorkingDir() {
return workspaceRoot;
},
async getAgentSessionPolicy() {
return {
enableContextMemory: false,
extensionOverrides: [
{
type: 'platform',
name: 'developer',
available_tools: ['write'],
},
],
unrestricted: false,
};
},
async getUserPublishLayout() {
return { publishDir: workspaceRoot, displayName: '唐', username: 'wx_ul610et8', slug: 'wx_ul610et8', constraints: null };
},
async registerAgentSession() {},
async upsertWechatAgentRoute({ agentSessionId }) {
assert.equal(agentSessionId, 'session-2');
},
async billSessionUsage() {},
async recordWechatMpMessage() {
return { inserted: true };
},
async finishWechatMpMessage() {},
async insertWechatMpMessageDetail() {},
},
apiFetch: async (pathname, init = {}) => {
if (pathname === '/agent/start') {
started = true;
return new Response(JSON.stringify({ id: 'session-2' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected api path: ${pathname}`);
},
sessionApiFetch: async (sessionId, pathname, init = {}) => {
if (pathname === `/sessions/${sessionId}/extensions`) {
return new Response(JSON.stringify({ extensions: [{ name: 'developer', available_tools: ['write'] }] }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (
pathname === '/agent/update_working_dir' ||
pathname === '/agent/update_session' ||
pathname === '/agent/restart' ||
pathname === '/agent/add_extension' ||
pathname === '/agent/harness_remember' ||
pathname === '/agent/harness_bootstrap'
) {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === `/sessions/${sessionId}`) {
return new Response(JSON.stringify({ working_dir: workspaceRoot }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (pathname === `/sessions/${sessionId}/reply`) {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === `/sessions/${sessionId}/events`) {
if (sessionId === 'session-1') {
return new Response(
[
'data: {"type":"Error","request_id":"req-tool-poison","error":"Request failed: Bad request (400): An assistant message with \'tool_calls\' must be followed by tool messages responding to each \'tool_call_id\'. (insufficient tool messages following tool_calls message)."}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
return new Response(
[
'data: {"type":"Message","request_id":"req-tool-retry","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"已恢复,可以继续对话。"}]}}\n\n',
'data: {"type":"Finish","request_id":"req-tool-retry","token_state":{"inputTokens":1,"outputTokens":1}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
throw new Error(`unexpected session api path: ${sessionId} ${pathname}`);
},
wechatFetch: async (url, init = {}) => {
wechatCalls.push([url, init.method ?? 'GET', init.body ?? null]);
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = (() => {
const ids = ['req-tool-poison', 'req-tool-retry'];
return () => ids.shift() ?? 'req-tool-retry';
})();
try {
const result = await service.handleInboundMessage(
inboundXml({ content: '帮我查一下今天的待办' }),
{
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
},
);
assert.equal(result.status, 200);
await result.task;
} finally {
crypto.randomUUID = originalRandomUuid;
}
assert.equal(routeCleared, true);
assert.equal(started, true);
const sendCall = wechatCalls.find(([url]) => String(url).includes('/cgi-bin/message/custom/send'));
const payload = JSON.parse(sendCall[2]);
assert.match(payload.text.content, /已恢复,可以继续对话/);
});
test('wechat mp service retries poisoned publish claims before forwarding H5 retry text', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const wechatCalls = [];
let routeCleared = false;
let started = false;
const workspaceRoot = fs.mkdtempSync('/tmp/wechat-mp-publish-claim-');
const service = createWechatMpService({
config: {
enabled: true,
appId: 'wx123',
appSecret: 'secret',
token,
publicBaseUrl: 'https://m.tkmind.cn',
bindPath: '/auth/wechat/authorize?intent=login',
ackText: 'ack',
unsupportedText: 'unsupported',
unboundTextPrefix: '请先绑定',
progressDelayMs: 0,
},
userAuth: {
async findWechatUserByOpenid() {
return { userId: 'user-1', status: 'active', nickname: '毕升' };
},
async getWechatAgentRoute() {
return routeCleared ? null : { agentSessionId: 'session-1' };
},
async clearWechatAgentRoute() {
routeCleared = true;
},
async canUseChat() {
return { ok: true };
},
async resolveWorkingDir() {
return workspaceRoot;
},
async getAgentSessionPolicy() {
return {
enableContextMemory: false,
extensionOverrides: [
{
type: 'platform',
name: 'developer',
available_tools: ['write'],
},
],
unrestricted: false,
};
},
async getUserPublishLayout() {
return { publishDir: workspaceRoot, displayName: 'John', username: 'john', slug: 'john', constraints: null };
},
async registerAgentSession() {},
async upsertWechatAgentRoute({ agentSessionId }) {
assert.equal(agentSessionId, 'session-2');
},
async billSessionUsage() {},
async recordWechatMpMessage() {
return { inserted: true };
},
async finishWechatMpMessage() {},
async insertWechatMpMessageDetail() {},
},
apiFetch: async (pathname, init = {}) => {
if (pathname === '/agent/start') {
started = true;
const body = JSON.parse(init.body);
assert.equal(body.working_dir, workspaceRoot);
return new Response(JSON.stringify({ id: 'session-2' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected api path: ${pathname}`);
},
sessionApiFetch: async (sessionId, pathname) => {
if (pathname === `/sessions/${sessionId}/extensions`) {
return new Response(JSON.stringify({ extensions: [{ name: 'developer', available_tools: ['write'] }] }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (pathname === '/agent/update_working_dir' || pathname === '/agent/update_session' || pathname === '/agent/restart') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/add_extension') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === `/sessions/${sessionId}`) {
return new Response(JSON.stringify({ working_dir: workspaceRoot }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (pathname === `/sessions/${sessionId}/reply`) {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === `/sessions/${sessionId}/events`) {
if (sessionId === 'session-1') {
return new Response(
[
'data: {"type":"Message","request_id":"req-claim","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"好消息!m.tkmind.cn 其实是可以访问的,页面都已经成功发布了!以下是您的夏日主题页面:\\n\\n🌴 夏日主题页面已发布\\n\\nhttps://m.tkmind.cn/MindSpace/john/public/summer-breeze-journal.html"}]}}\n\n',
'data: {"type":"Finish","request_id":"req-claim","token_state":{"inputTokens":1,"outputTokens":1}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
return new Response(
[
'data: {"type":"Message","request_id":"req-claim-retry","message":{"id":"assistant-2","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"🌴 夏日主题页面"}]}}\n\n',
'data: {"type":"Finish","request_id":"req-claim-retry","token_state":{"inputTokens":1,"outputTokens":1}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
throw new Error(`unexpected session api path: ${sessionId} ${pathname}`);
},
wechatFetch: async (url, init = {}) => {
wechatCalls.push([url, init.method ?? 'GET', init.body ?? null]);
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = (() => {
const ids = ['req-claim', 'req-claim-retry'];
return () => ids.shift() ?? 'req-claim-retry';
})();
try {
const result = await service.handleInboundMessage(
inboundXml({ content: '帮我生成一个夏日页面' }),
{
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
},
);
assert.equal(result.status, 200);
await result.task;
} finally {
crypto.randomUUID = originalRandomUuid;
}
assert.equal(routeCleared, true);
assert.equal(started, true);
const sendCall = wechatCalls.find(([url]) => String(url).includes('/cgi-bin/message/custom/send'));
const payload = JSON.parse(sendCall[2]);
assert.doesNotMatch(payload.text.content, /页面都已经成功发布了|主题页面已发布/);
assert.match(payload.text.content, /🌴 夏日主题页面/);
assert.doesNotMatch(payload.text.content, /没有按 H5 里的页面技能真正生成成功/);
assert.doesNotMatch(payload.text.content, /summer-breeze-journal\.html/);
});
test('wechat mp service blocks schedule confirmation when no schedule item was written', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const outbound = [];
const service = createBoundWechatService({
token,
scheduleService: {
async listItemsBySourceMessage(input) {
assert.equal(input.userId, 'user-1');
assert.equal(input.sourceMessageId, '10001');
return [];
},
},
sessionApiFetch: async (sessionId, pathname, init = {}) => {
assert.equal(sessionId, 'session-1');
if (pathname === '/sessions/session-1/events') {
return new Response(
[
'data: {"type":"Message","request_id":"req-guard","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"已经设置好了!明早 5:30 提醒起床,6:00 去跑步。"}]}}\n\n',
'data: {"type":"Finish","request_id":"req-guard","token_state":{"inputTokens":1,"outputTokens":2}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === '/sessions/session-1/reply') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
throw new Error(`unexpected api path: ${pathname}`);
},
wechatFetch: async (url, init = {}) => {
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
outbound.push(JSON.parse(init.body).text.content);
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = () => 'req-guard';
try {
const result = await service.handleInboundMessage(
inboundXml({ content: '明天早上六点去跑步,五点半提醒我' }),
{
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
},
);
assert.equal(result.status, 200);
await result.task;
} finally {
crypto.randomUUID = originalRandomUuid;
}
assert.equal(outbound.length, 1);
assert.match(outbound[0], /没有确认到待办\/提醒已经写入系统/);
assert.doesNotMatch(outbound[0], /已经设置好了/);
});
test('wechat mp service creates daily todo digest without routing to agent', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const apiCalls = [];
const service = createWechatMpService({
config: {
enabled: true,
appId: 'wx123',
appSecret: 'secret',
token,
publicBaseUrl: 'https://example.com',
bindPath: '/auth/wechat/authorize?intent=login',
ackText: 'ack',
unsupportedText: 'unsupported',
unboundTextPrefix: '请先绑定',
},
userAuth: {
async findWechatUserByOpenid() {
return { userId: 'user-1', status: 'active', nickname: '毕升' };
},
async recordWechatMpMessage() {
return { inserted: true };
},
async finishWechatMpMessage(input) {
apiCalls.push(['finish', input.status, input.agentSessionId]);
},
},
scheduleService: {
async createDailyTodoDigest(input) {
apiCalls.push(['digest', input.userId, input.hour, input.minute, input.channel]);
return {
id: 'sub-1',
userId: input.userId,
hour: input.hour,
minute: input.minute,
timezone: input.timezone,
channel: input.channel,
};
},
},
apiFetch: async (pathname) => {
apiCalls.push(['agent', pathname]);
return new Response('{}', { status: 200 });
},
});
const result = await service.handleInboundMessage(
inboundXml({ content: '每天早上 7 点给我发一天的待办记录' }),
{
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
},
);
assert.equal(result.status, 200);
assert.match(result.body, /每天早上 7点/);
assert.deepEqual(apiCalls, [
['digest', 'user-1', 7, 0, 'wechat'],
['finish', 'done', null],
]);
});
test('wechat mp service records plain todo without routing to agent', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const apiCalls = [];
const service = createWechatMpService({
config: {
enabled: true,
appId: 'wx123',
appSecret: 'secret',
token,
publicBaseUrl: 'https://example.com',
bindPath: '/auth/wechat/authorize?intent=login',
ackText: 'ack',
unsupportedText: 'unsupported',
unboundTextPrefix: '请先绑定',
},
userAuth: {
async findWechatUserByOpenid() {
return { userId: 'user-1', status: 'active', nickname: '毕升' };
},
async recordWechatMpMessage() {
return { inserted: true };
},
async finishWechatMpMessage(input) {
apiCalls.push(['finish', input.status, input.agentSessionId]);
},
},
scheduleService: {
async createItem(input) {
apiCalls.push(['todo', input.userId, input.kind, input.title, input.sourceChannel]);
return {
id: 'item-1',
title: input.title,
};
},
},
apiFetch: async (pathname) => {
apiCalls.push(['agent', pathname]);
return new Response('{}', { status: 200 });
},
});
const result = await service.handleInboundMessage(
inboundXml({ content: '帮我记一下「🍽️ 跟段吃饭」' }),
{
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
},
);
assert.equal(result.status, 200);
assert.match(result.body, /已记录到待办列表/);
assert.deepEqual(apiCalls, [
['todo', 'user-1', 'task', '🍽️ 跟段吃饭', 'wechat'],
['finish', 'done', null],
]);
});
test('wechat mp service creates balance low alert without routing to agent', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const apiCalls = [];
const service = createWechatMpService({
config: {
enabled: true,
appId: 'wx123',
appSecret: 'secret',
token,
publicBaseUrl: 'https://example.com',
bindPath: '/auth/wechat/authorize?intent=login',
ackText: 'ack',
unsupportedText: 'unsupported',
unboundTextPrefix: '请先绑定',
},
userAuth: {
async findWechatUserByOpenid() {
return { userId: 'user-1', status: 'active', nickname: '毕升' };
},
async recordWechatMpMessage() {
return { inserted: true };
},
async finishWechatMpMessage(input) {
apiCalls.push(['finish', input.status, input.agentSessionId]);
},
},
scheduleService: {
async createBalanceLowAlert(input) {
apiCalls.push(['balance', input.userId, input.thresholdCents, input.sourceChannel]);
return {
id: 'alert-1',
thresholdCents: input.thresholdCents,
};
},
},
apiFetch: async (pathname) => {
apiCalls.push(['agent', pathname]);
return new Response('{}', { status: 200 });
},
});
const result = await service.handleInboundMessage(inboundXml({ content: '余额低于 20 元提醒我' }), {
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
});
assert.equal(result.status, 200);
assert.match(result.body, /已设置:当余额低于 20\.00 元时,我会通过服务号提醒你。/);
assert.deepEqual(apiCalls, [
['balance', 'user-1', 2000, 'wechat'],
['finish', 'done', null],
]);
});
test('wechat mp service asks user to retry when voice recognition is empty', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
let replyCalled = false;
const service = createWechatMpService({
config: {
enabled: true,
appId: 'wx123',
appSecret: 'secret',
token,
publicBaseUrl: 'https://example.com',
bindPath: '/auth/wechat/authorize?intent=login',
ackText: 'ack',
unsupportedText: 'unsupported',
unboundTextPrefix: '请先绑定',
},
userAuth: {
async findWechatUserByOpenid() {
return { userId: 'user-1', status: 'active', nickname: '毕升' };
},
async insertWechatMpMessageDetail() {},
},
apiFetch: async (pathname) => {
if (pathname.includes('/reply')) replyCalled = true;
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
},
});
const result = await service.handleInboundMessage(
inboundXml({
msgType: 'voice',
content: '',
extraFields: { Format: 'amr' },
}),
{
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
},
);
assert.equal(result.status, 200);
assert.match(result.body, /未识别到语音文字/);
assert.equal(replyCalled, false);
});
test('wechat mp service falls back to ASR when voice recognition is empty', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
let replyCalled = false;
const prompts = [];
const service = createWechatMpService({
config: {
enabled: true,
appId: 'wx123',
appSecret: 'secret',
token,
publicBaseUrl: 'https://example.com',
bindPath: '/auth/wechat/authorize?intent=login',
ackText: 'ack',
unsupportedText: 'unsupported',
unboundTextPrefix: '请先绑定',
asrTarget: 'https://asr.example.com',
},
userAuth: {
async findWechatUserByOpenid() {
return { userId: 'user-1', status: 'active', nickname: '毕升' };
},
async getWechatAgentRoute() {
return { agentSessionId: 'session-1' };
},
async clearWechatAgentRoute() {},
async canUseChat() {
return { ok: true };
},
async resolveWorkingDir() {
return '/tmp/user-1';
},
async getAgentSessionPolicy() {
return { enableContextMemory: false, extensionOverrides: [], unrestricted: true };
},
async getUserPublishLayout() {
return { displayName: 'John', username: 'john', slug: 'john', constraints: null };
},
async registerAgentSession() {},
async upsertWechatAgentRoute() {},
async billSessionUsage() {},
async insertWechatMpMessageDetail() {},
},
sessionApiFetch: async (sessionId, pathname, init = {}) => {
assert.equal(sessionId, 'session-1');
if (pathname === '/sessions/session-1/events') {
return new Response(
[
'data: {"type":"Message","request_id":"req-voice-fallback","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"我收到你的语音了。"}]}}\n\n',
'data: {"type":"Finish","request_id":"req-voice-fallback","token_state":{"inputTokens":1,"outputTokens":2}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === '/sessions/session-1/reply') {
replyCalled = true;
const body = JSON.parse(init.body);
prompts.push(body.user_message.content[0].text);
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
throw new Error(`unexpected api path: ${pathname}`);
},
wechatFetch: async (url, init = {}) => {
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/media/get')) {
return new Response(Buffer.from('fake-amr-audio'), {
status: 200,
headers: { 'Content-Type': 'audio/amr' },
});
}
if (String(url).includes('https://asr.example.com/asr/oneshot')) {
assert.equal(init.method, 'POST');
return new Response(JSON.stringify({ code: 200, data: { text: '帮我整理今天的会议纪要' } }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = () => 'req-voice-fallback';
try {
const result = await service.handleInboundMessage(
inboundXml({
msgType: 'voice',
content: '',
extraFields: { MediaId: 'media-1', Format: 'amr' },
}),
{
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
},
);
assert.equal(result.status, 200);
assert.doesNotMatch(result.body, /未识别到语音文字/);
await result.task;
assert.equal(replyCalled, true);
assert.match(prompts[0], /帮我整理今天的会议纪要/);
} finally {
crypto.randomUUID = originalRandomUuid;
}
});
test('wechat mp wildcard media access persists image and routes image url into agent prompt', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const testUserId = 'test-user-image';
const prompts = [];
const metadataCalls = [];
const detailCalls = [];
const service = createWechatMpService({
config: {
enabled: true,
appId: 'wx123',
appSecret: 'secret',
token,
publicBaseUrl: 'https://example.com',
mediaPublicBaseUrl: 'https://example.com',
bindPath: '/auth/wechat/authorize?intent=login',
ackText: 'ack',
unsupportedText: 'unsupported',
unboundTextPrefix: '请先绑定',
progressDelayMs: 0,
maxImageBytes: 1024 * 1024,
mediaAnalysisGrayUsers: ['*'],
},
userAuth: {
async findWechatUserByOpenid() {
return { userId: testUserId, status: 'active', nickname: '毕升' };
},
async getWechatAgentRoute() {
return { agentSessionId: 'session-1' };
},
async clearWechatAgentRoute() {},
async canUseChat() {
return { ok: true };
},
async resolveWorkingDir() {
return '/tmp/user-1';
},
async getAgentSessionPolicy() {
return { enableContextMemory: false, extensionOverrides: [], unrestricted: true };
},
async getUserPublishLayout() {
return { displayName: 'John', username: 'john', slug: 'john', constraints: null };
},
async registerAgentSession() {},
async upsertWechatAgentRoute() {},
async billSessionUsage() {},
async recordWechatMpMessage() {
return { inserted: true };
},
async finishWechatMpMessage() {},
async insertWechatMpMessageDetail(input) {
detailCalls.push(input);
},
},
sessionApiFetch: async (sessionId, pathname, init = {}) => {
assert.equal(sessionId, 'session-1');
if (pathname === '/sessions/session-1/events') {
return new Response(
[
'data: {"type":"Message","request_id":"req-image","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"请告诉我希望怎么处理这张图片。"}]}}\n\n',
'data: {"type":"Finish","request_id":"req-image","token_state":{"inputTokens":1,"outputTokens":2}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === '/sessions/session-1/reply') {
const body = JSON.parse(init.body);
prompts.push(body.user_message.content[0].text);
metadataCalls.push(body.user_message.metadata);
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
throw new Error(`unexpected api path: ${pathname}`);
},
wechatFetch: async (url, init = {}) => {
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/media/get')) {
return new Response(Buffer.from([0x89, 0x50, 0x4e, 0x47]), {
status: 200,
headers: { 'Content-Type': 'image/png' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = () => 'req-image';
try {
const result = await service.handleInboundMessage(
inboundXml({
msgType: 'image',
content: '',
extraFields: { MediaId: 'media-1', PicUrl: 'https://wx.example.com/image.png' },
}),
{
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
},
);
assert.equal(result.status, 200);
assert.match(result.body, /<Content>/);
await result.task;
} finally {
crypto.randomUUID = originalRandomUuid;
fs.rmSync(path.join(process.cwd(), 'MindSpace', testUserId), {
recursive: true,
force: true,
});
}
assert.equal(prompts.length, 1);
assert.match(prompts[0], /【微信服务号图片消息】/);
assert.match(
prompts[0],
/\[图片1\]: https:\/\/example\.com\/MindSpace\/test-user-image\/public\/wechat-mp\//,
);
assert.equal(metadataCalls.length, 1);
assert.equal(metadataCalls[0].source, 'wechat_mp');
assert.equal(metadataCalls[0].msgType, 'image');
assert.equal(metadataCalls[0].imageUrls.length, 1);
assert.match(metadataCalls[0].imageUrls[0], /\/public\/wechat-mp\//);
assert.equal(detailCalls.length, 1);
assert.match(detailCalls[0].mediaPublicUrl, /\/wechat-mp\//);
});
test('wechat mp image submission reuses the H5 prepared reply path', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const testUserId = 'test-user-image-prepared';
const submitCalls = [];
const service = createBoundWechatService({
token,
config: {
mediaAnalysisGrayUsers: [testUserId],
},
userAuth: {
async findWechatUserByOpenid() {
return { userId: testUserId, status: 'active', nickname: '唐' };
},
},
sessionApiFetch: async (_sessionId, pathname) => {
if (pathname === '/sessions/session-1/events') {
return new Response(
[
'data: {"type":"Message","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"图片内容已识别。"}]}}\n\n',
'data: {"type":"Finish"}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
throw new Error(`unexpected api path: ${pathname}`);
},
submitSessionReply: async (input) => {
submitCalls.push(input);
return { ok: true };
},
wechatFetch: async (url) => {
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/media/get')) {
return new Response(Buffer.from([0x89, 0x50, 0x4e, 0x47]), {
status: 200,
headers: { 'Content-Type': 'image/png' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
});
try {
const result = await service.handleInboundMessage(
inboundXml({
msgType: 'image',
content: '',
extraFields: { MediaId: 'media-prepared', PicUrl: 'https://wx.example.com/image.png' },
}),
{ timestamp, nonce, signature: signatureFor(token, timestamp, nonce) },
);
await result.task;
} finally {
fs.rmSync(path.join(process.cwd(), 'MindSpace', testUserId), { recursive: true, force: true });
}
assert.equal(submitCalls.length, 1);
assert.equal(submitCalls[0].userId, testUserId);
assert.equal(submitCalls[0].sessionId, 'session-1');
assert.equal(submitCalls[0].userMessage.metadata.imageUrls.length, 1);
assert.match(submitCalls[0].userMessage.metadata.imageUrls[0], /\/public\/wechat-mp\//);
});
test('wechat mp serializes image and follow-up text and reattaches recent image', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const testUserId = 'test-user-image-followup';
const submitCalls = [];
let eventCall = 0;
let releaseFirst = null;
const service = createBoundWechatService({
token,
config: {
mediaAnalysisGrayUsers: [testUserId],
},
userAuth: {
async findWechatUserByOpenid() {
return { userId: testUserId, status: 'active', nickname: '唐' };
},
},
sessionApiFetch: async (_sessionId, pathname) => {
if (pathname === '/sessions/session-1/events') {
eventCall += 1;
if (eventCall === 1) {
return new Response(
new ReadableStream({
start(controller) {
releaseFirst = () => {
controller.enqueue(
new TextEncoder().encode(
'data: {"type":"Message","message":{"id":"assistant-image","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"图片已识别。"}]}}\n\n' +
'data: {"type":"Finish"}\n\n',
),
);
controller.close();
};
},
}),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
return new Response(
[
'data: {"type":"Message","message":{"id":"assistant-followup","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"已结合刚才图片分析主题。"}]}}\n\n',
'data: {"type":"Finish"}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
throw new Error(`unexpected api path: ${pathname}`);
},
submitSessionReply: async (input) => {
submitCalls.push(input);
return { ok: true };
},
wechatFetch: async (url) => {
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/media/get')) {
return new Response(Buffer.from([0x89, 0x50, 0x4e, 0x47]), {
status: 200,
headers: { 'Content-Type': 'image/png' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
});
try {
const imageResult = await service.handleInboundMessage(
inboundXml({
msgType: 'image',
content: '',
extraFields: { MediaId: 'media-followup', PicUrl: 'https://wx.example.com/image.png' },
}),
{ timestamp, nonce, signature: signatureFor(token, timestamp, nonce) },
);
while (submitCalls.length === 0) await new Promise((resolve) => setTimeout(resolve, 0));
const followupResult = await service.handleInboundMessage(
inboundXml({ msgType: 'text', content: '请根据刚才图片分析主题' }),
{ timestamp, nonce, signature: signatureFor(token, timestamp, nonce) },
);
await new Promise((resolve) => setTimeout(resolve, 0));
assert.equal(submitCalls.length, 1);
releaseFirst();
await imageResult.task;
await followupResult.task;
} finally {
fs.rmSync(path.join(process.cwd(), 'MindSpace', testUserId), { recursive: true, force: true });
}
assert.equal(submitCalls.length, 2);
assert.deepEqual(
submitCalls[1].userMessage.metadata.imageUrls,
submitCalls[0].userMessage.metadata.imageUrls,
);
assert.equal(submitCalls[1].userMessage.metadata.msgType, 'text');
});
test('wechat mp groups consecutive images for one follow-up and clears the consumed batch', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const testUserId = 'test-user-multi-image-followup';
const submitCalls = [];
let eventCall = 0;
let releaseFirst = null;
const service = createBoundWechatService({
token,
config: {
mediaAnalysisGrayUsers: [testUserId],
},
userAuth: {
async findWechatUserByOpenid() {
return { userId: testUserId, status: 'active', nickname: '唐' };
},
},
sessionApiFetch: async (_sessionId, pathname) => {
if (pathname === '/sessions/session-1/events') {
eventCall += 1;
if (eventCall === 1) {
return new Response(
new ReadableStream({
start(controller) {
releaseFirst = () => {
controller.enqueue(
new TextEncoder().encode(
'data: {"type":"Message","message":{"id":"assistant-first","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"第一张处理完成。"}]}}\n\n' +
'data: {"type":"Finish"}\n\n',
),
);
controller.close();
};
},
}),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
return new Response(
[
'data: {"type":"Message","message":{"id":"assistant-ok","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"处理完成。"}]}}\n\n',
'data: {"type":"Finish"}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
throw new Error(`unexpected api path: ${pathname}`);
},
submitSessionReply: async (input) => {
submitCalls.push(input);
return { ok: true };
},
wechatFetch: async (url) => {
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/media/get')) {
return new Response(Buffer.from([0x89, 0x50, 0x4e, 0x47]), {
status: 200,
headers: { 'Content-Type': 'image/png' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
});
try {
const firstImageResult = await service.handleInboundMessage(
inboundXml({
msgType: 'image',
content: '',
extraFields: {
MsgId: '10011',
MediaId: 'media-first',
PicUrl: 'https://wx.example.com/media-first.png',
},
}),
{ timestamp, nonce, signature: signatureFor(token, timestamp, nonce) },
);
while (submitCalls.length === 0) await new Promise((resolve) => setTimeout(resolve, 0));
const secondImageResult = await service.handleInboundMessage(
inboundXml({
msgType: 'image',
content: '',
extraFields: {
MsgId: '10012',
MediaId: 'media-second',
PicUrl: 'https://wx.example.com/media-second.png',
},
}),
{ timestamp, nonce, signature: signatureFor(token, timestamp, nonce) },
);
const followupResult = await service.handleInboundMessage(
inboundXml({
content: '请结合刚才两张图片分析主题',
extraFields: { MsgId: '10013' },
}),
{ timestamp, nonce, signature: signatureFor(token, timestamp, nonce) },
);
await new Promise((resolve) => setTimeout(resolve, 0));
assert.equal(submitCalls.length, 1);
releaseFirst();
await firstImageResult.task;
await secondImageResult.task;
await followupResult.task;
const laterResult = await service.handleInboundMessage(
inboundXml({
content: '请再次分析刚才图片',
extraFields: { MsgId: '10014' },
}),
{ timestamp, nonce, signature: signatureFor(token, timestamp, nonce) },
);
await laterResult.task;
} finally {
fs.rmSync(path.join(process.cwd(), 'MindSpace', testUserId), { recursive: true, force: true });
}
assert.equal(submitCalls.length, 4);
assert.equal(submitCalls[0].userMessage.metadata.imageUrls.length, 1);
assert.equal(submitCalls[1].userMessage.metadata.imageUrls.length, 1);
assert.equal(submitCalls[2].userMessage.metadata.imageUrls.length, 2);
assert.match(submitCalls[2].userMessage.metadata.imageUrls[0], /media-first/);
assert.match(submitCalls[2].userMessage.metadata.imageUrls[1], /media-second/);
assert.equal(submitCalls[2].userMessage.metadata.msgType, 'text');
assert.equal(submitCalls[3].userMessage.metadata.imageUrls, undefined);
});
test('wechat mp service persists Word and Excel files in user public area and reuses H5 attachment metadata', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const testUserId = 'test-user-wechat-file';
const userMessages = [];
const service = createBoundWechatService({
token,
config: {
maxFileBytes: 1024 * 1024,
acceptFile: true,
mediaAnalysisGrayUsers: [testUserId],
},
userAuth: {
async findWechatUserByOpenid() {
return { userId: testUserId, status: 'active', nickname: '毕升' };
},
},
sessionApiFetch: async (_sessionId, pathname, init = {}) => {
if (pathname === '/sessions/session-1/events') {
return new Response(
[
'data: {"type":"Message","request_id":"req-file","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"文件已解析。"}]}}\n\n',
'data: {"type":"Finish","request_id":"req-file","token_state":{"inputTokens":1,"outputTokens":2}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === '/sessions/session-1/reply') {
const body = JSON.parse(init.body);
userMessages.push(body.user_message);
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
throw new Error(`unexpected api path: ${pathname}`);
},
wechatFetch: async (url) => {
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/media/get')) {
return new Response(Buffer.from('fake-docx-content'), {
status: 200,
headers: { 'Content-Type': 'application/octet-stream' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = () => 'req-file';
try {
for (const [index, filename] of ['季度分析.docx', '销售数据.xlsx'].entries()) {
const result = await service.handleInboundMessage(
inboundXml({
msgType: 'file',
content: '',
extraFields: {
MediaId: `file-media-${index + 1}`,
FileName: filename,
FileSize: 17,
},
}),
{ timestamp, nonce, signature: signatureFor(token, timestamp, nonce) },
);
assert.equal(result.status, 200);
await result.task;
const attachment = userMessages.at(-1)?.metadata?.fileAttachments?.[0];
const publicFilename = decodeURIComponent(new URL(attachment.downloadUrl).pathname.split('/').at(-1));
assert.equal(
fs.existsSync(
path.join(process.cwd(), 'MindSpace', testUserId, 'public', 'wechat-mp', publicFilename),
),
true,
);
}
} finally {
crypto.randomUUID = originalRandomUuid;
fs.rmSync(path.join(process.cwd(), 'MindSpace', testUserId), {
recursive: true,
force: true,
});
}
assert.equal(userMessages.length, 2);
assert.match(userMessages[0].content[0].text, /【微信服务号文件消息】/);
assert.match(userMessages[0].content[0].text, /\[文件1: 季度分析\.docx\]:/);
assert.equal(userMessages[0].metadata.msgType, 'file');
assert.equal(userMessages[0].metadata.fileAttachments.length, 1);
assert.equal(userMessages[0].metadata.fileAttachments[0].filename, '季度分析.docx');
assert.equal(
userMessages[0].metadata.fileAttachments[0].mimeType,
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
);
assert.match(userMessages[0].metadata.fileAttachments[0].downloadUrl, /\/public\/wechat-mp\//);
assert.match(userMessages[1].content[0].text, /\[文件1: 销售数据\.xlsx\]:/);
assert.equal(userMessages[1].metadata.fileAttachments[0].filename, '销售数据.xlsx');
assert.equal(
userMessages[1].metadata.fileAttachments[0].mimeType,
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
);
assert.match(userMessages[1].metadata.fileAttachments[0].downloadUrl, /\/public\/wechat-mp\//);
});
test('wechat mp service rejects unsupported public file types without entering the agent flow', async () => {
let mediaDownloads = 0;
let sessionCalls = 0;
const service = createBoundWechatService({
config: { acceptFile: true, mediaAnalysisGrayUsers: ['user-1'] },
sessionApiFetch: async () => {
sessionCalls += 1;
throw new Error('session should not be called');
},
wechatFetch: async (url) => {
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/media/get')) mediaDownloads += 1;
throw new Error(`unexpected wechat url: ${url}`);
},
});
const result = await service.handleInboundMessage(
inboundXml({
msgType: 'file',
content: '',
extraFields: { MediaId: 'file-media-2', FileName: 'payload.exe' },
}),
{
timestamp: '1710000000',
nonce: 'nonce',
signature: signatureFor('token', '1710000000', 'nonce'),
},
);
assert.equal(result.status, 200);
assert.match(result.body, /仅支持 Word/);
assert.equal(mediaDownloads, 0);
assert.equal(sessionCalls, 0);
});
test('wechat mp service keeps file analysis disabled outside the media gray allowlist', async () => {
let mediaDownloads = 0;
let sessionCalls = 0;
const service = createBoundWechatService({
config: {
acceptFile: true,
mediaAnalysisGrayUsers: ['唐'],
},
sessionApiFetch: async () => {
sessionCalls += 1;
throw new Error('session should not be called');
},
wechatFetch: async (url) => {
if (String(url).includes('/cgi-bin/media/get')) mediaDownloads += 1;
throw new Error(`unexpected wechat url: ${url}`);
},
});
const result = await service.handleInboundMessage(
inboundXml({
msgType: 'file',
content: '',
extraFields: { MediaId: 'file-media-gray', FileName: '测试.docx' },
}),
{
timestamp: '1710000000',
nonce: 'nonce',
signature: signatureFor('token', '1710000000', 'nonce'),
},
);
assert.equal(result.status, 200);
assert.match(result.body, /尚未开启服务号文件分析灰度/);
assert.equal(mediaDownloads, 0);
assert.equal(sessionCalls, 0);
});
test('wechat mp service accepts full voice xml payload and routes recognition text into agent', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const prompts = [];
const metadataCalls = [];
const wechatCalls = [];
const service = createBoundWechatService({
token,
userAuth: {
async insertWechatMpMessageDetail(input) {
metadataCalls.push(input);
},
},
sessionApiFetch: async (sessionId, pathname, init = {}) => {
assert.equal(sessionId, 'session-1');
if (pathname === '/sessions/session-1/events') {
return new Response(
[
'data: {"type":"Message","request_id":"req-voice","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"我收到你的语音了。"}]}}\n\n',
'data: {"type":"Finish","request_id":"req-voice","token_state":{"inputTokens":1,"outputTokens":2}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === '/sessions/session-1/reply') {
const body = JSON.parse(init.body);
prompts.push(body.user_message.content[0].text);
metadataCalls.push(body.user_message.metadata);
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
throw new Error(`unexpected api path: ${pathname}`);
},
wechatFetch: async (url, init = {}) => {
wechatCalls.push([url, init.method ?? 'GET', init.body ?? null]);
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = () => 'req-voice';
try {
const result = await service.handleInboundMessage(
inboundXml({
msgType: 'voice',
content: '',
extraFields: {
MediaId: 'voice-media-1',
Format: 'amr',
Recognition: '帮我整理今天的会议纪要',
},
}),
{
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
},
);
assert.equal(result.status, 200);
assert.match(result.body, /<Content>/);
await result.task;
} finally {
crypto.randomUUID = originalRandomUuid;
}
assert.equal(prompts.length, 1);
assert.match(prompts[0], /【微信服务号语音消息】/);
assert.match(prompts[0], /用户语音识别文本:帮我整理今天的会议纪要/);
assert.equal(metadataCalls.some((item) => item?.msgType === 'voice'), true);
assert.equal(
wechatCalls.some(([, , body]) => String(body ?? '').includes('我收到你的语音了')),
true,
);
});
test('wechat mp service accepts full location xml payload and routes structured location prompt', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const prompts = [];
const detailCalls = [];
const service = createBoundWechatService({
token,
userAuth: {
async insertWechatMpMessageDetail(input) {
detailCalls.push(input);
},
},
sessionApiFetch: async (sessionId, pathname, init = {}) => {
assert.equal(sessionId, 'session-1');
if (pathname === '/sessions/session-1/events') {
return new Response(
[
'data: {"type":"Message","request_id":"req-location","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"你附近有几家咖啡馆。"}]}}\n\n',
'data: {"type":"Finish","request_id":"req-location","token_state":{"inputTokens":1,"outputTokens":2}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === '/sessions/session-1/reply') {
const body = JSON.parse(init.body);
prompts.push(body.user_message.content[0].text);
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
throw new Error(`unexpected api path: ${pathname}`);
},
wechatFetch: async (url) => {
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = () => 'req-location';
try {
const result = await service.handleInboundMessage(
inboundXml({
msgType: 'location',
content: '',
extraFields: {
Location_X: '31.2304',
Location_Y: '121.4737',
Scale: '15',
Label: '上海市黄浦区人民广场',
},
}),
{
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
},
);
assert.equal(result.status, 200);
assert.match(result.body, /<Content>/);
await result.task;
} finally {
crypto.randomUUID = originalRandomUuid;
}
assert.equal(prompts.length, 1);
assert.match(prompts[0], /【微信服务号位置消息】/);
assert.match(prompts[0], /地址:上海市黄浦区人民广场/);
assert.match(prompts[0], /纬度:31.2304/);
assert.match(prompts[0], /经度:121.4737/);
assert.equal(detailCalls.length, 1);
assert.equal(detailCalls[0].locationLabel, '上海市黄浦区人民广场');
});
test('wechat mp service accepts full link xml payload and routes structured link prompt', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const prompts = [];
const detailCalls = [];
const service = createBoundWechatService({
token,
userAuth: {
async insertWechatMpMessageDetail(input) {
detailCalls.push(input);
},
},
sessionApiFetch: async (sessionId, pathname, init = {}) => {
assert.equal(sessionId, 'session-1');
if (pathname === '/sessions/session-1/events') {
return new Response(
[
'data: {"type":"Message","request_id":"req-link","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"这个链接看起来是在介绍 TKMind 的新功能。"}]}}\n\n',
'data: {"type":"Finish","request_id":"req-link","token_state":{"inputTokens":1,"outputTokens":2}}\n\n',
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === '/sessions/session-1/reply') {
const body = JSON.parse(init.body);
prompts.push(body.user_message.content[0].text);
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
throw new Error(`unexpected api path: ${pathname}`);
},
wechatFetch: async (url) => {
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = () => 'req-link';
try {
const result = await service.handleInboundMessage(
inboundXml({
msgType: 'link',
content: '',
extraFields: {
Title: 'TKMind 发布说明',
Description: '一篇关于多模态服务号入口的介绍',
Url: 'https://example.com/articles/wechat-multimodal',
},
}),
{
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
},
);
assert.equal(result.status, 200);
assert.match(result.body, /<Content>/);
await result.task;
} finally {
crypto.randomUUID = originalRandomUuid;
}
assert.equal(prompts.length, 1);
assert.match(prompts[0], /【微信服务号链接消息】/);
assert.match(prompts[0], /标题:TKMind 发布说明/);
assert.match(prompts[0], /描述:一篇关于多模态服务号入口的介绍/);
assert.match(prompts[0], /URLhttps:\/\/example\.com\/articles\/wechat-multimodal/);
assert.equal(detailCalls.length, 1);
assert.equal(detailCalls[0].linkTitle, 'TKMind 发布说明');
});
test('wechat mp service records location events without routing to agent', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const detailCalls = [];
const service = createWechatMpService({
config: {
enabled: true,
appId: 'wx123',
appSecret: 'secret',
token,
publicBaseUrl: 'https://example.com',
bindPath: '/auth/wechat/authorize?intent=login',
ackText: 'ack',
unsupportedText: 'unsupported',
unboundTextPrefix: '请先绑定',
},
userAuth: {
async insertWechatMpMessageDetail(input) {
detailCalls.push(input);
},
},
apiFetch: async () => {
throw new Error('should not call agent api');
},
});
const result = await service.handleInboundMessage(
inboundXml({
msgType: 'event',
content: '',
event: 'LOCATION',
extraFields: { Latitude: 31.2, Longitude: 121.5, Precision: 15 },
}),
{
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
},
);
assert.equal(result.status, 200);
assert.equal(result.body, 'success');
assert.equal(detailCalls.length, 1);
assert.equal(detailCalls[0].msgType, 'event');
});
test('wechat mp service returns status text for question mark probes', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const service = createWechatMpService({
config: {
enabled: true,
appId: 'wx123',
appSecret: 'secret',
token,
publicBaseUrl: 'https://example.com',
bindPath: '/auth/wechat/authorize?intent=login',
ackText: 'ack',
statusText: '我在这边',
unsupportedText: 'unsupported',
unboundTextPrefix: '请先绑定',
},
userAuth: {
async findWechatUserByOpenid() {
return { userId: 'user-1', status: 'active', nickname: '毕升' };
},
},
apiFetch: async () => new Response('{}', { status: 200 }),
});
const result = await service.handleInboundMessage(inboundXml({ content: '?' }), {
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
});
assert.equal(result.status, 200);
assert.match(result.body, /我在这边/);
assert.match(result.body, /毕升/);
});
test('wechat mp service greets bound users with stored wechat nickname', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
let replyCalled = false;
const service = createWechatMpService({
config: {
enabled: true,
appId: 'wx123',
appSecret: 'secret',
token,
publicBaseUrl: 'https://example.com',
bindPath: '/auth/wechat/authorize?intent=login',
ackText: 'ack',
unsupportedText: 'unsupported',
unboundTextPrefix: '请先绑定',
},
userAuth: {
async findWechatUserByOpenid() {
return { userId: 'user-1', status: 'active', nickname: '毕升' };
},
},
apiFetch: async (pathname) => {
if (pathname === '/sessions/session-1/reply') replyCalled = true;
return new Response('{}', { status: 200 });
},
});
const result = await service.handleInboundMessage(inboundXml({ content: '你好' }), {
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
});
assert.equal(result.status, 200);
assert.match(result.body, /你好,毕升/);
assert.equal(replyCalled, false);
});
test('wechat mp service replies to connectivity test without agent', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
let replyCalled = false;
const service = createWechatMpService({
config: {
enabled: true,
appId: 'wx123',
appSecret: 'secret',
token,
publicBaseUrl: 'https://example.com',
bindPath: '/auth/wechat/authorize?intent=login',
ackText: 'ack',
unsupportedText: 'unsupported',
unboundTextPrefix: '请先绑定',
},
userAuth: {
async findWechatUserByOpenid() {
return { userId: 'user-1', status: 'active', nickname: '毕升' };
},
},
apiFetch: async (pathname) => {
if (pathname.includes('/reply')) replyCalled = true;
return new Response('{}', { status: 200 });
},
});
const result = await service.handleInboundMessage(inboundXml({ content: '测试999' }), {
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
});
assert.equal(result.status, 200);
assert.match(result.body, /公众号消息通道正常/);
assert.equal(replyCalled, false);
});
test('wechat mp service ignores duplicated message ids', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
let replyCalled = false;
const service = createWechatMpService({
config: {
enabled: true,
appId: 'wx123',
appSecret: 'secret',
token,
publicBaseUrl: 'https://example.com',
bindPath: '/auth/wechat/authorize?intent=login',
ackText: 'ack',
unsupportedText: 'unsupported',
unboundTextPrefix: '请先绑定',
},
userAuth: {
async findWechatUserByOpenid() {
return { userId: 'user-1', status: 'active' };
},
async recordWechatMpMessage() {
return { inserted: false };
},
},
apiFetch: async (pathname) => {
if (pathname === '/sessions/session-1/reply') replyCalled = true;
return new Response('{}', { status: 200 });
},
});
const result = await service.handleInboundMessage(inboundXml({ content: '帮我继续处理' }), {
timestamp,
nonce,
signature: signatureFor(token, timestamp, nonce),
});
assert.equal(result.status, 200);
assert.match(result.body, /上一条如果还没完成/);
assert.equal(replyCalled, false);
});
test('wechat mp service exposes route status and can recreate route for bound user', async () => {
const calls = [];
let routeCleared = false;
let routeUpserted = false;
const service = createWechatMpService({
config: {
enabled: true,
appId: 'wx123',
appSecret: 'secret',
token: 'token',
publicBaseUrl: 'https://example.com',
bindPath: '/auth/wechat/authorize?intent=login',
ackText: 'ack',
unsupportedText: 'unsupported',
unboundTextPrefix: '请先绑定',
},
userAuth: {
async getWechatOpenidForUser() {
return 'openid-1';
},
async getWechatAgentRoute(_appId, _openid) {
if (routeCleared && !routeUpserted) return null;
return {
agentSessionId: routeUpserted ? 'session-new' : 'session-old',
status: 'active',
updatedAt: 1710000000000,
};
},
async clearWechatAgentRoute() {
routeCleared = true;
calls.push('cleared');
},
async canUseChat() {
return { ok: true };
},
async resolveWorkingDir() {
return '/tmp/user-1';
},
async getAgentSessionPolicy() {
return { enableContextMemory: false, extensionOverrides: [], unrestricted: true };
},
async getUserPublishLayout() {
return { displayName: 'John', username: 'john', slug: 'john', constraints: null };
},
async registerAgentSession(_userId, sessionId) {
calls.push(`register:${sessionId}`);
},
async upsertWechatAgentRoute() {
routeUpserted = true;
calls.push('upserted');
},
},
apiFetch: async (pathname) => {
if (pathname === '/agent/start') {
return new Response(JSON.stringify({ id: 'session-new' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected fallback api path: ${pathname}`);
},
sessionApiFetch: async (_sessionId, pathname) => {
if (pathname === '/sessions/session-new') {
return new Response(JSON.stringify({ working_dir: '/tmp/user-1' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (pathname === '/sessions/session-new/extensions') {
return new Response(JSON.stringify({ extensions: [] }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (
pathname === '/agent/restart'
) {
return new Response('{}', {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected api path: ${pathname}`);
},
});
const status = await service.getRouteStatusForUser('user-1');
assert.deepEqual(status, {
enabled: true,
bound: true,
appId: 'wx123',
openid: 'openid-1',
agentSessionId: 'session-old',
routeStatus: 'active',
updatedAt: 1710000000000,
});
const recreated = await service.recreateRouteForUser('user-1');
assert.equal(recreated.ok, true);
assert.equal(recreated.route.agentSessionId, 'session-new');
assert.equal(calls.includes('cleared'), true);
assert.equal(calls.some((item) => item === 'register:session-new'), true);
assert.equal(calls.includes('upserted'), true);
});
test('wechat mp page delivery requires and verifies a current-run fresh thumbnail', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const workspaceRoot = fs.mkdtempSync('/tmp/wechat-mp-fresh-thumbnail-');
const htmlPath = path.join(workspaceRoot, 'public', 'fresh.html');
const wechatPayloads = [];
const generated = {
ok: true,
jobId: 'job-fresh-page',
source: { mimeType: 'image/webp', width: 1280, height: 720 },
asset: {
id: 'asset-fresh-page',
htmlSrc: 'images/fresh-page.webp',
publicUrl: 'https://example.com/MindSpace/user-1/public/images/fresh-page.webp',
workspaceRelativePath: 'public/images/fresh-page.webp',
},
};
const pageHtml = previewReadyPageHtml({
title: 'Fresh',
subtitle: '本轮新缩略图',
cover: generated.asset.htmlSrc,
});
const pageImage = await sharp({
create: { width: 64, height: 64, channels: 3, background: '#3366cc' },
}).webp().toBuffer();
const eventFrame = (event) => `data: ${JSON.stringify(event)}\n\n`;
const service = createBoundWechatService({
token,
config: { requireFreshPageThumbnail: true },
userAuth: {
async resolveWorkingDir() {
return workspaceRoot;
},
async getUserPublishLayout() {
return {
publishDir: workspaceRoot,
displayName: 'John',
username: 'john',
slug: 'john',
constraints: null,
};
},
},
sessionApiFetch: async (sessionId, pathname, init = {}) => {
assert.equal(sessionId, 'session-1');
if (pathname === '/sessions/session-1/events') {
return new Response(
[
eventFrame({
type: 'Message',
request_id: 'req-fresh-page',
message: {
id: 'assistant-tools',
role: 'assistant',
metadata: { userVisible: true },
content: [
{
id: 'call-page-skill',
type: 'toolRequest',
toolCall: {
value: { name: 'load_skill', arguments: { name: 'static-page-publish' } },
},
},
{
id: 'call-generate',
type: 'toolRequest',
toolCall: {
value: {
name: 'sandbox-fs__generate_image',
arguments: { purpose: 'hero' },
},
},
},
{
id: 'call-generate',
type: 'toolResponse',
toolResult: {
status: 'success',
value: { content: [{ type: 'text', text: JSON.stringify(generated) }] },
},
},
{
id: 'call-write-page',
type: 'toolRequest',
toolCall: {
value: {
name: 'sandbox-fs__write_file',
arguments: { path: 'public/fresh.html', content: pageHtml },
},
},
},
],
},
}),
eventFrame({
type: 'Message',
request_id: 'req-fresh-page',
message: {
id: 'assistant-final',
role: 'assistant',
metadata: { userVisible: true },
content: [{
type: 'text',
text: '页面已完成:https://example.com/MindSpace/user-1/public/fresh.html',
}],
},
}),
eventFrame({
type: 'Finish',
request_id: 'req-fresh-page',
token_state: { inputTokens: 1, outputTokens: 2 },
}),
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === '/sessions/session-1/reply') {
const body = JSON.parse(init.body);
assert.equal(body.user_message.metadata.memindRun.channel, 'wechat_mp');
assert.equal(body.user_message.metadata.memindRun.pageThumbnailMode, 'required_fresh');
assert.equal(body.user_message.metadata.memindRun.inlineImageMode, 'auto');
assert.match(body.user_message.content[0].text, /服务号页面新缩略图硬要求/);
assert.match(body.user_message.content[0].text, /本轮新生成资产/);
fs.mkdirSync(path.dirname(htmlPath), { recursive: true });
fs.writeFileSync(
htmlPath,
pageHtml,
'utf8',
);
fs.mkdirSync(path.join(workspaceRoot, 'public', 'images'), { recursive: true });
fs.writeFileSync(path.join(workspaceRoot, 'public', generated.asset.htmlSrc), pageImage);
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
throw new Error(`unexpected session path: ${pathname}`);
},
wechatFetch: async (url, init = {}) => {
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
wechatPayloads.push(JSON.parse(init.body));
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = () => 'req-fresh-page';
try {
const result = await service.handleInboundMessage(
inboundXml({ content: '请生成一个旅行 html 页面,文件名 public/fresh.html' }),
{ timestamp, nonce, signature: signatureFor(token, timestamp, nonce) },
);
assert.equal(result.status, 200);
await result.task;
assert.equal(fs.existsSync(path.join(workspaceRoot, 'public', 'fresh.thumbnail.svg')), true);
} finally {
crypto.randomUUID = originalRandomUuid;
fs.rmSync(workspaceRoot, { recursive: true, force: true });
}
assert.equal(wechatPayloads.length, 1);
assert.equal(wechatPayloads[0].msgtype, 'text');
assert.match(wechatPayloads[0].text.content, /fresh\.html/);
});
test('wechat mp retries a page in a new session before reporting a missing fresh thumbnail', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const workspaceRoot = fs.mkdtempSync('/tmp/wechat-mp-fresh-thumbnail-retry-');
const htmlPath = path.join(workspaceRoot, 'public', 'retry.html');
const generated = {
ok: true,
jobId: 'job-fresh-page-retry',
source: { mimeType: 'image/webp', width: 1280, height: 720 },
asset: {
id: 'asset-fresh-page-retry',
htmlSrc: 'images/retry-cover.webp',
publicUrl: 'https://example.com/MindSpace/user-1/public/images/retry-cover.webp',
workspaceRelativePath: 'public/images/retry-cover.webp',
},
};
const firstHtml = previewReadyPageHtml({
title: 'Retry',
subtitle: '首次没有新缩略图',
cover: 'images/missing-cover.webp',
});
const retryHtml = previewReadyPageHtml({
title: 'Retry',
subtitle: '重试生成新缩略图',
cover: generated.asset.htmlSrc,
});
const pageImage = await sharp({
create: { width: 64, height: 64, channels: 3, background: '#cc6633' },
}).webp().toBuffer();
const eventFrame = (event) => `data: ${JSON.stringify(event)}\n\n`;
const wechatPayloads = [];
let routeCleared = false;
let startedSessions = 0;
const replyEvents = ({ requestId, html, includeImage = false }) => [
eventFrame({
type: 'Message',
request_id: requestId,
message: {
id: `${requestId}-tools`,
role: 'assistant',
metadata: { userVisible: true },
content: [
{
id: `${requestId}-page-skill`,
type: 'toolRequest',
toolCall: { value: { name: 'load_skill', arguments: { name: 'static-page-publish' } } },
},
...(includeImage
? [
{
id: `${requestId}-image`,
type: 'toolRequest',
toolCall: {
value: { name: 'sandbox-fs__generate_image', arguments: { purpose: 'hero' } },
},
},
{
id: `${requestId}-image`,
type: 'toolResponse',
toolResult: {
status: 'success',
value: { content: [{ type: 'text', text: JSON.stringify(generated) }] },
},
},
]
: []),
{
id: `${requestId}-write`,
type: 'toolRequest',
toolCall: {
value: {
name: 'sandbox-fs__write_file',
arguments: { path: 'public/retry.html', content: html },
},
},
},
],
},
}),
eventFrame({
type: 'Message',
request_id: requestId,
message: {
id: `${requestId}-final`,
role: 'assistant',
metadata: { userVisible: true },
content: [{
type: 'text',
text: '页面已完成:https://example.com/MindSpace/user-1/public/retry.html',
}],
},
}),
eventFrame({
type: 'Finish',
request_id: requestId,
token_state: { inputTokens: 1, outputTokens: 2 },
}),
].join('');
const service = createBoundWechatService({
token,
config: { requireFreshPageThumbnail: true },
startAgentSession: async () => {
startedSessions += 1;
return { id: 'session-2' };
},
userAuth: {
async getWechatAgentRoute() {
return routeCleared ? null : { agentSessionId: 'session-1' };
},
async clearWechatAgentRoute() {
routeCleared = true;
},
async upsertWechatAgentRoute({ agentSessionId }) {
assert.equal(agentSessionId, 'session-2');
},
async resolveWorkingDir() {
return workspaceRoot;
},
async getUserPublishLayout() {
return {
publishDir: workspaceRoot,
displayName: 'John',
username: 'john',
slug: 'john',
constraints: null,
};
},
},
sessionApiFetch: async (sessionId, pathname) => {
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === `/sessions/${sessionId}/reply`) {
fs.mkdirSync(path.dirname(htmlPath), { recursive: true });
if (sessionId === 'session-1') {
fs.writeFileSync(htmlPath, firstHtml, 'utf8');
} else {
fs.writeFileSync(htmlPath, retryHtml, 'utf8');
fs.mkdirSync(path.join(workspaceRoot, 'public', 'images'), { recursive: true });
fs.writeFileSync(path.join(workspaceRoot, 'public', generated.asset.htmlSrc), pageImage);
}
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/sessions/session-1/events') {
return new Response(
replyEvents({ requestId: 'req-thumbnail-first', html: firstHtml }),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === '/sessions/session-2/events') {
return new Response(
replyEvents({ requestId: 'req-thumbnail-retry', html: retryHtml, includeImage: true }),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
throw new Error(`unexpected session path: ${sessionId} ${pathname}`);
},
wechatFetch: async (url, init = {}) => {
if (String(url).includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (String(url).includes('/cgi-bin/message/custom/send')) {
wechatPayloads.push(JSON.parse(init.body));
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = (() => {
const ids = ['req-thumbnail-first', 'req-thumbnail-retry'];
return () => ids.shift() ?? 'req-thumbnail-retry';
})();
try {
const result = await service.handleInboundMessage(
inboundXml({ content: '生成一个活动页面,只要文字,不要正文图片' }),
{ timestamp, nonce, signature: signatureFor(token, timestamp, nonce) },
);
assert.equal(result.status, 200);
await result.task;
assert.equal(fs.existsSync(path.join(workspaceRoot, 'public', 'retry.thumbnail.svg')), true);
} finally {
crypto.randomUUID = originalRandomUuid;
fs.rmSync(workspaceRoot, { recursive: true, force: true });
}
assert.equal(routeCleared, true);
assert.equal(startedSessions, 1);
assert.equal(wechatPayloads.length, 1);
assert.equal(wechatPayloads[0].msgtype, 'text');
assert.match(wechatPayloads[0].text.content, /retry\.html/);
assert.doesNotMatch(wechatPayloads[0].text.content, /没有完成服务号要求的本轮新缩略图/);
});
test('wechat mp standalone image intent sends a native image message and text', async () => {
const token = 'token';
const timestamp = '1710000000';
const nonce = 'nonce';
const sourceImage = await sharp({
create: { width: 48, height: 48, channels: 3, background: '#cc8844' },
}).webp().toBuffer();
const wechatPayloads = [];
const generated = {
ok: true,
jobId: 'job-chat-image',
source: { mimeType: 'image/webp', width: 1024, height: 1024 },
asset: {
id: 'asset-chat-image',
htmlSrc: 'images/chat-image.webp',
publicUrl: 'https://example.com/MindSpace/user-1/public/images/chat-image.webp',
workspaceRelativePath: 'public/images/chat-image.webp',
},
};
const eventFrame = (event) => `data: ${JSON.stringify(event)}\n\n`;
const service = createBoundWechatService({
token,
sessionApiFetch: async (sessionId, pathname, init = {}) => {
assert.equal(sessionId, 'session-1');
if (pathname === '/sessions/session-1/events') {
return new Response(
[
eventFrame({
type: 'Message',
request_id: 'req-chat-image',
message: {
id: 'assistant-tools',
role: 'assistant',
metadata: { userVisible: true },
content: [
{
id: 'call-image',
type: 'toolRequest',
toolCall: {
value: {
name: 'sandbox-fs__generate_image',
arguments: { purpose: 'inline_image' },
},
},
},
{
id: 'call-image',
type: 'toolResponse',
toolResult: {
status: 'success',
value: { content: [{ type: 'text', text: JSON.stringify(generated) }] },
},
},
],
},
}),
eventFrame({
type: 'Message',
request_id: 'req-chat-image',
message: {
id: 'assistant-final',
role: 'assistant',
metadata: { userVisible: true },
content: [{ type: 'text', text: '新图片已经生成。' }],
},
}),
eventFrame({
type: 'Finish',
request_id: 'req-chat-image',
token_state: { inputTokens: 1, outputTokens: 2 },
}),
].join(''),
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
);
}
if (pathname === '/sessions/session-1/reply') {
const body = JSON.parse(init.body);
assert.equal(body.user_message.metadata.memindRun.imageGenerationMode, 'required');
assert.equal(body.user_message.metadata.memindRun.pageThumbnailMode, 'auto');
assert.match(body.user_message.content[0].text, /purpose=`inline_image`/);
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
}
throw new Error(`unexpected session path: ${pathname}`);
},
wechatFetch: async (url, init = {}) => {
const target = String(url);
if (target.includes('/cgi-bin/stable_token')) {
return new Response(JSON.stringify({ access_token: 'access-1', expires_in: 7200 }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (target === generated.asset.publicUrl) {
return new Response(sourceImage, { status: 200, headers: { 'Content-Type': 'image/webp' } });
}
if (target.includes('/cgi-bin/media/upload')) {
assert.ok(init.body instanceof FormData);
return new Response(JSON.stringify({ type: 'image', media_id: 'wx-generated-1' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
if (target.includes('/cgi-bin/message/custom/send')) {
wechatPayloads.push(JSON.parse(init.body));
return new Response(JSON.stringify({ errcode: 0, errmsg: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
}
throw new Error(`unexpected wechat url: ${url}`);
},
});
const originalRandomUuid = crypto.randomUUID;
crypto.randomUUID = () => 'req-chat-image';
try {
const result = await service.handleInboundMessage(
inboundXml({ content: '帮我生成一张雨夜橘猫图片' }),
{ timestamp, nonce, signature: signatureFor(token, timestamp, nonce) },
);
assert.equal(result.status, 200);
await result.task;
} finally {
crypto.randomUUID = originalRandomUuid;
}
assert.deepEqual(wechatPayloads.map((payload) => payload.msgtype), ['image', 'text']);
assert.equal(wechatPayloads[0].image.media_id, 'wx-generated-1');
assert.match(wechatPayloads[1].text.content, /新图片已经生成/);
});