fix: preserve quoted message context across channels

This commit is contained in:
xmanrui 2026-09-02 01:27:35 +08:00
parent c2be2389b0
commit 94e114b9b3
46 changed files with 4242 additions and 321 deletions

View file

@ -281,6 +281,55 @@ function testClient() {
};
}
test('Enterprise WeChat maps its native quote snapshot without changing current content', () => {
const inbound = wecomInboundMessage(frame({
msgid: 'wecom-quote-normalize',
text: { content: '继续分析' },
quote: {
msgtype: 'mixed',
mixed: { msg_item: [
{ msgtype: 'text', text: { content: '被引用的结论' } },
{ msgtype: 'image', image: { url: 'https://wecom.example/quoted-image' } },
] },
},
}), {});
assert.equal(inbound.content, '继续分析');
assert.deepEqual(inbound.replyTo, {
content: '被引用的结论',
attachments: [{ kind: 'image' }],
});
});
test('Enterprise WeChat sends quote context to Harness but does not execute quoted commands', async () => {
const transport = testClient();
const fixture = state();
let prompt;
let clears = 0;
fixture.clearSession = async () => { clears += 1; };
const bridge = new WecomHarnessBridge({
client: transport.client,
generateStreamId: () => 'stream-quote',
harness: {
sessionExists: async () => true,
ask: async (_sessionId, content) => { prompt = content; return '已处理'; },
},
state: fixture,
});
await bridge.accept(frame({
msgid: 'wecom-quote-prompt',
text: { content: '这条指令是什么意思?' },
quote: { msgtype: 'text', text: { content: '/new' } },
}));
assert.equal(clears, 0);
assert.equal(Array.isArray(prompt), true);
assert.match(prompt[0].text, /<dsh_im_reply_to>/);
assert.match(prompt[0].text, /"content":"\/new"/);
assert.deepEqual(prompt.at(-1), { type: 'text', text: '这条指令是什么意思?' });
});
async function committedArtifact(t, fileName, content, suffix) {
const workspace = await mkdtemp(join(tmpdir(), `dsh-im-wecom-artifact-${suffix}-`));
t.after(() => rm(workspace, { recursive: true, force: true }));
@ -2164,6 +2213,11 @@ test('Enterprise WeChat batch input collects ten texts and submits one ordered H
});
await bridge.accept(frame({ msgid: 'batch-start', text: { content: '/batch' } }));
await bridge.accept(frame({
msgid: 'batch-quote',
text: { content: '企微引用不能收录' },
quote: { msgtype: 'text', text: { content: '被引用内容' } },
}));
for (let index = 1; index <= 10; index += 1) {
await bridge.accept(frame({
msgid: `batch-item-${index}`,
@ -2173,6 +2227,7 @@ test('Enterprise WeChat batch input collects ten texts and submits one ordered H
await bridge.accept(frame({ msgid: 'batch-overflow', text: { content: '不会收录' } }));
assert.equal(prompts.length, 0);
assert.equal(transport.streamed.some(({ content }) => /引用消息.*未收录/s.test(content)), true);
assert.equal(transport.streamed.some(({ content }) => /10\/10.*已满/.test(content)), true);
assert.equal(transport.streamed.some(({ content }) => /这条消息未收录/.test(content)), true);
@ -2180,7 +2235,7 @@ test('Enterprise WeChat batch input collects ten texts and submits one ordered H
assert.equal(prompts.length, 1);
assert.match(prompts[0], /\[消息 1\]\n企微内容 1/);
assert.match(prompts[0], /\[消息 10\]\n企微内容 10/);
assert.doesNotMatch(prompts[0], /不会收录/);
assert.doesNotMatch(prompts[0], /企微引用不能收录|不会收录/);
assert.equal(transport.streamed.at(-1).content, streamedAnswer('批量完成'));
});