feat: add message source context enhancement

This commit is contained in:
xmanrui 2026-08-29 10:46:06 +08:00
parent 5928b33057
commit 0cb79f74e9
69 changed files with 6217 additions and 597 deletions

View file

@ -109,6 +109,7 @@ export async function createProductionController(ctx, config = {}, internals = {
clientSecret,
harness: workspaceScope.harness,
state: workspaceScope.state,
contextEnhancement: { botId, getSettings: () => workspaces.contextEnhancementFor(botId) },
replyTimeoutMs: config.replyTimeoutMs ?? 600_000,
maxMessageChars: config.maxMessageChars ?? 4_000,
connectTimeoutMs: config.connectTimeoutMs ?? 15_000,

View file

@ -1,4 +1,5 @@
import QRCode from 'qrcode';
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
import { publicWorkspaceError, SET_WORKSPACE_ENDPOINT, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
import { SET_AGENT_PRESET_ENDPOINT, validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
@ -18,6 +19,7 @@ export const DINGTALK_ENDPOINTS = Object.freeze({
deleteBot: 'bot.delete',
setWorkspace: SET_WORKSPACE_ENDPOINT,
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
approveSender: 'bot.sender.approve',
revokeSender: 'bot.sender.revoke',
});
@ -94,6 +96,10 @@ function payloadFailure(endpoint, payload) {
return validAgentPresetPayload(payload)
? null : '请选择 Agent Preset。';
}
if (endpoint === DINGTALK_ENDPOINTS.setContextEnhancement) {
return validContextEnhancementPayload(payload)
? null : '请提交有效的上下文增强设置。';
}
if (endpoint === DINGTALK_ENDPOINTS.approveSender) {
return exactKeys(payload, ['botId', 'requestId', 'confirm'])
&& validId(payload.botId)
@ -251,6 +257,11 @@ export function createDingtalkRpcHandler(controller, { encodeQr = qrDataUrl } =
await controller.updateWorkspace(payload.botId, payload.workspace),
cachedEncode,
);
} else if (endpoint === DINGTALK_ENDPOINTS.setContextEnhancement) {
if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
value = await controller.updateContextEnhancement(
payload.botId, payload.config, (status) => publicStatus(status, cachedEncode),
);
} else if (endpoint === DINGTALK_ENDPOINTS.setAgentPreset) {
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
value = await publicStatus(

View file

@ -98,6 +98,8 @@ export async function createProductionController(ctx, config = {}, internals = {
workspaces,
method: 'removeBot',
botIdFromRemoved: (removed) => removed.id,
saveMethod: 'saveBot',
botIdFromSave: (bot) => bot?.id,
})
: configStore;
// State is lazy per bot. A corrupt legacy file can therefore fail only the
@ -167,6 +169,7 @@ export async function createProductionController(ctx, config = {}, internals = {
ownerOpenIds: botConfig.ownerOpenIds ?? [botConfig.ownerOpenId],
harness: workspaceScope.harness,
state: workspaceScope.state,
contextEnhancement: { botId: id, getSettings: () => workspaces.contextEnhancementFor(id) },
replyTimeoutMs: config.replyTimeoutMs ?? 600_000,
...(wsAgent ? { wsAgent } : {}),
logger: {

View file

@ -8,6 +8,8 @@ import { publicMessageFailure } from '../../../../src/channels/shared/message-fa
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
import { publicWorkspaceError, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
import { validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
import { validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
import { normalizeContextEnhancementConfig } from '../../../../src/channels/shared/context-enhancement.mjs';
import {
isFeishuGroupResponseMode,
normalizeFeishuGroupResponseMode,
@ -276,6 +278,7 @@ function publicBotEntry(entry) {
connected,
configured: source.configured === true,
agentPreset: normalizeAgentPresetId(source.agentPreset),
contextEnhancement: normalizeContextEnhancementConfig(source.contextEnhancement),
groupResponseMode: normalizeFeishuGroupResponseMode(source.groupResponseMode),
groupMessagePermissionGranted: source.groupMessagePermissionGranted === true,
bot: publicBot(source.bot),
@ -415,6 +418,10 @@ function validPayload(endpoint, payload) {
return validAgentPresetPayload(payload)
? null : '请选择 Agent Preset。';
}
if (endpoint === FEISHU_ENDPOINTS.setContextEnhancement) {
return validContextEnhancementPayload(payload)
? null : '请提交有效的上下文增强设置。';
}
if (endpoint === FEISHU_ENDPOINTS.setGroupResponseMode) {
return hasOnlyKeys(payload, new Set(['botId', 'groupResponseMode']))
&& safeOpaqueId(payload.botId)
@ -666,6 +673,12 @@ export function createFeishuRpcHandler(controller, { encodeQr = qrCodeDataUrl }
await controller.updateWorkspace(payload.botId, payload.workspace),
{ encodeQr: cachedEncodeQr },
);
} else if (endpoint === FEISHU_ENDPOINTS.setContextEnhancement) {
if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
value = await controller.updateContextEnhancement(
payload.botId, payload.config,
(status) => toPublicFeishuStatus(status, { encodeQr: cachedEncodeQr }),
);
} else if (endpoint === FEISHU_ENDPOINTS.setAgentPreset) {
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
value = await toPublicFeishuStatus(

View file

@ -100,6 +100,7 @@ export async function createProductionController(ctx, config = {}, internals = {
appSecret,
harness: workspaceScope.harness,
state: workspaceScope.state,
contextEnhancement: { botId, getSettings: () => workspaces.contextEnhancementFor(botId) },
replyTimeoutMs: config.replyTimeoutMs ?? 600_000,
connectTimeoutMs: config.connectTimeoutMs ?? 20_000,
logger: {

View file

@ -3,6 +3,7 @@ import {
connectionTestTargetUnavailable,
publicConnectionTestResult,
} from '../../../../src/channels/shared/connection-test.mjs';
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
import { publicWorkspaceError, SET_WORKSPACE_ENDPOINT, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
import { SET_AGENT_PRESET_ENDPOINT, validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
@ -18,6 +19,7 @@ export const QQ_ENDPOINTS = Object.freeze({
deleteBot: 'bot.delete',
setWorkspace: SET_WORKSPACE_ENDPOINT,
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
});
export const QQ_RPC_ENDPOINTS = Object.freeze(Object.values(QQ_ENDPOINTS));
@ -76,6 +78,10 @@ function payloadFailure(endpoint, payload) {
return validAgentPresetPayload(payload)
? null : '请选择 Agent Preset。';
}
if (endpoint === QQ_ENDPOINTS.setContextEnhancement) {
return validContextEnhancementPayload(payload)
? null : '请提交有效的上下文增强设置。';
}
return 'Unknown QQ endpoint.';
}
@ -171,6 +177,11 @@ export function createQqRpcHandler(controller, { encodeQr = qrDataUrl } = {}) {
await controller.updateWorkspace(payload.botId, payload.workspace),
cachedEncode,
);
} else if (endpoint === QQ_ENDPOINTS.setContextEnhancement) {
if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
value = await controller.updateContextEnhancement(
payload.botId, payload.config, (status) => publicStatus(status, cachedEncode),
);
} else if (endpoint === QQ_ENDPOINTS.setAgentPreset) {
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
value = await publicStatus(

View file

@ -0,0 +1,17 @@
import { validateContextEnhancementConfig } from '../../../../src/channels/shared/context-enhancement.mjs';
export const SET_CONTEXT_ENHANCEMENT_ENDPOINT = 'bot.context-enhancement.set';
export function validContextEnhancementPayload(payload) {
try {
if (!payload || typeof payload !== 'object' || Array.isArray(payload)
|| Reflect.ownKeys(payload).length !== 2
|| !Object.hasOwn(payload, 'botId') || !Object.hasOwn(payload, 'config')
|| typeof payload.botId !== 'string'
|| !/^[A-Za-z0-9_-]{1,128}$/.test(payload.botId)) return false;
validateContextEnhancementConfig(payload.config);
return true;
} catch {
return false;
}
}

View file

@ -102,6 +102,7 @@ export async function createTokenProductionController(ctx, config, internals, de
token,
harness: workspaceScope.harness,
state: workspaceScope.state,
contextEnhancement: { botId, getSettings: () => workspaces.contextEnhancementFor(botId) },
replyTimeoutMs: config.replyTimeoutMs ?? 600_000,
connectTimeoutMs: config.connectTimeoutMs ?? 20_000,
logger: {

View file

@ -1,3 +1,4 @@
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from './context-enhancement-rpc.mjs';
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
import { publicConnectionTestResult } from '../../../../src/channels/shared/connection-test.mjs';
import {
@ -17,6 +18,7 @@ export const TOKEN_BOT_ENDPOINTS = Object.freeze({
deleteBot: 'bot.delete',
setWorkspace: SET_WORKSPACE_ENDPOINT,
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
});
const ENDPOINTS = Object.freeze(Object.values(TOKEN_BOT_ENDPOINTS));
@ -71,6 +73,10 @@ function payloadFailure(endpoint, payload) {
return validAgentPresetPayload(payload)
? null : '请选择 Agent Preset。';
}
if (endpoint === TOKEN_BOT_ENDPOINTS.setContextEnhancement) {
return validContextEnhancementPayload(payload)
? null : '请提交有效的上下文增强设置。';
}
return 'Unknown bot endpoint.';
}
@ -152,6 +158,9 @@ export function createTokenBotRpcHandler(controller, { channel }) {
} else if (endpoint === TOKEN_BOT_ENDPOINTS.setWorkspace) {
if (typeof controller.updateWorkspace !== 'function') throw new Error('Workspace update is unavailable');
value = await controller.updateWorkspace(payload.botId, payload.workspace);
} else if (endpoint === TOKEN_BOT_ENDPOINTS.setContextEnhancement) {
if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
value = await controller.updateContextEnhancement(payload.botId, payload.config);
} else if (endpoint === TOKEN_BOT_ENDPOINTS.setAgentPreset) {
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
value = await controller.updateAgentPreset(payload.botId, payload.agentPreset);

View file

@ -22,6 +22,7 @@ export function publicWorkspaceError(error) {
'workspace-bot-not-found',
'agent-preset-invalid',
'agent-preset-unavailable',
'context-enhancement-invalid',
].includes(error?.code)) return null;
return { code: error.code, message: error.message };
}

View file

@ -89,6 +89,7 @@ export async function createProductionController(ctx, config = {}, internals = {
appToken,
harness: workspaceScope.harness,
state: workspaceScope.state,
contextEnhancement: { botId, getSettings: () => workspaces.contextEnhancementFor(botId) },
replyTimeoutMs: config.replyTimeoutMs ?? 600_000,
connectTimeoutMs: config.connectTimeoutMs ?? 20_000,
logger: {

View file

@ -1,3 +1,4 @@
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
import { publicConnectionTestResult } from '../../../../src/channels/shared/connection-test.mjs';
import {
@ -18,6 +19,7 @@ export const SLACK_ENDPOINTS = Object.freeze({
deleteBot: 'bot.delete',
setWorkspace: SET_WORKSPACE_ENDPOINT,
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
});
export const SLACK_RPC_ENDPOINTS = Object.freeze(Object.values(SLACK_ENDPOINTS));
@ -75,6 +77,10 @@ function payloadFailure(endpoint, payload) {
return validAgentPresetPayload(payload)
? null : '请选择 Agent Preset。';
}
if (endpoint === SLACK_ENDPOINTS.setContextEnhancement) {
return validContextEnhancementPayload(payload)
? null : '请提交有效的上下文增强设置。';
}
return 'Unknown Slack endpoint.';
}
@ -154,6 +160,10 @@ export function createSlackRpcHandler(controller) {
if (typeof controller.updateWorkspace !== 'function') throw new Error('Workspace update is unavailable');
value = await controller.updateWorkspace(payload.botId, payload.workspace);
}
else if (endpoint === SLACK_ENDPOINTS.setContextEnhancement) {
if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
value = await controller.updateContextEnhancement(payload.botId, payload.config);
}
else if (endpoint === SLACK_ENDPOINTS.setAgentPreset) {
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
value = await controller.updateAgentPreset(payload.botId, payload.agentPreset);

View file

@ -103,6 +103,7 @@ export async function createProductionController(ctx, config = {}, internals = {
secret,
harness: workspaceScope.harness,
state: workspaceScope.state,
contextEnhancement: { botId, getSettings: () => workspaces.contextEnhancementFor(botId) },
replyTimeoutMs: config.replyTimeoutMs ?? 600_000,
connectTimeoutMs: config.connectTimeoutMs ?? 20_000,
maxReconnectAttempts: config.maxReconnectAttempts ?? 10,

View file

@ -1,4 +1,5 @@
import QRCode from 'qrcode';
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
import { publicWorkspaceError, SET_WORKSPACE_ENDPOINT, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
import { SET_AGENT_PRESET_ENDPOINT, validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
@ -18,6 +19,7 @@ export const WECOM_ENDPOINTS = Object.freeze({
deleteBot: 'bot.delete',
setWorkspace: SET_WORKSPACE_ENDPOINT,
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
});
export const WECOM_RPC_ENDPOINTS = Object.freeze(Object.values(WECOM_ENDPOINTS));
@ -76,6 +78,10 @@ function payloadFailure(endpoint, payload) {
return validAgentPresetPayload(payload)
? null : '请选择 Agent Preset。';
}
if (endpoint === WECOM_ENDPOINTS.setContextEnhancement) {
return validContextEnhancementPayload(payload)
? null : '请提交有效的上下文增强设置。';
}
return 'Unknown Enterprise WeChat endpoint.';
}
@ -172,6 +178,11 @@ export function createWecomRpcHandler(controller, { encodeQr = qrDataUrl } = {})
await controller.updateWorkspace(payload.botId, payload.workspace),
cachedEncode,
);
} else if (endpoint === WECOM_ENDPOINTS.setContextEnhancement) {
if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
value = await controller.updateContextEnhancement(
payload.botId, payload.config, (status) => publicStatus(status, cachedEncode),
);
} else if (endpoint === WECOM_ENDPOINTS.setAgentPreset) {
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
value = await publicStatus(

View file

@ -107,6 +107,7 @@ export async function createProductionController(ctx, config = {}, internals = {
token,
harness: workspaceScope.harness,
state: workspaceScope.state,
contextEnhancement: { botId, getSettings: () => workspaces.contextEnhancementFor(botId) },
replyTimeoutMs: config.replyTimeoutMs ?? 600_000,
maxMessageChars: config.maxMessageChars ?? DEFAULT_WEIXIN_MAX_MESSAGE_CHARS,
logger: {

View file

@ -1,4 +1,5 @@
import QRCode from 'qrcode';
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
import {
publicWorkspaceError,
@ -25,6 +26,7 @@ export const WEIXIN_ENDPOINTS = Object.freeze({
deleteBot: 'bot.delete',
setWorkspace: SET_WORKSPACE_ENDPOINT,
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
});
export const WEIXIN_RPC_ENDPOINTS = Object.freeze(Object.values(WEIXIN_ENDPOINTS));
@ -83,6 +85,10 @@ function payloadFailure(endpoint, payload) {
return validAgentPresetPayload(payload)
? null : '请选择 Agent Preset。';
}
if (endpoint === WEIXIN_ENDPOINTS.setContextEnhancement) {
return validContextEnhancementPayload(payload)
? null : '请提交有效的上下文增强设置。';
}
return 'Unknown Weixin endpoint.';
}
@ -207,6 +213,11 @@ export function createWeixinRpcHandler(controller, { encodeQr = qrDataUrl } = {}
await controller.updateWorkspace(payload.botId, payload.workspace),
cachedEncode,
);
} else if (endpoint === WEIXIN_ENDPOINTS.setContextEnhancement) {
if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
value = await controller.updateContextEnhancement(
payload.botId, payload.config, (status) => publicStatus(status, cachedEncode),
);
} else if (endpoint === WEIXIN_ENDPOINTS.setAgentPreset) {
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
value = await publicStatus(

View file

@ -106,6 +106,7 @@ export async function createProductionController(ctx, config = {}, internals = {
authDir,
harness: workspaceScope.harness,
state: workspaceScope.state,
contextEnhancement: { botId, getSettings: () => workspaces.contextEnhancementFor(botId) },
replyTimeoutMs: config.replyTimeoutMs ?? 600_000,
connectTimeoutMs: config.connectTimeoutMs ?? 30_000,
createSession,

View file

@ -2,6 +2,7 @@ import QRCode from 'qrcode';
import { publicConnectionTestResult } from '../../../../src/channels/shared/connection-test.mjs';
import { normalizeWhatsappAccessPolicy } from '../../../../src/channels/whatsapp/config-store.mjs';
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
import { publicWorkspaceError, SET_WORKSPACE_ENDPOINT, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
import { SET_AGENT_PRESET_ENDPOINT, validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
@ -17,6 +18,7 @@ export const WHATSAPP_ENDPOINTS = Object.freeze({
setAccessPolicy: 'bot.access-policy.set',
setWorkspace: SET_WORKSPACE_ENDPOINT,
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
});
export const WHATSAPP_RPC_ENDPOINTS = Object.freeze(Object.values(WHATSAPP_ENDPOINTS));
@ -71,6 +73,10 @@ function payloadFailure(endpoint, payload) {
return validAgentPresetPayload(payload)
? null : '请选择 Agent Preset。';
}
if (endpoint === WHATSAPP_ENDPOINTS.setContextEnhancement) {
return validContextEnhancementPayload(payload)
? null : '请提交有效的上下文增强设置。';
}
return 'Unknown WhatsApp endpoint.';
}
@ -173,6 +179,11 @@ export function createWhatsappRpcHandler(controller, { encodeQr = qrDataUrl } =
await controller.updateWorkspace(payload.botId, payload.workspace),
cachedEncode,
);
} else if (endpoint === WHATSAPP_ENDPOINTS.setContextEnhancement) {
if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
value = await controller.updateContextEnhancement(
payload.botId, payload.config, (status) => publicStatus(status, cachedEncode),
);
} else if (endpoint === WHATSAPP_ENDPOINTS.setAgentPreset) {
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
value = await publicStatus(