@xmanrui/dsh-im 4.23.0 → 4.24.1

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 (41) hide show
  1. package/README.en.md +7 -2
  2. package/README.md +7 -2
  3. package/lib/client.js +498 -347
  4. package/lib/index.js +284 -283
  5. package/package.json +11 -2
  6. package/plugin-src/client/channel-card-meta.js +3 -9
  7. package/plugin-src/client/channels/dingtalk/index.js +9 -8
  8. package/plugin-src/client/channels/feishu/index.js +21 -17
  9. package/plugin-src/client/channels/qq/index.js +9 -8
  10. package/plugin-src/client/channels/shared/collapsible-account.js +49 -26
  11. package/plugin-src/client/channels/shared/token-api.js +3 -0
  12. package/plugin-src/client/channels/shared/token-channel.js +9 -8
  13. package/plugin-src/client/channels/telegram/index.js +3 -0
  14. package/plugin-src/client/channels/telegram/styles.js +12 -0
  15. package/plugin-src/client/channels/telegram/thinking-traces.js +25 -0
  16. package/plugin-src/client/channels/wecom/index.js +9 -8
  17. package/plugin-src/client/channels/wecom-app/index.js +9 -8
  18. package/plugin-src/client/channels/weixin/index.js +9 -8
  19. package/plugin-src/client/channels/whatsapp/index.js +9 -8
  20. package/plugin-src/client/i18n.js +13 -1
  21. package/plugin-src/client/index.js +10 -2
  22. package/plugin-src/client/styles.js +12 -8
  23. package/plugin-src/client/update-panel.js +18 -8
  24. package/plugin-src/host/channels/shared/rpc.mjs +9 -0
  25. package/plugin-src/host/channels/shared/thinking-traces-rpc.mjs +11 -0
  26. package/plugin-src/host/modern-harness-api.mjs +7 -2
  27. package/plugin-src/host/update-service.mjs +19 -14
  28. package/scripts/verify-package.mjs +11 -5
  29. package/src/channels/email/email-runtime.mjs +9 -2
  30. package/src/channels/email/transports/agent-mail.mjs +14 -3
  31. package/src/channels/feishu/bridge.mjs +122 -46
  32. package/src/channels/feishu/feishu-channel.mjs +35 -0
  33. package/src/channels/feishu/live-cot.mjs +260 -0
  34. package/src/channels/feishu/slash-command-registry.mjs +17 -0
  35. package/src/channels/feishu/step-push-mode.mjs +10 -4
  36. package/src/channels/shared/harness-client.mjs +224 -39
  37. package/src/channels/shared/text-harness-bridge.mjs +60 -14
  38. package/src/channels/shared/workspace-session.mjs +7 -1
  39. package/src/channels/telegram/config-store.mjs +4 -1
  40. package/src/channels/telegram/telegram-controller.mjs +13 -1
  41. package/src/channels/telegram/telegram-runtime.mjs +198 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xmanrui/dsh-im",
3
- "version": "4.23.0",
3
+ "version": "4.24.1",
4
4
  "description": "把十一种 IM 渠道和公网 AI Office 接入本机 DeepSeek Harness。 Connect eleven IM channels and a public AI Office to a local DeepSeek Harness.",
5
5
  "keywords": [
6
6
  "deepseek-harness",
@@ -80,6 +80,10 @@
80
80
  {
81
81
  "name": "wings1848",
82
82
  "url": "https://github.com/wings1848"
83
+ },
84
+ {
85
+ "name": "Zhe (Phil) Yang",
86
+ "url": "https://github.com/yangzhe1991"
83
87
  }
84
88
  ],
85
89
  "license": "MIT",
@@ -110,7 +114,12 @@
110
114
  "cordis.patch.yml",
111
115
  "lib",
112
116
  "plugin-src",
113
- "scripts",
117
+ "scripts/verify-interface-language.mjs",
118
+ "scripts/verify-lan-management.mjs",
119
+ "scripts/verify-model-setting.mjs",
120
+ "scripts/verify-package.mjs",
121
+ "scripts/verify-session-channel-logos.mjs",
122
+ "scripts/verify-session-title-prefix.mjs",
114
123
  "src",
115
124
  "PROACTIVE_DELIVERY.md",
116
125
  "PROACTIVE_DELIVERY.en.md",
@@ -7,7 +7,7 @@ export const BotSettingsContext = React.createContext(Object.freeze({
7
7
  openBotSettings() {},
8
8
  }));
9
9
 
10
- function SettingsGlyph() {
10
+ export function SettingsGlyph() {
11
11
  return h('svg', {
12
12
  viewBox: '0 0 24 24',
13
13
  width: 16,
@@ -32,14 +32,12 @@ export function BotSettingsButton({
32
32
  channelSettings,
33
33
  }) {
34
34
  const { openBotSettings } = React.useContext(BotSettingsContext);
35
- const tooltipId = React.useId();
36
35
  return h('span', { className: 'dim-botSettingsAction' },
37
36
  h('button', {
38
37
  type: 'button',
39
38
  className: 'dim-botSettingsButton',
40
39
  'data-delivery-channel': channel,
41
40
  'aria-label': '更多机器人设置',
42
- 'aria-describedby': tooltipId,
43
41
  onClick: () => openBotSettings?.({
44
42
  ...(channelSettings && typeof channelSettings === 'object' ? channelSettings : {}),
45
43
  channel,
@@ -48,12 +46,8 @@ export function BotSettingsButton({
48
46
  connected: Boolean(connected),
49
47
  accessPolicy,
50
48
  }),
51
- }, h(SettingsGlyph)),
52
- h('span', {
53
- id: tooltipId,
54
- className: 'dim-botSettingsTooltip',
55
- role: 'tooltip',
56
- }, '更多机器人设置'));
49
+ }, h(SettingsGlyph), h('span', null, '更多设置'),
50
+ h('span', { className: 'dim-moreSettingsChevron', 'aria-hidden': 'true' })));
57
51
  }
58
52
 
59
53
  function messageErrorTime(value) {
@@ -3,7 +3,7 @@ import { BotName } from '../../bot-alias.js';
3
3
  import * as React from 'react';
4
4
 
5
5
  import { CredentialActionIcon, CredentialBindingPanel, QrActionIcon } from '../../credential-binding.js';
6
- import { CollapsibleAccountSection } from '../shared/collapsible-account.js';
6
+ import { AccountSettingsToggle, CollapsibleAccountSection } from '../shared/collapsible-account.js';
7
7
  import { h } from '../../i18n.js';
8
8
  import { WorkspaceEditor } from '../../workspace-editor.js';
9
9
  import { ContextEnhancementEditor } from '../../context-enhancement.js';
@@ -251,6 +251,13 @@ export function AccountCard({
251
251
  return h('article', { className: 'ddt-card dim-botCard', tabIndex: -1, 'data-bot-id': account.botId },
252
252
  h('div', { className: 'ddt-cardBody dim-botCardBody' },
253
253
  h(CollapsibleAccountSection, {
254
+ settings: h(BotSettingsButton, {
255
+ channel: 'dingtalk',
256
+ botId: account.botId,
257
+ botName: account.bot.name,
258
+ connected: account.connected,
259
+ accessPolicy: account.accessPolicy,
260
+ }),
254
261
  id: `ddt-settings-${account.botId.replace(/[^a-zA-Z0-9_-]/g, '-')}`,
255
262
  header: h('div', { className: 'ddt-accountTop dim-botCardTop' },
256
263
  h('div', { className: 'ddt-accountIdentity dim-botIdentity' },
@@ -272,13 +279,7 @@ export function AccountCard({
272
279
  lastCheckedAt: account.health.lastCheckedAt,
273
280
  formatCheckedTime: checkedTime,
274
281
  }),
275
- h(BotSettingsButton, {
276
- channel: 'dingtalk',
277
- botId: account.botId,
278
- botName: account.bot.name,
279
- connected: account.connected,
280
- accessPolicy: account.accessPolicy,
281
- })))
282
+ h(AccountSettingsToggle)))
282
283
  },
283
284
  h(WorkspaceEditor, {
284
285
  workspace: account.workspace,
@@ -4,7 +4,7 @@ import * as React from "react";
4
4
 
5
5
  import { FeishuLogoGlyph } from "../../channel-logos.js";
6
6
  import { CredentialActionIcon, CredentialBindingPanel, QrActionIcon } from "../../credential-binding.js";
7
- import { CollapsibleAccountSection } from "../shared/collapsible-account.js";
7
+ import { AccountSettingsToggle, CollapsibleAccountSection } from "../shared/collapsible-account.js";
8
8
  import { h } from "../../i18n.js";
9
9
  import {
10
10
  FEISHU_ENDPOINTS,
@@ -466,7 +466,7 @@ function RemoveConfirmation({ bot, busy, onConfirm, onCancel }) {
466
466
  );
467
467
  }
468
468
 
469
- /** One select for the step-push presentation: off / per-step posts / process card. */
469
+ /** One select for the step-push presentation. */
470
470
  function StepPushEditor({ value = false, mode = "post", disabled = false, onSave, onModeSave }) {
471
471
  const titleId = React.useId();
472
472
  const helpId = `${titleId}-help`;
@@ -496,7 +496,7 @@ function StepPushEditor({ value = false, mode = "post", disabled = false, onSave
496
496
  if (value === true) await onSave?.(false);
497
497
  return;
498
498
  }
499
- const nextMode = next === "streaming_card" ? "streaming_card" : "post";
499
+ const nextMode = ["streaming_card", "live_cot"].includes(next) ? next : "post";
500
500
  // Enabling (or switching presentation) may need both writes; the flag
501
501
  // must land before the mode so the runtime never sees a mode without
502
502
  // step push enabled.
@@ -507,7 +507,9 @@ function StepPushEditor({ value = false, mode = "post", disabled = false, onSave
507
507
 
508
508
  const helpText = current === "off"
509
509
  ? "适合日常问答:执行过程中不显示工具调用等中间步骤,只回复最终结果"
510
- : current === "streaming_card"
510
+ : current === "live_cot"
511
+ ? "使用飞书原生思考过程展示推理、工具调用与结果,最终答案单独发送"
512
+ : current === "streaming_card"
511
513
  ? "推荐长任务使用:过程与最终答案都在同一张卡片里实时更新,不刷屏"
512
514
  : "每一步都单独发一条消息(含工具调用和过程说明);注意长任务会连续发送较多消息";
513
515
 
@@ -529,7 +531,7 @@ function StepPushEditor({ value = false, mode = "post", disabled = false, onSave
529
531
  id: helpId,
530
532
  className: "dim-presetTooltip",
531
533
  role: "tooltip",
532
- }, "设置任务执行过程的呈现方式:不显示、实时卡片或逐步消息"))),
534
+ }, "设置任务执行过程的呈现方式:不显示、原生直播、实时卡片或逐步消息"))),
533
535
  saving
534
536
  ? h("span", { className: "dim-feishuGroupControlStatus", role: "status" }, "保存中…")
535
537
  : null),
@@ -541,6 +543,7 @@ function StepPushEditor({ value = false, mode = "post", disabled = false, onSave
541
543
  onChange: change,
542
544
  },
543
545
  h("option", { value: "off" }, "不显示过程(只发送最终答案)"),
546
+ h("option", { value: "live_cot" }, "实时直播(飞书原生思考过程)"),
544
547
  h("option", { value: "streaming_card" }, "实时过程卡(全程一张卡片动态更新)"),
545
548
  h("option", { value: "post" }, "逐步直播(每一步单独发一条消息)")),
546
549
  h("p", { className: "dim-feishuGroupHelp" }, helpText),
@@ -597,6 +600,18 @@ export function BotCard({
597
600
  },
598
601
  h("div", { className: "bxf-cardBody dim-botCardBody" },
599
602
  h(CollapsibleAccountSection, {
603
+ settings: h(BotSettingsButton, {
604
+ channel: "feishu",
605
+ botId: connection.botId,
606
+ botName: bot.name,
607
+ connected,
608
+ accessPolicy: connection.accessPolicy,
609
+ channelSettings: {
610
+ groupResponseMode: connection.groupResponseMode,
611
+ groupTopicReply: connection.groupTopicReply,
612
+ groupMessagePermissionGranted: connection.groupMessagePermissionGranted,
613
+ },
614
+ }),
600
615
  id: `bxf-settings-${connection.botId.replace(/[^a-zA-Z0-9_-]/g, "-")}`,
601
616
  header: h("div", { className: "bxf-connectedTop dim-botCardTop" },
602
617
  h("div", { className: "bxf-botIdentity dim-botIdentity" },
@@ -621,18 +636,7 @@ export function BotCard({
621
636
  formatCheckedTime,
622
637
  healthState: stateForDisplay,
623
638
  }),
624
- h(BotSettingsButton, {
625
- channel: "feishu",
626
- botId: connection.botId,
627
- botName: bot.name,
628
- connected,
629
- accessPolicy: connection.accessPolicy,
630
- channelSettings: {
631
- groupResponseMode: connection.groupResponseMode,
632
- groupTopicReply: connection.groupTopicReply,
633
- groupMessagePermissionGranted: connection.groupMessagePermissionGranted,
634
- },
635
- }))),
639
+ h(AccountSettingsToggle))),
636
640
  },
637
641
  h(WorkspaceEditor, {
638
642
  workspace: connection.workspace,
@@ -4,7 +4,7 @@ import * as React from 'react';
4
4
 
5
5
  import { QqLogoGlyph } from '../../channel-logos.js';
6
6
  import { CredentialActionIcon, CredentialBindingPanel, QrActionIcon } from '../../credential-binding.js';
7
- import { CollapsibleAccountSection } from '../shared/collapsible-account.js';
7
+ import { AccountSettingsToggle, CollapsibleAccountSection } from '../shared/collapsible-account.js';
8
8
  import { h } from '../../i18n.js';
9
9
  import { WorkspaceEditor } from '../../workspace-editor.js';
10
10
  import { ContextEnhancementEditor } from '../../context-enhancement.js';
@@ -189,6 +189,13 @@ export function AccountCard({
189
189
  return h('article', { className: 'ddt-card dim-botCard', 'data-bot-id': account.botId },
190
190
  h('div', { className: 'ddt-cardBody dim-botCardBody' },
191
191
  h(CollapsibleAccountSection, {
192
+ settings: h(BotSettingsButton, {
193
+ channel: 'qq',
194
+ botId: account.botId,
195
+ botName: account.bot.name,
196
+ connected: account.connected,
197
+ accessPolicy: account.accessPolicy,
198
+ }),
192
199
  id: `qq-settings-${account.botId.replace(/[^a-zA-Z0-9_-]/g, '-')}`,
193
200
  header: h('div', { className: 'ddt-accountTop dim-botCardTop' },
194
201
  h('div', { className: 'ddt-accountIdentity dim-botIdentity' },
@@ -209,13 +216,7 @@ export function AccountCard({
209
216
  lastCheckedAt: account.health.lastCheckedAt,
210
217
  formatCheckedTime: checkedTime,
211
218
  }),
212
- h(BotSettingsButton, {
213
- channel: 'qq',
214
- botId: account.botId,
215
- botName: account.bot.name,
216
- connected: account.connected,
217
- accessPolicy: account.accessPolicy,
218
- })))
219
+ h(AccountSettingsToggle)))
219
220
  },
220
221
  h(WorkspaceEditor, {
221
222
  workspace: account.workspace,
@@ -3,32 +3,64 @@
3
3
  *
4
4
  * When several accounts are connected, each account's workspace / preset /
5
5
  * context-enhancement editors make the list extremely long. This component
6
- * renders the account header as the toggle itself: collapsed, the card shows
7
- * only the header line; clicking the header expands the settings details.
6
+ * keeps one settings toggle in the existing header tools slot. Clicking the
7
+ * header also expands the settings details; more settings live in the body.
8
8
  *
9
9
  * Design notes:
10
- * - The whole header is the toggle (click + keyboard). Buttons rendered inside
11
- * the header must call stopPropagation so they keep their own behaviour.
12
- * - A chevron sits at the end of the header line as the affordance.
10
+ * - The native toggle owns keyboard interaction. Header controls stop click
11
+ * propagation so editing a name cannot accidentally toggle the card.
13
12
  * - Height animation uses grid-template-rows: 0fr -> 1fr (pure CSS).
14
- * - Full a11y: the header is a button-like region with aria-expanded /
15
- * aria-controls; the details region keeps native semantics.
13
+ * - The toggle exposes aria-expanded / aria-controls; the body keeps native
14
+ * semantics and hides collapsed controls from focus and accessibility.
16
15
  * - Honors prefers-reduced-motion.
17
16
  * - Styles are managed by the IM settings plugin, not individual accounts.
18
17
  */
19
18
  import * as React from 'react';
20
19
  import { h } from '../../i18n.js';
20
+ import { SettingsGlyph } from '../../channel-card-meta.js';
21
+
22
+ const AccountSectionContext = React.createContext(null);
23
+
24
+ /** Renders in the original settings-button slot, beside the status metadata. */
25
+ export function AccountSettingsToggle() {
26
+ const { open, toggle, contentId } = React.useContext(AccountSectionContext);
27
+ return h('button', {
28
+ type: 'button',
29
+ className: 'dim-accountSettingsToggle',
30
+ 'aria-label': open ? '收起该账号的设置' : '展开该账号的设置',
31
+ 'aria-expanded': open ? 'true' : 'false',
32
+ 'aria-controls': contentId,
33
+ onClick: (event) => {
34
+ event.stopPropagation();
35
+ toggle();
36
+ },
37
+ }, h(SettingsGlyph),
38
+ h('svg', {
39
+ className: 'dim-collapsibleChevron',
40
+ viewBox: '0 0 12 16',
41
+ width: 12,
42
+ height: 16,
43
+ fill: 'none',
44
+ stroke: 'currentColor',
45
+ strokeWidth: 1.8,
46
+ strokeLinecap: 'round',
47
+ strokeLinejoin: 'round',
48
+ 'aria-hidden': 'true',
49
+ }, h('path', { d: 'M2 6 6 10 10 6' })));
50
+ }
21
51
 
22
52
  /**
23
53
  * Collapsible account card.
24
54
  *
25
55
  * `header` renders the always-visible header line; clicking it toggles.
26
56
  * Any interactive control inside the header should stop the click event so it
27
- * does not toggle the section (e.g. the bot settings button).
57
+ * does not toggle the section (e.g. the alias editor).
58
+ * `settings` is the original more-settings action, shown above the editors.
28
59
  * `children` is the collapsed details region.
29
60
  */
30
61
  export function CollapsibleAccountSection({
31
62
  header,
63
+ settings,
32
64
  defaultOpen = false,
33
65
  open: controlledOpen,
34
66
  onToggle,
@@ -37,44 +69,35 @@ export function CollapsibleAccountSection({
37
69
  children,
38
70
  }) {
39
71
  const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen);
72
+ const generatedId = React.useId();
40
73
  const open = controlledOpen ?? uncontrolledOpen;
41
- const contentId = id ? `${id}-content` : undefined;
74
+ const contentId = `${id ?? generatedId}-content`;
42
75
 
43
76
  const toggle = () => {
44
77
  if (controlledOpen === undefined) setUncontrolledOpen((value) => !value);
45
78
  onToggle?.(!open);
46
79
  };
47
80
 
48
- const onKeyDown = (event) => {
49
- if (event.key === 'Enter' || event.key === ' ' || event.key === 'Spacebar') {
50
- event.preventDefault();
51
- toggle();
52
- }
53
- };
54
-
55
- return h('div', {
81
+ return h(AccountSectionContext.Provider, { value: { open, toggle, contentId } },
82
+ h('div', {
56
83
  className: `dim-collapsibleAccount ${open ? 'is-open' : ''} ${className}`.trim(),
57
84
  'data-open': open ? 'true' : 'false',
58
85
  },
59
86
  h('div', {
60
87
  className: 'dim-collapsibleHead',
61
- role: 'button',
62
- tabIndex: 0,
63
88
  onClick: toggle,
64
- onKeyDown,
65
- 'aria-expanded': open ? 'true' : 'false',
66
- 'aria-controls': contentId,
67
- 'aria-label': open ? '收起该账号的设置' : '展开该账号的设置',
68
89
  },
69
90
  h('div', { className: 'dim-collapsibleHeaderContent' }, header),
70
- h('span', { className: 'dim-collapsibleChevron', 'aria-hidden': 'true' }),
71
91
  ),
72
92
  h('div', {
73
93
  id: contentId,
74
94
  className: 'dim-collapsibleBody',
75
95
  role: 'region',
76
96
  },
77
- h('div', { className: 'dim-collapsibleBodyInner' }, children),
97
+ h('div', { className: 'dim-collapsibleBodyInner' },
98
+ settings ? h('div', { className: 'dim-accountSettingsHeader' },
99
+ h('span', null, '常用配置'), settings) : null,
100
+ children),
78
101
  ),
79
- );
102
+ ));
80
103
  }
@@ -38,6 +38,7 @@ export const TOKEN_BOT_ENDPOINTS = Object.freeze({
38
38
  setContextEnhancement: 'bot.context-enhancement.set',
39
39
  setAccessPolicy: 'bot.access-policy.set',
40
40
  setAlias: 'bot.alias.set',
41
+ setThinkingTraces: 'bot.thinking-traces.set',
41
42
  });
42
43
 
43
44
  export function createTokenChannelApi(channel, connectionSummary, {
@@ -69,6 +70,8 @@ export function createTokenChannelApi(channel, connectionSummary, {
69
70
  model: normalizeModelSelection(value.model),
70
71
  agentPreset: normalizeAgentPresetId(value.agentPreset),
71
72
  contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
73
+ // Absent from the backend means ON; only an explicit false opts out.
74
+ thinkingTraces: value.thinkingTraces !== false,
72
75
  ...(Object.hasOwn(value, 'accessPolicy')
73
76
  ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) }
74
77
  : {}),
@@ -3,7 +3,7 @@ import { BotName } from '../../bot-alias.js';
3
3
  import * as React from 'react';
4
4
 
5
5
  import { CredentialActionIcon, CredentialBindingPanel } from '../../credential-binding.js';
6
- import { CollapsibleAccountSection } from './collapsible-account.js';
6
+ import { AccountSettingsToggle, CollapsibleAccountSection } from './collapsible-account.js';
7
7
  import { h } from '../../i18n.js';
8
8
  import { installDingtalkStyles } from '../dingtalk/styles.js';
9
9
  import { WorkspaceEditor } from '../../workspace-editor.js';
@@ -92,6 +92,13 @@ export function createTokenChannelSettings(definition) {
92
92
  return h('article', { className: 'ddt-card dim-botCard', 'data-bot-id': account.botId },
93
93
  h('div', { className: 'ddt-cardBody dim-botCardBody' },
94
94
  h(CollapsibleAccountSection, {
95
+ settings: h(BotSettingsButton, {
96
+ channel: channel.toLowerCase(),
97
+ botId: account.botId,
98
+ botName: account.bot.name,
99
+ connected: account.connected,
100
+ accessPolicy: account.accessPolicy,
101
+ }),
95
102
  id: `tok-settings-${account.botId.replace(/[^a-zA-Z0-9_-]/g, '-')}`,
96
103
  header: h('div', { className: 'ddt-accountTop dim-botCardTop' },
97
104
  h('div', { className: 'ddt-accountIdentity dim-botIdentity' },
@@ -113,13 +120,7 @@ export function createTokenChannelSettings(definition) {
113
120
  lastCheckedAt: account.health.lastCheckedAt,
114
121
  formatCheckedTime: checkedTime,
115
122
  }),
116
- h(BotSettingsButton, {
117
- channel: channel.toLowerCase(),
118
- botId: account.botId,
119
- botName: account.bot.name,
120
- connected: account.connected,
121
- accessPolicy: account.accessPolicy,
122
- }))),
123
+ h(AccountSettingsToggle))),
123
124
  },
124
125
  h(WorkspaceEditor, {
125
126
  workspace: account.workspace,
@@ -4,6 +4,7 @@ import {
4
4
  TELEGRAM_ENDPOINTS,
5
5
  telegramClientApi,
6
6
  } from './api.js';
7
+ import { ThinkingTracesSettings } from './thinking-traces.js';
7
8
  import { installTelegramStyles } from './styles.js';
8
9
 
9
10
  const channel = createTokenChannelSettings({
@@ -19,6 +20,8 @@ const channel = createTokenChannelSettings({
19
20
  emptyTitle: '接入 Telegram 机器人',
20
21
  emptyDescription: '先通过 @BotFather 获取 Bot Token,再在这里完成接入。',
21
22
  platformLabel: 'Telegram',
23
+ AccountSettings: ThinkingTracesSettings,
24
+ accountSettingsEndpoint: 'bot.thinking-traces.set',
22
25
  });
23
26
 
24
27
  export const TelegramSettingsTab = channel.SettingsTab;
@@ -4,6 +4,18 @@ const CSS = String.raw`
4
4
  .dtg-page { --ddt-accent: #229ed9; --ddt-accent-deep: #1687bd; --ddt-accent-wash: #eaf7fd; }
5
5
  .dtg-avatar { color: #fff; background: #229ed9; }
6
6
  .dtg-avatar svg { display: block; }
7
+ .dim-accountSettings { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--dsw-alias-border-l2, #eceef1); }
8
+ .dim-accountSettingsTitle { margin: 0 0 8px; font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-secondary, #646a73); letter-spacing: 0.02em; }
9
+ .dim-accountSettingsRow { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 40px; cursor: pointer; }
10
+ .dim-accountSettingsText { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
11
+ .dim-accountSettingsLabel { font-size: 13px; font-weight: 500; color: var(--dsw-alias-label, #1f2329); }
12
+ .dim-accountSettingsHelp { font-size: 12px; color: var(--dsw-alias-label-tertiary, #8a919f); }
13
+ .dim-accountSettingsSwitch { appearance: none; flex: none; width: 32px; height: 19px; margin: 0; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 12px; background: var(--dsw-alias-interactive-bg-hover, #eef0f3); cursor: pointer; position: relative; }
14
+ .dim-accountSettingsSwitch::before { content: ""; position: absolute; top: 2px; left: 2px; width: 13px; height: 13px; border-radius: 50%; background: var(--dsw-alias-label-secondary, #646a73); transition: transform 120ms ease, background 120ms ease; }
15
+ .dim-accountSettingsSwitch:checked { border-color: #229ed9; background: #229ed9; }
16
+ .dim-accountSettingsSwitch:checked::before { transform: translateX(13px); background: #fff; }
17
+ .dim-accountSettingsSwitch:disabled { opacity: 0.5; cursor: not-allowed; }
18
+ @media (prefers-reduced-motion: reduce) { .dim-accountSettingsSwitch::before { transition: none; } }
7
19
  `;
8
20
 
9
21
  export function installTelegramStyles() {
@@ -0,0 +1,25 @@
1
+ /**
2
+ * 会话行为设置块:思考过程留痕开关(Telegram 专属)。
3
+ *
4
+ * 开启后,机器人把 agent 的推理与工具调用作为独立的 💭/🔧 消息实时发出来,
5
+ * 最终答案单独成条;关闭则回到「占位符被最终答案覆盖」的原有单消息行为。
6
+ */
7
+ import { h } from '../../i18n.js';
8
+
9
+ export function ThinkingTracesSettings({ account, busy, onSave }) {
10
+ const checked = account.thinkingTraces !== false;
11
+ return h('div', { className: 'dim-accountSettings' },
12
+ h('h4', { className: 'dim-accountSettingsTitle' }, '会话行为'),
13
+ h('label', { className: 'dim-accountSettingsRow' },
14
+ h('span', { className: 'dim-accountSettingsText' },
15
+ h('span', { className: 'dim-accountSettingsLabel' }, '思考过程留痕'),
16
+ h('span', { className: 'dim-accountSettingsHelp' },
17
+ '实时显示思考步骤与工具调用,最终答案单独成条')),
18
+ h('input', {
19
+ type: 'checkbox',
20
+ className: 'dim-accountSettingsSwitch',
21
+ checked,
22
+ disabled: Boolean(busy),
23
+ onChange: (event) => { onSave?.({ thinkingTraces: event.target.checked }); },
24
+ })));
25
+ }
@@ -4,7 +4,7 @@ import * as React from 'react';
4
4
 
5
5
  import { WecomLogoGlyph } from '../../channel-logos.js';
6
6
  import { CredentialActionIcon, CredentialBindingPanel, QrActionIcon } from '../../credential-binding.js';
7
- import { CollapsibleAccountSection } from '../shared/collapsible-account.js';
7
+ import { AccountSettingsToggle, CollapsibleAccountSection } from '../shared/collapsible-account.js';
8
8
  import { h } from '../../i18n.js';
9
9
  import { WorkspaceEditor } from '../../workspace-editor.js';
10
10
  import { ContextEnhancementEditor } from '../../context-enhancement.js';
@@ -188,6 +188,13 @@ export function AccountCard({
188
188
  return h('article', { className: 'ddt-card dim-botCard', 'data-bot-id': account.botId },
189
189
  h('div', { className: 'ddt-cardBody dim-botCardBody' },
190
190
  h(CollapsibleAccountSection, {
191
+ settings: h(BotSettingsButton, {
192
+ channel: 'wecom',
193
+ botId: account.botId,
194
+ botName: account.bot.name,
195
+ connected: account.connected,
196
+ accessPolicy: account.accessPolicy,
197
+ }),
191
198
  id: `wec-settings-${account.botId.replace(/[^a-zA-Z0-9_-]/g, '-')}`,
192
199
  header: h('div', { className: 'ddt-accountTop dim-botCardTop' },
193
200
  h('div', { className: 'ddt-accountIdentity dim-botIdentity' },
@@ -208,13 +215,7 @@ export function AccountCard({
208
215
  lastCheckedAt: account.health.lastCheckedAt,
209
216
  formatCheckedTime: checkedTime,
210
217
  }),
211
- h(BotSettingsButton, {
212
- channel: 'wecom',
213
- botId: account.botId,
214
- botName: account.bot.name,
215
- connected: account.connected,
216
- accessPolicy: account.accessPolicy,
217
- })))
218
+ h(AccountSettingsToggle)))
218
219
  },
219
220
  h(WorkspaceEditor, {
220
221
  workspace: account.workspace,
@@ -3,7 +3,7 @@ import { BotName } from '../../bot-alias.js';
3
3
  import * as React from 'react';
4
4
 
5
5
  import { WecomLogoGlyph } from '../../channel-logos.js';
6
- import { CollapsibleAccountSection } from '../shared/collapsible-account.js';
6
+ import { AccountSettingsToggle, CollapsibleAccountSection } from '../shared/collapsible-account.js';
7
7
  import { h } from '../../i18n.js';
8
8
  import { WorkspaceEditor } from '../../workspace-editor.js';
9
9
  import { ContextEnhancementEditor } from '../../context-enhancement.js';
@@ -263,6 +263,13 @@ export function AccountCard({
263
263
  return h('article', { className: 'ddt-card dim-botCard', 'data-bot-id': account.botId },
264
264
  h('div', { className: 'ddt-cardBody dim-botCardBody' },
265
265
  h(CollapsibleAccountSection, {
266
+ settings: h(BotSettingsButton, {
267
+ channel: 'wecomApp',
268
+ botId: account.botId,
269
+ botName: account.bot.name,
270
+ connected: account.connected,
271
+ accessPolicy: account.accessPolicy,
272
+ }),
266
273
  id: elementId,
267
274
  header: h('div', { className: 'ddt-accountTop dim-botCardTop' },
268
275
  h('div', { className: 'ddt-accountIdentity dim-botIdentity' },
@@ -283,13 +290,7 @@ export function AccountCard({
283
290
  lastCheckedAt: account.health.lastCheckedAt,
284
291
  formatCheckedTime: checkedTime,
285
292
  }),
286
- h(BotSettingsButton, {
287
- channel: 'wecomApp',
288
- botId: account.botId,
289
- botName: account.bot.name,
290
- connected: account.connected,
291
- accessPolicy: account.accessPolicy,
292
- })))
293
+ h(AccountSettingsToggle)))
293
294
  },
294
295
  h(CallbackUrlBox, {
295
296
  url: account.bot.callbackUrl,
@@ -4,7 +4,7 @@ import * as React from 'react';
4
4
 
5
5
  import { WeixinLogoGlyph } from '../../channel-logos.js';
6
6
  import { QrActionIcon } from '../../credential-binding.js';
7
- import { CollapsibleAccountSection } from '../shared/collapsible-account.js';
7
+ import { AccountSettingsToggle, CollapsibleAccountSection } from '../shared/collapsible-account.js';
8
8
  import { h } from '../../i18n.js';
9
9
  import {
10
10
  WEIXIN_ENDPOINTS,
@@ -230,6 +230,13 @@ export function AccountCard({
230
230
  return h('article', { className: 'dxw-card dim-botCard', tabIndex: -1, 'data-bot-id': account.botId },
231
231
  h('div', { className: 'dxw-cardBody dim-botCardBody' },
232
232
  h(CollapsibleAccountSection, {
233
+ settings: h(BotSettingsButton, {
234
+ channel: 'weixin',
235
+ botId: account.botId,
236
+ botName: account.bot.name,
237
+ connected: account.connected,
238
+ accessPolicy: account.accessPolicy,
239
+ }),
233
240
  id: `dxw-settings-${account.botId.replace(/[^a-zA-Z0-9_-]/g, '-')}`,
234
241
  header: h('div', { className: 'dxw-accountTop dim-botCardTop' },
235
242
  h('div', { className: 'dxw-accountIdentity dim-botIdentity' },
@@ -249,13 +256,7 @@ export function AccountCard({
249
256
  lastCheckedAt: account.health.lastCheckedAt,
250
257
  formatCheckedTime: checkedTime,
251
258
  }),
252
- h(BotSettingsButton, {
253
- channel: 'weixin',
254
- botId: account.botId,
255
- botName: account.bot.name,
256
- connected: account.connected,
257
- accessPolicy: account.accessPolicy,
258
- })))
259
+ h(AccountSettingsToggle)))
259
260
  },
260
261
  h(WorkspaceEditor, {
261
262
  workspace: account.workspace,
@@ -4,7 +4,7 @@ import * as React from 'react';
4
4
 
5
5
  import { WhatsappLogoGlyph } from '../../channel-logos.js';
6
6
  import { QrActionIcon } from '../../credential-binding.js';
7
- import { CollapsibleAccountSection } from '../shared/collapsible-account.js';
7
+ import { AccountSettingsToggle, CollapsibleAccountSection } from '../shared/collapsible-account.js';
8
8
  import { h } from '../../i18n.js';
9
9
  import { WorkspaceEditor } from '../../workspace-editor.js';
10
10
  import { ContextEnhancementEditor } from '../../context-enhancement.js';
@@ -208,6 +208,13 @@ export function WhatsappAccountCard({
208
208
  return h('article', { className: 'ddt-card dim-botCard', 'data-bot-id': account.botId },
209
209
  h('div', { className: 'ddt-cardBody dim-botCardBody' },
210
210
  h(CollapsibleAccountSection, {
211
+ settings: h(BotSettingsButton, {
212
+ channel: 'whatsapp',
213
+ botId: account.botId,
214
+ botName: account.bot.name,
215
+ connected: account.connected,
216
+ accessPolicy: account.accessPolicy,
217
+ }),
211
218
  id: `wsp-settings-${account.botId.replace(/[^a-zA-Z0-9_-]/g, '-')}`,
212
219
  header: h('div', { className: 'ddt-accountTop dim-botCardTop' },
213
220
  h('div', { className: 'ddt-accountIdentity dim-botIdentity' },
@@ -231,13 +238,7 @@ export function WhatsappAccountCard({
231
238
  lastCheckedAt: account.health.lastCheckedAt,
232
239
  formatCheckedTime: checkedTime,
233
240
  }),
234
- h(BotSettingsButton, {
235
- channel: 'whatsapp',
236
- botId: account.botId,
237
- botName: account.bot.name,
238
- connected: account.connected,
239
- accessPolicy: account.accessPolicy,
240
- })))
241
+ h(AccountSettingsToggle)))
241
242
  },
242
243
  h(WorkspaceEditor, {
243
244
  workspace: account.workspace,