fix: tighten WeChat session rotation and defer snapshot refresh

Use session snapshot message counts alongside WeChat route counts so long-lived dedicated sessions rotate before Goose history bloats, and refresh snapshots asynchronously so customer replies are not blocked.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-07-04 12:21:14 +08:00
parent bef72b2768
commit 8c72d0c950
3 changed files with 127 additions and 8 deletions
+84
View File
@@ -1424,6 +1424,90 @@ test('wechat mp service rotates a dedicated session after the message-count thre
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';