@xmanrui/dsh-im 4.18.1 → 4.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +1 -0
- package/README.md +1 -0
- package/lib/client.js +917 -521
- package/lib/index.js +269 -267
- package/package.json +1 -1
- package/plugin-src/client/bot-alias.js +169 -0
- package/plugin-src/client/channels/dingtalk/api.js +3 -0
- package/plugin-src/client/channels/dingtalk/index.js +7 -1
- package/plugin-src/client/channels/feishu/api.js +3 -0
- package/plugin-src/client/channels/feishu/index.js +7 -1
- package/plugin-src/client/channels/qq/api.js +3 -0
- package/plugin-src/client/channels/qq/index.js +9 -1
- package/plugin-src/client/channels/shared/token-api.js +3 -0
- package/plugin-src/client/channels/shared/token-channel.js +9 -2
- package/plugin-src/client/channels/wecom/api.js +3 -0
- package/plugin-src/client/channels/wecom/index.js +9 -1
- package/plugin-src/client/channels/wecom-app/api.js +3 -0
- package/plugin-src/client/channels/wecom-app/index.js +9 -1
- package/plugin-src/client/channels/weixin/api.js +3 -0
- package/plugin-src/client/channels/weixin/index.js +7 -1
- package/plugin-src/client/channels/whatsapp/api.js +3 -0
- package/plugin-src/client/channels/whatsapp/index.js +9 -1
- package/plugin-src/client/i18n.js +10 -0
- package/plugin-src/client/styles.js +41 -6
- package/plugin-src/host/channels/dingtalk/rpc.mjs +11 -0
- package/plugin-src/host/channels/feishu/production.mjs +22 -0
- package/plugin-src/host/channels/feishu/rpc.mjs +14 -0
- package/plugin-src/host/channels/imessage/rpc.mjs +1 -0
- package/plugin-src/host/channels/qq/rpc.mjs +11 -0
- package/plugin-src/host/channels/shared/bot-alias-rpc.mjs +15 -0
- package/plugin-src/host/channels/shared/rpc.mjs +9 -0
- package/plugin-src/host/channels/slack/rpc.mjs +10 -0
- package/plugin-src/host/channels/wecom/rpc.mjs +11 -0
- package/plugin-src/host/channels/wecom-app/rpc.mjs +10 -0
- package/plugin-src/host/channels/weixin/rpc.mjs +11 -0
- package/plugin-src/host/channels/whatsapp/rpc.mjs +13 -0
- package/plugin-src/host/session-sync-coordinator.mjs +20 -1
- package/src/channels/feishu/bridge.mjs +330 -10
- package/src/channels/feishu/feishu-cards.mjs +1 -1
- package/src/channels/feishu/feishu-runtime.mjs +6 -0
- package/src/channels/feishu/state-store.mjs +17 -0
- package/src/channels/shared/bot-alias.mjs +29 -0
- package/src/channels/shared/bot-workspace-store.mjs +71 -3
- package/src/channels/shared/i18n-en/shared-a.mjs +2 -2
- package/src/channels/shared/message-failure.mjs +2 -1
- package/src/channels/shared/model-command.mjs +3 -2
- package/src/channels/shared/session-sync-registry.mjs +22 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { validateBotAlias, withBotAlias } from './bot-alias.mjs';
|
|
1
2
|
import {
|
|
2
3
|
mkdir,
|
|
3
4
|
readFile,
|
|
@@ -264,6 +265,16 @@ function normalizeDocument(value) {
|
|
|
264
265
|
if (value.version === 1 && value.deliveryTargets !== undefined) return null;
|
|
265
266
|
const deliveryTargets = normalizeDeliveryTargets(value.deliveryTargets, { version: value.version });
|
|
266
267
|
if (!deliveryTargets) return null;
|
|
268
|
+
const aliases = Object.create(null);
|
|
269
|
+
if (value.aliases && typeof value.aliases === 'object' && !Array.isArray(value.aliases)) {
|
|
270
|
+
for (const [botId, alias] of Object.entries(value.aliases)) {
|
|
271
|
+
try {
|
|
272
|
+
botIdOf(botId);
|
|
273
|
+
const normalized = validateBotAlias(alias);
|
|
274
|
+
if (normalized) aliases[botId] = normalized;
|
|
275
|
+
} catch { /* A damaged display name must not disable the bot. */ }
|
|
276
|
+
}
|
|
277
|
+
}
|
|
267
278
|
const accessPolicies = normalizeAccessPolicies(value.accessPolicies, workspaces);
|
|
268
279
|
const version = Math.max(
|
|
269
280
|
value.version,
|
|
@@ -279,6 +290,7 @@ function normalizeDocument(value) {
|
|
|
279
290
|
contextEnhancement,
|
|
280
291
|
deliveryTargets,
|
|
281
292
|
accessPolicies,
|
|
293
|
+
aliases,
|
|
282
294
|
};
|
|
283
295
|
}
|
|
284
296
|
|
|
@@ -290,8 +302,10 @@ function storedDocument({
|
|
|
290
302
|
contextEnhancement,
|
|
291
303
|
deliveryTargets,
|
|
292
304
|
accessPolicies,
|
|
305
|
+
aliases,
|
|
293
306
|
}) {
|
|
294
307
|
const document = { version, workspaces };
|
|
308
|
+
if (Object.keys(aliases).length > 0) document.aliases = aliases;
|
|
295
309
|
if (Object.keys(agentPresets).length > 0) document.agentPresets = agentPresets;
|
|
296
310
|
if (Object.keys(models).length > 0) document.models = models;
|
|
297
311
|
if (Object.keys(contextEnhancement).length > 0) {
|
|
@@ -346,6 +360,7 @@ export class BotWorkspaceStore {
|
|
|
346
360
|
#workspaces = {};
|
|
347
361
|
#agentPresets = {};
|
|
348
362
|
#models = {};
|
|
363
|
+
#aliases = Object.create(null);
|
|
349
364
|
#contextEnhancement = {};
|
|
350
365
|
#deliveryTargets = Object.create(null);
|
|
351
366
|
#accessPolicies = Object.create(null);
|
|
@@ -373,6 +388,7 @@ export class BotWorkspaceStore {
|
|
|
373
388
|
this.#workspaces = normalized.workspaces;
|
|
374
389
|
this.#agentPresets = normalized.agentPresets;
|
|
375
390
|
this.#models = normalized.models;
|
|
391
|
+
this.#aliases = normalized.aliases;
|
|
376
392
|
this.#contextEnhancement = normalized.contextEnhancement;
|
|
377
393
|
this.#deliveryTargets = normalized.deliveryTargets;
|
|
378
394
|
this.#accessPolicies = normalized.accessPolicies;
|
|
@@ -382,6 +398,7 @@ export class BotWorkspaceStore {
|
|
|
382
398
|
this.#workspaces = {};
|
|
383
399
|
this.#agentPresets = {};
|
|
384
400
|
this.#models = {};
|
|
401
|
+
this.#aliases = Object.create(null);
|
|
385
402
|
this.#contextEnhancement = {};
|
|
386
403
|
this.#deliveryTargets = Object.create(null);
|
|
387
404
|
this.#accessPolicies = Object.create(null);
|
|
@@ -427,6 +444,11 @@ export class BotWorkspaceStore {
|
|
|
427
444
|
return selection ? { ...selection } : null;
|
|
428
445
|
}
|
|
429
446
|
|
|
447
|
+
aliasFor(botId) {
|
|
448
|
+
const id = botIdOf(botId);
|
|
449
|
+
return this.has(id) && Object.hasOwn(this.#aliases, id) ? this.#aliases[id] : '';
|
|
450
|
+
}
|
|
451
|
+
|
|
430
452
|
contextEnhancementFor(botId) {
|
|
431
453
|
const id = botIdOf(botId);
|
|
432
454
|
return this.has(id) && Object.hasOwn(this.#contextEnhancement, id)
|
|
@@ -745,6 +767,26 @@ export class BotWorkspaceStore {
|
|
|
745
767
|
});
|
|
746
768
|
}
|
|
747
769
|
|
|
770
|
+
async setAlias(botId, value, { incarnation } = {}) {
|
|
771
|
+
const id = botIdOf(botId);
|
|
772
|
+
const expectedIncarnation = incarnation === undefined ? this.incarnationFor(id) : incarnation;
|
|
773
|
+
const alias = validateBotAlias(value);
|
|
774
|
+
return this.#enqueue(id, async () => {
|
|
775
|
+
if (!this.has(id) || expectedIncarnation !== this.incarnationFor(id)) {
|
|
776
|
+
const error = new Error('找不到要修改的机器人。');
|
|
777
|
+
error.code = 'workspace-bot-not-found';
|
|
778
|
+
throw error;
|
|
779
|
+
}
|
|
780
|
+
const next = { ...this.#aliases };
|
|
781
|
+
if (alias) next[id] = alias;
|
|
782
|
+
else delete next[id];
|
|
783
|
+
await this.#persist(this.#contextEnhancement, this.#deliveryTargets,
|
|
784
|
+
this.#version, this.#accessPolicies, next);
|
|
785
|
+
this.#aliases = next;
|
|
786
|
+
return alias;
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
|
|
748
790
|
async setContextEnhancement(botId, value, { incarnation } = {}) {
|
|
749
791
|
const id = botIdOf(botId);
|
|
750
792
|
const expectedIncarnation = incarnation === undefined ? this.incarnationFor(id) : incarnation;
|
|
@@ -945,6 +987,7 @@ export class BotWorkspaceStore {
|
|
|
945
987
|
...Object.keys(this.#workspaces),
|
|
946
988
|
...Object.keys(this.#agentPresets),
|
|
947
989
|
...Object.keys(this.#models),
|
|
990
|
+
...Object.keys(this.#aliases),
|
|
948
991
|
...Object.keys(this.#contextEnhancement),
|
|
949
992
|
...Object.keys(this.#deliveryTargets),
|
|
950
993
|
...Object.keys(this.#accessPolicies),
|
|
@@ -962,6 +1005,7 @@ export class BotWorkspaceStore {
|
|
|
962
1005
|
bots: status.bots.map((bot) => bot?.botId
|
|
963
1006
|
? {
|
|
964
1007
|
...bot,
|
|
1008
|
+
...(this.aliasFor(bot.botId) ? { bot: withBotAlias(bot.bot, this.aliasFor(bot.botId)) } : {}),
|
|
965
1009
|
workspace: this.workspaceFor(bot.botId),
|
|
966
1010
|
agentPreset: this.agentPresetFor(bot.botId),
|
|
967
1011
|
model: this.modelFor(bot.botId),
|
|
@@ -997,14 +1041,16 @@ export class BotWorkspaceStore {
|
|
|
997
1041
|
const hadWorkspace = Object.hasOwn(this.#workspaces, id);
|
|
998
1042
|
const hadPreset = Object.hasOwn(this.#agentPresets, id);
|
|
999
1043
|
const hadModel = Object.hasOwn(this.#models, id);
|
|
1044
|
+
const hadAlias = Object.hasOwn(this.#aliases, id);
|
|
1000
1045
|
const hadContextEnhancement = Object.hasOwn(this.#contextEnhancement, id);
|
|
1001
1046
|
const hadDeliveryTargets = Object.hasOwn(this.#deliveryTargets, id);
|
|
1002
1047
|
const hadAccessPolicy = Object.hasOwn(this.#accessPolicies, id);
|
|
1003
|
-
const needsCleanup = hadWorkspace || hadPreset || hadModel || hadContextEnhancement
|
|
1048
|
+
const needsCleanup = hadWorkspace || hadPreset || hadModel || hadAlias || hadContextEnhancement
|
|
1004
1049
|
|| hadDeliveryTargets || hadAccessPolicy || this.#dirtyRemovals.has(id);
|
|
1005
1050
|
delete this.#workspaces[id];
|
|
1006
1051
|
delete this.#agentPresets[id];
|
|
1007
1052
|
delete this.#models[id];
|
|
1053
|
+
delete this.#aliases[id];
|
|
1008
1054
|
delete this.#contextEnhancement[id];
|
|
1009
1055
|
delete this.#deliveryTargets[id];
|
|
1010
1056
|
delete this.#accessPolicies[id];
|
|
@@ -1042,6 +1088,7 @@ export class BotWorkspaceStore {
|
|
|
1042
1088
|
deliveryTargets = this.#deliveryTargets,
|
|
1043
1089
|
version = this.#version,
|
|
1044
1090
|
accessPolicies = this.#accessPolicies,
|
|
1091
|
+
aliases = this.#aliases,
|
|
1045
1092
|
) {
|
|
1046
1093
|
await writeStoredDocument(this.#path, storedDocument({
|
|
1047
1094
|
version,
|
|
@@ -1051,6 +1098,7 @@ export class BotWorkspaceStore {
|
|
|
1051
1098
|
contextEnhancement,
|
|
1052
1099
|
deliveryTargets,
|
|
1053
1100
|
accessPolicies,
|
|
1101
|
+
aliases,
|
|
1054
1102
|
}));
|
|
1055
1103
|
this.#dirtyRemovals.clear();
|
|
1056
1104
|
}
|
|
@@ -1059,6 +1107,7 @@ export class BotWorkspaceStore {
|
|
|
1059
1107
|
if (Object.keys(this.#workspaces).length > 0
|
|
1060
1108
|
|| Object.keys(this.#agentPresets).length > 0
|
|
1061
1109
|
|| Object.keys(this.#models).length > 0
|
|
1110
|
+
|| Object.keys(this.#aliases).length > 0
|
|
1062
1111
|
|| Object.keys(this.#contextEnhancement).length > 0
|
|
1063
1112
|
|| Object.keys(this.#deliveryTargets).length > 0
|
|
1064
1113
|
|| Object.keys(this.#accessPolicies).length > 0) {
|
|
@@ -1350,6 +1399,7 @@ export function createBotWorkspaceScope(
|
|
|
1350
1399
|
}
|
|
1351
1400
|
if (property === 'createSession') {
|
|
1352
1401
|
return async (options = {}) => {
|
|
1402
|
+
const { inheritBotModel = true, ...createOptions } = options;
|
|
1353
1403
|
await workspaces.whenBotIdle(botId);
|
|
1354
1404
|
if (!isCurrentScope()) {
|
|
1355
1405
|
const error = new Error('找不到要修改的机器人。');
|
|
@@ -1358,9 +1408,9 @@ export function createBotWorkspaceScope(
|
|
|
1358
1408
|
}
|
|
1359
1409
|
const generation = workspaces.generationFor(botId);
|
|
1360
1410
|
const agentPreset = workspaces.agentPresetFor(botId);
|
|
1361
|
-
const model = workspaces.modelFor(botId);
|
|
1411
|
+
const model = inheritBotModel === false ? null : workspaces.modelFor(botId);
|
|
1362
1412
|
const sessionId = await target.createSession({
|
|
1363
|
-
...
|
|
1413
|
+
...createOptions,
|
|
1364
1414
|
workspace: workspaces.workspaceFor(botId),
|
|
1365
1415
|
...(agentPreset == null ? {} : { agentPreset }),
|
|
1366
1416
|
});
|
|
@@ -1645,6 +1695,23 @@ export function createWorkspaceAwareController(controller, {
|
|
|
1645
1695
|
);
|
|
1646
1696
|
});
|
|
1647
1697
|
};
|
|
1698
|
+
const updateAlias = (botId, value, projectStatus) => {
|
|
1699
|
+
const incarnation = workspaces.incarnationFor(botId);
|
|
1700
|
+
const alias = validateBotAlias(value);
|
|
1701
|
+
return withBotTransition(botId, async () => {
|
|
1702
|
+
const snapshot = await decorate(await controller.status());
|
|
1703
|
+
if (!snapshot?.bots?.some((bot) => bot?.botId === botId)) {
|
|
1704
|
+
const error = new Error('找不到要修改的机器人。');
|
|
1705
|
+
error.code = 'workspace-bot-not-found';
|
|
1706
|
+
throw error;
|
|
1707
|
+
}
|
|
1708
|
+
const updated = { ...snapshot, bots: snapshot.bots.map((bot) => bot.botId === botId
|
|
1709
|
+
? { ...bot, bot: withBotAlias(bot.bot, alias) } : bot) };
|
|
1710
|
+
const result = projectStatus ? await projectStatus(updated) : updated;
|
|
1711
|
+
await workspaces.setAlias(botId, alias, { incarnation });
|
|
1712
|
+
return result;
|
|
1713
|
+
});
|
|
1714
|
+
};
|
|
1648
1715
|
const updateContextEnhancement = (botId, value, projectStatus) => {
|
|
1649
1716
|
const incarnation = workspaces.incarnationFor(botId);
|
|
1650
1717
|
const config = validateContextEnhancementConfig(value);
|
|
@@ -1743,6 +1810,7 @@ export function createWorkspaceAwareController(controller, {
|
|
|
1743
1810
|
if (property === 'updateWorkspace') return updateWorkspace;
|
|
1744
1811
|
if (property === 'updateAgentPreset') return updateAgentPreset;
|
|
1745
1812
|
if (property === 'updateModel') return updateModel;
|
|
1813
|
+
if (property === 'updateAlias') return updateAlias;
|
|
1746
1814
|
if (property === 'updateContextEnhancement') return updateContextEnhancement;
|
|
1747
1815
|
if (property === 'updateAccessPolicy') return updateAccessPolicy;
|
|
1748
1816
|
const value = Reflect.get(target, property, target);
|
|
@@ -57,8 +57,8 @@ export default {
|
|
|
57
57
|
'This conversation exceeds the model context limit. Send /compact or /new, then try again.',
|
|
58
58
|
'当前模型不存在或暂不可用。请发送 /models 查看并使用 /model 切换模型。':
|
|
59
59
|
'The current model does not exist or is unavailable. Send /models and use /model to switch models.',
|
|
60
|
-
'当前模型不存在或不支持所选配置。请发送 /models
|
|
61
|
-
'The current model does not exist or does not support the selected settings. Send /models
|
|
60
|
+
'当前模型不存在或不支持所选配置。请发送 /models,再用 /model <序号> 为当前聊天重新选择。若要修改后续新会话的默认模型,请到 DSH 设置 → IM机器人 → 对应机器人卡片修改。':
|
|
61
|
+
'The current model does not exist or does not support the selected settings. Send /models, then use /model <index> to choose a model for the current conversation. To change the default model for new conversations, open DSH Settings → IM Bots → the corresponding bot card.',
|
|
62
62
|
'当前模型不支持这类内容或所选配置。请调整内容、模型或推理等级后重试。':
|
|
63
63
|
'The current model does not support this content or the selected settings. Adjust the content, model, or reasoning effort and try again.',
|
|
64
64
|
'当前模型不支持所选配置。请切换模型或推理等级后重试。':
|
|
@@ -47,7 +47,7 @@ const FAILURE_MESSAGES = Object.freeze({
|
|
|
47
47
|
MODEL_CONTEXT_LIMIT:
|
|
48
48
|
'当前会话内容超过模型上下文上限。请发送 /compact 或 /new 后重试。',
|
|
49
49
|
MODEL_UNAVAILABLE:
|
|
50
|
-
'当前模型不存在或不支持所选配置。请发送 /models
|
|
50
|
+
'当前模型不存在或不支持所选配置。请发送 /models,再用 /model <序号> 为当前聊天重新选择。若要修改后续新会话的默认模型,请到 DSH 设置 → IM机器人 → 对应机器人卡片修改。',
|
|
51
51
|
MODEL_CONFIG:
|
|
52
52
|
'当前模型不支持这类内容或所选配置。请调整内容、模型或推理等级后重试。',
|
|
53
53
|
MODEL_TIMEOUT:
|
|
@@ -125,6 +125,7 @@ function failureCode(error) {
|
|
|
125
125
|
}
|
|
126
126
|
if (code === 'harness-turn-failed') return 'INTERNAL_UNKNOWN';
|
|
127
127
|
if (['harness-http-failed', 'harness-rpc-rejected'].includes(code)) return 'HARNESS_SERVICE';
|
|
128
|
+
if (code === 'model-unavailable' || code === 'session/model-unavailable') return 'MODEL_UNAVAILABLE';
|
|
128
129
|
if (code === 'model-empty-response') return 'MODEL_EMPTY_REPLY';
|
|
129
130
|
if (code === 'model-max-tokens') return 'MODEL_OUTPUT_LIMIT';
|
|
130
131
|
if (code === 'turn-blocked') return 'TURN_BLOCKED';
|
|
@@ -393,7 +393,7 @@ function modelErrorMessage(error, action) {
|
|
|
393
393
|
if (code === 'session-not-found') {
|
|
394
394
|
return t('当前聊天绑定的会话已不存在,请重试。');
|
|
395
395
|
}
|
|
396
|
-
if (code === 'model-unavailable') {
|
|
396
|
+
if (code === 'model-unavailable' || code === 'session/model-unavailable') {
|
|
397
397
|
if (action === 'reasoning-select') {
|
|
398
398
|
return t('无法切换推理等级。当前模型或推理等级不可用。');
|
|
399
399
|
}
|
|
@@ -704,7 +704,8 @@ export async function runModelCommand(text, harness, state, key, options = {}) {
|
|
|
704
704
|
|| typeof state?.setSession !== 'function') {
|
|
705
705
|
throw new TypeError('Harness cannot create a conversation session');
|
|
706
706
|
}
|
|
707
|
-
|
|
707
|
+
// An explicit choice must remain usable even when the saved bot model expires.
|
|
708
|
+
const sessionId = await harness.createSession({ ...requestOptions, inheritBotModel: false });
|
|
708
709
|
if (typeof sessionId !== 'string' || !sessionId) {
|
|
709
710
|
throw new TypeError('Harness returned an invalid session id');
|
|
710
711
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Optional per-bot card delivery. A registration is not a delivery receipt:
|
|
2
|
+
* the coordinator must await the renderer before suppressing final text. */
|
|
3
|
+
const renderers = new Map();
|
|
4
|
+
|
|
5
|
+
function botKey({ channel, botId }) {
|
|
6
|
+
return JSON.stringify([channel, botId]);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function registerSessionSyncMirror(target, render) {
|
|
10
|
+
const key = botKey(target);
|
|
11
|
+
const entry = { render };
|
|
12
|
+
renderers.set(key, entry);
|
|
13
|
+
return () => {
|
|
14
|
+
if (renderers.get(key) === entry) renderers.delete(key);
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function deliverSessionSyncMirror(target, sessionId, turn, text) {
|
|
19
|
+
const entry = renderers.get(botKey(target));
|
|
20
|
+
if (!entry) return false;
|
|
21
|
+
return await entry.render({ target, sessionId, turn, text }) === true;
|
|
22
|
+
}
|