mirror of
https://github.com/hansjone/dsh-im-ops.git
synced 2026-10-14 16:50:47 +08:00
feat(feishu): isolate Harness sessions per topic in topic groups
conversationKey derived the group session key from chat_id alone, so in
Feishu topic groups every topic shared one Harness session: context,
/new, pending approvals, and batch-input state all leaked across topics.
Append the event's thread_id to the session key when present. Topic
groups stamp every message with a thread_id, so each topic now maps to
its own group:<chat_id>🧵<thread_id> session. Regular group chats
carry no thread_id and keep the single shared group:<chat_id> key; p2p
keys are untouched.
- message-utils.mjs: thread-aware conversationKey
- message-utils.test.mjs: topic isolation + regular-group fallback cases
- lib/index.js: regenerated host bundle
This commit is contained in:
parent
5ac63dd26b
commit
da4114dfe3
3 changed files with 330 additions and 214 deletions
507
lib/index.js
507
lib/index.js
File diff suppressed because one or more lines are too long
|
|
@ -16,6 +16,11 @@ export function conversationKey(event) {
|
||||||
}
|
}
|
||||||
const chatId = event?.message?.chat_id;
|
const chatId = event?.message?.chat_id;
|
||||||
if (!chatId) throw new Error('Feishu group event has no chat id');
|
if (!chatId) throw new Error('Feishu group event has no chat id');
|
||||||
|
// Topic groups: every message belongs to a thread, so key the session per
|
||||||
|
// thread to keep each topic's Harness conversation isolated. Regular group
|
||||||
|
// chats carry no thread_id and keep the single shared `group:<chat_id>` key.
|
||||||
|
const threadId = event?.message?.thread_id;
|
||||||
|
if (typeof threadId === 'string' && threadId.trim()) return `group:${chatId}:thread:${threadId}`;
|
||||||
return `group:${chatId}`;
|
return `group:${chatId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -232,6 +232,38 @@ test('conversationKey isolates p2p users and groups', () => {
|
||||||
}), 'group:oc_group');
|
}), 'group:oc_group');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('conversationKey isolates topic-group threads without affecting regular groups', () => {
|
||||||
|
// Topic groups: each message belongs to a thread, so every topic gets its own session.
|
||||||
|
assert.equal(conversationKey({
|
||||||
|
sender: { sender_id: { open_id: 'ou_test' } },
|
||||||
|
message: { chat_type: 'group', chat_id: 'oc_topic_group', thread_id: 'om_thread_a' },
|
||||||
|
}), 'group:oc_topic_group:thread:om_thread_a');
|
||||||
|
assert.equal(conversationKey({
|
||||||
|
sender: { sender_id: { open_id: 'ou_other' } },
|
||||||
|
message: { chat_type: 'group', chat_id: 'oc_topic_group', thread_id: 'om_thread_b' },
|
||||||
|
}), 'group:oc_topic_group:thread:om_thread_b');
|
||||||
|
assert.notEqual(
|
||||||
|
conversationKey({
|
||||||
|
sender: { sender_id: { open_id: 'ou_test' } },
|
||||||
|
message: { chat_type: 'group', chat_id: 'oc_topic_group', thread_id: 'om_thread_a' },
|
||||||
|
}),
|
||||||
|
conversationKey({
|
||||||
|
sender: { sender_id: { open_id: 'ou_test' } },
|
||||||
|
message: { chat_type: 'group', chat_id: 'oc_topic_group', thread_id: 'om_thread_b' },
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
// Regular group chats: no thread_id keeps the single shared group key.
|
||||||
|
assert.equal(conversationKey({
|
||||||
|
sender: { sender_id: { open_id: 'ou_test' } },
|
||||||
|
message: { chat_type: 'group', chat_id: 'oc_group' },
|
||||||
|
}), 'group:oc_group');
|
||||||
|
// Blank thread_id values fall back to the shared group key.
|
||||||
|
assert.equal(conversationKey({
|
||||||
|
sender: { sender_id: { open_id: 'ou_test' } },
|
||||||
|
message: { chat_type: 'group', chat_id: 'oc_group', thread_id: ' ' },
|
||||||
|
}), 'group:oc_group');
|
||||||
|
});
|
||||||
|
|
||||||
test('splitText preserves all text', () => {
|
test('splitText preserves all text', () => {
|
||||||
const input = `${'a'.repeat(12)}\n${'b'.repeat(12)}`;
|
const input = `${'a'.repeat(12)}\n${'b'.repeat(12)}`;
|
||||||
const chunks = splitText(input, 15);
|
const chunks = splitText(input, 15);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue