merge: fix WeChat runtime delivery path
Memind CI / Test, build, and release guards (push) Successful in 1m59s
Memind CI / Test, build, and release guards (push) Successful in 1m59s
This commit is contained in:
@@ -268,7 +268,13 @@ test('integration: WeChat Page Data runs Aider review before the delivery contra
|
|||||||
const order = [];
|
const order = [];
|
||||||
const reviewCalls = [];
|
const reviewCalls = [];
|
||||||
const wechatCalls = [];
|
const wechatCalls = [];
|
||||||
|
const submittedMessages = [];
|
||||||
|
const finishedMessages = [];
|
||||||
let failReview = false;
|
let failReview = false;
|
||||||
|
const userAuth = createWechatUserAuth(workspaceRoot);
|
||||||
|
userAuth.finishWechatMpMessage = async (input) => {
|
||||||
|
finishedMessages.push(input);
|
||||||
|
};
|
||||||
|
|
||||||
const service = createWechatMpService({
|
const service = createWechatMpService({
|
||||||
config: {
|
config: {
|
||||||
@@ -286,7 +292,7 @@ test('integration: WeChat Page Data runs Aider review before the delivery contra
|
|||||||
pageDataAiderReviewEnabled: true,
|
pageDataAiderReviewEnabled: true,
|
||||||
pageDataAiderReviewUsers: ['唐'],
|
pageDataAiderReviewUsers: ['唐'],
|
||||||
},
|
},
|
||||||
userAuth: createWechatUserAuth(workspaceRoot),
|
userAuth,
|
||||||
htmlDeliveryAuthority: {
|
htmlDeliveryAuthority: {
|
||||||
async prepareWechatHtmlDelivery() {
|
async prepareWechatHtmlDelivery() {
|
||||||
const artifact = {
|
const artifact = {
|
||||||
@@ -339,7 +345,7 @@ test('integration: WeChat Page Data runs Aider review before the delivery contra
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
sessionApiFetch: async (sessionId, pathname) => {
|
sessionApiFetch: async (sessionId, pathname, init = {}) => {
|
||||||
if (pathname === `/sessions/${sessionId}/events`) {
|
if (pathname === `/sessions/${sessionId}/events`) {
|
||||||
return new Response(
|
return new Response(
|
||||||
[
|
[
|
||||||
@@ -351,6 +357,7 @@ test('integration: WeChat Page Data runs Aider review before the delivery contra
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (pathname === `/sessions/${sessionId}/reply`) {
|
if (pathname === `/sessions/${sessionId}/reply`) {
|
||||||
|
submittedMessages.push(JSON.parse(init.body ?? '{}').user_message);
|
||||||
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
|
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
|
||||||
}
|
}
|
||||||
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
|
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
|
||||||
@@ -377,6 +384,10 @@ test('integration: WeChat Page Data runs Aider review before the delivery contra
|
|||||||
assert.equal(reviewCalls[0].sourceChannel, 'wechat_mp');
|
assert.equal(reviewCalls[0].sourceChannel, 'wechat_mp');
|
||||||
assert.equal(reviewCalls[0].sourceMessageId, '10001');
|
assert.equal(reviewCalls[0].sourceMessageId, '10001');
|
||||||
assert.equal(reviewCalls[0].forcePageData, true);
|
assert.equal(reviewCalls[0].forcePageData, true);
|
||||||
|
assert.equal(
|
||||||
|
submittedMessages[0]?.metadata?.memindRun?.pgRequired,
|
||||||
|
true,
|
||||||
|
);
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
reviewCalls[0].relativePaths,
|
reviewCalls[0].relativePaths,
|
||||||
['public/children-diet-survey.html'],
|
['public/children-diet-survey.html'],
|
||||||
@@ -398,6 +409,11 @@ test('integration: WeChat Page Data runs Aider review before the delivery contra
|
|||||||
assert.ok(failedSend);
|
assert.ok(failedSend);
|
||||||
const failedPayload = JSON.parse(failedSend[2]);
|
const failedPayload = JSON.parse(failedSend[2]);
|
||||||
assert.match(failedPayload.text.content, /Page Data|数据收集|绑定/);
|
assert.match(failedPayload.text.content, /Page Data|数据收集|绑定/);
|
||||||
|
assert.equal(finishedMessages.at(-1)?.status, 'failed');
|
||||||
|
assert.equal(
|
||||||
|
finishedMessages.at(-1)?.agentSessionId,
|
||||||
|
'session-page-data-1',
|
||||||
|
);
|
||||||
assert.doesNotMatch(
|
assert.doesNotMatch(
|
||||||
failedPayload.text.content,
|
failedPayload.text.content,
|
||||||
/https:\/\/m\.tkmind\.cn\/MindSpace\/user-page-data\/public\/children-diet-survey\.html/,
|
/https:\/\/m\.tkmind\.cn\/MindSpace\/user-page-data\/public\/children-diet-survey\.html/,
|
||||||
|
|||||||
@@ -512,6 +512,7 @@ async function bootstrapUserAuth() {
|
|||||||
await bootstrapPortalIntegrationServices({
|
await bootstrapPortalIntegrationServices({
|
||||||
pool,
|
pool,
|
||||||
h5Root: H5_ROOT,
|
h5Root: H5_ROOT,
|
||||||
|
codeRoot: __dirname,
|
||||||
env: process.env,
|
env: process.env,
|
||||||
usersRoot: USERS_ROOT,
|
usersRoot: USERS_ROOT,
|
||||||
wechatMpConfig: WECHAT_MP_CONFIG,
|
wechatMpConfig: WECHAT_MP_CONFIG,
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import {
|
|||||||
export async function bootstrapPortalIntegrationServices({
|
export async function bootstrapPortalIntegrationServices({
|
||||||
pool,
|
pool,
|
||||||
h5Root,
|
h5Root,
|
||||||
|
codeRoot = h5Root,
|
||||||
env = process.env,
|
env = process.env,
|
||||||
usersRoot,
|
usersRoot,
|
||||||
wechatMpConfig,
|
wechatMpConfig,
|
||||||
@@ -76,7 +77,9 @@ export async function bootstrapPortalIntegrationServices({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const wechatMp = await loadWechatMpModuleFn(h5Root);
|
const wechatMpRoot = codeRoot || h5Root;
|
||||||
|
logger.log?.(`[WeChat MP] Loading runtime module from code root: ${wechatMpRoot}`);
|
||||||
|
const wechatMp = await loadWechatMpModuleFn(wechatMpRoot);
|
||||||
const pageDataDeliveryReviewer =
|
const pageDataDeliveryReviewer =
|
||||||
wechatMpConfig?.pageDataAiderReviewEnabled === true
|
wechatMpConfig?.pageDataAiderReviewEnabled === true
|
||||||
? createPageDataDeliveryCodeReviewServiceFn({
|
? createPageDataDeliveryCodeReviewServiceFn({
|
||||||
@@ -122,6 +125,7 @@ export async function bootstrapPortalIntegrationServices({
|
|||||||
tkmindProxy.startSessionForUser(userId, {
|
tkmindProxy.startSessionForUser(userId, {
|
||||||
workingDir,
|
workingDir,
|
||||||
sessionPolicy,
|
sessionPolicy,
|
||||||
|
origin: 'wechat',
|
||||||
}),
|
}),
|
||||||
sessionApiFetch: async (
|
sessionApiFetch: async (
|
||||||
sessionId,
|
sessionId,
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ function createSetup(overrides = {}) {
|
|||||||
const options = {
|
const options = {
|
||||||
pool,
|
pool,
|
||||||
h5Root: '/app',
|
h5Root: '/app',
|
||||||
|
codeRoot: '/runtime',
|
||||||
env: {
|
env: {
|
||||||
H5_SCHEDULE_ENABLED: '1',
|
H5_SCHEDULE_ENABLED: '1',
|
||||||
H5_REMINDER_WORKER_ENABLED: '1',
|
H5_REMINDER_WORKER_ENABLED: '1',
|
||||||
@@ -236,6 +237,10 @@ test('preserves WeChat service configuration and proxy callbacks', async () => {
|
|||||||
);
|
);
|
||||||
const { wechatOptions } = setup.getCaptured();
|
const { wechatOptions } = setup.getCaptured();
|
||||||
|
|
||||||
|
assert.deepEqual(
|
||||||
|
setup.calls.find(([kind]) => kind === 'load-wechat'),
|
||||||
|
['load-wechat', '/runtime'],
|
||||||
|
);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
wechatOptions.pageDataFinishGuard,
|
wechatOptions.pageDataFinishGuard,
|
||||||
setup.options.mindSpacePublicFinish,
|
setup.options.mindSpacePublicFinish,
|
||||||
@@ -268,6 +273,18 @@ test('preserves WeChat service configuration and proxy callbacks', async () => {
|
|||||||
}),
|
}),
|
||||||
{ sessionId: 'session-1' },
|
{ sessionId: 'session-1' },
|
||||||
);
|
);
|
||||||
|
assert.deepEqual(
|
||||||
|
setup.calls.find(([kind]) => kind === 'start-session'),
|
||||||
|
[
|
||||||
|
'start-session',
|
||||||
|
'user-1',
|
||||||
|
{
|
||||||
|
workingDir: '/workspace',
|
||||||
|
sessionPolicy: { sandboxed: true },
|
||||||
|
origin: 'wechat',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
await wechatOptions.sessionApiFetch(
|
await wechatOptions.sessionApiFetch(
|
||||||
'session-1',
|
'session-1',
|
||||||
|
|||||||
+7
-1
@@ -1430,6 +1430,7 @@ export function createTkmindProxy({
|
|||||||
sessionPolicy = null,
|
sessionPolicy = null,
|
||||||
recipe = null,
|
recipe = null,
|
||||||
disableImageReading = false,
|
disableImageReading = false,
|
||||||
|
origin = 'h5',
|
||||||
} = {},
|
} = {},
|
||||||
) {
|
) {
|
||||||
const resolvedWorkingDir = workingDir ?? await userAuth.resolveWorkingDir(userId);
|
const resolvedWorkingDir = workingDir ?? await userAuth.resolveWorkingDir(userId);
|
||||||
@@ -1458,7 +1459,12 @@ export function createTkmindProxy({
|
|||||||
throw new Error('创建会话失败:缺少 session id');
|
throw new Error('创建会话失败:缺少 session id');
|
||||||
}
|
}
|
||||||
await rememberSessionTarget(session.id, startTarget);
|
await rememberSessionTarget(session.id, startTarget);
|
||||||
await sessionStore.registerAgentSession(userId, session.id, startTarget);
|
await sessionStore.registerSession({
|
||||||
|
userId,
|
||||||
|
sessionId: session.id,
|
||||||
|
target: startTarget,
|
||||||
|
origin: origin === 'wechat' ? 'wechat' : 'h5',
|
||||||
|
});
|
||||||
const baseSessionScopedPolicy =
|
const baseSessionScopedPolicy =
|
||||||
typeof userAuth.getAgentSessionPolicy ===
|
typeof userAuth.getAgentSessionPolicy ===
|
||||||
'function'
|
'function'
|
||||||
|
|||||||
@@ -1006,6 +1006,34 @@ test('startSessionForUser resolves memories through Memory V2 facade', async ()
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('startSessionForUser records WeChat origin without changing the selected target', async () => {
|
||||||
|
const registrations = [];
|
||||||
|
let selectedTarget = '';
|
||||||
|
await withFakeGoosedSession(async ({ apiTarget, workingDir }) => {
|
||||||
|
selectedTarget = apiTarget;
|
||||||
|
const proxy = createTkmindProxy({
|
||||||
|
apiTarget,
|
||||||
|
apiSecret: 'test-secret',
|
||||||
|
userAuth: createMemoryTestUserAuth(workingDir),
|
||||||
|
sessionAccess: {
|
||||||
|
enabled: true,
|
||||||
|
async registerSession(input) {
|
||||||
|
registrations.push(input);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await proxy.startSessionForUser('user-1', { origin: 'wechat' });
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepEqual(registrations, [{
|
||||||
|
userId: 'user-1',
|
||||||
|
sessionId: 'session-1',
|
||||||
|
target: selectedTarget,
|
||||||
|
origin: 'wechat',
|
||||||
|
}]);
|
||||||
|
});
|
||||||
|
|
||||||
test('startSessionForUser does not inject memories when Memory V2 is disabled', async () => {
|
test('startSessionForUser does not inject memories when Memory V2 is disabled', async () => {
|
||||||
let legacyTouched = false;
|
let legacyTouched = false;
|
||||||
await withFakeGoosedSession(async ({ apiTarget, workingDir, harnessEntries }) => {
|
await withFakeGoosedSession(async ({ apiTarget, workingDir, harnessEntries }) => {
|
||||||
|
|||||||
+62
-5
@@ -65,6 +65,7 @@ const DEFAULT_ASR_TARGET = process.env.H5_ASR_TARGET ?? 'https://asr.tkmind.cn';
|
|||||||
const WECHAT_RECENT_MEDIA_TTL_MS = 15 * 60 * 1000;
|
const WECHAT_RECENT_MEDIA_TTL_MS = 15 * 60 * 1000;
|
||||||
const WECHAT_RECENT_IMAGE_MAX_COUNT = 10;
|
const WECHAT_RECENT_IMAGE_MAX_COUNT = 10;
|
||||||
const DEFAULT_WECHAT_AGENT_REPLY_TIMEOUT_MS = 15 * 60 * 1000;
|
const DEFAULT_WECHAT_AGENT_REPLY_TIMEOUT_MS = 15 * 60 * 1000;
|
||||||
|
const WECHAT_SESSION_RESET_ACK_TEXT = '已切换到新会话,请发送你的新需求。';
|
||||||
export { loadWechatMpConfig };
|
export { loadWechatMpConfig };
|
||||||
const PUBLIC_HTML_LINK_PATTERN =
|
const PUBLIC_HTML_LINK_PATTERN =
|
||||||
/https?:\/\/[^\s<>"')\]]+\/MindSpace\/([0-9a-f-]{36}|[a-z0-9._-]+)\/public\/([^\s<>"')\]]+\.html)/gi;
|
/https?:\/\/[^\s<>"')\]]+\/MindSpace\/([0-9a-f-]{36}|[a-z0-9._-]+)\/public\/([^\s<>"')\]]+\.html)/gi;
|
||||||
@@ -1517,6 +1518,7 @@ export function createWechatMpService({
|
|||||||
expiresAt: 0,
|
expiresAt: 0,
|
||||||
};
|
};
|
||||||
const rememberedWechatContexts = new Map();
|
const rememberedWechatContexts = new Map();
|
||||||
|
const confirmedWechatSessionOrigins = new Set();
|
||||||
const messageTasksByOpenid = new Map();
|
const messageTasksByOpenid = new Map();
|
||||||
const recentMediaByOpenid = new Map();
|
const recentMediaByOpenid = new Map();
|
||||||
let jsapiTicketCache = {
|
let jsapiTicketCache = {
|
||||||
@@ -1527,6 +1529,19 @@ export function createWechatMpService({
|
|||||||
const fetchForSession = (sessionId, pathname, init) =>
|
const fetchForSession = (sessionId, pathname, init) =>
|
||||||
sessionApiFetch ? sessionApiFetch(sessionId, pathname, init) : apiFetch(pathname, init);
|
sessionApiFetch ? sessionApiFetch(sessionId, pathname, init) : apiFetch(pathname, init);
|
||||||
|
|
||||||
|
const confirmWechatSessionOrigin = async (sessionId) => {
|
||||||
|
if (!sessionId || confirmedWechatSessionOrigins.has(sessionId)) return;
|
||||||
|
if (typeof userAuth.setSessionOrigin === 'function') {
|
||||||
|
try {
|
||||||
|
await userAuth.setSessionOrigin(sessionId, 'wechat');
|
||||||
|
} catch (err) {
|
||||||
|
logger.warn?.('WeChat MP session origin update failed:', err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
confirmedWechatSessionOrigins.add(sessionId);
|
||||||
|
};
|
||||||
|
|
||||||
const enqueueMessageTask = (openid, taskFactory) => {
|
const enqueueMessageTask = (openid, taskFactory) => {
|
||||||
const key = String(openid ?? '').trim();
|
const key = String(openid ?? '').trim();
|
||||||
const previous = messageTasksByOpenid.get(key) ?? Promise.resolve();
|
const previous = messageTasksByOpenid.get(key) ?? Promise.resolve();
|
||||||
@@ -2038,6 +2053,7 @@ export function createWechatMpService({
|
|||||||
sessionPolicy,
|
sessionPolicy,
|
||||||
).catch(() => false);
|
).catch(() => false);
|
||||||
if (routeHasTools) {
|
if (routeHasTools) {
|
||||||
|
await confirmWechatSessionOrigin(existingRoute.agentSessionId);
|
||||||
if (typeof userAuth.touchWechatAgentRoute === 'function') {
|
if (typeof userAuth.touchWechatAgentRoute === 'function') {
|
||||||
await userAuth.touchWechatAgentRoute(config.appId, openid, now).catch((err) => {
|
await userAuth.touchWechatAgentRoute(config.appId, openid, now).catch((err) => {
|
||||||
logger.warn?.('WeChat MP route touch failed:', err);
|
logger.warn?.('WeChat MP route touch failed:', err);
|
||||||
@@ -2057,7 +2073,12 @@ export function createWechatMpService({
|
|||||||
throw new Error(gate.message || '当前用户无法使用聊天能力');
|
throw new Error(gate.message || '当前用户无法使用聊天能力');
|
||||||
}
|
}
|
||||||
const started = startAgentSession
|
const started = startAgentSession
|
||||||
? await startAgentSession({ userId, workingDir, sessionPolicy })
|
? await startAgentSession({
|
||||||
|
userId,
|
||||||
|
workingDir,
|
||||||
|
sessionPolicy,
|
||||||
|
origin: 'wechat',
|
||||||
|
})
|
||||||
: await readJsonResponse(
|
: await readJsonResponse(
|
||||||
await apiFetch('/agent/start', {
|
await apiFetch('/agent/start', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -2094,6 +2115,7 @@ export function createWechatMpService({
|
|||||||
origin: 'wechat',
|
origin: 'wechat',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
await confirmWechatSessionOrigin(sessionId);
|
||||||
await reconcileAgentSession(
|
await reconcileAgentSession(
|
||||||
(pathname, init) => fetchForSession(sessionId, pathname, init),
|
(pathname, init) => fetchForSession(sessionId, pathname, init),
|
||||||
sessionId,
|
sessionId,
|
||||||
@@ -2167,7 +2189,14 @@ export function createWechatMpService({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const buildIntentMetadata = (intent, { mediaAnalysisEnabled = false, imagePolicy = null } = {}) => {
|
const buildIntentMetadata = (
|
||||||
|
intent,
|
||||||
|
{
|
||||||
|
mediaAnalysisEnabled = false,
|
||||||
|
imagePolicy = null,
|
||||||
|
pgRequired = false,
|
||||||
|
} = {},
|
||||||
|
) => {
|
||||||
const mediaPublicUrl = intent.media?.publicUrl || null;
|
const mediaPublicUrl = intent.media?.publicUrl || null;
|
||||||
const mediaItems = Array.isArray(intent.recentMediaItems) && intent.recentMediaItems.length > 0
|
const mediaItems = Array.isArray(intent.recentMediaItems) && intent.recentMediaItems.length > 0
|
||||||
? intent.recentMediaItems
|
? intent.recentMediaItems
|
||||||
@@ -2199,7 +2228,10 @@ export function createWechatMpService({
|
|||||||
recognition: intent.msgType === 'voice' ? intent.agentText || null : null,
|
recognition: intent.msgType === 'voice' ? intent.agentText || null : null,
|
||||||
location: intent.location || null,
|
location: intent.location || null,
|
||||||
link: intent.link || null,
|
link: intent.link || null,
|
||||||
memindRun: buildWechatImageRunMetadata(imagePolicy),
|
memindRun: {
|
||||||
|
...buildWechatImageRunMetadata(imagePolicy),
|
||||||
|
...(pgRequired ? { pgRequired: true } : {}),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2348,6 +2380,14 @@ export function createWechatMpService({
|
|||||||
});
|
});
|
||||||
let sessionId = route.sessionId;
|
let sessionId = route.sessionId;
|
||||||
await ensureSessionProvider(sessionId);
|
await ensureSessionProvider(sessionId);
|
||||||
|
if (wechatIntent.kind === 'session.reset') {
|
||||||
|
await sendCustomerServiceText(
|
||||||
|
inbound.fromUserName,
|
||||||
|
WECHAT_SESSION_RESET_ACK_TEXT,
|
||||||
|
user,
|
||||||
|
);
|
||||||
|
return { sessionId };
|
||||||
|
}
|
||||||
await rememberWechatUserContext(sessionId, user, { forceBootstrap: route.isNewSession });
|
await rememberWechatUserContext(sessionId, user, { forceBootstrap: route.isNewSession });
|
||||||
let finished = false;
|
let finished = false;
|
||||||
let progressTimer = null;
|
let progressTimer = null;
|
||||||
@@ -2384,7 +2424,11 @@ export function createWechatMpService({
|
|||||||
sessionId,
|
sessionId,
|
||||||
requestId,
|
requestId,
|
||||||
agentPrompt,
|
agentPrompt,
|
||||||
buildIntentMetadata(intent, { mediaAnalysisEnabled, imagePolicy }),
|
buildIntentMetadata(intent, {
|
||||||
|
mediaAnalysisEnabled,
|
||||||
|
imagePolicy,
|
||||||
|
pgRequired: isPageDataRequest,
|
||||||
|
}),
|
||||||
{
|
{
|
||||||
prepareUserMessage: (userMessage) => prepareWechatAgentUserMessage({
|
prepareUserMessage: (userMessage) => prepareWechatAgentUserMessage({
|
||||||
userId: user.userId,
|
userId: user.userId,
|
||||||
@@ -2574,6 +2618,14 @@ export function createWechatMpService({
|
|||||||
});
|
});
|
||||||
return { sessionId };
|
return { sessionId };
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
if (
|
||||||
|
err &&
|
||||||
|
typeof err === 'object' &&
|
||||||
|
sessionId &&
|
||||||
|
!err.wechatAgentSessionId
|
||||||
|
) {
|
||||||
|
err.wechatAgentSessionId = sessionId;
|
||||||
|
}
|
||||||
const message = err instanceof Error ? err.message : String(err);
|
const message = err instanceof Error ? err.message : String(err);
|
||||||
if (err?.code === 'WECHAT_AGENT_REPLY_TIMEOUT') {
|
if (err?.code === 'WECHAT_AGENT_REPLY_TIMEOUT') {
|
||||||
await userAuth.clearWechatAgentRoute(config.appId, inbound.fromUserName).catch((clearErr) => {
|
await userAuth.clearWechatAgentRoute(config.appId, inbound.fromUserName).catch((clearErr) => {
|
||||||
@@ -2628,7 +2680,11 @@ export function createWechatMpService({
|
|||||||
sessionId,
|
sessionId,
|
||||||
retryId,
|
retryId,
|
||||||
retryPrompt,
|
retryPrompt,
|
||||||
buildIntentMetadata(intent, { mediaAnalysisEnabled, imagePolicy }),
|
buildIntentMetadata(intent, {
|
||||||
|
mediaAnalysisEnabled,
|
||||||
|
imagePolicy,
|
||||||
|
pgRequired: isPageDataRequest,
|
||||||
|
}),
|
||||||
{
|
{
|
||||||
prepareUserMessage: (userMessage) => prepareWechatAgentUserMessage({
|
prepareUserMessage: (userMessage) => prepareWechatAgentUserMessage({
|
||||||
userId: user.userId,
|
userId: user.userId,
|
||||||
@@ -3284,6 +3340,7 @@ export function createWechatMpService({
|
|||||||
openid: inbound.fromUserName,
|
openid: inbound.fromUserName,
|
||||||
msgId: inbound.msgId,
|
msgId: inbound.msgId,
|
||||||
status: 'failed',
|
status: 'failed',
|
||||||
|
agentSessionId: err?.wechatAgentSessionId ?? null,
|
||||||
}).catch(() => {});
|
}).catch(() => {});
|
||||||
}
|
}
|
||||||
logger.error?.('WeChat MP background reply failed:', err);
|
logger.error?.('WeChat MP background reply failed:', err);
|
||||||
|
|||||||
@@ -333,6 +333,94 @@ 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' }, '换新会话'), true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('WeChat session reset acknowledges without sending the control text to Agent', async () => {
|
||||||
|
const token = 'token';
|
||||||
|
const timestamp = '1710000000';
|
||||||
|
const nonce = 'nonce';
|
||||||
|
const sentPayloads = [];
|
||||||
|
const finishedMessages = [];
|
||||||
|
let activeRoute = { agentSessionId: 'session-old' };
|
||||||
|
let startedSessions = 0;
|
||||||
|
let agentReplies = 0;
|
||||||
|
const sessionOrigins = [];
|
||||||
|
|
||||||
|
const service = createBoundWechatService({
|
||||||
|
token,
|
||||||
|
startAgentSession: async (input) => {
|
||||||
|
startedSessions += 1;
|
||||||
|
assert.equal(input.origin, 'wechat');
|
||||||
|
return { id: 'session-new' };
|
||||||
|
},
|
||||||
|
userAuth: {
|
||||||
|
async getWechatAgentRoute() {
|
||||||
|
return activeRoute;
|
||||||
|
},
|
||||||
|
async clearWechatAgentRoute() {
|
||||||
|
activeRoute = null;
|
||||||
|
},
|
||||||
|
async upsertWechatAgentRoute({ agentSessionId }) {
|
||||||
|
activeRoute = { agentSessionId };
|
||||||
|
},
|
||||||
|
async setSessionOrigin(sessionId, origin) {
|
||||||
|
sessionOrigins.push({ sessionId, origin });
|
||||||
|
},
|
||||||
|
async finishWechatMpMessage(input) {
|
||||||
|
finishedMessages.push(input);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sessionApiFetch: async (_sessionId, pathname) => {
|
||||||
|
if (pathname.includes('/reply')) {
|
||||||
|
agentReplies += 1;
|
||||||
|
throw new Error('session reset must not reach Agent');
|
||||||
|
}
|
||||||
|
return new Response('{}', {
|
||||||
|
status: 200,
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
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}`);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await service.handleInboundMessage(
|
||||||
|
inboundXml({ content: '新会话' }),
|
||||||
|
{
|
||||||
|
timestamp,
|
||||||
|
nonce,
|
||||||
|
signature: signatureFor(token, timestamp, nonce),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
await result.task;
|
||||||
|
|
||||||
|
assert.equal(startedSessions, 1);
|
||||||
|
assert.equal(agentReplies, 0);
|
||||||
|
assert.equal(activeRoute?.agentSessionId, 'session-new');
|
||||||
|
assert.deepEqual(sessionOrigins, [{
|
||||||
|
sessionId: 'session-new',
|
||||||
|
origin: 'wechat',
|
||||||
|
}]);
|
||||||
|
assert.match(sentPayloads[0]?.text?.content ?? '', /已切换到新会话/);
|
||||||
|
assert.equal(finishedMessages.at(-1)?.status, 'done');
|
||||||
|
assert.equal(finishedMessages.at(-1)?.agentSessionId, 'session-new');
|
||||||
|
});
|
||||||
|
|
||||||
test('WeChat Page Data repair intent receives the guarded repair workflow', () => {
|
test('WeChat Page Data repair intent receives the guarded repair workflow', () => {
|
||||||
assert.equal(isWechatPageDataTask('提交失败,数据没有保存成功'), true);
|
assert.equal(isWechatPageDataTask('提交失败,数据没有保存成功'), true);
|
||||||
assert.equal(isWechatPageDataTask('修复问卷的 Page Data 绑定'), true);
|
assert.equal(isWechatPageDataTask('修复问卷的 Page Data 绑定'), true);
|
||||||
|
|||||||
Reference in New Issue
Block a user