@xmanrui/dsh-im 4.10.0 → 4.11.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 (56) hide show
  1. package/PROACTIVE_DELIVERY.en.md +15 -1
  2. package/PROACTIVE_DELIVERY.md +15 -1
  3. package/README.en.md +5 -1
  4. package/README.md +6 -2
  5. package/lib/client.js +1619 -769
  6. package/lib/index.js +248 -244
  7. package/package.json +4 -3
  8. package/plugin-src/client/channel-card-meta.js +9 -1
  9. package/plugin-src/client/channels/dingtalk/api.js +4 -0
  10. package/plugin-src/client/channels/dingtalk/index.js +25 -2
  11. package/plugin-src/client/channels/feishu/api.js +4 -0
  12. package/plugin-src/client/channels/feishu/group-settings.js +581 -0
  13. package/plugin-src/client/channels/feishu/index.js +26 -204
  14. package/plugin-src/client/channels/qq/api.js +4 -0
  15. package/plugin-src/client/channels/qq/index.js +25 -2
  16. package/plugin-src/client/channels/shared/token-api.js +4 -0
  17. package/plugin-src/client/channels/shared/token-channel.js +25 -3
  18. package/plugin-src/client/channels/wecom/api.js +4 -0
  19. package/plugin-src/client/channels/wecom/index.js +25 -2
  20. package/plugin-src/client/channels/weixin/api.js +4 -0
  21. package/plugin-src/client/channels/weixin/index.js +25 -2
  22. package/plugin-src/client/channels/whatsapp/api.js +4 -0
  23. package/plugin-src/client/channels/whatsapp/index.js +24 -2
  24. package/plugin-src/client/delivery-settings.js +73 -5
  25. package/plugin-src/client/i18n.js +20 -0
  26. package/plugin-src/client/model-setting.js +104 -0
  27. package/plugin-src/client/styles.js +50 -0
  28. package/plugin-src/host/channels/dingtalk/production.mjs +3 -0
  29. package/plugin-src/host/channels/dingtalk/rpc.mjs +11 -0
  30. package/plugin-src/host/channels/feishu/production.mjs +3 -0
  31. package/plugin-src/host/channels/feishu/rpc.mjs +16 -0
  32. package/plugin-src/host/channels/qq/production.mjs +3 -0
  33. package/plugin-src/host/channels/qq/rpc.mjs +11 -0
  34. package/plugin-src/host/channels/shared/model-setting-rpc.mjs +19 -0
  35. package/plugin-src/host/channels/shared/production.mjs +3 -0
  36. package/plugin-src/host/channels/shared/rpc.mjs +9 -0
  37. package/plugin-src/host/channels/shared/workspace-rpc.mjs +2 -0
  38. package/plugin-src/host/channels/slack/production.mjs +3 -0
  39. package/plugin-src/host/channels/slack/rpc.mjs +9 -0
  40. package/plugin-src/host/channels/wecom/production.mjs +3 -0
  41. package/plugin-src/host/channels/wecom/rpc.mjs +11 -0
  42. package/plugin-src/host/channels/weixin/production.mjs +3 -0
  43. package/plugin-src/host/channels/weixin/rpc.mjs +11 -0
  44. package/plugin-src/host/channels/whatsapp/production.mjs +3 -0
  45. package/plugin-src/host/channels/whatsapp/rpc.mjs +11 -0
  46. package/plugin-src/host/delivery-adapter.mjs +141 -3
  47. package/plugin-src/host/delivery-rpc.mjs +12 -0
  48. package/plugin-src/host/delivery-service.mjs +110 -3
  49. package/plugin-src/host/delivery-suggestions.mjs +46 -0
  50. package/plugin-src/host/harness-session-coordinator.mjs +6 -5
  51. package/plugin-src/host/index.mjs +18 -1
  52. package/plugin-src/host/session-sync-coordinator.mjs +217 -0
  53. package/scripts/verify-package.mjs +12 -6
  54. package/src/channels/shared/bot-workspace-store.mjs +285 -38
  55. package/src/channels/shared/harness-client.mjs +124 -43
  56. package/src/channels/shared/model-setting.mjs +110 -0
@@ -391,6 +391,45 @@ const CSS = String.raw`
391
391
  .dim-botSettingsTab[aria-selected="true"]::after { background: var(--dsw-alias-state-business-primary, #3370ff); transform: scaleX(1); }
392
392
  .dim-botSettingsTab:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: -2px; border-radius: 5px; }
393
393
  .dim-botSettingsTabPanel { min-width: 0; display: grid; gap: 14px; padding-top: 14px; }
394
+ .dim-feishuGroupSettings { min-width: 0; display: grid; gap: 14px; }
395
+ .dim-feishuGroupControls { min-width: 0; display: grid; gap: 12px; }
396
+ .dim-feishuGroupControl { min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; padding: 16px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 12px; background: var(--dsw-alias-bg-layer-3, #fff); }
397
+ .dim-feishuGroupControlHeader { min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 12px; }
398
+ .dim-feishuGroupControlHeader h3 { min-width: 0; margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 14px; line-height: 21px; font-weight: 650; }
399
+ .dim-feishuGroupControlStatus { flex: none; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; line-height: 17px; }
400
+ .dim-feishuGroupSelect { min-width: 0; width: 100%; height: 38px; padding: 0 34px 0 11px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 8px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 13px; line-height: 38px; cursor: pointer; transition: border-color .15s ease, box-shadow .15s ease; }
401
+ .dim-feishuGroupSelect:hover:not(:disabled) { border-color: color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 42%, var(--dsw-alias-border-l2, #dfe1e5)); }
402
+ .dim-feishuGroupSelect:focus-visible { outline: none; border-color: var(--dsw-alias-state-business-primary, #3370ff); box-shadow: 0 0 0 2px color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 15%, transparent); }
403
+ .dim-feishuGroupSelect:disabled { cursor: not-allowed; opacity: .55; }
404
+ .dim-feishuGroupHelp { margin: -2px 0 0; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 12px; line-height: 19px; }
405
+ .dim-feishuGroupPermissionAction { display: flex; justify-content: flex-start; }
406
+ .dim-feishuGroupPermissionAction .dim-deliveryButton { color: var(--dsw-alias-state-business-primary, #3370ff); border-color: color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 30%, var(--dsw-alias-border-l2, #dfe1e5)); }
407
+ .dim-feishuGroupError, .dim-feishuGroupRefreshError { margin: 0; padding: 9px 11px; border-radius: 8px; font-size: 12px; line-height: 18px; }
408
+ .dim-feishuGroupError, .dim-feishuGroupRefreshError { color: var(--dsw-alias-state-error-primary, #d54941); background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #d54941) 7%, var(--dsw-alias-bg-layer-1, #fff)); }
409
+ .dim-feishuGroupAuthorization { min-width: 0; display: grid; grid-template-columns: 184px minmax(0, 1fr); align-items: start; gap: 24px; padding: 18px; border: 1px solid color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 30%, var(--dsw-alias-border-l2, #dfe1e5)); border-radius: 12px; background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 2.5%, var(--dsw-alias-bg-layer-3, #fff)); }
410
+ .dim-feishuGroupAuthorizationState { min-height: 126px; grid-template-columns: 32px minmax(0, 1fr); align-items: center; }
411
+ .dim-feishuGroupAuthorizationState h3, .dim-feishuGroupAuthorizationError h3, .dim-feishuGroupAuthorizationCopy h3 { margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 16px; line-height: 24px; font-weight: 650; }
412
+ .dim-feishuGroupAuthorizationState p, .dim-feishuGroupAuthorizationError p, .dim-feishuGroupAuthorizationCopy > p { margin: 5px 0 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: 19px; }
413
+ .dim-feishuGroupSpinner { width: 24px; height: 24px; border: 3px solid var(--dsw-alias-border-l2, #dfe1e5); border-top-color: var(--dsw-alias-state-business-primary, #3370ff); border-radius: 50%; animation: dim-spin .8s linear infinite; }
414
+ .dim-feishuGroupAuthorizationError { grid-template-columns: minmax(0, 1fr) max-content; align-items: center; border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary, #d54941) 28%, var(--dsw-alias-border-l2, #dfe1e5)); background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #d54941) 4%, var(--dsw-alias-bg-layer-3, #fff)); }
415
+ .dim-feishuGroupAuthorizationError code { display: inline-block; margin-top: 6px; color: var(--dsw-alias-label-tertiary, #8f959e); font: 11px/17px ui-monospace, SFMono-Regular, Menlo, monospace; }
416
+ .dim-feishuGroupQrColumn { min-width: 0; }
417
+ .dim-feishuGroupQrFrame { position: relative; width: 176px; height: 176px; display: grid; place-items: center; padding: 10px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 11px; background: #fff; box-shadow: 0 6px 18px rgb(31 35 41 / 7%); }
418
+ .dim-feishuGroupQrFrame img { width: 100%; height: 100%; display: block; object-fit: contain; }
419
+ .dim-feishuGroupQrFallback { width: 100%; height: 100%; display: grid; place-content: center; justify-items: center; gap: 7px; border-radius: 7px; color: var(--dsw-alias-state-business-primary, #3370ff); background: #f7f9ff; text-align: center; }
420
+ .dim-feishuGroupQrFallback span { color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 17px; }
421
+ .dim-feishuGroupQrExpired { position: absolute; inset: 10px; display: grid; place-content: center; gap: 2px; border-radius: 8px; color: var(--dsw-alias-label-primary, #1f2329); background: rgb(255 255 255 / 94%); backdrop-filter: blur(3px); text-align: center; }
422
+ .dim-feishuGroupQrExpired span { font-size: 13px; line-height: 20px; font-weight: 650; }
423
+ .dim-feishuGroupQrExpired small { color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 17px; }
424
+ .dim-feishuGroupCountdown { width: 176px; display: grid; grid-template-columns: minmax(0, 1fr) max-content; gap: 5px 10px; margin-top: 9px; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; line-height: 17px; font-variant-numeric: tabular-nums; }
425
+ .dim-feishuGroupCountdown strong { color: var(--dsw-alias-label-secondary, #646a73); font-weight: 600; }
426
+ .dim-feishuGroupProgress { grid-column: 1 / -1; height: 3px; overflow: hidden; border-radius: 99px; background: var(--dsw-alias-bg-module-platform, #f2f3f5); }
427
+ .dim-feishuGroupProgress > span { display: block; height: 100%; border-radius: inherit; background: var(--dsw-alias-state-business-primary, #3370ff); transition: width 1s linear; }
428
+ .dim-feishuGroupAuthorizationCopy { min-width: 0; }
429
+ .dim-feishuGroupAuthorizationEyebrow { display: block; margin-bottom: 7px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 17px; font-weight: 600; }
430
+ .dim-feishuGroupAuthorizationCopy ol { display: grid; gap: 6px; margin: 13px 0 0; padding-left: 18px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 18px; }
431
+ .dim-feishuGroupAuthorizationActions { display: flex; align-items: center; justify-content: flex-start; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
432
+ .dim-feishuGroupAuthorizationLink { text-decoration: none; }
394
433
  .dim-deliveryDocsLink { min-height: 30px; flex: none; display: inline-flex; align-items: center; gap: 4px; padding: 5px 8px; border-radius: 7px; color: var(--dsw-alias-state-business-primary, #3370ff); font-size: 12px; line-height: 18px; text-decoration: none; white-space: nowrap; }
395
434
  .dim-deliveryDocsLink:hover { background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 8%, transparent); }
396
435
  .dim-deliveryDocsLink:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: 2px; }
@@ -420,6 +459,12 @@ const CSS = String.raw`
420
459
  .dim-targetTitle span { flex: none; padding: 1px 6px; border-radius: 5px; color: var(--dsw-alias-state-business-primary, #3370ff); background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 9%, transparent); font-size: 10px; line-height: 16px; }
421
460
  .dim-targetSummary code { display: block; overflow: hidden; margin-top: 3px; color: var(--dsw-alias-label-secondary, #646a73); font: 11px/17px ui-monospace, SFMono-Regular, Menlo, monospace; text-overflow: ellipsis; white-space: nowrap; }
422
461
  .dim-targetActions { display: flex; align-items: center; justify-content: flex-end; gap: 6px; flex-wrap: wrap; }
462
+ .dim-targetSessionSync { grid-column: 1 / -1; min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 12px; padding-top: 9px; border-top: 1px solid var(--dsw-alias-border-l1, #eef0f3); cursor: pointer; }
463
+ .dim-targetSessionSyncCopy { min-width: 0; display: grid; gap: 1px; }
464
+ .dim-targetSessionSyncCopy strong { color: var(--dsw-alias-label-primary, #1f2329); font-size: 12px; line-height: 18px; font-weight: 600; }
465
+ .dim-targetSessionSyncCopy small { color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 17px; }
466
+ .dim-targetSessionSync input { width: 16px; height: 16px; flex: none; margin: 0; accent-color: var(--dsw-alias-state-business-primary, #3370ff); }
467
+ .dim-targetSessionSync:has(input:disabled) { cursor: default; }
423
468
  .dim-targetFeedback { grid-column: 1 / -1; margin: 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 17px; }
424
469
  .dim-targetFeedback[data-tone="success"] { color: var(--dsw-alias-state-success-primary, #20a162); }
425
470
  .dim-targetFeedback[data-tone="error"], .dim-targetFormError { color: var(--dsw-alias-state-error-primary, #d54941); }
@@ -543,6 +588,10 @@ const CSS = String.raw`
543
588
  .dim-accessControls { grid-template-columns: minmax(0, 1fr); }
544
589
  .dim-accessUserRow { grid-template-columns: minmax(0, 1fr); }
545
590
  .dim-accessDeleteUser { justify-self: start; }
591
+ .dim-feishuGroupAuthorization { grid-template-columns: minmax(0, 1fr); justify-items: center; }
592
+ .dim-feishuGroupAuthorizationCopy { width: 100%; }
593
+ .dim-feishuGroupAuthorizationState { grid-template-columns: 32px minmax(0, 1fr); justify-items: stretch; }
594
+ .dim-feishuGroupAuthorizationError { justify-items: stretch; }
546
595
  }
547
596
  @media (max-width: 840px) {
548
597
  .dim-title { align-items: flex-start; }
@@ -572,6 +621,7 @@ const CSS = String.raw`
572
621
  .dim-deliveryBotId > span { grid-column: 1 / -1; }
573
622
  .dim-targetActions .dim-deliveryButton { flex: 1 1 auto; }
574
623
  .dim-accessActions .dim-deliveryButton { width: 100%; }
624
+ .dim-feishuGroupAuthorizationActions .dim-deliveryButton { flex: 1 1 auto; }
575
625
  .dim-directoryPickerBackdrop { padding: 10px; }
576
626
  .dim-directoryPicker { height: calc(100vh - 20px); min-height: 0; border-radius: 14px; }
577
627
  .dim-directoryPickerHeader { padding: 18px 17px 14px; }
@@ -15,6 +15,7 @@ import {
15
15
  observeBotWorkspaceRemovals,
16
16
  } from '../../../../src/channels/shared/bot-workspace-store.mjs';
17
17
  import { listAgentPresetCatalog } from '../../../../src/channels/shared/agent-preset.mjs';
18
+ import { listModelCatalog } from '../../../../src/channels/shared/model-setting.mjs';
18
19
  import { createDeliveryAdapter } from '../../delivery-adapter.mjs';
19
20
  import { createConnectionSupervisor } from './connection-supervisor.mjs';
20
21
  import { createHarnessCommandExecutor } from '../../harness-command-executor.mjs';
@@ -111,6 +112,7 @@ export async function createProductionController(ctx, config = {}, internals = {
111
112
  ...(sessionMaintenanceExecutor ? { sessionMaintenanceExecutor } : {}),
112
113
  ...(fileIngressExecutor ? { fileIngressExecutor } : {}),
113
114
  });
115
+ const modelCatalog = () => listModelCatalog(harness);
114
116
  const coreController = new Controller({
115
117
  deviceAuth,
116
118
  credentials: ctx.credentials,
@@ -163,6 +165,7 @@ export async function createProductionController(ctx, config = {}, internals = {
163
165
  workspaces,
164
166
  stateFor,
165
167
  agentPresetCatalog,
168
+ modelCatalog,
166
169
  });
167
170
  const supervisor = createSupervisor({
168
171
  controller,
@@ -4,6 +4,7 @@ import { SET_ACCESS_POLICY_ENDPOINT, validAccessPolicyPayload } from '../shared/
4
4
  import { resolveRpcAuthority } from '../../rpc-authority.mjs';
5
5
  import { publicWorkspaceError, SET_WORKSPACE_ENDPOINT, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
6
6
  import { SET_AGENT_PRESET_ENDPOINT, validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
7
+ import { SET_MODEL_ENDPOINT, validModelPayload } from '../shared/model-setting-rpc.mjs';
7
8
  import {
8
9
  connectionTestTargetUnavailable,
9
10
  publicConnectionTestResult,
@@ -19,6 +20,7 @@ export const DINGTALK_ENDPOINTS = Object.freeze({
19
20
  reconnectBot: 'bot.reconnect',
20
21
  deleteBot: 'bot.delete',
21
22
  setWorkspace: SET_WORKSPACE_ENDPOINT,
23
+ setModel: SET_MODEL_ENDPOINT,
22
24
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
23
25
  setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
24
26
  setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
@@ -94,6 +96,9 @@ function payloadFailure(endpoint, payload) {
94
96
  return validWorkspacePayload(payload)
95
97
  ? null : '请输入工作区绝对路径。';
96
98
  }
99
+ if (endpoint === DINGTALK_ENDPOINTS.setModel) {
100
+ return validModelPayload(payload) ? null : '请选择有效模型。';
101
+ }
97
102
  if (endpoint === DINGTALK_ENDPOINTS.setAgentPreset) {
98
103
  return validAgentPresetPayload(payload)
99
104
  ? null : '请选择 Agent Preset。';
@@ -284,6 +289,12 @@ export function createDingtalkRpcHandler(controller, { encodeQr = qrDataUrl } =
284
289
  await controller.updateWorkspace(payload.botId, payload.workspace),
285
290
  cachedEncode,
286
291
  );
292
+ } else if (endpoint === DINGTALK_ENDPOINTS.setModel) {
293
+ if (typeof controller.updateModel !== 'function') throw new Error('Model update is unavailable');
294
+ value = await publicStatus(
295
+ await controller.updateModel(payload.botId, payload.model),
296
+ cachedEncode,
297
+ );
287
298
  } else if (endpoint === DINGTALK_ENDPOINTS.setContextEnhancement) {
288
299
  if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
289
300
  value = await controller.updateContextEnhancement(
@@ -27,6 +27,7 @@ import {
27
27
  observeBotWorkspaceRemovals,
28
28
  } from '../../../../src/channels/shared/bot-workspace-store.mjs';
29
29
  import { listAgentPresetCatalog } from '../../../../src/channels/shared/agent-preset.mjs';
30
+ import { listModelCatalog } from '../../../../src/channels/shared/model-setting.mjs';
30
31
  import { createDeliveryAdapter } from '../../delivery-adapter.mjs';
31
32
  import {
32
33
  accessPolicyProvider,
@@ -191,6 +192,7 @@ export async function createProductionController(ctx, config = {}, internals = {
191
192
  const proxyEnv = internals.proxyEnv ?? process.env;
192
193
  const wsAgent = createFeishuWebSocketAgent(proxyEnv, internals.createProxyAgent);
193
194
 
195
+ const modelCatalog = () => listModelCatalog(harness);
194
196
  const coreController = new Controller({
195
197
  registerApp: (options) => lark.registerApp(options),
196
198
  verifyApp,
@@ -247,6 +249,7 @@ export async function createProductionController(ctx, config = {}, internals = {
247
249
  workspaces,
248
250
  stateFor: stateForBotId,
249
251
  agentPresetCatalog,
252
+ modelCatalog,
250
253
  });
251
254
 
252
255
  const supervisor = createSupervisor({
@@ -5,10 +5,15 @@ import {
5
5
  } from '../../../../src/channels/shared/agent-preset.mjs';
6
6
  import { publicConnectionTestResult } from '../../../../src/channels/shared/connection-test.mjs';
7
7
  import { publicMessageFailure } from '../../../../src/channels/shared/message-failure.mjs';
8
+ import {
9
+ normalizeModelCatalog,
10
+ normalizeModelSelection,
11
+ } from '../../../../src/channels/shared/model-setting.mjs';
8
12
  import { normalizeAccessPolicy } from '../../../../src/channels/shared/access-policy.mjs';
9
13
  import { resolveRpcAuthority } from '../../rpc-authority.mjs';
10
14
  import { publicWorkspaceError, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
11
15
  import { validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
16
+ import { validModelPayload } from '../shared/model-setting-rpc.mjs';
12
17
  import { validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
13
18
  import { SET_ACCESS_POLICY_ENDPOINT, validAccessPolicyPayload } from '../shared/access-policy-rpc.mjs';
14
19
  import { normalizeContextEnhancementConfig } from '../../../../src/channels/shared/context-enhancement.mjs';
@@ -283,6 +288,7 @@ function publicBotEntry(entry) {
283
288
  state: connectionState(source, registration, connected),
284
289
  connected,
285
290
  configured: source.configured === true,
291
+ model: normalizeModelSelection(source.model),
286
292
  agentPreset: normalizeAgentPresetId(source.agentPreset),
287
293
  contextEnhancement: normalizeContextEnhancementConfig(source.contextEnhancement),
288
294
  accessPolicy: normalizeAccessPolicy(source.accessPolicy),
@@ -333,6 +339,7 @@ export async function toPublicFeishuStatus(status, { encodeQr = qrCodeDataUrl }
333
339
  health: publicHealth(source, connected),
334
340
  bots,
335
341
  agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog),
342
+ modelCatalog: normalizeModelCatalog(source.modelCatalog),
336
343
  totals: {
337
344
  configured: bots.length || (source.configured === true ? 1 : 0),
338
345
  connected: bots.length ? bots.filter((bot) => bot.connected).length : (connected ? 1 : 0),
@@ -422,6 +429,9 @@ function validPayload(endpoint, payload) {
422
429
  return validWorkspacePayload(payload)
423
430
  ? null : '请输入工作区绝对路径。';
424
431
  }
432
+ if (endpoint === FEISHU_ENDPOINTS.setModel) {
433
+ return validModelPayload(payload) ? null : '请选择有效模型。';
434
+ }
425
435
  if (endpoint === FEISHU_ENDPOINTS.setAgentPreset) {
426
436
  return validAgentPresetPayload(payload)
427
437
  ? null : '请选择 Agent Preset。';
@@ -692,6 +702,12 @@ export function createFeishuRpcHandler(controller, { encodeQr = qrCodeDataUrl }
692
702
  await controller.updateWorkspace(payload.botId, payload.workspace),
693
703
  { encodeQr: cachedEncodeQr },
694
704
  );
705
+ } else if (endpoint === FEISHU_ENDPOINTS.setModel) {
706
+ if (typeof controller.updateModel !== 'function') throw new Error('Model update is unavailable');
707
+ value = await toPublicFeishuStatus(
708
+ await controller.updateModel(payload.botId, payload.model),
709
+ { encodeQr: cachedEncodeQr },
710
+ );
695
711
  } else if (endpoint === FEISHU_ENDPOINTS.setContextEnhancement) {
696
712
  if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
697
713
  value = await controller.updateContextEnhancement(
@@ -15,6 +15,7 @@ import {
15
15
  observeBotWorkspaceRemovals,
16
16
  } from '../../../../src/channels/shared/bot-workspace-store.mjs';
17
17
  import { listAgentPresetCatalog } from '../../../../src/channels/shared/agent-preset.mjs';
18
+ import { listModelCatalog } from '../../../../src/channels/shared/model-setting.mjs';
18
19
  import { createDeliveryAdapter } from '../../delivery-adapter.mjs';
19
20
  import { createConnectionSupervisor } from './connection-supervisor.mjs';
20
21
  import { createHarnessCommandExecutor } from '../../harness-command-executor.mjs';
@@ -102,6 +103,7 @@ export async function createProductionController(ctx, config = {}, internals = {
102
103
  ...(sessionMaintenanceExecutor ? { sessionMaintenanceExecutor } : {}),
103
104
  ...(fileIngressExecutor ? { fileIngressExecutor } : {}),
104
105
  });
106
+ const modelCatalog = () => listModelCatalog(harness);
105
107
  const coreController = new Controller({
106
108
  qrAuth,
107
109
  credentials: ctx.credentials,
@@ -153,6 +155,7 @@ export async function createProductionController(ctx, config = {}, internals = {
153
155
  workspaces,
154
156
  stateFor,
155
157
  agentPresetCatalog,
158
+ modelCatalog,
156
159
  });
157
160
  const supervisor = createSupervisor({
158
161
  controller,
@@ -8,6 +8,7 @@ import { SET_ACCESS_POLICY_ENDPOINT, validAccessPolicyPayload } from '../shared/
8
8
  import { resolveRpcAuthority } from '../../rpc-authority.mjs';
9
9
  import { publicWorkspaceError, SET_WORKSPACE_ENDPOINT, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
10
10
  import { SET_AGENT_PRESET_ENDPOINT, validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
11
+ import { SET_MODEL_ENDPOINT, validModelPayload } from '../shared/model-setting-rpc.mjs';
11
12
 
12
13
  export const QQ_RPC_CHANNEL = '/qq';
13
14
  export const QQ_ENDPOINTS = Object.freeze({
@@ -19,6 +20,7 @@ export const QQ_ENDPOINTS = Object.freeze({
19
20
  reconnectBot: 'bot.reconnect',
20
21
  deleteBot: 'bot.delete',
21
22
  setWorkspace: SET_WORKSPACE_ENDPOINT,
23
+ setModel: SET_MODEL_ENDPOINT,
22
24
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
23
25
  setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
24
26
  setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
@@ -76,6 +78,9 @@ function payloadFailure(endpoint, payload) {
76
78
  return validWorkspacePayload(payload)
77
79
  ? null : '请输入工作区绝对路径。';
78
80
  }
81
+ if (endpoint === QQ_ENDPOINTS.setModel) {
82
+ return validModelPayload(payload) ? null : '请选择有效模型。';
83
+ }
79
84
  if (endpoint === QQ_ENDPOINTS.setAgentPreset) {
80
85
  return validAgentPresetPayload(payload)
81
86
  ? null : '请选择 Agent Preset。';
@@ -183,6 +188,12 @@ export function createQqRpcHandler(controller, { encodeQr = qrDataUrl } = {}) {
183
188
  await controller.updateWorkspace(payload.botId, payload.workspace),
184
189
  cachedEncode,
185
190
  );
191
+ } else if (endpoint === QQ_ENDPOINTS.setModel) {
192
+ if (typeof controller.updateModel !== 'function') throw new Error('Model update is unavailable');
193
+ value = await publicStatus(
194
+ await controller.updateModel(payload.botId, payload.model),
195
+ cachedEncode,
196
+ );
186
197
  } else if (endpoint === QQ_ENDPOINTS.setContextEnhancement) {
187
198
  if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
188
199
  value = await controller.updateContextEnhancement(
@@ -0,0 +1,19 @@
1
+ import { normalizeModelSelection } from '../../../../src/channels/shared/model-setting.mjs';
2
+
3
+ export const SET_MODEL_ENDPOINT = 'bot.model.set';
4
+
5
+ function isRecord(value) {
6
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
7
+ }
8
+
9
+ export function validModelPayload(payload) {
10
+ if (!isRecord(payload)
11
+ || !Object.keys(payload).every((key) => ['botId', 'model'].includes(key))
12
+ || typeof payload.botId !== 'string'
13
+ || !/^[A-Za-z0-9_-]{1,128}$/.test(payload.botId)) return false;
14
+ if (payload.model === null) return true;
15
+ return isRecord(payload.model)
16
+ && Object.keys(payload.model).length === 2
17
+ && Object.keys(payload.model).every((key) => ['provider', 'model'].includes(key))
18
+ && normalizeModelSelection(payload.model) !== null;
19
+ }
@@ -17,6 +17,7 @@ import {
17
17
  observeBotWorkspaceRemovals,
18
18
  } from '../../../../src/channels/shared/bot-workspace-store.mjs';
19
19
  import { listAgentPresetCatalog } from '../../../../src/channels/shared/agent-preset.mjs';
20
+ import { listModelCatalog } from '../../../../src/channels/shared/model-setting.mjs';
20
21
  import {
21
22
  createDeliveryAdapter,
22
23
  supportsDeliveryChannel,
@@ -111,6 +112,7 @@ export async function createTokenProductionController(ctx, config, internals, de
111
112
  ...(sessionMaintenanceExecutor ? { sessionMaintenanceExecutor } : {}),
112
113
  ...(fileIngressExecutor ? { fileIngressExecutor } : {}),
113
114
  });
115
+ const modelCatalog = () => listModelCatalog(harness);
114
116
  const coreController = new ResolvedController({
115
117
  credentials: ctx.credentials,
116
118
  configStore: observedConfigStore,
@@ -161,6 +163,7 @@ export async function createTokenProductionController(ctx, config, internals, de
161
163
  workspaces,
162
164
  stateFor,
163
165
  agentPresetCatalog,
166
+ modelCatalog,
164
167
  });
165
168
  const supervisor = createSupervisor({
166
169
  channel,
@@ -11,6 +11,7 @@ import {
11
11
  SET_AGENT_PRESET_ENDPOINT,
12
12
  validAgentPresetPayload,
13
13
  } from './agent-preset-rpc.mjs';
14
+ import { SET_MODEL_ENDPOINT, validModelPayload } from './model-setting-rpc.mjs';
14
15
 
15
16
  export const TOKEN_BOT_ENDPOINTS = Object.freeze({
16
17
  status: 'connection.status',
@@ -18,6 +19,7 @@ export const TOKEN_BOT_ENDPOINTS = Object.freeze({
18
19
  reconnectBot: 'bot.reconnect',
19
20
  deleteBot: 'bot.delete',
20
21
  setWorkspace: SET_WORKSPACE_ENDPOINT,
22
+ setModel: SET_MODEL_ENDPOINT,
21
23
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
22
24
  setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
23
25
  setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
@@ -71,6 +73,10 @@ function payloadFailure(endpoint, payload) {
71
73
  return validWorkspacePayload(payload)
72
74
  ? null : '请输入工作区绝对路径。';
73
75
  }
76
+ if (endpoint === TOKEN_BOT_ENDPOINTS.setModel) {
77
+ return validModelPayload(payload)
78
+ ? null : '请选择有效模型。';
79
+ }
74
80
  if (endpoint === TOKEN_BOT_ENDPOINTS.setAgentPreset) {
75
81
  return validAgentPresetPayload(payload)
76
82
  ? null : '请选择 Agent Preset。';
@@ -164,6 +170,9 @@ export function createTokenBotRpcHandler(controller, { channel }) {
164
170
  } else if (endpoint === TOKEN_BOT_ENDPOINTS.setWorkspace) {
165
171
  if (typeof controller.updateWorkspace !== 'function') throw new Error('Workspace update is unavailable');
166
172
  value = await controller.updateWorkspace(payload.botId, payload.workspace);
173
+ } else if (endpoint === TOKEN_BOT_ENDPOINTS.setModel) {
174
+ if (typeof controller.updateModel !== 'function') throw new Error('Model update is unavailable');
175
+ value = await controller.updateModel(payload.botId, payload.model);
167
176
  } else if (endpoint === TOKEN_BOT_ENDPOINTS.setContextEnhancement) {
168
177
  if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
169
178
  value = await controller.updateContextEnhancement(payload.botId, payload.config);
@@ -22,6 +22,8 @@ export function publicWorkspaceError(error) {
22
22
  'workspace-bot-not-found',
23
23
  'agent-preset-invalid',
24
24
  'agent-preset-unavailable',
25
+ 'model-selection-invalid',
26
+ 'model-selection-unavailable',
25
27
  'context-enhancement-invalid',
26
28
  ].includes(error?.code)) return null;
27
29
  return { code: error.code, message: error.message };
@@ -13,6 +13,7 @@ import {
13
13
  observeBotWorkspaceRemovals,
14
14
  } from '../../../../src/channels/shared/bot-workspace-store.mjs';
15
15
  import { listAgentPresetCatalog } from '../../../../src/channels/shared/agent-preset.mjs';
16
+ import { listModelCatalog } from '../../../../src/channels/shared/model-setting.mjs';
16
17
  import { createDeliveryAdapter } from '../../delivery-adapter.mjs';
17
18
  import { createTokenConnectionSupervisor } from '../shared/connection-supervisor.mjs';
18
19
  import { pluginPaths } from '../shared/production.mjs';
@@ -90,6 +91,7 @@ export async function createProductionController(ctx, config = {}, internals = {
90
91
  ...(sessionMaintenanceExecutor ? { sessionMaintenanceExecutor } : {}),
91
92
  ...(fileIngressExecutor ? { fileIngressExecutor } : {}),
92
93
  });
94
+ const modelCatalog = () => listModelCatalog(harness);
93
95
  const coreController = new ResolvedController({
94
96
  credentials: ctx.credentials,
95
97
  configStore: observedConfigStore,
@@ -142,6 +144,7 @@ export async function createProductionController(ctx, config = {}, internals = {
142
144
  workspaces,
143
145
  stateFor,
144
146
  agentPresetCatalog,
147
+ modelCatalog,
145
148
  });
146
149
  const supervisor = createSupervisor({
147
150
  channel: 'slack',
@@ -11,6 +11,7 @@ import {
11
11
  SET_AGENT_PRESET_ENDPOINT,
12
12
  validAgentPresetPayload,
13
13
  } from '../shared/agent-preset-rpc.mjs';
14
+ import { SET_MODEL_ENDPOINT, validModelPayload } from '../shared/model-setting-rpc.mjs';
14
15
 
15
16
  export const SLACK_RPC_CHANNEL = '/slack';
16
17
  export const SLACK_ENDPOINTS = Object.freeze({
@@ -19,6 +20,7 @@ export const SLACK_ENDPOINTS = Object.freeze({
19
20
  reconnectBot: 'bot.reconnect',
20
21
  deleteBot: 'bot.delete',
21
22
  setWorkspace: SET_WORKSPACE_ENDPOINT,
23
+ setModel: SET_MODEL_ENDPOINT,
22
24
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
23
25
  setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
24
26
  setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
@@ -75,6 +77,9 @@ function payloadFailure(endpoint, payload) {
75
77
  return validWorkspacePayload(payload)
76
78
  ? null : '请输入工作区绝对路径。';
77
79
  }
80
+ if (endpoint === SLACK_ENDPOINTS.setModel) {
81
+ return validModelPayload(payload) ? null : '请选择有效模型。';
82
+ }
78
83
  if (endpoint === SLACK_ENDPOINTS.setAgentPreset) {
79
84
  return validAgentPresetPayload(payload)
80
85
  ? null : '请选择 Agent Preset。';
@@ -166,6 +171,10 @@ export function createSlackRpcHandler(controller) {
166
171
  if (typeof controller.updateWorkspace !== 'function') throw new Error('Workspace update is unavailable');
167
172
  value = await controller.updateWorkspace(payload.botId, payload.workspace);
168
173
  }
174
+ else if (endpoint === SLACK_ENDPOINTS.setModel) {
175
+ if (typeof controller.updateModel !== 'function') throw new Error('Model update is unavailable');
176
+ value = await controller.updateModel(payload.botId, payload.model);
177
+ }
169
178
  else if (endpoint === SLACK_ENDPOINTS.setContextEnhancement) {
170
179
  if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
171
180
  value = await controller.updateContextEnhancement(payload.botId, payload.config);
@@ -15,6 +15,7 @@ import {
15
15
  observeBotWorkspaceRemovals,
16
16
  } from '../../../../src/channels/shared/bot-workspace-store.mjs';
17
17
  import { listAgentPresetCatalog } from '../../../../src/channels/shared/agent-preset.mjs';
18
+ import { listModelCatalog } from '../../../../src/channels/shared/model-setting.mjs';
18
19
  import { createDeliveryAdapter } from '../../delivery-adapter.mjs';
19
20
  import { createConnectionSupervisor } from './connection-supervisor.mjs';
20
21
  import { createHarnessCommandExecutor } from '../../harness-command-executor.mjs';
@@ -105,6 +106,7 @@ export async function createProductionController(ctx, config = {}, internals = {
105
106
  ...(sessionMaintenanceExecutor ? { sessionMaintenanceExecutor } : {}),
106
107
  ...(fileIngressExecutor ? { fileIngressExecutor } : {}),
107
108
  });
109
+ const modelCatalog = () => listModelCatalog(harness);
108
110
  const coreController = new Controller({
109
111
  qrAuth,
110
112
  credentials: ctx.credentials,
@@ -157,6 +159,7 @@ export async function createProductionController(ctx, config = {}, internals = {
157
159
  workspaces,
158
160
  stateFor,
159
161
  agentPresetCatalog,
162
+ modelCatalog,
160
163
  });
161
164
  const supervisor = createSupervisor({
162
165
  controller,
@@ -4,6 +4,7 @@ import { SET_ACCESS_POLICY_ENDPOINT, validAccessPolicyPayload } from '../shared/
4
4
  import { resolveRpcAuthority } from '../../rpc-authority.mjs';
5
5
  import { publicWorkspaceError, SET_WORKSPACE_ENDPOINT, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
6
6
  import { SET_AGENT_PRESET_ENDPOINT, validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
7
+ import { SET_MODEL_ENDPOINT, validModelPayload } from '../shared/model-setting-rpc.mjs';
7
8
  import {
8
9
  connectionTestTargetUnavailable,
9
10
  publicConnectionTestResult,
@@ -19,6 +20,7 @@ export const WECOM_ENDPOINTS = Object.freeze({
19
20
  reconnectBot: 'bot.reconnect',
20
21
  deleteBot: 'bot.delete',
21
22
  setWorkspace: SET_WORKSPACE_ENDPOINT,
23
+ setModel: SET_MODEL_ENDPOINT,
22
24
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
23
25
  setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
24
26
  setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
@@ -76,6 +78,9 @@ function payloadFailure(endpoint, payload) {
76
78
  return validWorkspacePayload(payload)
77
79
  ? null : '请输入工作区绝对路径。';
78
80
  }
81
+ if (endpoint === WECOM_ENDPOINTS.setModel) {
82
+ return validModelPayload(payload) ? null : '请选择有效模型。';
83
+ }
79
84
  if (endpoint === WECOM_ENDPOINTS.setAgentPreset) {
80
85
  return validAgentPresetPayload(payload)
81
86
  ? null : '请选择 Agent Preset。';
@@ -184,6 +189,12 @@ export function createWecomRpcHandler(controller, { encodeQr = qrDataUrl } = {})
184
189
  await controller.updateWorkspace(payload.botId, payload.workspace),
185
190
  cachedEncode,
186
191
  );
192
+ } else if (endpoint === WECOM_ENDPOINTS.setModel) {
193
+ if (typeof controller.updateModel !== 'function') throw new Error('Model update is unavailable');
194
+ value = await publicStatus(
195
+ await controller.updateModel(payload.botId, payload.model),
196
+ cachedEncode,
197
+ );
187
198
  } else if (endpoint === WECOM_ENDPOINTS.setContextEnhancement) {
188
199
  if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
189
200
  value = await controller.updateContextEnhancement(
@@ -18,6 +18,7 @@ import {
18
18
  observeBotWorkspaceRemovals,
19
19
  } from '../../../../src/channels/shared/bot-workspace-store.mjs';
20
20
  import { listAgentPresetCatalog } from '../../../../src/channels/shared/agent-preset.mjs';
21
+ import { listModelCatalog } from '../../../../src/channels/shared/model-setting.mjs';
21
22
  import { createDeliveryAdapter } from '../../delivery-adapter.mjs';
22
23
  import { createConnectionSupervisor } from './connection-supervisor.mjs';
23
24
  import { createHarnessCommandExecutor } from '../../harness-command-executor.mjs';
@@ -108,6 +109,7 @@ export async function createProductionController(ctx, config = {}, internals = {
108
109
  ...(sessionMaintenanceExecutor ? { sessionMaintenanceExecutor } : {}),
109
110
  ...(fileIngressExecutor ? { fileIngressExecutor } : {}),
110
111
  });
112
+ const modelCatalog = () => listModelCatalog(harness);
111
113
  const coreController = new Controller({
112
114
  api,
113
115
  credentials: ctx.credentials,
@@ -160,6 +162,7 @@ export async function createProductionController(ctx, config = {}, internals = {
160
162
  workspaces,
161
163
  stateFor,
162
164
  agentPresetCatalog,
165
+ modelCatalog,
163
166
  });
164
167
  const supervisor = createSupervisor({
165
168
  controller,
@@ -11,6 +11,7 @@ import {
11
11
  SET_AGENT_PRESET_ENDPOINT,
12
12
  validAgentPresetPayload,
13
13
  } from '../shared/agent-preset-rpc.mjs';
14
+ import { SET_MODEL_ENDPOINT, validModelPayload } from '../shared/model-setting-rpc.mjs';
14
15
  import {
15
16
  connectionTestTargetUnavailable,
16
17
  publicConnectionTestResult,
@@ -26,6 +27,7 @@ export const WEIXIN_ENDPOINTS = Object.freeze({
26
27
  reconnectBot: 'bot.reconnect',
27
28
  deleteBot: 'bot.delete',
28
29
  setWorkspace: SET_WORKSPACE_ENDPOINT,
30
+ setModel: SET_MODEL_ENDPOINT,
29
31
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
30
32
  setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
31
33
  setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
@@ -83,6 +85,9 @@ function payloadFailure(endpoint, payload) {
83
85
  return validWorkspacePayload(payload)
84
86
  ? null : '请输入工作区绝对路径。';
85
87
  }
88
+ if (endpoint === WEIXIN_ENDPOINTS.setModel) {
89
+ return validModelPayload(payload) ? null : '请选择有效模型。';
90
+ }
86
91
  if (endpoint === WEIXIN_ENDPOINTS.setAgentPreset) {
87
92
  return validAgentPresetPayload(payload)
88
93
  ? null : '请选择 Agent Preset。';
@@ -219,6 +224,12 @@ export function createWeixinRpcHandler(controller, { encodeQr = qrDataUrl } = {}
219
224
  await controller.updateWorkspace(payload.botId, payload.workspace),
220
225
  cachedEncode,
221
226
  );
227
+ } else if (endpoint === WEIXIN_ENDPOINTS.setModel) {
228
+ if (typeof controller.updateModel !== 'function') throw new Error('Model update is unavailable');
229
+ value = await publicStatus(
230
+ await controller.updateModel(payload.botId, payload.model),
231
+ cachedEncode,
232
+ );
222
233
  } else if (endpoint === WEIXIN_ENDPOINTS.setContextEnhancement) {
223
234
  if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
224
235
  value = await controller.updateContextEnhancement(
@@ -18,6 +18,7 @@ import {
18
18
  observeBotWorkspaceRemovals,
19
19
  } from '../../../../src/channels/shared/bot-workspace-store.mjs';
20
20
  import { listAgentPresetCatalog } from '../../../../src/channels/shared/agent-preset.mjs';
21
+ import { listModelCatalog } from '../../../../src/channels/shared/model-setting.mjs';
21
22
  import { createDeliveryAdapter } from '../../delivery-adapter.mjs';
22
23
  import { createTokenConnectionSupervisor } from '../shared/connection-supervisor.mjs';
23
24
  import { createHarnessCommandExecutor } from '../../harness-command-executor.mjs';
@@ -111,6 +112,7 @@ export async function createProductionController(ctx, config = {}, internals = {
111
112
  ...(sessionMaintenanceExecutor ? { sessionMaintenanceExecutor } : {}),
112
113
  ...(fileIngressExecutor ? { fileIngressExecutor } : {}),
113
114
  });
115
+ const modelCatalog = () => listModelCatalog(harness);
114
116
  const coreController = new Controller({
115
117
  configStore: observedConfigStore,
116
118
  authPath: paths.authPath,
@@ -167,6 +169,7 @@ export async function createProductionController(ctx, config = {}, internals = {
167
169
  workspaces,
168
170
  stateFor,
169
171
  agentPresetCatalog,
172
+ modelCatalog,
170
173
  });
171
174
  const supervisor = createSupervisor({
172
175
  channel: 'whatsapp',
@@ -6,6 +6,7 @@ import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from
6
6
  import { resolveRpcAuthority } from '../../rpc-authority.mjs';
7
7
  import { publicWorkspaceError, SET_WORKSPACE_ENDPOINT, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
8
8
  import { SET_AGENT_PRESET_ENDPOINT, validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
9
+ import { SET_MODEL_ENDPOINT, validModelPayload } from '../shared/model-setting-rpc.mjs';
9
10
 
10
11
  export const WHATSAPP_RPC_CHANNEL = '/whatsapp';
11
12
  export const WHATSAPP_ENDPOINTS = Object.freeze({
@@ -17,6 +18,7 @@ export const WHATSAPP_ENDPOINTS = Object.freeze({
17
18
  deleteBot: 'bot.delete',
18
19
  setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
19
20
  setWorkspace: SET_WORKSPACE_ENDPOINT,
21
+ setModel: SET_MODEL_ENDPOINT,
20
22
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
21
23
  setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
22
24
  });
@@ -62,6 +64,9 @@ function payloadFailure(endpoint, payload) {
62
64
  return validWorkspacePayload(payload)
63
65
  ? null : '请输入工作区绝对路径。';
64
66
  }
67
+ if (endpoint === WHATSAPP_ENDPOINTS.setModel) {
68
+ return validModelPayload(payload) ? null : '请选择有效模型。';
69
+ }
65
70
  if (endpoint === WHATSAPP_ENDPOINTS.setAgentPreset) {
66
71
  return validAgentPresetPayload(payload)
67
72
  ? null : '请选择 Agent Preset。';
@@ -172,6 +177,12 @@ export function createWhatsappRpcHandler(controller, { encodeQr = qrDataUrl } =
172
177
  await controller.updateWorkspace(payload.botId, payload.workspace),
173
178
  cachedEncode,
174
179
  );
180
+ } else if (endpoint === WHATSAPP_ENDPOINTS.setModel) {
181
+ if (typeof controller.updateModel !== 'function') throw new Error('Model update is unavailable');
182
+ value = await publicStatus(
183
+ await controller.updateModel(payload.botId, payload.model),
184
+ cachedEncode,
185
+ );
175
186
  } else if (endpoint === WHATSAPP_ENDPOINTS.setContextEnhancement) {
176
187
  if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
177
188
  value = await controller.updateContextEnhancement(