@xmanrui/dsh-im 4.36.0 → 4.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.en.md +14 -2
  2. package/README.md +13 -1
  3. package/lib/client.js +348 -195
  4. package/lib/index.js +314 -313
  5. package/package.json +2 -2
  6. package/plugin-src/client/bot-general-settings.js +83 -0
  7. package/plugin-src/client/channels/dingtalk/api.js +3 -1
  8. package/plugin-src/client/channels/feishu/api.js +3 -1
  9. package/plugin-src/client/channels/qq/api.js +3 -1
  10. package/plugin-src/client/channels/shared/token-api.js +3 -1
  11. package/plugin-src/client/channels/wecom/api.js +3 -1
  12. package/plugin-src/client/channels/wecom-app/api.js +3 -1
  13. package/plugin-src/client/channels/weixin/api.js +3 -1
  14. package/plugin-src/client/channels/whatsapp/api.js +3 -1
  15. package/plugin-src/client/connection-error.js +2 -1
  16. package/plugin-src/client/delivery-settings.js +5 -1
  17. package/plugin-src/client/i18n.js +13 -0
  18. package/plugin-src/host/channels/dingtalk/rpc.mjs +11 -0
  19. package/plugin-src/host/channels/email/rpc.mjs +1 -0
  20. package/plugin-src/host/channels/feishu/rpc.mjs +13 -0
  21. package/plugin-src/host/channels/imessage/rpc.mjs +1 -0
  22. package/plugin-src/host/channels/matrix/rpc.mjs +10 -0
  23. package/plugin-src/host/channels/qq/rpc.mjs +11 -0
  24. package/plugin-src/host/channels/shared/rpc.mjs +9 -0
  25. package/plugin-src/host/channels/slack/rpc.mjs +10 -0
  26. package/plugin-src/host/channels/wecom/rpc.mjs +11 -0
  27. package/plugin-src/host/channels/wecom-app/rpc.mjs +10 -0
  28. package/plugin-src/host/channels/weixin/rpc.mjs +11 -0
  29. package/plugin-src/host/channels/whatsapp/rpc.mjs +11 -0
  30. package/src/channels/dingtalk/dingtalk-bridge.mjs +31 -2
  31. package/src/channels/dingtalk/dingtalk-menu.mjs +1 -1
  32. package/src/channels/dingtalk/dingtalk-runtime.mjs +2 -1
  33. package/src/channels/discord/discord-runtime.mjs +3 -1
  34. package/src/channels/feishu/bridge.mjs +69 -5
  35. package/src/channels/feishu/feishu-cards.mjs +8 -4
  36. package/src/channels/feishu/slash-command-panel.mjs +1 -0
  37. package/src/channels/qq/qq-bridge.mjs +40 -2
  38. package/src/channels/qq/qq-menu.mjs +2 -2
  39. package/src/channels/shared/bot-workspace-store.mjs +69 -1
  40. package/src/channels/shared/busy-message-dispatcher.mjs +94 -0
  41. package/src/channels/shared/clear-command.mjs +85 -0
  42. package/src/channels/shared/command-catalog.mjs +3 -0
  43. package/src/channels/shared/command-permission.mjs +2 -0
  44. package/src/channels/shared/connection-error.mjs +1 -0
  45. package/src/channels/shared/control-command.mjs +20 -11
  46. package/src/channels/shared/diagnostic-details.mjs +7 -1
  47. package/src/channels/shared/harness-client.mjs +15 -8
  48. package/src/channels/shared/i18n-en/diagnostics.mjs +3 -0
  49. package/src/channels/shared/i18n-en/feishu.mjs +6 -4
  50. package/src/channels/shared/i18n-en/qq.mjs +2 -1
  51. package/src/channels/shared/i18n-en/shared-b.mjs +27 -0
  52. package/src/channels/shared/message-failure.mjs +1 -1
  53. package/src/channels/shared/message-mode.mjs +19 -0
  54. package/src/channels/shared/text-harness-bridge.mjs +48 -5
  55. package/src/channels/wecom/wecom-bridge.mjs +26 -2
  56. package/src/channels/wecom/wecom-cards.mjs +1 -1
  57. package/src/channels/wecom-app/wecom-app-bridge.mjs +26 -2
  58. package/src/channels/weixin/weixin-bridge.mjs +22 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xmanrui/dsh-im",
3
- "version": "4.36.0",
3
+ "version": "4.37.0",
4
4
  "icon": "./assets/logo-plugin-message-link-gradient.webp",
5
5
  "description": "把微信、飞书、企微、钉钉、QQ、Telegram、WhatsApp、Slack、Discord、Matrix接入DSH。 Connect Telegram, WhatsApp, Slack, Discord, Matrix, WeChat, Feishu, WeCom, DingTalk, and QQ to DSH.",
6
6
  "keywords": [
@@ -134,6 +134,6 @@
134
134
  },
135
135
  "optionalDependencies": {
136
136
  "@tencent-qqmail/agently-cli": "^1.0.18",
137
- "sharp": "0.35.4"
137
+ "sharp": "0.35.5"
138
138
  }
139
139
  }
@@ -0,0 +1,83 @@
1
+ import * as React from 'react';
2
+ import { h } from './i18n.js';
3
+ import { SET_MESSAGE_MODE_ENDPOINT, normalizeBusyMessageMode } from '../../src/channels/shared/message-mode.mjs';
4
+
5
+ function modeFromResult(result, botId) {
6
+ if (result?.ok !== true) throw new Error(result?.error?.message || '无法读取消息处理设置,请稍后重试。');
7
+ const bot = result.value?.bots?.find((entry) => entry.botId === botId);
8
+ if (!bot) throw new Error('找不到要修改的机器人。');
9
+ return normalizeBusyMessageMode(bot.busyMessageMode);
10
+ }
11
+
12
+ export function BotGeneralSettingsPage({ account, rpcCall }) {
13
+ const [mode, setMode] = React.useState('queue');
14
+ const [savedMode, setSavedMode] = React.useState(null);
15
+ const [busy, setBusy] = React.useState(false);
16
+ const [feedback, setFeedback] = React.useState(null);
17
+ const mounted = React.useRef(false);
18
+
19
+ const load = React.useCallback(async () => {
20
+ setBusy(true);
21
+ setFeedback(null);
22
+ try {
23
+ const value = modeFromResult(await rpcCall('connection.status', {}), account.botId);
24
+ if (!mounted.current) return;
25
+ setMode(value);
26
+ setSavedMode(value);
27
+ } catch (error) {
28
+ if (mounted.current) setFeedback({ error: true, text: error.message });
29
+ } finally {
30
+ if (mounted.current) setBusy(false);
31
+ }
32
+ }, [account.botId, rpcCall]);
33
+
34
+ React.useEffect(() => {
35
+ mounted.current = true;
36
+ void load();
37
+ return () => { mounted.current = false; };
38
+ }, [load]);
39
+
40
+ const save = async () => {
41
+ setBusy(true);
42
+ setFeedback(null);
43
+ try {
44
+ const value = modeFromResult(await rpcCall(SET_MESSAGE_MODE_ENDPOINT, {
45
+ botId: account.botId, busyMessageMode: mode,
46
+ }), account.botId);
47
+ if (!mounted.current) return;
48
+ setMode(value);
49
+ setSavedMode(value);
50
+ setFeedback({ text: '已保存,对新收到的消息生效。' });
51
+ } catch (error) {
52
+ if (mounted.current) setFeedback({ error: true, text: error.message });
53
+ } finally {
54
+ if (mounted.current) setBusy(false);
55
+ }
56
+ };
57
+
58
+ return h('section', { className: 'dim-accessPage', 'aria-label': '机器人通用设置' },
59
+ h('h2', null, '通用'),
60
+ h('p', null, '以下设置仅影响当前机器人。'),
61
+ h('label', { className: 'dim-accessField' },
62
+ h('span', null, '任务运行时的新消息处理方式'),
63
+ h('select', {
64
+ value: mode, disabled: busy || savedMode === null,
65
+ 'aria-label': '任务运行时的新消息处理方式',
66
+ onChange: (event) => { setMode(event.target.value); setFeedback(null); },
67
+ },
68
+ h('option', { value: 'queue' }, '排队(默认)'),
69
+ h('option', { value: 'steer' }, '插话'))),
70
+ h('p', null, mode === 'queue'
71
+ ? '当前任务结束后,再处理新消息。可使用 /steer 手动插话。'
72
+ : '将新的纯文字消息作为补充指令加入当前任务,Agent 在下一步读取。'),
73
+ h('p', null, '没有正在运行的任务时,正常开始新任务。图片、文件、语音和带引用的消息按原有方式处理。'),
74
+ h('button', {
75
+ type: 'button', className: 'dim-deliveryButton', 'data-kind': 'primary',
76
+ disabled: busy || savedMode === null || mode === savedMode,
77
+ onClick: () => void save(),
78
+ }, busy ? '处理中…' : '保存'),
79
+ savedMode === null && !busy ? h('button', {
80
+ type: 'button', className: 'dim-deliveryButton', onClick: () => void load(),
81
+ }, '重新读取') : null,
82
+ feedback ? h('p', { className: 'dim-targetFeedback', role: feedback.error ? 'alert' : 'status' }, feedback.text) : null);
83
+ }
@@ -1,3 +1,4 @@
1
+ import { normalizeBusyMessageMode } from '../../../../src/channels/shared/message-mode.mjs';
1
2
  import { normalizeConnectionError as normalizeTestError } from '../../connection-error.js';
2
3
  import { diagnosticFields } from '../../../../src/channels/shared/diagnostic-details.mjs';
3
4
  import { normalizeBotAlias } from '../../../../src/channels/shared/bot-alias.mjs';
@@ -21,6 +22,7 @@ export const DINGTALK_ENDPOINTS = Object.freeze({
21
22
  setModel: SET_MODEL_ENDPOINT,
22
23
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
23
24
  setContextEnhancement: 'bot.context-enhancement.set',
25
+ setBusyMessageMode: 'bot.message-mode.set',
24
26
  setAccessPolicy: 'bot.access-policy.set',
25
27
  setAlias: 'bot.alias.set',
26
28
  });
@@ -191,7 +193,7 @@ function normalizeBot(value) {
191
193
  workspace: optionalString(value.workspace, 4_096) ?? '',
192
194
  model: normalizeModelSelection(value.model),
193
195
  agentPreset: normalizeAgentPresetId(value.agentPreset),
194
- contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
196
+ busyMessageMode: normalizeBusyMessageMode(value.busyMessageMode), contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
195
197
  ...(Object.hasOwn(value, 'accessPolicy')
196
198
  ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) }
197
199
  : {}),
@@ -1,3 +1,4 @@
1
+ import { normalizeBusyMessageMode } from '../../../../src/channels/shared/message-mode.mjs';
1
2
  import { diagnosticFields } from '../../../../src/channels/shared/diagnostic-details.mjs';
2
3
  import { normalizeBotAlias } from '../../../../src/channels/shared/bot-alias.mjs';
3
4
  /**
@@ -34,6 +35,7 @@ export const FEISHU_ENDPOINTS = Object.freeze({
34
35
  setModel: SET_MODEL_ENDPOINT,
35
36
  setAgentPreset: "bot.preset.set",
36
37
  setContextEnhancement: "bot.context-enhancement.set",
38
+ setBusyMessageMode: 'bot.message-mode.set',
37
39
  setAccessPolicy: "bot.access-policy.set",
38
40
  setAlias: 'bot.alias.set',
39
41
  setGroupResponseMode: "bot.group-response-mode.set",
@@ -223,7 +225,7 @@ export function normalizeBotConnection(value, fallbackBotId) {
223
225
  workspace: optionalString(value.workspace)?.slice(0, 4_096) ?? "",
224
226
  model: normalizeModelSelection(value.model),
225
227
  agentPreset: normalizeAgentPresetId(value.agentPreset),
226
- contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
228
+ busyMessageMode: normalizeBusyMessageMode(value.busyMessageMode), contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
227
229
  ...(Object.hasOwn(value, "accessPolicy")
228
230
  ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) }
229
231
  : {}),
@@ -1,3 +1,4 @@
1
+ import { normalizeBusyMessageMode } from '../../../../src/channels/shared/message-mode.mjs';
1
2
  import { normalizeConnectionError as normalizeTestError } from '../../connection-error.js';
2
3
  import { diagnosticFields } from '../../../../src/channels/shared/diagnostic-details.mjs';
3
4
  import { normalizeBotAlias } from '../../../../src/channels/shared/bot-alias.mjs';
@@ -21,6 +22,7 @@ export const QQ_ENDPOINTS = Object.freeze({
21
22
  setModel: SET_MODEL_ENDPOINT,
22
23
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
23
24
  setContextEnhancement: 'bot.context-enhancement.set',
25
+ setBusyMessageMode: 'bot.message-mode.set',
24
26
  setAccessPolicy: 'bot.access-policy.set',
25
27
  setAlias: 'bot.alias.set',
26
28
  });
@@ -100,7 +102,7 @@ function normalizeBot(value) {
100
102
  workspace: text(value.workspace, '', 4_096),
101
103
  model: normalizeModelSelection(value.model),
102
104
  agentPreset: normalizeAgentPresetId(value.agentPreset),
103
- contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
105
+ busyMessageMode: normalizeBusyMessageMode(value.busyMessageMode), contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
104
106
  ...(Object.hasOwn(value, 'accessPolicy')
105
107
  ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) }
106
108
  : {}),
@@ -1,3 +1,4 @@
1
+ import { normalizeBusyMessageMode } from '../../../../src/channels/shared/message-mode.mjs';
1
2
  import { diagnosticFields } from '../../../../src/channels/shared/diagnostic-details.mjs';
2
3
  import { normalizeBotAlias } from '../../../../src/channels/shared/bot-alias.mjs';
3
4
  import { normalizeAgentPresetCatalog, normalizeAgentPresetId, SET_AGENT_PRESET_ENDPOINT } from '../../agent-preset.js';
@@ -36,6 +37,7 @@ export const TOKEN_BOT_ENDPOINTS = Object.freeze({
36
37
  setModel: SET_MODEL_ENDPOINT,
37
38
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
38
39
  setContextEnhancement: 'bot.context-enhancement.set',
40
+ setBusyMessageMode: 'bot.message-mode.set',
39
41
  setAccessPolicy: 'bot.access-policy.set',
40
42
  setAlias: 'bot.alias.set',
41
43
  setThinkingTraces: 'bot.thinking-traces.set',
@@ -69,7 +71,7 @@ export function createTokenChannelApi(channel, connectionSummary, {
69
71
  workspace: text(value.workspace, '', 4_096),
70
72
  model: normalizeModelSelection(value.model),
71
73
  agentPreset: normalizeAgentPresetId(value.agentPreset),
72
- contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
74
+ busyMessageMode: normalizeBusyMessageMode(value.busyMessageMode), contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
73
75
  // Absent from the backend means ON; only an explicit false opts out.
74
76
  thinkingTraces: value.thinkingTraces !== false,
75
77
  ...(Object.hasOwn(value, 'accessPolicy')
@@ -1,3 +1,4 @@
1
+ import { normalizeBusyMessageMode } from '../../../../src/channels/shared/message-mode.mjs';
1
2
  import { normalizeConnectionError as normalizeTestError } from '../../connection-error.js';
2
3
  import { diagnosticFields } from '../../../../src/channels/shared/diagnostic-details.mjs';
3
4
  import { normalizeBotAlias } from '../../../../src/channels/shared/bot-alias.mjs';
@@ -21,6 +22,7 @@ export const WECOM_ENDPOINTS = Object.freeze({
21
22
  setModel: SET_MODEL_ENDPOINT,
22
23
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
23
24
  setContextEnhancement: 'bot.context-enhancement.set',
25
+ setBusyMessageMode: 'bot.message-mode.set',
24
26
  setAccessPolicy: 'bot.access-policy.set',
25
27
  setAlias: 'bot.alias.set',
26
28
  });
@@ -109,7 +111,7 @@ function normalizeBot(value) {
109
111
  workspace: text(value.workspace, '', 4_096),
110
112
  model: normalizeModelSelection(value.model),
111
113
  agentPreset: normalizeAgentPresetId(value.agentPreset),
112
- contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
114
+ busyMessageMode: normalizeBusyMessageMode(value.busyMessageMode), contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
113
115
  ...(Object.hasOwn(value, 'accessPolicy')
114
116
  ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) }
115
117
  : {}),
@@ -1,3 +1,4 @@
1
+ import { normalizeBusyMessageMode } from '../../../../src/channels/shared/message-mode.mjs';
1
2
  import { normalizeConnectionError as normalizeTestError } from '../../connection-error.js';
2
3
  import { diagnosticFields } from '../../../../src/channels/shared/diagnostic-details.mjs';
3
4
  import { normalizeBotAlias } from '../../../../src/channels/shared/bot-alias.mjs';
@@ -20,6 +21,7 @@ export const WECOM_APP_ENDPOINTS = Object.freeze({
20
21
  setModel: SET_MODEL_ENDPOINT,
21
22
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
22
23
  setContextEnhancement: 'bot.context-enhancement.set',
24
+ setBusyMessageMode: 'bot.message-mode.set',
23
25
  setAccessPolicy: 'bot.access-policy.set',
24
26
  setAlias: 'bot.alias.set',
25
27
  });
@@ -82,7 +84,7 @@ function normalizeBot(value) {
82
84
  workspace: text(value.workspace, '', 4_096),
83
85
  model: normalizeModelSelection(value.model),
84
86
  agentPreset: normalizeAgentPresetId(value.agentPreset),
85
- contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
87
+ busyMessageMode: normalizeBusyMessageMode(value.busyMessageMode), contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
86
88
  ...(Object.hasOwn(value, 'accessPolicy')
87
89
  ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) }
88
90
  : {}),
@@ -1,3 +1,4 @@
1
+ import { normalizeBusyMessageMode } from '../../../../src/channels/shared/message-mode.mjs';
1
2
  import { normalizeConnectionError as normalizeTestError } from '../../connection-error.js';
2
3
  import { normalizeWeixinDiagnosticDetails } from '../../../../src/channels/weixin/diagnostic-details.mjs';
3
4
  import { normalizeBotAlias } from '../../../../src/channels/shared/bot-alias.mjs';
@@ -20,6 +21,7 @@ export const WEIXIN_ENDPOINTS = Object.freeze({
20
21
  setModel: SET_MODEL_ENDPOINT,
21
22
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
22
23
  setContextEnhancement: 'bot.context-enhancement.set',
24
+ setBusyMessageMode: 'bot.message-mode.set',
23
25
  setAccessPolicy: 'bot.access-policy.set',
24
26
  setAlias: 'bot.alias.set',
25
27
  });
@@ -154,7 +156,7 @@ function normalizeBot(value) {
154
156
  workspace: string(value.workspace).slice(0, 4_096),
155
157
  model: normalizeModelSelection(value.model),
156
158
  agentPreset: normalizeAgentPresetId(value.agentPreset),
157
- contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
159
+ busyMessageMode: normalizeBusyMessageMode(value.busyMessageMode), contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
158
160
  ...(Object.hasOwn(value, 'accessPolicy')
159
161
  ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) }
160
162
  : {}),
@@ -1,3 +1,4 @@
1
+ import { normalizeBusyMessageMode } from '../../../../src/channels/shared/message-mode.mjs';
1
2
  import { diagnosticFields } from '../../../../src/channels/shared/diagnostic-details.mjs';
2
3
  import { normalizeBotAlias } from '../../../../src/channels/shared/bot-alias.mjs';
3
4
  import { normalizeAgentPresetCatalog, normalizeAgentPresetId, SET_AGENT_PRESET_ENDPOINT } from '../../agent-preset.js';
@@ -21,6 +22,7 @@ export const WHATSAPP_ENDPOINTS = Object.freeze({
21
22
  setModel: SET_MODEL_ENDPOINT,
22
23
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
23
24
  setContextEnhancement: 'bot.context-enhancement.set',
25
+ setBusyMessageMode: 'bot.message-mode.set',
24
26
  });
25
27
 
26
28
  const PROVISION_STATES = new Set(['starting', 'pending', 'connecting', 'connected', 'failed', 'cancelled']);
@@ -99,7 +101,7 @@ function normalizeBot(value) {
99
101
  workspace: text(value.workspace, '', 4_096),
100
102
  model: normalizeModelSelection(value.model),
101
103
  agentPreset: normalizeAgentPresetId(value.agentPreset),
102
- contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
104
+ busyMessageMode: normalizeBusyMessageMode(value.busyMessageMode), contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
103
105
  ...(Object.hasOwn(value, 'accessPolicy')
104
106
  ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) }
105
107
  : {}),
@@ -30,7 +30,7 @@ export function formatConnectionDiagnostic(value) {
30
30
  return [
31
31
  localizeText(error.message), details.hint ? localizeText(details.hint) : null,
32
32
  localizeText('错误码') + ': ' + error.code,
33
- ...['channel', 'operation', 'stage', 'reason', 'reasons', 'host', 'durationMs', 'timeoutMs', 'nodeVersion', 'dshVersion', 'platform', 'dependencies', 'proxyConfigured', 'truncated', 'httpStatus', 'providerCode', 'resource', 'file', 'field', 'issue', 'referenceId', 'occurredAt', 'rollback', 'pluginVersion']
33
+ ...['channel', 'operation', 'stage', 'method', 'transport', 'reason', 'reasons', 'host', 'durationMs', 'timeoutMs', 'nodeVersion', 'dshVersion', 'platform', 'dependencies', 'proxyConfigured', 'truncated', 'httpStatus', 'providerCode', 'resource', 'file', 'field', 'issue', 'referenceId', 'occurredAt', 'rollback', 'pluginVersion']
34
34
  .filter(field => details[field] !== undefined)
35
35
  .map(field => `${field}: ${typeof details[field] === 'object' ? JSON.stringify(details[field]) : details[field]}`),
36
36
  ].filter(Boolean).join('\n');
@@ -51,6 +51,7 @@ export function ConnectionError({ error: value, warning = false, showMessage = t
51
51
  };
52
52
  const fields = [
53
53
  ['错误码', error.code], ['失败阶段', localizeText(STAGE_LABELS[details.stage] ?? details.stage ?? '')],
54
+ ['RPC 方法', details.method], ['调用方式', details.transport === 'host-api' ? localizeText('宿主 API(进程内)') : details.transport === 'http' ? 'HTTP' : undefined],
54
55
  ['底层原因', details.reason === 'unknown' ? localizeText('暂未识别') : details.reason], ['多个原因', details.reasons?.join(', ')], ['服务域名', details.host], ['请求耗时(毫秒)', details.durationMs], ['超时阈值(毫秒)', details.timeoutMs], ['Node 版本', details.nodeVersion], ['DSH 版本', details.dshVersion], ['依赖版本', details.dependencies ? Object.entries(details.dependencies).map(([name, version]) => `${name}: ${version}`).join(', ') : undefined], ['代理已配置', details.proxyConfigured], ['原因链已截断', details.truncated], ['运行平台', details.platform], ['HTTP 状态', details.httpStatus], ['服务返回码', details.providerCode],
55
56
  ['配置文件', details.file], ['配置字段', details.field],
56
57
  ['校验原因', details.issue ? localizeText(CONFIG_ISSUE_LABELS[details.issue]) : undefined],
@@ -1,6 +1,7 @@
1
1
  import * as React from 'react';
2
2
 
3
3
  import { AccessPolicySettingsPage } from './access-policy-settings.js';
4
+ import { BotGeneralSettingsPage } from './bot-general-settings.js';
4
5
  import { BotSettingsTabs } from './bot-settings-tabs.js';
5
6
  import { FeishuGroupSettingsPage } from './channels/feishu/group-settings.js';
6
7
  import { FeishuVoiceSettingsPage } from './channels/feishu/voice-settings.js';
@@ -27,6 +28,7 @@ export const DELIVERY_ENDPOINTS = Object.freeze({
27
28
  export const BOT_SETTINGS_TABS = Object.freeze([
28
29
  Object.freeze({ id: 'delivery', label: '投递设置' }),
29
30
  Object.freeze({ id: 'access', label: '访问设置' }),
31
+ Object.freeze({ id: 'general', label: '通用' }),
30
32
  ]);
31
33
 
32
34
  export const FEISHU_BOT_SETTINGS_TABS = Object.freeze([
@@ -778,7 +780,9 @@ export function DeliveryTargetSettingsPage({
778
780
  role: 'tabpanel',
779
781
  'aria-labelledby': activeTabDomId,
780
782
  },
781
- activeTab.id === 'access'
783
+ activeTab.id === 'general'
784
+ ? h(BotGeneralSettingsPage, { key: account.botId, account, rpcCall: accessRpcCall })
785
+ : activeTab.id === 'access'
782
786
  ? h(AccessPolicySettingsPage, {
783
787
  channel,
784
788
  account: { ...account, accessPolicy },
@@ -1025,6 +1025,19 @@ const EN = Object.freeze({
1025
1025
  '尚未配置投递目标': 'No delivery targets yet',
1026
1026
  '点击“新建目标”可从已聊过的会话选择,也可手动填写。': 'Select New target to choose a conversation or enter the details manually.',
1027
1027
  '全局设置': 'Global settings',
1028
+ "通用": "General",
1029
+ "处理中…": "Processing…",
1030
+ "机器人通用设置": "General bot settings",
1031
+ "以下设置仅影响当前机器人。": "These settings apply only to this bot.",
1032
+ "任务运行时的新消息处理方式": "New messages while a task is running",
1033
+ "排队(默认)": "Queue (default)",
1034
+ "插话": "Steer",
1035
+ "当前任务结束后,再处理新消息。可使用 /steer 手动插话。": "Process new messages after the current task finishes. Use /steer to send an explicit correction.",
1036
+ "将新的纯文字消息作为补充指令加入当前任务,Agent 在下一步读取。": "Add new plain-text messages to the running task. The Agent reads them at its next step.",
1037
+ "没有正在运行的任务时,正常开始新任务。图片、文件、语音和带引用的消息按原有方式处理。": "When idle, start a new task normally. Images, files, voice and quoted messages keep their existing handling.",
1038
+ "已保存,对新收到的消息生效。": "Saved. Applies to newly received messages.",
1039
+ "无法读取消息处理设置,请稍后重试。": "Could not read message handling settings. Try again later.",
1040
+ "请提交有效的消息处理方式。": "Choose a valid message handling mode.",
1028
1041
  '通用设置': 'General settings',
1029
1042
  '通用设置分类': 'General settings categories',
1030
1043
  '附件': 'Attachments',
@@ -1,3 +1,4 @@
1
+ import { SET_MESSAGE_MODE_ENDPOINT, validMessageModePayload } from '../../../../src/channels/shared/message-mode.mjs';
1
2
  import { diagnosticFields } from '../../../../src/channels/shared/diagnostic-details.mjs';
2
3
  import { createConnectionDiagnostics, diagnosticRpcResult } from '../../../../src/channels/shared/connection-error.mjs';
3
4
  import { SET_ALIAS_ENDPOINT, validAliasPayload } from '../shared/bot-alias-rpc.mjs';
@@ -27,6 +28,7 @@ export const DINGTALK_ENDPOINTS = Object.freeze({
27
28
  setModel: SET_MODEL_ENDPOINT,
28
29
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
29
30
  setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
31
+ setBusyMessageMode: SET_MESSAGE_MODE_ENDPOINT,
30
32
  setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
31
33
  setAlias: SET_ALIAS_ENDPOINT,
32
34
  approveSender: 'bot.sender.approve',
@@ -108,6 +110,10 @@ function payloadFailure(endpoint, payload) {
108
110
  return validAgentPresetPayload(payload)
109
111
  ? null : '请选择 Agent Preset。';
110
112
  }
113
+ if (endpoint === DINGTALK_ENDPOINTS.setBusyMessageMode) {
114
+ return validMessageModePayload(payload)
115
+ ? null : '请提交有效的消息处理方式。';
116
+ }
111
117
  if (endpoint === DINGTALK_ENDPOINTS.setContextEnhancement) {
112
118
  return validContextEnhancementPayload(payload)
113
119
  ? null : '请提交有效的上下文增强设置。';
@@ -305,6 +311,11 @@ export function createDingtalkRpcHandler(controller, { encodeQr = qrDataUrl } =
305
311
  await controller.updateModel(payload.botId, payload.model),
306
312
  cachedEncode,
307
313
  );
314
+ } else if (endpoint === DINGTALK_ENDPOINTS.setBusyMessageMode) {
315
+ if (typeof controller.updateBusyMessageMode !== 'function') throw new Error('Message mode update is unavailable');
316
+ value = await controller.updateBusyMessageMode(
317
+ payload.botId, payload.busyMessageMode, (status) => publicStatus(status, cachedEncode),
318
+ );
308
319
  } else if (endpoint === DINGTALK_ENDPOINTS.setContextEnhancement) {
309
320
  if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
310
321
  value = await controller.updateContextEnhancement(
@@ -17,6 +17,7 @@ export const EMAIL_ENDPOINTS = Object.freeze({
17
17
  setModel: 'bot.model.set',
18
18
  setAgentPreset: 'bot.agent-preset.set',
19
19
  setContextEnhancement: 'bot.context-enhancement.set',
20
+ setBusyMessageMode: 'bot.message-mode.set',
20
21
  setAccessPolicy: 'bot.access-policy.set',
21
22
  setAlias: 'bot.alias.set',
22
23
  // Session binding: read the current bindings, change them, and list the
@@ -1,3 +1,4 @@
1
+ import { SET_MESSAGE_MODE_ENDPOINT, validMessageModePayload, normalizeBusyMessageMode } from '../../../../src/channels/shared/message-mode.mjs';
1
2
  import { diagnosticFields } from '../../../../src/channels/shared/diagnostic-details.mjs';
2
3
  import { createConnectionDiagnostics, diagnosticRpcResult } from '../../../../src/channels/shared/connection-error.mjs';
3
4
  import { normalizeBotAlias } from '../../../../src/channels/shared/bot-alias.mjs';
@@ -42,6 +43,7 @@ import {
42
43
 
43
44
  export const FEISHU_ENDPOINTS = Object.freeze({
44
45
  ...FEISHU_CLIENT_ENDPOINTS,
46
+ setBusyMessageMode: SET_MESSAGE_MODE_ENDPOINT,
45
47
  setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
46
48
  setAlias: SET_ALIAS_ENDPOINT,
47
49
  });
@@ -307,6 +309,7 @@ function publicBotEntry(entry) {
307
309
  model: normalizeModelSelection(source.model),
308
310
  agentPreset: normalizeAgentPresetId(source.agentPreset),
309
311
  contextEnhancement: normalizeContextEnhancementConfig(source.contextEnhancement),
312
+ busyMessageMode: normalizeBusyMessageMode(source.busyMessageMode),
310
313
  accessPolicy: normalizeAccessPolicy(source.accessPolicy),
311
314
  groupResponseMode: normalizeFeishuGroupResponseMode(source.groupResponseMode),
312
315
  mentionTopicReply: source.mentionTopicReply !== false,
@@ -457,6 +460,10 @@ function validPayload(endpoint, payload) {
457
460
  return validAgentPresetPayload(payload)
458
461
  ? null : '请选择 Agent Preset。';
459
462
  }
463
+ if (endpoint === FEISHU_ENDPOINTS.setBusyMessageMode) {
464
+ return validMessageModePayload(payload)
465
+ ? null : '请提交有效的消息处理方式。';
466
+ }
460
467
  if (endpoint === FEISHU_ENDPOINTS.setContextEnhancement) {
461
468
  return validContextEnhancementPayload(payload)
462
469
  ? null : '请提交有效的上下文增强设置。';
@@ -766,6 +773,12 @@ export function createFeishuRpcHandler(controller, { encodeQr = qrCodeDataUrl }
766
773
  await controller.updateModel(payload.botId, payload.model),
767
774
  { encodeQr: cachedEncodeQr },
768
775
  );
776
+ } else if (endpoint === FEISHU_ENDPOINTS.setBusyMessageMode) {
777
+ if (typeof controller.updateBusyMessageMode !== 'function') throw new Error('Message mode update is unavailable');
778
+ value = await controller.updateBusyMessageMode(
779
+ payload.botId, payload.busyMessageMode,
780
+ (status) => toPublicFeishuStatus(status, { encodeQr: cachedEncodeQr }),
781
+ );
769
782
  } else if (endpoint === FEISHU_ENDPOINTS.setContextEnhancement) {
770
783
  if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
771
784
  value = await controller.updateContextEnhancement(
@@ -15,6 +15,7 @@ export const IMESSAGE_ENDPOINTS = Object.freeze({
15
15
  setModel: 'bot.model.set',
16
16
  setAgentPreset: 'bot.agent-preset.set',
17
17
  setContextEnhancement: 'bot.context-enhancement.set',
18
+ setBusyMessageMode: 'bot.message-mode.set',
18
19
  setAccessPolicy: 'bot.access-policy.set',
19
20
  setAlias: 'bot.alias.set',
20
21
  });
@@ -1,3 +1,4 @@
1
+ import { SET_MESSAGE_MODE_ENDPOINT, validMessageModePayload } from '../../../../src/channels/shared/message-mode.mjs';
1
2
  import { SET_ALIAS_ENDPOINT, validAliasPayload } from '../shared/bot-alias-rpc.mjs';
2
3
  import { registerManagementRpc } from '../../../management-rpc.mjs';
3
4
  import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
@@ -26,6 +27,7 @@ export const MATRIX_ENDPOINTS = Object.freeze({
26
27
  setModel: SET_MODEL_ENDPOINT,
27
28
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
28
29
  setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
30
+ setBusyMessageMode: SET_MESSAGE_MODE_ENDPOINT,
29
31
  setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
30
32
  setAlias: SET_ALIAS_ENDPOINT,
31
33
  });
@@ -99,6 +101,10 @@ function payloadFailure(endpoint, payload) {
99
101
  return validAgentPresetPayload(payload)
100
102
  ? null : '请选择 Agent Preset。';
101
103
  }
104
+ if (endpoint === MATRIX_ENDPOINTS.setBusyMessageMode) {
105
+ return validMessageModePayload(payload)
106
+ ? null : '请提交有效的消息处理方式。';
107
+ }
102
108
  if (endpoint === MATRIX_ENDPOINTS.setContextEnhancement) {
103
109
  return validContextEnhancementPayload(payload)
104
110
  ? null : '请提交有效的上下文增强设置。';
@@ -191,6 +197,10 @@ export function createMatrixRpcHandler(controller) {
191
197
  if (typeof controller.updateModel !== 'function') throw new Error('Model update is unavailable');
192
198
  value = await controller.updateModel(payload.botId, payload.model);
193
199
  }
200
+ else if (endpoint === MATRIX_ENDPOINTS.setBusyMessageMode) {
201
+ if (typeof controller.updateBusyMessageMode !== 'function') throw new Error('Message mode update is unavailable');
202
+ value = await controller.updateBusyMessageMode(payload.botId, payload.busyMessageMode);
203
+ }
194
204
  else if (endpoint === MATRIX_ENDPOINTS.setContextEnhancement) {
195
205
  if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
196
206
  value = await controller.updateContextEnhancement(payload.botId, payload.config);
@@ -1,3 +1,4 @@
1
+ import { SET_MESSAGE_MODE_ENDPOINT, validMessageModePayload } from '../../../../src/channels/shared/message-mode.mjs';
1
2
  import { createConnectionDiagnostics, diagnosticRpcResult } from '../../../../src/channels/shared/connection-error.mjs';
2
3
  import { SET_ALIAS_ENDPOINT, validAliasPayload } from '../shared/bot-alias-rpc.mjs';
3
4
  import { registerManagementRpc } from '../../../management-rpc.mjs';
@@ -27,6 +28,7 @@ export const QQ_ENDPOINTS = Object.freeze({
27
28
  setModel: SET_MODEL_ENDPOINT,
28
29
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
29
30
  setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
31
+ setBusyMessageMode: SET_MESSAGE_MODE_ENDPOINT,
30
32
  setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
31
33
  setAlias: SET_ALIAS_ENDPOINT,
32
34
  });
@@ -90,6 +92,10 @@ function payloadFailure(endpoint, payload) {
90
92
  return validAgentPresetPayload(payload)
91
93
  ? null : '请选择 Agent Preset。';
92
94
  }
95
+ if (endpoint === QQ_ENDPOINTS.setBusyMessageMode) {
96
+ return validMessageModePayload(payload)
97
+ ? null : '请提交有效的消息处理方式。';
98
+ }
93
99
  if (endpoint === QQ_ENDPOINTS.setContextEnhancement) {
94
100
  return validContextEnhancementPayload(payload)
95
101
  ? null : '请提交有效的上下文增强设置。';
@@ -204,6 +210,11 @@ export function createQqRpcHandler(controller, { encodeQr = qrDataUrl } = {}) {
204
210
  await controller.updateModel(payload.botId, payload.model),
205
211
  cachedEncode,
206
212
  );
213
+ } else if (endpoint === QQ_ENDPOINTS.setBusyMessageMode) {
214
+ if (typeof controller.updateBusyMessageMode !== 'function') throw new Error('Message mode update is unavailable');
215
+ value = await controller.updateBusyMessageMode(
216
+ payload.botId, payload.busyMessageMode, (status) => publicStatus(status, cachedEncode),
217
+ );
207
218
  } else if (endpoint === QQ_ENDPOINTS.setContextEnhancement) {
208
219
  if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
209
220
  value = await controller.updateContextEnhancement(
@@ -1,3 +1,4 @@
1
+ import { SET_MESSAGE_MODE_ENDPOINT, validMessageModePayload } from '../../../../src/channels/shared/message-mode.mjs';
1
2
  import { createConnectionDiagnostics, diagnosticRpcResult } from '../../../../src/channels/shared/connection-error.mjs';
2
3
  import { SET_ALIAS_ENDPOINT, validAliasPayload } from './bot-alias-rpc.mjs';
3
4
  import { registerManagementRpc } from '../../../management-rpc.mjs';
@@ -26,6 +27,7 @@ export const TOKEN_BOT_ENDPOINTS = Object.freeze({
26
27
  setModel: SET_MODEL_ENDPOINT,
27
28
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
28
29
  setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
30
+ setBusyMessageMode: SET_MESSAGE_MODE_ENDPOINT,
29
31
  setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
30
32
  setAlias: SET_ALIAS_ENDPOINT,
31
33
  setThinkingTraces: SET_THINKING_TRACES_ENDPOINT,
@@ -87,6 +89,10 @@ function payloadFailure(endpoint, payload) {
87
89
  return validAgentPresetPayload(payload)
88
90
  ? null : '请选择 Agent Preset。';
89
91
  }
92
+ if (endpoint === TOKEN_BOT_ENDPOINTS.setBusyMessageMode) {
93
+ return validMessageModePayload(payload)
94
+ ? null : '请提交有效的消息处理方式。';
95
+ }
90
96
  if (endpoint === TOKEN_BOT_ENDPOINTS.setContextEnhancement) {
91
97
  return validContextEnhancementPayload(payload)
92
98
  ? null : '请提交有效的上下文增强设置。';
@@ -188,6 +194,9 @@ export function createTokenBotRpcHandler(controller, { channel }) {
188
194
  } else if (endpoint === TOKEN_BOT_ENDPOINTS.setModel) {
189
195
  if (typeof controller.updateModel !== 'function') throw new Error('Model update is unavailable');
190
196
  value = await controller.updateModel(payload.botId, payload.model);
197
+ } else if (endpoint === TOKEN_BOT_ENDPOINTS.setBusyMessageMode) {
198
+ if (typeof controller.updateBusyMessageMode !== 'function') throw new Error('Message mode update is unavailable');
199
+ value = await controller.updateBusyMessageMode(payload.botId, payload.busyMessageMode);
191
200
  } else if (endpoint === TOKEN_BOT_ENDPOINTS.setContextEnhancement) {
192
201
  if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
193
202
  value = await controller.updateContextEnhancement(payload.botId, payload.config);
@@ -1,3 +1,4 @@
1
+ import { SET_MESSAGE_MODE_ENDPOINT, validMessageModePayload } from '../../../../src/channels/shared/message-mode.mjs';
1
2
  import { createConnectionDiagnostics, diagnosticRpcResult } from '../../../../src/channels/shared/connection-error.mjs';
2
3
  import { SET_ALIAS_ENDPOINT, validAliasPayload } from '../shared/bot-alias-rpc.mjs';
3
4
  import { registerManagementRpc } from '../../../management-rpc.mjs';
@@ -26,6 +27,7 @@ export const SLACK_ENDPOINTS = Object.freeze({
26
27
  setModel: SET_MODEL_ENDPOINT,
27
28
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
28
29
  setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
30
+ setBusyMessageMode: SET_MESSAGE_MODE_ENDPOINT,
29
31
  setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
30
32
  setAlias: SET_ALIAS_ENDPOINT,
31
33
  });
@@ -88,6 +90,10 @@ function payloadFailure(endpoint, payload) {
88
90
  return validAgentPresetPayload(payload)
89
91
  ? null : '请选择 Agent Preset。';
90
92
  }
93
+ if (endpoint === SLACK_ENDPOINTS.setBusyMessageMode) {
94
+ return validMessageModePayload(payload)
95
+ ? null : '请提交有效的消息处理方式。';
96
+ }
91
97
  if (endpoint === SLACK_ENDPOINTS.setContextEnhancement) {
92
98
  return validContextEnhancementPayload(payload)
93
99
  ? null : '请提交有效的上下文增强设置。';
@@ -184,6 +190,10 @@ export function createSlackRpcHandler(controller) {
184
190
  if (typeof controller.updateModel !== 'function') throw new Error('Model update is unavailable');
185
191
  value = await controller.updateModel(payload.botId, payload.model);
186
192
  }
193
+ else if (endpoint === SLACK_ENDPOINTS.setBusyMessageMode) {
194
+ if (typeof controller.updateBusyMessageMode !== 'function') throw new Error('Message mode update is unavailable');
195
+ value = await controller.updateBusyMessageMode(payload.botId, payload.busyMessageMode);
196
+ }
187
197
  else if (endpoint === SLACK_ENDPOINTS.setContextEnhancement) {
188
198
  if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
189
199
  value = await controller.updateContextEnhancement(payload.botId, payload.config);
@@ -1,3 +1,4 @@
1
+ import { SET_MESSAGE_MODE_ENDPOINT, validMessageModePayload } from '../../../../src/channels/shared/message-mode.mjs';
1
2
  import { createConnectionDiagnostics, diagnosticRpcResult } from '../../../../src/channels/shared/connection-error.mjs';
2
3
  import { SET_ALIAS_ENDPOINT, validAliasPayload } from '../shared/bot-alias-rpc.mjs';
3
4
  import { registerManagementRpc } from '../../../management-rpc.mjs';
@@ -26,6 +27,7 @@ export const WECOM_ENDPOINTS = Object.freeze({
26
27
  setModel: SET_MODEL_ENDPOINT,
27
28
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
28
29
  setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
30
+ setBusyMessageMode: SET_MESSAGE_MODE_ENDPOINT,
29
31
  setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
30
32
  setAlias: SET_ALIAS_ENDPOINT,
31
33
  });
@@ -89,6 +91,10 @@ function payloadFailure(endpoint, payload) {
89
91
  return validAgentPresetPayload(payload)
90
92
  ? null : '请选择 Agent Preset。';
91
93
  }
94
+ if (endpoint === WECOM_ENDPOINTS.setBusyMessageMode) {
95
+ return validMessageModePayload(payload)
96
+ ? null : '请提交有效的消息处理方式。';
97
+ }
92
98
  if (endpoint === WECOM_ENDPOINTS.setContextEnhancement) {
93
99
  return validContextEnhancementPayload(payload)
94
100
  ? null : '请提交有效的上下文增强设置。';
@@ -204,6 +210,11 @@ export function createWecomRpcHandler(controller, { encodeQr = qrDataUrl } = {})
204
210
  await controller.updateModel(payload.botId, payload.model),
205
211
  cachedEncode,
206
212
  );
213
+ } else if (endpoint === WECOM_ENDPOINTS.setBusyMessageMode) {
214
+ if (typeof controller.updateBusyMessageMode !== 'function') throw new Error('Message mode update is unavailable');
215
+ value = await controller.updateBusyMessageMode(
216
+ payload.botId, payload.busyMessageMode, (status) => publicStatus(status, cachedEncode),
217
+ );
207
218
  } else if (endpoint === WECOM_ENDPOINTS.setContextEnhancement) {
208
219
  if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
209
220
  value = await controller.updateContextEnhancement(