feat(context-enhancement): 新增 chatId 与 threadId 来源字段 (#128)

Add chatId and threadId source fields across all nine IM channels, update UI/docs/tests, reconcile the latest main branch, and credit @Chan-0312 as a contributor.
This commit is contained in:
Chan 2026-09-02 23:28:53 +08:00 • committed by GitHub
parent 3c71712962
commit 3562b9244e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 266 additions and 158 deletions

View file

@ -1136,6 +1136,7 @@ export class DingtalkHarnessBridge {
senderId: sender,
senderName: message.senderNick,
conversationTitle: message.conversationTitle,
chatId: message.conversationId,
}));
contextEnhanced = content !== originalContent;
}

View file

@ -3330,6 +3330,8 @@ export class FeishuHarnessBridge {
content = enhanceContextContent(originalContent, snapshot, () => ({
channel: 'feishu',
senderId: senderOpenId(event),
chatId: event.message.chat_id,
threadId: event.message.thread_id,
}));
contextEnhanced = content !== originalContent;
}

View file

@ -890,6 +890,7 @@ export class QqHarnessBridge {
channel: 'qq',
senderId: sender,
senderName: message.kind === 'group' ? message.senderName : undefined,
chatId: message.kind === 'group' ? message.groupOpenid : message.senderId,
}));
contextEnhanced = content !== originalContent;
}

View file

@ -1,6 +1,7 @@
// Shared by the Host and settings UI; keep this module browser-compatible.
export const CONTEXT_ENHANCEMENT_FIELDS = Object.freeze([
'channel', 'conversationType', 'senderId', 'senderName', 'conversationTitle', 'botId',
'channel', 'conversationType', 'senderId', 'senderName', 'conversationTitle',
'chatId', 'threadId', 'botId',
]);
export const CONTEXT_ENHANCEMENT_GUIDANCE_MAX_LENGTH = 8_000;
@ -38,7 +39,7 @@ const CHANNELS = new Set([
]);
const SOURCE_LIMITS = {
channel: 16, conversationType: 6, senderId: 256, senderName: 256,
conversationTitle: 256, botId: 128,
conversationTitle: 256, chatId: 256, threadId: 256, botId: 128,
};
const CONTROL_CHARACTERS = /[\u0000-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/g;
@ -64,7 +65,7 @@ function validateContextEnhancementScope(input) {
throw invalidConfig('群聊和私聊开关必须是布尔值。');
}
if (!Array.isArray(fields) || ![...fields].every((field) => CONTEXT_ENHANCEMENT_FIELDS.includes(field))) {
throw invalidConfig('来源字段只能选择已定义的六个字段。');
throw invalidConfig('来源字段只能选择已定义的八个字段。');
}
if (typeof guidance !== 'string' || guidance.length > CONTEXT_ENHANCEMENT_GUIDANCE_MAX_LENGTH) {
throw invalidConfig(`增强提示词不得超过 ${CONTEXT_ENHANCEMENT_GUIDANCE_MAX_LENGTH} 个字符。`);
@ -150,7 +151,7 @@ function sourceString(value, field) {
function sourceBlock(snapshot, sourceFactory) {
const { fields } = snapshot.config;
const needsSource = fields.some((field) => [
'channel', 'senderId', 'senderName', 'conversationTitle',
'channel', 'senderId', 'senderName', 'conversationTitle', 'chatId', 'threadId',
].includes(field));
const source = needsSource ? sourceFactory?.() : null;
const projected = {};

View file

@ -683,11 +683,14 @@ export class TextHarnessBridge {
let contextEnhanced = false;
if (snapshot) {
const originalContent = content ?? text;
const contextSource = message.contextSource?.();
content = enhanceContextContent(originalContent, snapshot, () => ({
channel: this.#descriptor.key,
senderId,
senderName: message.contextSource?.()?.senderName,
conversationTitle: message.contextSource?.()?.conversationTitle,
senderName: contextSource?.senderName,
conversationTitle: contextSource?.conversationTitle,
chatId: contextSource?.chatId ?? message.conversationId,
threadId: contextSource?.threadId,
}));
contextEnhanced = content !== originalContent;
}

View file

@ -172,6 +172,10 @@ export function normalizeSlackEvent(payload, botUserId, {
senderIsBot: String(event.user) === String(botUserId),
kind: direct ? 'direct' : 'group',
conversationId: direct ? String(event.channel) : `${event.channel}:${threadTs}`,
contextSource: () => ({
chatId: String(event.channel),
threadId: event.thread_ts ? String(event.thread_ts) : undefined,
}),
content: stripBotMention(event.text ?? '', botUserId),
plainText: !Array.isArray(event.files) || event.files.length === 0,
images: Array.isArray(event.files)

View file

@ -266,6 +266,8 @@ export function normalizeTelegramUpdate(update, {
.filter((value) => typeof value === 'string' && value.trim())
.map((value) => value.trim()).join(' ') || message.from?.username,
conversationTitle: direct ? undefined : message.chat?.title,
chatId: String(chatId),
threadId: messageThreadId === undefined ? undefined : String(messageThreadId),
}),
senderIsBot: message.from?.is_bot === true,
kind: direct ? 'direct' : 'group',

View file

@ -1056,6 +1056,7 @@ export class WecomHarnessBridge {
content = enhanceContextContent(originalContent, snapshot, () => ({
channel: 'wecom',
senderId,
chatId,
}));
contextEnhanced = content !== originalContent;
}

View file

@ -786,6 +786,7 @@ export class WeixinHarnessBridge {
content = enhanceContextContent(originalContent, snapshot, () => ({
channel: 'weixin',
senderId: sender,
chatId: sender,
}));
contextEnhanced = content !== originalContent;
}