@xmanrui/dsh-im 4.36.1 → 4.38.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 (63) hide show
  1. package/README.en.md +14 -2
  2. package/README.md +13 -1
  3. package/assets/logo-plugin-message-link-gradient.webp +0 -0
  4. package/lib/client.js +417 -189
  5. package/lib/index.js +317 -316
  6. package/package.json +2 -2
  7. package/plugin-src/client/bot-general-settings.js +120 -0
  8. package/plugin-src/client/channels/dingtalk/api.js +3 -1
  9. package/plugin-src/client/channels/feishu/api.js +6 -2
  10. package/plugin-src/client/channels/qq/api.js +3 -1
  11. package/plugin-src/client/channels/shared/token-api.js +3 -1
  12. package/plugin-src/client/channels/wecom/api.js +3 -1
  13. package/plugin-src/client/channels/wecom-app/api.js +3 -1
  14. package/plugin-src/client/channels/weixin/api.js +3 -1
  15. package/plugin-src/client/channels/whatsapp/api.js +3 -1
  16. package/plugin-src/client/delivery-settings.js +5 -1
  17. package/plugin-src/client/i18n.js +14 -0
  18. package/plugin-src/client/styles.js +19 -0
  19. package/plugin-src/host/channels/dingtalk/rpc.mjs +11 -0
  20. package/plugin-src/host/channels/email/rpc.mjs +1 -0
  21. package/plugin-src/host/channels/feishu/production.mjs +1 -0
  22. package/plugin-src/host/channels/feishu/rpc.mjs +31 -0
  23. package/plugin-src/host/channels/imessage/rpc.mjs +1 -0
  24. package/plugin-src/host/channels/matrix/rpc.mjs +10 -0
  25. package/plugin-src/host/channels/qq/rpc.mjs +11 -0
  26. package/plugin-src/host/channels/shared/rpc.mjs +9 -0
  27. package/plugin-src/host/channels/slack/rpc.mjs +10 -0
  28. package/plugin-src/host/channels/wecom/rpc.mjs +11 -0
  29. package/plugin-src/host/channels/wecom-app/rpc.mjs +10 -0
  30. package/plugin-src/host/channels/weixin/rpc.mjs +11 -0
  31. package/plugin-src/host/channels/whatsapp/rpc.mjs +11 -0
  32. package/scripts/verify-package.mjs +11 -6
  33. package/src/channels/dingtalk/dingtalk-bridge.mjs +31 -2
  34. package/src/channels/dingtalk/dingtalk-menu.mjs +1 -1
  35. package/src/channels/dingtalk/dingtalk-runtime.mjs +2 -1
  36. package/src/channels/discord/discord-runtime.mjs +3 -1
  37. package/src/channels/feishu/bridge.mjs +91 -15
  38. package/src/channels/feishu/feishu-cards.mjs +16 -11
  39. package/src/channels/feishu/feishu-runtime.mjs +10 -1
  40. package/src/channels/feishu/multi-bot-controller.mjs +18 -0
  41. package/src/channels/feishu/plugin-config-store.mjs +2 -1
  42. package/src/channels/feishu/slash-command-panel.mjs +1 -0
  43. package/src/channels/feishu/step-push-mode.mjs +21 -0
  44. package/src/channels/matrix/matrix-crypto.mjs +19 -5
  45. package/src/channels/qq/qq-bridge.mjs +40 -2
  46. package/src/channels/qq/qq-menu.mjs +2 -2
  47. package/src/channels/shared/bot-workspace-store.mjs +69 -1
  48. package/src/channels/shared/busy-message-dispatcher.mjs +94 -0
  49. package/src/channels/shared/clear-command.mjs +85 -0
  50. package/src/channels/shared/command-catalog.mjs +3 -0
  51. package/src/channels/shared/command-permission.mjs +2 -0
  52. package/src/channels/shared/control-command.mjs +20 -11
  53. package/src/channels/shared/harness-approval.mjs +102 -2
  54. package/src/channels/shared/i18n-en/feishu.mjs +6 -4
  55. package/src/channels/shared/i18n-en/qq.mjs +2 -1
  56. package/src/channels/shared/i18n-en/shared-b.mjs +27 -0
  57. package/src/channels/shared/i18n-en/shared-c.mjs +4 -0
  58. package/src/channels/shared/message-mode.mjs +19 -0
  59. package/src/channels/shared/text-harness-bridge.mjs +48 -5
  60. package/src/channels/wecom/wecom-bridge.mjs +26 -2
  61. package/src/channels/wecom/wecom-cards.mjs +1 -1
  62. package/src/channels/wecom-app/wecom-app-bridge.mjs +26 -2
  63. 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.1",
3
+ "version": "4.38.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,120 @@
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
+ import { normalizeFeishuStepCardPanels } from '../../src/channels/feishu/step-push-mode.mjs';
5
+ import { FEISHU_ENDPOINTS } from './channels/feishu/api.js';
6
+
7
+ function settingsFromResult(result, botId) {
8
+ if (result?.ok !== true) throw new Error(result?.error?.message || '无法读取通用设置,请稍后重试。');
9
+ const bot = result.value?.bots?.find((entry) => entry.botId === botId);
10
+ if (!bot) throw new Error('找不到要修改的机器人。');
11
+ return {
12
+ busyMessageMode: normalizeBusyMessageMode(bot.busyMessageMode),
13
+ stepCardPanels: normalizeFeishuStepCardPanels(bot.stepCardPanels),
14
+ };
15
+ }
16
+
17
+ export function BotGeneralSettingsPage({ channel, account, rpcCall }) {
18
+ const modeId = React.useId();
19
+ const [settings, setSettings] = React.useState(() => ({
20
+ busyMessageMode: 'queue', stepCardPanels: normalizeFeishuStepCardPanels(),
21
+ }));
22
+ const [savedSettings, setSavedSettings] = React.useState(null);
23
+ const [busy, setBusy] = React.useState(false);
24
+ const [feedback, setFeedback] = React.useState(null);
25
+ const mounted = React.useRef(false);
26
+ const inFlight = React.useRef(false);
27
+ const mode = settings.busyMessageMode;
28
+
29
+ const load = React.useCallback(async () => {
30
+ if (inFlight.current) return;
31
+ inFlight.current = true;
32
+ setBusy(true);
33
+ setFeedback(null);
34
+ try {
35
+ const value = settingsFromResult(await rpcCall('connection.status', {}), account.botId);
36
+ if (!mounted.current) return;
37
+ setSettings(value);
38
+ setSavedSettings(value);
39
+ } catch (error) {
40
+ if (mounted.current) setFeedback(error.message);
41
+ } finally {
42
+ inFlight.current = false;
43
+ if (mounted.current) setBusy(false);
44
+ }
45
+ }, [account.botId, rpcCall]);
46
+
47
+ React.useEffect(() => {
48
+ mounted.current = true;
49
+ void load();
50
+ return () => { mounted.current = false; };
51
+ }, [load]);
52
+
53
+ const save = async (field, value) => {
54
+ if (inFlight.current || savedSettings === null || value === savedSettings[field]) return;
55
+ inFlight.current = true;
56
+ setSettings({ ...settings, [field]: value });
57
+ setBusy(true);
58
+ setFeedback(null);
59
+ try {
60
+ const endpoint = field === 'stepCardPanels' ? FEISHU_ENDPOINTS.setStepCardPanels : SET_MESSAGE_MODE_ENDPOINT;
61
+ const saved = settingsFromResult(await rpcCall(endpoint, {
62
+ botId: account.botId, [field]: value,
63
+ }), account.botId);
64
+ if (!mounted.current) return;
65
+ setSettings(saved);
66
+ setSavedSettings(saved);
67
+ } catch (error) {
68
+ if (mounted.current) {
69
+ setSettings(savedSettings);
70
+ setFeedback(error.message);
71
+ }
72
+ } finally {
73
+ inFlight.current = false;
74
+ if (mounted.current) setBusy(false);
75
+ }
76
+ };
77
+
78
+ return h('section', { className: 'dim-botGeneralSettings', 'aria-label': '机器人通用设置', 'aria-busy': busy },
79
+ h('div', { className: 'dim-botGeneralList' },
80
+ h('div', { className: 'dim-botGeneralRow' },
81
+ h('div', { className: 'dim-botGeneralText' },
82
+ h('label', { className: 'dim-botGeneralTitle', htmlFor: modeId }, '消息处理方式'),
83
+ h('p', { className: 'dim-botGeneralHelp', id: `${modeId}-description` }, mode === 'queue'
84
+ ? '当前任务结束后处理新消息'
85
+ : '将新消息补充到当前任务')),
86
+ h('div', { className: 'dim-botGeneralControl' },
87
+ h('select', {
88
+ id: modeId, value: mode, disabled: busy || savedSettings === null,
89
+ 'aria-label': '消息处理方式',
90
+ 'aria-describedby': `${modeId}-description`,
91
+ onChange: (event) => void save('busyMessageMode', event.target.value),
92
+ },
93
+ h('option', { value: 'queue' }, '排队'),
94
+ h('option', { value: 'steer' }, '插话')),
95
+ h('svg', { width: 14, height: 14, viewBox: '0 0 16 16', fill: 'none', 'aria-hidden': true },
96
+ h('path', { d: 'm4 6 4 4 4-4', stroke: 'currentColor', strokeWidth: 1.5, strokeLinecap: 'round', strokeLinejoin: 'round' })))),
97
+ channel === 'feishu' ? [
98
+ ['thinkingExpanded', '展开思考过程'],
99
+ ['toolsExpanded', '展开工具摘要'],
100
+ ].map(([field, label]) => h('div', { key: field, className: 'dim-botGeneralRow dim-botGeneralPanelRow' },
101
+ h('label', { className: 'dim-botGeneralTitle', htmlFor: `${modeId}-${field}` }, label),
102
+ h('div', { className: 'dim-botGeneralControl' },
103
+ h('input', {
104
+ id: `${modeId}-${field}`, type: 'checkbox', role: 'switch', className: 'dim-contextSwitch',
105
+ checked: settings.stepCardPanels[field], disabled: busy || savedSettings === null,
106
+ 'aria-label': label, 'aria-describedby': `${modeId}-panels-description`,
107
+ onChange: (event) => void save('stepCardPanels', {
108
+ ...settings.stepCardPanels, [field]: event.target.checked,
109
+ }),
110
+ })))) : null),
111
+ channel === 'feishu' ? h('p', { className: 'dim-botGeneralHelp dim-botGeneralPanelHelp', id: `${modeId}-panels-description` },
112
+ '仅对实时过程卡生效,任务结束后自动收起。') : null,
113
+ feedback ? h('p', {
114
+ className: 'dim-accessFeedback', 'data-tone': 'error', role: 'alert',
115
+ }, feedback) : null,
116
+ savedSettings === null && !busy ? h('div', { className: 'dim-botGeneralActions' },
117
+ h('button', {
118
+ type: 'button', className: 'dim-deliveryButton', onClick: () => void load(),
119
+ }, '重新读取')) : null);
120
+ }
@@ -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
  /**
@@ -13,7 +14,7 @@ import { normalizeModelCatalog, normalizeModelSelection, SET_MODEL_ENDPOINT } fr
13
14
  import { normalizeLastMessageError } from "../../last-message-error.js";
14
15
  import { normalizeAccessPolicy } from "../../../../src/channels/shared/access-policy.mjs";
15
16
  import { normalizeContextEnhancementConfig } from "../../../../src/channels/shared/context-enhancement.mjs";
16
- import { normalizeFeishuStepPushMode } from "../../../../src/channels/feishu/step-push-mode.mjs";
17
+ import { normalizeFeishuStepPushMode, normalizeFeishuStepCardPanels } from "../../../../src/channels/feishu/step-push-mode.mjs";
17
18
  import { normalizeFeishuVoiceConfig } from "../../../../src/channels/feishu/voice-config.mjs";
18
19
  import { normalizeSlashPanelConfig } from "../../../../src/channels/feishu/slash-command-panel.mjs";
19
20
 
@@ -34,12 +35,14 @@ 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",
40
42
  setMentionTopicReply: "bot.mention-topic-reply.set",
41
43
  setStepPush: "bot.step-push.set",
42
44
  setStepPushMode: "bot.step-push-mode.set",
45
+ setStepCardPanels: "bot.step-card-panels.set",
43
46
  setVoice: "bot.voice.set",
44
47
  setSlashPanel: "bot.slash-panel.set",
45
48
  // Kept for rolling upgrades. The multi-bot UI never calls these endpoints.
@@ -223,7 +226,7 @@ export function normalizeBotConnection(value, fallbackBotId) {
223
226
  workspace: optionalString(value.workspace)?.slice(0, 4_096) ?? "",
224
227
  model: normalizeModelSelection(value.model),
225
228
  agentPreset: normalizeAgentPresetId(value.agentPreset),
226
- contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
229
+ busyMessageMode: normalizeBusyMessageMode(value.busyMessageMode), contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
227
230
  ...(Object.hasOwn(value, "accessPolicy")
228
231
  ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) }
229
232
  : {}),
@@ -231,6 +234,7 @@ export function normalizeBotConnection(value, fallbackBotId) {
231
234
  mentionTopicReply: value.mentionTopicReply !== false,
232
235
  stepPush: value.stepPush === true,
233
236
  stepPushMode: normalizeFeishuStepPushMode(value.stepPushMode),
237
+ stepCardPanels: normalizeFeishuStepCardPanels(value.stepCardPanels),
234
238
  voice: normalizeFeishuVoiceConfig(value.voice),
235
239
  slashPanel: normalizeSlashPanelConfig(value.slashPanel),
236
240
  groupMessagePermissionGranted: value.groupMessagePermissionGranted === true,
@@ -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
  : {}),
@@ -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';
@@ -25,6 +26,7 @@ export const DELIVERY_ENDPOINTS = Object.freeze({
25
26
  });
26
27
 
27
28
  export const BOT_SETTINGS_TABS = Object.freeze([
29
+ Object.freeze({ id: 'general', label: '通用' }),
28
30
  Object.freeze({ id: 'delivery', label: '投递设置' }),
29
31
  Object.freeze({ id: 'access', label: '访问设置' }),
30
32
  ]);
@@ -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: `${channel}:${account.botId}`, channel, account, rpcCall: accessRpcCall })
785
+ : activeTab.id === 'access'
782
786
  ? h(AccessPolicySettingsPage, {
783
787
  channel,
784
788
  account: { ...account, accessPolicy },
@@ -1025,6 +1025,20 @@ 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
+ '展开思考过程': 'Expand thinking',
1032
+ '展开工具摘要': 'Expand tool summaries',
1033
+ '仅对实时过程卡生效,任务结束后自动收起。': 'Applies to streaming process cards only. Panels collapse when the task ends.',
1034
+ '请选择实时过程卡面板的展开状态。': 'Choose whether to expand the streaming process card panels.',
1035
+ "消息处理方式": "Message handling",
1036
+ "排队": "Queue",
1037
+ "插话": "Steer",
1038
+ "当前任务结束后处理新消息": "Process new messages after the current task",
1039
+ "将新消息补充到当前任务": "Add new messages to the current task",
1040
+ "无法读取消息处理设置,请稍后重试。": "Could not read message handling settings. Try again later.",
1041
+ "请提交有效的消息处理方式。": "Choose a valid message handling mode.",
1028
1042
  '通用设置': 'General settings',
1029
1043
  '通用设置分类': 'General settings categories',
1030
1044
  '附件': 'Attachments',
@@ -633,6 +633,25 @@ const CSS = String.raw`
633
633
  .dim-accessFeedback[data-tone="success"] { color: var(--dsw-alias-state-success-primary, #20a162); }
634
634
  .dim-accessFeedback[data-tone="error"] { color: var(--dsw-alias-state-error-primary, #d54941); }
635
635
  .dim-accessActions { display: flex; justify-content: flex-end; }
636
+ .dim-botGeneralSettings { min-width: 0; }
637
+ .dim-botGeneralList { min-width: 0; display: grid; }
638
+ .dim-botGeneralRow { min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr) max-content; align-items: center; gap: 24px; padding: 16px 0; border-bottom: 1px solid var(--dsw-alias-border-l2, #dfe1e5); }
639
+ .dim-botGeneralRow:last-child { border-bottom: 0; }
640
+ .dim-botGeneralPanelRow { border-bottom: 0; }
641
+ .dim-botGeneralText { min-width: 0; display: grid; gap: 4px; }
642
+ .dim-botGeneralTitle { color: var(--dsw-alias-label-primary, #1f2329); font-size: 14px; line-height: 22px; font-weight: 400; }
643
+ .dim-botGeneralHelp { margin: 0; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 12px; line-height: 18px; overflow-wrap: anywhere; }
644
+ .dim-botGeneralPanelHelp { padding-bottom: 16px; border-bottom: 1px solid var(--dsw-alias-border-l2, #dfe1e5); }
645
+ .dim-botGeneralControl { position: relative; min-width: 0; max-width: 100%; justify-self: end; color: var(--dsw-alias-label-primary, #1f2329); }
646
+ .dim-botGeneralControl select { display: block; min-width: 104px; max-width: 100%; height: 36px; padding: 0 36px 0 14px; border: 0; border-radius: 10px; color: inherit; background: var(--dsw-alias-bg-module-platform, #f5f6f7); font: inherit; font-size: 14px; line-height: 22px; appearance: none; cursor: pointer; }
647
+ .dim-botGeneralControl select:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover, #eef0f3); }
648
+ .dim-botGeneralControl select:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: 2px; }
649
+ .dim-botGeneralControl select:disabled { opacity: .5; cursor: not-allowed; }
650
+ .dim-botGeneralControl svg { position: absolute; top: 50%; right: 12px; transform: translateY(-50%); pointer-events: none; }
651
+ .dim-botGeneralControl .dim-contextSwitch { display: block; }
652
+ .dim-botGeneralControl .dim-contextSwitch:disabled { opacity: .5; cursor: not-allowed; }
653
+ .dim-botGeneralControl .dim-contextSwitch:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: 3px; }
654
+ .dim-botGeneralActions { display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: 8px; padding-top: 16px; }
636
655
  .dim-generalSettingsPage { min-width: 0; display: grid; }
637
656
  .dim-generalSettingsHeader { min-width: 0; margin: 0 0 8px; }
638
657
  .dim-generalSettingsHeader h2 { margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 18px; line-height: 25px; font-weight: 680; }
@@ -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
@@ -260,6 +260,7 @@ export async function createProductionController(ctx, config = {}, internals = {
260
260
  mentionTopicReply: botConfig.mentionTopicReply,
261
261
  stepPush: botConfig.stepPush,
262
262
  stepPushMode: botConfig.stepPushMode,
263
+ stepCardPanels: botConfig.stepCardPanels,
263
264
  voice: voiceSource,
264
265
  sessionSyncTargetsFor: sessionSyncTargetsFor,
265
266
  ownerOpenIds: botConfig.ownerOpenIds ?? [botConfig.ownerOpenId],
@@ -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';
@@ -29,6 +30,8 @@ import {
29
30
  import {
30
31
  isFeishuStepPushMode,
31
32
  normalizeFeishuStepPushMode,
33
+ isFeishuStepCardPanels,
34
+ normalizeFeishuStepCardPanels,
32
35
  } from '../../../../src/channels/feishu/step-push-mode.mjs';
33
36
  import { normalizeFeishuVoiceConfig } from '../../../../src/channels/feishu/voice-config.mjs';
34
37
  import {
@@ -42,6 +45,7 @@ import {
42
45
 
43
46
  export const FEISHU_ENDPOINTS = Object.freeze({
44
47
  ...FEISHU_CLIENT_ENDPOINTS,
48
+ setBusyMessageMode: SET_MESSAGE_MODE_ENDPOINT,
45
49
  setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
46
50
  setAlias: SET_ALIAS_ENDPOINT,
47
51
  });
@@ -307,11 +311,13 @@ function publicBotEntry(entry) {
307
311
  model: normalizeModelSelection(source.model),
308
312
  agentPreset: normalizeAgentPresetId(source.agentPreset),
309
313
  contextEnhancement: normalizeContextEnhancementConfig(source.contextEnhancement),
314
+ busyMessageMode: normalizeBusyMessageMode(source.busyMessageMode),
310
315
  accessPolicy: normalizeAccessPolicy(source.accessPolicy),
311
316
  groupResponseMode: normalizeFeishuGroupResponseMode(source.groupResponseMode),
312
317
  mentionTopicReply: source.mentionTopicReply !== false,
313
318
  stepPush: source.stepPush === true,
314
319
  stepPushMode: normalizeFeishuStepPushMode(source.stepPushMode),
320
+ stepCardPanels: normalizeFeishuStepCardPanels(source.stepCardPanels),
315
321
  voice: normalizeFeishuVoiceConfig(source.voice),
316
322
  slashPanel: normalizeSlashPanelConfig(source.slashPanel),
317
323
  groupMessagePermissionGranted: source.groupMessagePermissionGranted === true,
@@ -457,6 +463,10 @@ function validPayload(endpoint, payload) {
457
463
  return validAgentPresetPayload(payload)
458
464
  ? null : '请选择 Agent Preset。';
459
465
  }
466
+ if (endpoint === FEISHU_ENDPOINTS.setBusyMessageMode) {
467
+ return validMessageModePayload(payload)
468
+ ? null : '请提交有效的消息处理方式。';
469
+ }
460
470
  if (endpoint === FEISHU_ENDPOINTS.setContextEnhancement) {
461
471
  return validContextEnhancementPayload(payload)
462
472
  ? null : '请提交有效的上下文增强设置。';
@@ -497,6 +507,13 @@ function validPayload(endpoint, payload) {
497
507
  ? null
498
508
  : '请选择分步直推的呈现方式。';
499
509
  }
510
+ if (endpoint === FEISHU_ENDPOINTS.setStepCardPanels) {
511
+ return hasOnlyKeys(payload, new Set(['botId', 'stepCardPanels']))
512
+ && safeOpaqueId(payload.botId)
513
+ && isFeishuStepCardPanels(payload.stepCardPanels)
514
+ ? null
515
+ : '请选择实时过程卡面板的展开状态。';
516
+ }
500
517
  if (endpoint === FEISHU_ENDPOINTS.setVoice) {
501
518
  const voice = payload?.voice;
502
519
  const validVoice = voice === null
@@ -766,6 +783,12 @@ export function createFeishuRpcHandler(controller, { encodeQr = qrCodeDataUrl }
766
783
  await controller.updateModel(payload.botId, payload.model),
767
784
  { encodeQr: cachedEncodeQr },
768
785
  );
786
+ } else if (endpoint === FEISHU_ENDPOINTS.setBusyMessageMode) {
787
+ if (typeof controller.updateBusyMessageMode !== 'function') throw new Error('Message mode update is unavailable');
788
+ value = await controller.updateBusyMessageMode(
789
+ payload.botId, payload.busyMessageMode,
790
+ (status) => toPublicFeishuStatus(status, { encodeQr: cachedEncodeQr }),
791
+ );
769
792
  } else if (endpoint === FEISHU_ENDPOINTS.setContextEnhancement) {
770
793
  if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
771
794
  value = await controller.updateContextEnhancement(
@@ -822,6 +845,14 @@ export function createFeishuRpcHandler(controller, { encodeQr = qrCodeDataUrl }
822
845
  await controller.updateStepPushMode(payload.botId, payload.stepPushMode),
823
846
  { encodeQr: cachedEncodeQr },
824
847
  );
848
+ } else if (endpoint === FEISHU_ENDPOINTS.setStepCardPanels) {
849
+ if (typeof controller.updateStepCardPanels !== 'function') {
850
+ throw new Error('Step card panel update is unavailable');
851
+ }
852
+ value = await toPublicFeishuStatus(
853
+ await controller.updateStepCardPanels(payload.botId, payload.stepCardPanels),
854
+ { encodeQr: cachedEncodeQr },
855
+ );
825
856
  } else if (endpoint === FEISHU_ENDPOINTS.setVoice) {
826
857
  if (typeof controller.updateVoice !== 'function') {
827
858
  throw new Error('Voice update is unavailable');
@@ -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(