@xmanrui/dsh-im 4.35.1 → 4.36.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 (60) hide show
  1. package/PROACTIVE_DELIVERY.en.md +50 -0
  2. package/PROACTIVE_DELIVERY.md +22 -0
  3. package/README.en.md +3 -1
  4. package/README.md +3 -1
  5. package/lib/client.js +4 -3
  6. package/lib/index.js +309 -309
  7. package/package.json +3 -3
  8. package/plugin-src/host/channels/dingtalk/production.mjs +1 -1
  9. package/plugin-src/host/channels/feishu/production.mjs +4 -2
  10. package/plugin-src/host/channels/matrix/production.mjs +1 -1
  11. package/plugin-src/host/channels/qq/production.mjs +1 -1
  12. package/plugin-src/host/channels/shared/production.mjs +1 -1
  13. package/plugin-src/host/channels/slack/production.mjs +1 -1
  14. package/plugin-src/host/channels/wecom/production.mjs +1 -1
  15. package/plugin-src/host/channels/wecom-app/production.mjs +1 -1
  16. package/plugin-src/host/channels/weixin/production.mjs +1 -1
  17. package/plugin-src/host/channels/whatsapp/production.mjs +1 -1
  18. package/plugin-src/host/delivery-adapter.mjs +60 -12
  19. package/plugin-src/host/delivery-service.mjs +127 -1
  20. package/plugin-src/host/delivery-suggestions.mjs +2 -1
  21. package/plugin-src/host/harness-connection.mjs +3 -7
  22. package/plugin-src/host/index.mjs +5 -2
  23. package/plugin-src/host/modern-harness-api.mjs +139 -4
  24. package/src/channels/dingtalk/dingtalk-bridge.mjs +6 -0
  25. package/src/channels/dingtalk/dingtalk-controller.mjs +14 -0
  26. package/src/channels/dingtalk/dingtalk-runtime.mjs +10 -0
  27. package/src/channels/discord/discord-runtime.mjs +13 -0
  28. package/src/channels/feishu/bridge.mjs +30 -13
  29. package/src/channels/feishu/external-consumer.mjs +118 -0
  30. package/src/channels/feishu/feishu-runtime.mjs +72 -3
  31. package/src/channels/feishu/history-reader.mjs +92 -0
  32. package/src/channels/feishu/multi-bot-controller.mjs +130 -2
  33. package/src/channels/feishu/nearby-history.mjs +72 -0
  34. package/src/channels/feishu/plugin-config-store.mjs +1 -0
  35. package/src/channels/matrix/matrix-controller.mjs +9 -0
  36. package/src/channels/matrix/matrix-runtime.mjs +14 -0
  37. package/src/channels/qq/qq-bridge.mjs +6 -0
  38. package/src/channels/qq/qq-controller.mjs +14 -0
  39. package/src/channels/qq/qq-runtime.mjs +10 -0
  40. package/src/channels/shared/harness-approval.mjs +84 -7
  41. package/src/channels/shared/harness-client.mjs +36 -32
  42. package/src/channels/shared/i18n-en/shared-c.mjs +3 -0
  43. package/src/channels/shared/text-harness-bridge.mjs +6 -0
  44. package/src/channels/shared/token-bot-controller.mjs +16 -0
  45. package/src/channels/slack/manifest.mjs +1 -0
  46. package/src/channels/slack/slack-api.mjs +7 -0
  47. package/src/channels/slack/slack-controller.mjs +14 -0
  48. package/src/channels/slack/slack-runtime.mjs +12 -0
  49. package/src/channels/telegram/telegram-runtime.mjs +10 -0
  50. package/src/channels/wecom/wecom-bridge.mjs +6 -0
  51. package/src/channels/wecom/wecom-controller.mjs +14 -0
  52. package/src/channels/wecom/wecom-runtime.mjs +10 -0
  53. package/src/channels/wecom-app/wecom-app-bridge.mjs +6 -0
  54. package/src/channels/wecom-app/wecom-app-controller.mjs +14 -0
  55. package/src/channels/wecom-app/wecom-app-runtime.mjs +10 -0
  56. package/src/channels/weixin/weixin-bridge.mjs +6 -0
  57. package/src/channels/weixin/weixin-controller.mjs +14 -0
  58. package/src/channels/weixin/weixin-runtime.mjs +10 -0
  59. package/src/channels/whatsapp/whatsapp-controller.mjs +14 -0
  60. package/src/channels/whatsapp/whatsapp-runtime.mjs +10 -0
@@ -70,6 +70,9 @@ export default {
70
70
  '已批准,仅对本次操作有效。':
71
71
  'Approved — valid for this operation only.',
72
72
  '已拒绝此次操作。': 'This operation was rejected.',
73
+ '机器人端无法处理这次审批,请到 Web 查看。': 'The bot cannot handle this approval. Please check the Web interface.',
74
+ '未找到可处理端,本次操作未获批准。': 'No approval interface is available. This operation was not approved.',
75
+ '有操作在等待审批:{tool},请到 Web 处理。': 'An operation is awaiting approval: {tool}. Please handle it in the Web interface.',
73
76
  '无法完整展示这次操作,已安全拒绝此次审批。':
74
77
  'The operation could not be displayed in full, so this approval was safely rejected.',
75
78
  '审批提交失败,请重新回复「批准」或「拒绝」。':
@@ -1381,6 +1381,12 @@ export class TextHarnessBridge {
1381
1381
  });
1382
1382
  }
1383
1383
 
1384
+ async presentSessionSyncApproval(interaction, context, options = {}) {
1385
+ return this.#approvals.handleSessionSyncRequested(interaction, context, {
1386
+ ...options, runtimeSignal: this.#signal ?? options.runtimeSignal, accessPolicy: this.#accessPolicy,
1387
+ });
1388
+ }
1389
+
1384
1390
  async #handleInteraction(interaction, {
1385
1391
  key,
1386
1392
  actor,
@@ -247,6 +247,22 @@ export class TokenBotController {
247
247
  });
248
248
  }
249
249
 
250
+ async presentSessionSyncApproval(botId, target, interaction, options = {}) {
251
+ const config = this.#configStore.get(botId);
252
+ if (!config) throw new Error(`Unknown ${this.#descriptor.label} bot`);
253
+ return this.#withBotTransition(botId, async () => {
254
+ const runtime = this.#runtimes.get(botId);
255
+ if (!runtime?.status?.ready || typeof runtime.presentSessionSyncApproval !== 'function') {
256
+ const error = new Error(t('{label}机器人尚未连接', {
257
+ label: this.#descriptor.label,
258
+ }));
259
+ error.code = 'bot-not-connected';
260
+ throw error;
261
+ }
262
+ return runtime.presentSessionSyncApproval(target, interaction, options);
263
+ });
264
+ }
265
+
250
266
  async deleteBot(botId) {
251
267
  const warnings = [];
252
268
  const config = this.#configStore.get(botId);
@@ -21,6 +21,7 @@ oauth_config:
21
21
  - files:read
22
22
  - files:write
23
23
  - groups:history
24
+ - im:read
24
25
  - im:history
25
26
  - mpim:history
26
27
  - reactions:write
@@ -234,6 +234,13 @@ export class SlackApi {
234
234
  });
235
235
  }
236
236
 
237
+ async conversationInfo({ channelId, signal } = {}) {
238
+ const value = await this.#request('conversations.info', {
239
+ tokenKind: 'bot', signal, body: { channel: slackId(channelId, 'channel id') },
240
+ });
241
+ return value.channel;
242
+ }
243
+
237
244
  async fileInfo({ fileId, signal } = {}) {
238
245
  const value = await this.#request('files.info', {
239
246
  tokenKind: 'bot',
@@ -207,6 +207,20 @@ export class SlackController {
207
207
  });
208
208
  }
209
209
 
210
+ async presentSessionSyncApproval(botId, target, interaction, options = {}) {
211
+ const config = this.#configStore.get(botId);
212
+ if (!config) throw new Error('Unknown Slack bot');
213
+ return this.#withBotTransition(botId, async () => {
214
+ const runtime = this.#runtimes.get(botId);
215
+ if (!runtime?.status?.ready || typeof runtime.presentSessionSyncApproval !== 'function') {
216
+ const error = new Error(t('Slack机器人尚未连接'));
217
+ error.code = 'bot-not-connected';
218
+ throw error;
219
+ }
220
+ return runtime.presentSessionSyncApproval(target, interaction, options);
221
+ });
222
+ }
223
+
210
224
  async deleteBot(botId) {
211
225
  const warnings = [];
212
226
  const config = this.#configStore.get(botId);
@@ -473,6 +473,18 @@ export class SlackRuntime {
473
473
  return this.#bridge.sendConnectionTest(text);
474
474
  }
475
475
 
476
+ async presentSessionSyncApproval(target, interaction, options = {}) {
477
+ if (!this.#status.ready || !this.#bridge) return false;
478
+ const channelId = target?.route?.channelId;
479
+ const channel = await this.#api.conversationInfo({ channelId, signal: options.signal });
480
+ const actor = channel?.is_im === true && typeof channel.user === 'string' ? channel.user : '';
481
+ if (!actor) return false;
482
+ return this.#bridge.presentSessionSyncApproval(interaction, {
483
+ key: options.key, actor, validate: options.validate,
484
+ send: (text) => this.sendProactiveText(target, text),
485
+ }, options);
486
+ }
487
+
476
488
  async sendProactiveText(target, text, options = {}) {
477
489
  if (!this.#status.ready || !this.#bridge) {
478
490
  const error = new Error('Slack bot is not connected');
@@ -1171,6 +1171,16 @@ export class TelegramRuntime {
1171
1171
  return this.#bridge.sendConnectionTest(text);
1172
1172
  }
1173
1173
 
1174
+ async presentSessionSyncApproval(target, interaction, options = {}) {
1175
+ if (!this.#status.ready || !this.#bridge) return false;
1176
+ const actor = typeof target?.route?.chatId === 'string' ? target.route.chatId.trim() : '';
1177
+ if (!actor) return false;
1178
+ return this.#bridge.presentSessionSyncApproval(interaction, {
1179
+ key: options.key, actor, validate: options.validate,
1180
+ send: (text) => this.sendProactiveText(target, text),
1181
+ }, options);
1182
+ }
1183
+
1174
1184
  async sendProactiveText(target, text, options = {}) {
1175
1185
  if (!this.#status.ready || !this.#bridge) {
1176
1186
  const error = new Error('Telegram bot is not connected');
@@ -1668,6 +1668,12 @@ export class WecomHarnessBridge {
1668
1668
  }
1669
1669
  }
1670
1670
 
1671
+ async presentSessionSyncApproval(interaction, context, options = {}) {
1672
+ return this.#approvals.handleSessionSyncRequested(interaction, context, {
1673
+ ...options, runtimeSignal: this.#signal, accessPolicy: this.#accessPolicy,
1674
+ });
1675
+ }
1676
+
1671
1677
  async #handleInteraction(interaction, {
1672
1678
  key,
1673
1679
  actor,
@@ -281,6 +281,20 @@ export class WecomController {
281
281
  });
282
282
  }
283
283
 
284
+ async presentSessionSyncApproval(botId, target, interaction, options = {}) {
285
+ const config = this.#configStore.get(botId);
286
+ if (!config) throw new Error('Unknown Enterprise WeChat bot');
287
+ return this.#withBotTransition(botId, async () => {
288
+ const runtime = this.#runtimes.get(botId);
289
+ if (!runtime?.status?.ready || typeof runtime.presentSessionSyncApproval !== 'function') {
290
+ const error = new Error(t('企业微信连接当前离线'));
291
+ error.code = 'bot-not-connected';
292
+ throw error;
293
+ }
294
+ return runtime.presentSessionSyncApproval(target, interaction, options);
295
+ });
296
+ }
297
+
284
298
  async deleteBot(botId) {
285
299
  const warnings = [];
286
300
  const config = this.#configStore.get(botId);
@@ -230,6 +230,16 @@ export class WecomRuntime {
230
230
  });
231
231
  }
232
232
 
233
+ async presentSessionSyncApproval(target, interaction, options = {}) {
234
+ if (!this.#status.ready || !this.#bridge) return false;
235
+ const actor = typeof target?.route?.chatId === 'string' ? target.route.chatId.trim() : '';
236
+ if (!actor) return false;
237
+ return this.#bridge.presentSessionSyncApproval(interaction, {
238
+ key: options.key, actor, validate: options.validate,
239
+ send: (text) => this.sendProactiveText(target, text),
240
+ }, options);
241
+ }
242
+
233
243
  async sendProactiveText(target, text, { signal } = {}) {
234
244
  const chatId = typeof target?.route?.chatId === 'string'
235
245
  ? target.route.chatId.trim() : '';
@@ -855,6 +855,12 @@ export class WecomAppBridge {
855
855
  }
856
856
  }
857
857
 
858
+ async presentSessionSyncApproval(interaction, context, options = {}) {
859
+ return this.#approvals.handleSessionSyncRequested(interaction, context, {
860
+ ...options, runtimeSignal: this.#signal, accessPolicy: this.#accessPolicy,
861
+ });
862
+ }
863
+
858
864
  async #handleInteraction(interaction, { key, actor }) {
859
865
  if (interaction?.kind === 'approval') {
860
866
  return this.#approvals.handleRequested(interaction, {
@@ -290,6 +290,20 @@ export class WecomAppController {
290
290
  });
291
291
  }
292
292
 
293
+ async presentSessionSyncApproval(botId, target, interaction, options = {}) {
294
+ const config = this.#configStore.get(botId);
295
+ if (!config) throw new Error('Unknown Enterprise WeChat app bot');
296
+ return this.#withBotTransition(botId, async () => {
297
+ const runtime = this.#runtimes.get(botId);
298
+ if (!runtime?.status?.ready || typeof runtime.presentSessionSyncApproval !== 'function') {
299
+ const error = new Error(t('企业微信应用当前离线'));
300
+ error.code = 'bot-not-connected';
301
+ throw error;
302
+ }
303
+ return runtime.presentSessionSyncApproval(target, interaction, options);
304
+ });
305
+ }
306
+
293
307
  async deleteBot(botId) {
294
308
  const warnings = [];
295
309
  const config = this.#configStore.get(botId);
@@ -193,6 +193,16 @@ export class WecomAppRuntime {
193
193
  });
194
194
  }
195
195
 
196
+ async presentSessionSyncApproval(target, interaction, options = {}) {
197
+ if (!this.#started || !this.#bridge) return false;
198
+ const actor = typeof target?.route?.chatId === 'string' ? target.route.chatId.trim() : '';
199
+ if (!actor) return false;
200
+ return this.#bridge.presentSessionSyncApproval(interaction, {
201
+ key: options.key, actor, validate: options.validate,
202
+ send: (text) => this.sendProactiveText(target, text),
203
+ }, options);
204
+ }
205
+
196
206
  async sendProactiveText(target, text, { signal } = {}) {
197
207
  const chatId = typeof target?.route?.chatId === 'string' ? target.route.chatId.trim() : '';
198
208
  if ((target?.kind !== 'user') || !chatId) {
@@ -1102,6 +1102,12 @@ export class WeixinHarnessBridge {
1102
1102
  }
1103
1103
  }
1104
1104
 
1105
+ async presentSessionSyncApproval(interaction, context, options = {}) {
1106
+ return this.#approvals.handleSessionSyncRequested(interaction, context, {
1107
+ ...options, runtimeSignal: this.#signal, accessPolicy: this.#accessPolicy,
1108
+ });
1109
+ }
1110
+
1105
1111
  async #handleInteraction(interaction, {
1106
1112
  key,
1107
1113
  actor,
@@ -289,6 +289,20 @@ export class WeixinController {
289
289
  });
290
290
  }
291
291
 
292
+ async presentSessionSyncApproval(botId, target, interaction, options = {}) {
293
+ const config = this.#configStore.get(botId);
294
+ if (!config) throw new Error('Unknown Weixin account');
295
+ return this.#withBotTransition(botId, async () => {
296
+ const runtime = this.#runtimes.get(botId);
297
+ if (!runtime?.status?.ready || typeof runtime.presentSessionSyncApproval !== 'function') {
298
+ const error = new Error(t('微信连接当前离线'));
299
+ error.code = 'bot-not-connected';
300
+ throw error;
301
+ }
302
+ return runtime.presentSessionSyncApproval(target, interaction, options);
303
+ });
304
+ }
305
+
292
306
  async deleteBot(botId) {
293
307
  const config = this.#configStore.get(botId);
294
308
  if (!config) throw weixinStageError('workspace-bot-not-found', undefined, 'account.remove');
@@ -392,6 +392,16 @@ export class WeixinRuntime {
392
392
  return result;
393
393
  }
394
394
 
395
+ async presentSessionSyncApproval(target, interaction, options = {}) {
396
+ if (!this.#status.ready || !this.#bridge) return false;
397
+ const actor = typeof target?.route?.toUserId === 'string' ? target.route.toUserId.trim() : '';
398
+ if (!actor) return false;
399
+ return this.#bridge.presentSessionSyncApproval(interaction, {
400
+ key: options.key, actor, validate: options.validate,
401
+ send: (text) => this.sendProactiveText(target, text),
402
+ }, options);
403
+ }
404
+
395
405
  async sendProactiveText(target, text, { signal } = {}) {
396
406
  const toUserId = typeof target?.route?.toUserId === 'string'
397
407
  ? target.route.toUserId.trim() : '';
@@ -248,6 +248,20 @@ export class WhatsappController {
248
248
  });
249
249
  }
250
250
 
251
+ async presentSessionSyncApproval(botId, target, interaction, options = {}) {
252
+ const config = this.#configStore.get(botId);
253
+ if (!config) throw new Error('Unknown WhatsApp bot');
254
+ return this.#withBotTransition(botId, async () => {
255
+ const runtime = this.#runtimes.get(botId);
256
+ if (!runtime?.status?.ready || typeof runtime.presentSessionSyncApproval !== 'function') {
257
+ const error = new Error(t('WhatsApp机器人尚未连接'));
258
+ error.code = 'bot-not-connected';
259
+ throw error;
260
+ }
261
+ return runtime.presentSessionSyncApproval(target, interaction, options);
262
+ });
263
+ }
264
+
251
265
  async setAccessPolicy(botId, value) {
252
266
  if (this.#closed) throw new Error('WhatsApp controller is closed');
253
267
  const accessPolicy = normalizeWhatsappAccessPolicy(value);
@@ -876,6 +876,16 @@ export class WhatsappRuntime {
876
876
  return { sent: true };
877
877
  }
878
878
 
879
+ async presentSessionSyncApproval(target, interaction, options = {}) {
880
+ if (!this.#status.ready || !this.#bridge) return false;
881
+ const actor = typeof target?.route?.jid === 'string' ? target.route.jid.trim() : '';
882
+ if (!actor) return false;
883
+ return this.#bridge.presentSessionSyncApproval(interaction, {
884
+ key: options.key, actor, validate: options.validate,
885
+ send: (text) => this.sendProactiveText(target, text),
886
+ }, options);
887
+ }
888
+
879
889
  async sendProactiveText(target, text, { signal } = {}) {
880
890
  const jid = typeof target?.route?.jid === 'string' ? target.route.jid.trim() : '';
881
891
  const validUser = target?.kind === 'user'