@xmanrui/dsh-im 3.1.1 → 4.0.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 (75) hide show
  1. package/README.en.md +16 -1
  2. package/README.md +16 -1
  3. package/lib/client.js +1653 -333
  4. package/lib/index.js +235 -226
  5. package/package.json +5 -1
  6. package/plugin-src/client/channels/dingtalk/api.js +3 -0
  7. package/plugin-src/client/channels/dingtalk/index.js +18 -5
  8. package/plugin-src/client/channels/feishu/api.js +3 -0
  9. package/plugin-src/client/channels/feishu/index.js +18 -5
  10. package/plugin-src/client/channels/qq/api.js +3 -0
  11. package/plugin-src/client/channels/qq/index.js +13 -0
  12. package/plugin-src/client/channels/shared/token-api.js +3 -0
  13. package/plugin-src/client/channels/shared/token-channel.js +13 -1
  14. package/plugin-src/client/channels/wecom/api.js +3 -0
  15. package/plugin-src/client/channels/wecom/index.js +13 -0
  16. package/plugin-src/client/channels/weixin/api.js +3 -0
  17. package/plugin-src/client/channels/weixin/index.js +19 -5
  18. package/plugin-src/client/channels/whatsapp/api.js +3 -0
  19. package/plugin-src/client/channels/whatsapp/index.js +13 -0
  20. package/plugin-src/client/context-enhancement.js +275 -0
  21. package/plugin-src/client/i18n.js +54 -3
  22. package/plugin-src/client/styles.js +78 -0
  23. package/plugin-src/client/update-panel.js +108 -7
  24. package/plugin-src/host/channels/dingtalk/index.mjs +1 -1
  25. package/plugin-src/host/channels/dingtalk/production.mjs +1 -0
  26. package/plugin-src/host/channels/dingtalk/rpc.mjs +11 -0
  27. package/plugin-src/host/channels/discord/index.mjs +1 -1
  28. package/plugin-src/host/channels/feishu/index.mjs +1 -1
  29. package/plugin-src/host/channels/feishu/production.mjs +3 -0
  30. package/plugin-src/host/channels/feishu/rpc.mjs +13 -0
  31. package/plugin-src/host/channels/qq/index.mjs +1 -1
  32. package/plugin-src/host/channels/qq/production.mjs +1 -0
  33. package/plugin-src/host/channels/qq/rpc.mjs +11 -0
  34. package/plugin-src/host/channels/shared/context-enhancement-rpc.mjs +17 -0
  35. package/plugin-src/host/channels/shared/production.mjs +1 -0
  36. package/plugin-src/host/channels/shared/rpc.mjs +9 -0
  37. package/plugin-src/host/channels/shared/workspace-rpc.mjs +1 -0
  38. package/plugin-src/host/channels/slack/index.mjs +1 -1
  39. package/plugin-src/host/channels/slack/production.mjs +1 -0
  40. package/plugin-src/host/channels/slack/rpc.mjs +10 -0
  41. package/plugin-src/host/channels/telegram/index.mjs +1 -1
  42. package/plugin-src/host/channels/wecom/index.mjs +1 -1
  43. package/plugin-src/host/channels/wecom/production.mjs +1 -0
  44. package/plugin-src/host/channels/wecom/rpc.mjs +11 -0
  45. package/plugin-src/host/channels/weixin/index.mjs +1 -1
  46. package/plugin-src/host/channels/weixin/production.mjs +1 -0
  47. package/plugin-src/host/channels/weixin/rpc.mjs +11 -0
  48. package/plugin-src/host/channels/whatsapp/index.mjs +1 -1
  49. package/plugin-src/host/channels/whatsapp/production.mjs +1 -0
  50. package/plugin-src/host/channels/whatsapp/rpc.mjs +11 -0
  51. package/plugin-src/host/harness-connection.mjs +9 -4
  52. package/plugin-src/host/index.mjs +43 -29
  53. package/plugin-src/host/modern-harness-api.mjs +577 -0
  54. package/plugin-src/host/update-runtime.mjs +3 -2
  55. package/plugin-src/host/update-service.mjs +2 -0
  56. package/scripts/verify-package.mjs +15 -2
  57. package/src/channels/dingtalk/dingtalk-api.mjs +39 -16
  58. package/src/channels/dingtalk/dingtalk-bridge.mjs +52 -23
  59. package/src/channels/dingtalk/dingtalk-runtime.mjs +20 -1
  60. package/src/channels/discord/discord-runtime.mjs +18 -4
  61. package/src/channels/feishu/bridge.mjs +18 -3
  62. package/src/channels/feishu/feishu-runtime.mjs +4 -0
  63. package/src/channels/qq/qq-bridge.mjs +20 -4
  64. package/src/channels/qq/qq-runtime.mjs +4 -0
  65. package/src/channels/shared/bot-workspace-store.mjs +103 -6
  66. package/src/channels/shared/context-enhancement.mjs +135 -0
  67. package/src/channels/shared/harness-client.mjs +18 -0
  68. package/src/channels/shared/text-harness-bridge.mjs +19 -4
  69. package/src/channels/slack/slack-runtime.mjs +4 -0
  70. package/src/channels/telegram/telegram-runtime.mjs +24 -2
  71. package/src/channels/wecom/wecom-bridge.mjs +18 -3
  72. package/src/channels/wecom/wecom-runtime.mjs +4 -0
  73. package/src/channels/weixin/weixin-bridge.mjs +19 -4
  74. package/src/channels/weixin/weixin-runtime.mjs +4 -0
  75. package/src/channels/whatsapp/whatsapp-runtime.mjs +5 -0
@@ -1,4 +1,5 @@
1
1
  import QRCode from 'qrcode';
2
+ import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
2
3
  import { resolveRpcAuthority } from '../../rpc-authority.mjs';
3
4
  import { publicWorkspaceError, SET_WORKSPACE_ENDPOINT, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
4
5
  import { SET_AGENT_PRESET_ENDPOINT, validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
@@ -18,6 +19,7 @@ export const DINGTALK_ENDPOINTS = Object.freeze({
18
19
  deleteBot: 'bot.delete',
19
20
  setWorkspace: SET_WORKSPACE_ENDPOINT,
20
21
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
22
+ setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
21
23
  approveSender: 'bot.sender.approve',
22
24
  revokeSender: 'bot.sender.revoke',
23
25
  });
@@ -94,6 +96,10 @@ function payloadFailure(endpoint, payload) {
94
96
  return validAgentPresetPayload(payload)
95
97
  ? null : '请选择 Agent Preset。';
96
98
  }
99
+ if (endpoint === DINGTALK_ENDPOINTS.setContextEnhancement) {
100
+ return validContextEnhancementPayload(payload)
101
+ ? null : '请提交有效的上下文增强设置。';
102
+ }
97
103
  if (endpoint === DINGTALK_ENDPOINTS.approveSender) {
98
104
  return exactKeys(payload, ['botId', 'requestId', 'confirm'])
99
105
  && validId(payload.botId)
@@ -251,6 +257,11 @@ export function createDingtalkRpcHandler(controller, { encodeQr = qrDataUrl } =
251
257
  await controller.updateWorkspace(payload.botId, payload.workspace),
252
258
  cachedEncode,
253
259
  );
260
+ } else if (endpoint === DINGTALK_ENDPOINTS.setContextEnhancement) {
261
+ if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
262
+ value = await controller.updateContextEnhancement(
263
+ payload.botId, payload.config, (status) => publicStatus(status, cachedEncode),
264
+ );
254
265
  } else if (endpoint === DINGTALK_ENDPOINTS.setAgentPreset) {
255
266
  if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
256
267
  value = await publicStatus(
@@ -2,7 +2,7 @@ import { createProductionController } from './production.mjs';
2
2
  import { installDiscordRpc } from './rpc.mjs';
3
3
 
4
4
  export const name = 'dsh-im-discord-host';
5
- export const inject = ['connection', 'credentials', 'apiProxy', 'typertGateway'];
5
+ export const inject = ['connection', 'credentials', 'typertGateway'];
6
6
 
7
7
  export async function apply(ctx, config = {}) {
8
8
  if (config?.controller) {
@@ -3,7 +3,7 @@ import { createProductionController } from './production.mjs';
3
3
  import { installFeishuRpc } from './rpc.mjs';
4
4
 
5
5
  export const name = 'dsh-feishu-host';
6
- export const inject = ['connection', 'credentials', 'apiProxy', 'typertGateway'];
6
+ export const inject = ['connection', 'credentials', 'typertGateway'];
7
7
 
8
8
  function controllerFrom(ctx, config) {
9
9
  if (config?.controller) return config.controller;
@@ -98,6 +98,8 @@ export async function createProductionController(ctx, config = {}, internals = {
98
98
  workspaces,
99
99
  method: 'removeBot',
100
100
  botIdFromRemoved: (removed) => removed.id,
101
+ saveMethod: 'saveBot',
102
+ botIdFromSave: (bot) => bot?.id,
101
103
  })
102
104
  : configStore;
103
105
  // State is lazy per bot. A corrupt legacy file can therefore fail only the
@@ -167,6 +169,7 @@ export async function createProductionController(ctx, config = {}, internals = {
167
169
  ownerOpenIds: botConfig.ownerOpenIds ?? [botConfig.ownerOpenId],
168
170
  harness: workspaceScope.harness,
169
171
  state: workspaceScope.state,
172
+ contextEnhancement: { botId: id, getSettings: () => workspaces.contextEnhancementFor(id) },
170
173
  replyTimeoutMs: config.replyTimeoutMs ?? 600_000,
171
174
  ...(wsAgent ? { wsAgent } : {}),
172
175
  logger: {
@@ -8,6 +8,8 @@ import { publicMessageFailure } from '../../../../src/channels/shared/message-fa
8
8
  import { resolveRpcAuthority } from '../../rpc-authority.mjs';
9
9
  import { publicWorkspaceError, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
10
10
  import { validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
11
+ import { validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
12
+ import { normalizeContextEnhancementConfig } from '../../../../src/channels/shared/context-enhancement.mjs';
11
13
  import {
12
14
  isFeishuGroupResponseMode,
13
15
  normalizeFeishuGroupResponseMode,
@@ -276,6 +278,7 @@ function publicBotEntry(entry) {
276
278
  connected,
277
279
  configured: source.configured === true,
278
280
  agentPreset: normalizeAgentPresetId(source.agentPreset),
281
+ contextEnhancement: normalizeContextEnhancementConfig(source.contextEnhancement),
279
282
  groupResponseMode: normalizeFeishuGroupResponseMode(source.groupResponseMode),
280
283
  groupMessagePermissionGranted: source.groupMessagePermissionGranted === true,
281
284
  bot: publicBot(source.bot),
@@ -415,6 +418,10 @@ function validPayload(endpoint, payload) {
415
418
  return validAgentPresetPayload(payload)
416
419
  ? null : '请选择 Agent Preset。';
417
420
  }
421
+ if (endpoint === FEISHU_ENDPOINTS.setContextEnhancement) {
422
+ return validContextEnhancementPayload(payload)
423
+ ? null : '请提交有效的上下文增强设置。';
424
+ }
418
425
  if (endpoint === FEISHU_ENDPOINTS.setGroupResponseMode) {
419
426
  return hasOnlyKeys(payload, new Set(['botId', 'groupResponseMode']))
420
427
  && safeOpaqueId(payload.botId)
@@ -666,6 +673,12 @@ export function createFeishuRpcHandler(controller, { encodeQr = qrCodeDataUrl }
666
673
  await controller.updateWorkspace(payload.botId, payload.workspace),
667
674
  { encodeQr: cachedEncodeQr },
668
675
  );
676
+ } else if (endpoint === FEISHU_ENDPOINTS.setContextEnhancement) {
677
+ if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
678
+ value = await controller.updateContextEnhancement(
679
+ payload.botId, payload.config,
680
+ (status) => toPublicFeishuStatus(status, { encodeQr: cachedEncodeQr }),
681
+ );
669
682
  } else if (endpoint === FEISHU_ENDPOINTS.setAgentPreset) {
670
683
  if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
671
684
  value = await toPublicFeishuStatus(
@@ -2,7 +2,7 @@ import { createProductionController } from './production.mjs';
2
2
  import { installQqRpc } from './rpc.mjs';
3
3
 
4
4
  export const name = 'dsh-im-qq-host';
5
- export const inject = ['connection', 'credentials', 'apiProxy', 'typertGateway'];
5
+ export const inject = ['connection', 'credentials', 'typertGateway'];
6
6
 
7
7
  export async function apply(ctx, config = {}) {
8
8
  if (config?.controller) {
@@ -100,6 +100,7 @@ export async function createProductionController(ctx, config = {}, internals = {
100
100
  appSecret,
101
101
  harness: workspaceScope.harness,
102
102
  state: workspaceScope.state,
103
+ contextEnhancement: { botId, getSettings: () => workspaces.contextEnhancementFor(botId) },
103
104
  replyTimeoutMs: config.replyTimeoutMs ?? 600_000,
104
105
  connectTimeoutMs: config.connectTimeoutMs ?? 20_000,
105
106
  logger: {
@@ -3,6 +3,7 @@ import {
3
3
  connectionTestTargetUnavailable,
4
4
  publicConnectionTestResult,
5
5
  } from '../../../../src/channels/shared/connection-test.mjs';
6
+ import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
6
7
  import { resolveRpcAuthority } from '../../rpc-authority.mjs';
7
8
  import { publicWorkspaceError, SET_WORKSPACE_ENDPOINT, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
8
9
  import { SET_AGENT_PRESET_ENDPOINT, validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
@@ -18,6 +19,7 @@ export const QQ_ENDPOINTS = Object.freeze({
18
19
  deleteBot: 'bot.delete',
19
20
  setWorkspace: SET_WORKSPACE_ENDPOINT,
20
21
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
22
+ setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
21
23
  });
22
24
  export const QQ_RPC_ENDPOINTS = Object.freeze(Object.values(QQ_ENDPOINTS));
23
25
 
@@ -76,6 +78,10 @@ function payloadFailure(endpoint, payload) {
76
78
  return validAgentPresetPayload(payload)
77
79
  ? null : '请选择 Agent Preset。';
78
80
  }
81
+ if (endpoint === QQ_ENDPOINTS.setContextEnhancement) {
82
+ return validContextEnhancementPayload(payload)
83
+ ? null : '请提交有效的上下文增强设置。';
84
+ }
79
85
  return 'Unknown QQ endpoint.';
80
86
  }
81
87
 
@@ -171,6 +177,11 @@ export function createQqRpcHandler(controller, { encodeQr = qrDataUrl } = {}) {
171
177
  await controller.updateWorkspace(payload.botId, payload.workspace),
172
178
  cachedEncode,
173
179
  );
180
+ } else if (endpoint === QQ_ENDPOINTS.setContextEnhancement) {
181
+ if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
182
+ value = await controller.updateContextEnhancement(
183
+ payload.botId, payload.config, (status) => publicStatus(status, cachedEncode),
184
+ );
174
185
  } else if (endpoint === QQ_ENDPOINTS.setAgentPreset) {
175
186
  if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
176
187
  value = await publicStatus(
@@ -0,0 +1,17 @@
1
+ import { validateContextEnhancementConfig } from '../../../../src/channels/shared/context-enhancement.mjs';
2
+
3
+ export const SET_CONTEXT_ENHANCEMENT_ENDPOINT = 'bot.context-enhancement.set';
4
+
5
+ export function validContextEnhancementPayload(payload) {
6
+ try {
7
+ if (!payload || typeof payload !== 'object' || Array.isArray(payload)
8
+ || Reflect.ownKeys(payload).length !== 2
9
+ || !Object.hasOwn(payload, 'botId') || !Object.hasOwn(payload, 'config')
10
+ || typeof payload.botId !== 'string'
11
+ || !/^[A-Za-z0-9_-]{1,128}$/.test(payload.botId)) return false;
12
+ validateContextEnhancementConfig(payload.config);
13
+ return true;
14
+ } catch {
15
+ return false;
16
+ }
17
+ }
@@ -102,6 +102,7 @@ export async function createTokenProductionController(ctx, config, internals, de
102
102
  token,
103
103
  harness: workspaceScope.harness,
104
104
  state: workspaceScope.state,
105
+ contextEnhancement: { botId, getSettings: () => workspaces.contextEnhancementFor(botId) },
105
106
  replyTimeoutMs: config.replyTimeoutMs ?? 600_000,
106
107
  connectTimeoutMs: config.connectTimeoutMs ?? 20_000,
107
108
  logger: {
@@ -1,3 +1,4 @@
1
+ import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from './context-enhancement-rpc.mjs';
1
2
  import { resolveRpcAuthority } from '../../rpc-authority.mjs';
2
3
  import { publicConnectionTestResult } from '../../../../src/channels/shared/connection-test.mjs';
3
4
  import {
@@ -17,6 +18,7 @@ export const TOKEN_BOT_ENDPOINTS = Object.freeze({
17
18
  deleteBot: 'bot.delete',
18
19
  setWorkspace: SET_WORKSPACE_ENDPOINT,
19
20
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
21
+ setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
20
22
  });
21
23
 
22
24
  const ENDPOINTS = Object.freeze(Object.values(TOKEN_BOT_ENDPOINTS));
@@ -71,6 +73,10 @@ function payloadFailure(endpoint, payload) {
71
73
  return validAgentPresetPayload(payload)
72
74
  ? null : '请选择 Agent Preset。';
73
75
  }
76
+ if (endpoint === TOKEN_BOT_ENDPOINTS.setContextEnhancement) {
77
+ return validContextEnhancementPayload(payload)
78
+ ? null : '请提交有效的上下文增强设置。';
79
+ }
74
80
  return 'Unknown bot endpoint.';
75
81
  }
76
82
 
@@ -152,6 +158,9 @@ export function createTokenBotRpcHandler(controller, { channel }) {
152
158
  } else if (endpoint === TOKEN_BOT_ENDPOINTS.setWorkspace) {
153
159
  if (typeof controller.updateWorkspace !== 'function') throw new Error('Workspace update is unavailable');
154
160
  value = await controller.updateWorkspace(payload.botId, payload.workspace);
161
+ } else if (endpoint === TOKEN_BOT_ENDPOINTS.setContextEnhancement) {
162
+ if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
163
+ value = await controller.updateContextEnhancement(payload.botId, payload.config);
155
164
  } else if (endpoint === TOKEN_BOT_ENDPOINTS.setAgentPreset) {
156
165
  if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
157
166
  value = await controller.updateAgentPreset(payload.botId, payload.agentPreset);
@@ -22,6 +22,7 @@ export function publicWorkspaceError(error) {
22
22
  'workspace-bot-not-found',
23
23
  'agent-preset-invalid',
24
24
  'agent-preset-unavailable',
25
+ 'context-enhancement-invalid',
25
26
  ].includes(error?.code)) return null;
26
27
  return { code: error.code, message: error.message };
27
28
  }
@@ -2,7 +2,7 @@ import { createProductionController } from './production.mjs';
2
2
  import { installSlackRpc } from './rpc.mjs';
3
3
 
4
4
  export const name = 'dsh-im-slack-host';
5
- export const inject = ['connection', 'credentials', 'apiProxy', 'typertGateway'];
5
+ export const inject = ['connection', 'credentials', 'typertGateway'];
6
6
 
7
7
  export async function apply(ctx, config = {}) {
8
8
  if (config?.controller) return installSlackRpc(ctx, config.controller, config.rpcAuthority);
@@ -89,6 +89,7 @@ export async function createProductionController(ctx, config = {}, internals = {
89
89
  appToken,
90
90
  harness: workspaceScope.harness,
91
91
  state: workspaceScope.state,
92
+ contextEnhancement: { botId, getSettings: () => workspaces.contextEnhancementFor(botId) },
92
93
  replyTimeoutMs: config.replyTimeoutMs ?? 600_000,
93
94
  connectTimeoutMs: config.connectTimeoutMs ?? 20_000,
94
95
  logger: {
@@ -1,3 +1,4 @@
1
+ import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
1
2
  import { resolveRpcAuthority } from '../../rpc-authority.mjs';
2
3
  import { publicConnectionTestResult } from '../../../../src/channels/shared/connection-test.mjs';
3
4
  import {
@@ -18,6 +19,7 @@ export const SLACK_ENDPOINTS = Object.freeze({
18
19
  deleteBot: 'bot.delete',
19
20
  setWorkspace: SET_WORKSPACE_ENDPOINT,
20
21
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
22
+ setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
21
23
  });
22
24
  export const SLACK_RPC_ENDPOINTS = Object.freeze(Object.values(SLACK_ENDPOINTS));
23
25
 
@@ -75,6 +77,10 @@ function payloadFailure(endpoint, payload) {
75
77
  return validAgentPresetPayload(payload)
76
78
  ? null : '请选择 Agent Preset。';
77
79
  }
80
+ if (endpoint === SLACK_ENDPOINTS.setContextEnhancement) {
81
+ return validContextEnhancementPayload(payload)
82
+ ? null : '请提交有效的上下文增强设置。';
83
+ }
78
84
  return 'Unknown Slack endpoint.';
79
85
  }
80
86
 
@@ -154,6 +160,10 @@ export function createSlackRpcHandler(controller) {
154
160
  if (typeof controller.updateWorkspace !== 'function') throw new Error('Workspace update is unavailable');
155
161
  value = await controller.updateWorkspace(payload.botId, payload.workspace);
156
162
  }
163
+ else if (endpoint === SLACK_ENDPOINTS.setContextEnhancement) {
164
+ if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
165
+ value = await controller.updateContextEnhancement(payload.botId, payload.config);
166
+ }
157
167
  else if (endpoint === SLACK_ENDPOINTS.setAgentPreset) {
158
168
  if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
159
169
  value = await controller.updateAgentPreset(payload.botId, payload.agentPreset);
@@ -2,7 +2,7 @@ import { createProductionController } from './production.mjs';
2
2
  import { installTelegramRpc } from './rpc.mjs';
3
3
 
4
4
  export const name = 'dsh-im-telegram-host';
5
- export const inject = ['connection', 'credentials', 'apiProxy', 'typertGateway'];
5
+ export const inject = ['connection', 'credentials', 'typertGateway'];
6
6
 
7
7
  export async function apply(ctx, config = {}) {
8
8
  if (config?.controller) {
@@ -2,7 +2,7 @@ import { createProductionController } from './production.mjs';
2
2
  import { installWecomRpc } from './rpc.mjs';
3
3
 
4
4
  export const name = 'dsh-im-wecom-host';
5
- export const inject = ['connection', 'credentials', 'apiProxy', 'typertGateway'];
5
+ export const inject = ['connection', 'credentials', 'typertGateway'];
6
6
 
7
7
  export async function apply(ctx, config = {}) {
8
8
  if (config?.controller) {
@@ -103,6 +103,7 @@ export async function createProductionController(ctx, config = {}, internals = {
103
103
  secret,
104
104
  harness: workspaceScope.harness,
105
105
  state: workspaceScope.state,
106
+ contextEnhancement: { botId, getSettings: () => workspaces.contextEnhancementFor(botId) },
106
107
  replyTimeoutMs: config.replyTimeoutMs ?? 600_000,
107
108
  connectTimeoutMs: config.connectTimeoutMs ?? 20_000,
108
109
  maxReconnectAttempts: config.maxReconnectAttempts ?? 10,
@@ -1,4 +1,5 @@
1
1
  import QRCode from 'qrcode';
2
+ import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
2
3
  import { resolveRpcAuthority } from '../../rpc-authority.mjs';
3
4
  import { publicWorkspaceError, SET_WORKSPACE_ENDPOINT, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
4
5
  import { SET_AGENT_PRESET_ENDPOINT, validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
@@ -18,6 +19,7 @@ export const WECOM_ENDPOINTS = Object.freeze({
18
19
  deleteBot: 'bot.delete',
19
20
  setWorkspace: SET_WORKSPACE_ENDPOINT,
20
21
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
22
+ setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
21
23
  });
22
24
  export const WECOM_RPC_ENDPOINTS = Object.freeze(Object.values(WECOM_ENDPOINTS));
23
25
 
@@ -76,6 +78,10 @@ function payloadFailure(endpoint, payload) {
76
78
  return validAgentPresetPayload(payload)
77
79
  ? null : '请选择 Agent Preset。';
78
80
  }
81
+ if (endpoint === WECOM_ENDPOINTS.setContextEnhancement) {
82
+ return validContextEnhancementPayload(payload)
83
+ ? null : '请提交有效的上下文增强设置。';
84
+ }
79
85
  return 'Unknown Enterprise WeChat endpoint.';
80
86
  }
81
87
 
@@ -172,6 +178,11 @@ export function createWecomRpcHandler(controller, { encodeQr = qrDataUrl } = {})
172
178
  await controller.updateWorkspace(payload.botId, payload.workspace),
173
179
  cachedEncode,
174
180
  );
181
+ } else if (endpoint === WECOM_ENDPOINTS.setContextEnhancement) {
182
+ if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
183
+ value = await controller.updateContextEnhancement(
184
+ payload.botId, payload.config, (status) => publicStatus(status, cachedEncode),
185
+ );
175
186
  } else if (endpoint === WECOM_ENDPOINTS.setAgentPreset) {
176
187
  if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
177
188
  value = await publicStatus(
@@ -2,7 +2,7 @@ import { createProductionController } from './production.mjs';
2
2
  import { installWeixinRpc } from './rpc.mjs';
3
3
 
4
4
  export const name = 'dsh-weixin-host';
5
- export const inject = ['connection', 'credentials', 'apiProxy', 'typertGateway'];
5
+ export const inject = ['connection', 'credentials', 'typertGateway'];
6
6
 
7
7
  export async function apply(ctx, config = {}) {
8
8
  if (config?.controller) {
@@ -107,6 +107,7 @@ export async function createProductionController(ctx, config = {}, internals = {
107
107
  token,
108
108
  harness: workspaceScope.harness,
109
109
  state: workspaceScope.state,
110
+ contextEnhancement: { botId, getSettings: () => workspaces.contextEnhancementFor(botId) },
110
111
  replyTimeoutMs: config.replyTimeoutMs ?? 600_000,
111
112
  maxMessageChars: config.maxMessageChars ?? DEFAULT_WEIXIN_MAX_MESSAGE_CHARS,
112
113
  logger: {
@@ -1,4 +1,5 @@
1
1
  import QRCode from 'qrcode';
2
+ import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
2
3
  import { resolveRpcAuthority } from '../../rpc-authority.mjs';
3
4
  import {
4
5
  publicWorkspaceError,
@@ -25,6 +26,7 @@ export const WEIXIN_ENDPOINTS = Object.freeze({
25
26
  deleteBot: 'bot.delete',
26
27
  setWorkspace: SET_WORKSPACE_ENDPOINT,
27
28
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
29
+ setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
28
30
  });
29
31
  export const WEIXIN_RPC_ENDPOINTS = Object.freeze(Object.values(WEIXIN_ENDPOINTS));
30
32
 
@@ -83,6 +85,10 @@ function payloadFailure(endpoint, payload) {
83
85
  return validAgentPresetPayload(payload)
84
86
  ? null : '请选择 Agent Preset。';
85
87
  }
88
+ if (endpoint === WEIXIN_ENDPOINTS.setContextEnhancement) {
89
+ return validContextEnhancementPayload(payload)
90
+ ? null : '请提交有效的上下文增强设置。';
91
+ }
86
92
  return 'Unknown Weixin endpoint.';
87
93
  }
88
94
 
@@ -207,6 +213,11 @@ export function createWeixinRpcHandler(controller, { encodeQr = qrDataUrl } = {}
207
213
  await controller.updateWorkspace(payload.botId, payload.workspace),
208
214
  cachedEncode,
209
215
  );
216
+ } else if (endpoint === WEIXIN_ENDPOINTS.setContextEnhancement) {
217
+ if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
218
+ value = await controller.updateContextEnhancement(
219
+ payload.botId, payload.config, (status) => publicStatus(status, cachedEncode),
220
+ );
210
221
  } else if (endpoint === WEIXIN_ENDPOINTS.setAgentPreset) {
211
222
  if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
212
223
  value = await publicStatus(
@@ -2,7 +2,7 @@ import { createProductionController } from './production.mjs';
2
2
  import { installWhatsappRpc } from './rpc.mjs';
3
3
 
4
4
  export const name = 'dsh-im-whatsapp-host';
5
- export const inject = ['connection', 'apiProxy', 'typertGateway'];
5
+ export const inject = ['connection', 'typertGateway'];
6
6
 
7
7
  export async function apply(ctx, config = {}) {
8
8
  if (config?.controller) {
@@ -106,6 +106,7 @@ export async function createProductionController(ctx, config = {}, internals = {
106
106
  authDir,
107
107
  harness: workspaceScope.harness,
108
108
  state: workspaceScope.state,
109
+ contextEnhancement: { botId, getSettings: () => workspaces.contextEnhancementFor(botId) },
109
110
  replyTimeoutMs: config.replyTimeoutMs ?? 600_000,
110
111
  connectTimeoutMs: config.connectTimeoutMs ?? 30_000,
111
112
  createSession,
@@ -2,6 +2,7 @@ import QRCode from 'qrcode';
2
2
 
3
3
  import { publicConnectionTestResult } from '../../../../src/channels/shared/connection-test.mjs';
4
4
  import { normalizeWhatsappAccessPolicy } from '../../../../src/channels/whatsapp/config-store.mjs';
5
+ import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
5
6
  import { resolveRpcAuthority } from '../../rpc-authority.mjs';
6
7
  import { publicWorkspaceError, SET_WORKSPACE_ENDPOINT, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
7
8
  import { SET_AGENT_PRESET_ENDPOINT, validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
@@ -17,6 +18,7 @@ export const WHATSAPP_ENDPOINTS = Object.freeze({
17
18
  setAccessPolicy: 'bot.access-policy.set',
18
19
  setWorkspace: SET_WORKSPACE_ENDPOINT,
19
20
  setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
21
+ setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
20
22
  });
21
23
  export const WHATSAPP_RPC_ENDPOINTS = Object.freeze(Object.values(WHATSAPP_ENDPOINTS));
22
24
 
@@ -71,6 +73,10 @@ function payloadFailure(endpoint, payload) {
71
73
  return validAgentPresetPayload(payload)
72
74
  ? null : '请选择 Agent Preset。';
73
75
  }
76
+ if (endpoint === WHATSAPP_ENDPOINTS.setContextEnhancement) {
77
+ return validContextEnhancementPayload(payload)
78
+ ? null : '请提交有效的上下文增强设置。';
79
+ }
74
80
  return 'Unknown WhatsApp endpoint.';
75
81
  }
76
82
 
@@ -173,6 +179,11 @@ export function createWhatsappRpcHandler(controller, { encodeQr = qrDataUrl } =
173
179
  await controller.updateWorkspace(payload.botId, payload.workspace),
174
180
  cachedEncode,
175
181
  );
182
+ } else if (endpoint === WHATSAPP_ENDPOINTS.setContextEnhancement) {
183
+ if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
184
+ value = await controller.updateContextEnhancement(
185
+ payload.botId, payload.config, (status) => publicStatus(status, cachedEncode),
186
+ );
176
187
  } else if (endpoint === WHATSAPP_ENDPOINTS.setAgentPreset) {
177
188
  if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
178
189
  value = await publicStatus(
@@ -1,13 +1,18 @@
1
+ import { modernHarnessApi } from './modern-harness-api.mjs';
2
+
3
+ function optionalService(ctx, name) {
4
+ if (ctx && Object.hasOwn(ctx, name)) return ctx[name];
5
+ return typeof ctx?.get === 'function' ? ctx.get(name) : undefined;
6
+ }
7
+
1
8
  /** Connect to this Host directly unless an external Harness URL was configured. */
2
9
  export function harnessConnection(ctx, config = {}) {
3
10
  if (config.harnessBaseUrl !== undefined) {
4
11
  return { baseUrl: new URL(config.harnessBaseUrl) };
5
12
  }
6
- if (!ctx?.apiProxy) {
7
- throw new TypeError('dsh-im requires the Host apiProxy service; check that DSH has finished loading its Host services');
8
- }
13
+ const apiProxy = optionalService(ctx, 'apiProxy') ?? modernHarnessApi(ctx);
9
14
  return {
10
- apiProxy: ctx.apiProxy,
15
+ apiProxy,
11
16
  // Cordis child contexts share one root; different Hosts must not share
12
17
  // ownership of pending questions and approvals.
13
18
  interactionScope: ctx.root ?? ctx,
@@ -16,7 +16,6 @@ export const name = 'dsh-im-host';
16
16
  export const inject = [
17
17
  'connection',
18
18
  'credentials',
19
- 'apiProxy',
20
19
  'typertGateway',
21
20
  ];
22
21
 
@@ -55,38 +54,53 @@ export function createImHostPlugin(internals = {}) {
55
54
  name,
56
55
  inject,
57
56
  async apply(ctx, config = {}) {
58
- setImHostLanguage(config.language ?? process.env.DSH_IM_LANGUAGE);
57
+ const activate = async (readyCtx) => {
58
+ await activateChannels(readyCtx, config);
59
+ };
59
60
  if (typeof ctx?.inject === 'function') {
60
- ctx.inject(['tools', 'systemPrompt'], (artifactCtx) => {
61
- installOutboundArtifactTool(artifactCtx);
62
- });
63
- } else {
64
- installOutboundArtifactTool(ctx);
65
- }
66
- const logger = typeof ctx?.logger === 'function'
67
- ? ctx.logger(name)
68
- : (ctx?.logger ?? console);
69
- if (ctx?.connection?.rpc) {
70
- try {
71
- startUpdate(ctx);
72
- } catch (error) {
73
- logger.error?.('[dsh-im] failed to activate update management; continuing with channels', error);
74
- }
75
- }
76
- const failures = [];
77
- for (const [channel, start] of channels) {
78
- try {
79
- await start(ctx, channelConfig(config, channel));
80
- } catch (error) {
81
- failures.push(error);
82
- logger.error?.(`[dsh-im] failed to activate ${channel}; continuing with the remaining channels`, error);
83
- }
84
- }
85
- if (failures.length === channels.length) {
86
- throw new AggregateError(failures, 'dsh-im failed to activate every channel');
61
+ const modern = typeof ctx?.typertGateway?.stream === 'function';
62
+ await ctx.inject(
63
+ modern ? ['sessionController', 'workspaceController'] : ['apiProxy'],
64
+ activate,
65
+ );
66
+ return;
87
67
  }
68
+ await activate(ctx);
88
69
  },
89
70
  });
71
+
72
+ async function activateChannels(ctx, config) {
73
+ setImHostLanguage(config.language ?? process.env.DSH_IM_LANGUAGE);
74
+ if (typeof ctx?.inject === 'function') {
75
+ ctx.inject(['tools', 'systemPrompt'], (artifactCtx) => {
76
+ installOutboundArtifactTool(artifactCtx);
77
+ });
78
+ } else {
79
+ installOutboundArtifactTool(ctx);
80
+ }
81
+ const logger = typeof ctx?.logger === 'function'
82
+ ? ctx.logger(name)
83
+ : (ctx?.logger ?? console);
84
+ if (ctx?.connection?.rpc) {
85
+ try {
86
+ startUpdate(ctx);
87
+ } catch (error) {
88
+ logger.error?.('[dsh-im] failed to activate update management; continuing with channels', error);
89
+ }
90
+ }
91
+ const failures = [];
92
+ for (const [channel, start] of channels) {
93
+ try {
94
+ await start(ctx, channelConfig(config, channel));
95
+ } catch (error) {
96
+ failures.push(error);
97
+ logger.error?.(`[dsh-im] failed to activate ${channel}; continuing with the remaining channels`, error);
98
+ }
99
+ }
100
+ if (failures.length === channels.length) {
101
+ throw new AggregateError(failures, 'dsh-im failed to activate every channel');
102
+ }
103
+ }
90
104
  }
91
105
 
92
106
  export async function apply(ctx, config = {}) {