@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
|
@@ -4,6 +4,16 @@ export const IM_LOCALE_NAMESPACE = 'dsh-im';
|
|
|
4
4
|
|
|
5
5
|
const EN = Object.freeze({
|
|
6
6
|
'$locale': 'en',
|
|
7
|
+
'修改别名': 'Edit alias',
|
|
8
|
+
'关闭修改别名': 'Close alias editor',
|
|
9
|
+
'原名称': 'Original name',
|
|
10
|
+
'别名': 'Alias',
|
|
11
|
+
'恢复原名称': 'Restore original name',
|
|
12
|
+
'例如:客服助手': 'e.g. Customer support',
|
|
13
|
+
'仅更改显示名称,留空则显示原名称。': 'Only changes the display name. Leave blank to use the original name.',
|
|
14
|
+
'别名保存失败,请重试。': 'Could not save the alias. Try again.',
|
|
15
|
+
'别名不能包含换行或控制字符,且最多 80 个字符。': 'Use at most 80 characters without line breaks or control characters.',
|
|
16
|
+
'请输入有效的别名(最多 80 个字符)。': 'Enter a valid alias (up to 80 characters).',
|
|
7
17
|
' macOS Messages 连接': ' macOS Messages connection',
|
|
8
18
|
'接入 iMessage': 'Connect iMessage',
|
|
9
19
|
'先在 macOS 系统设置中授予 Messages 权限。': 'Grant Messages permissions in macOS System Settings first.',
|
|
@@ -1,6 +1,38 @@
|
|
|
1
1
|
export const IM_STYLE_ID = 'xmanrui-dsh-im-settings';
|
|
2
2
|
|
|
3
3
|
const CSS = String.raw`
|
|
4
|
+
.dim-aliasName { display: flex; align-items: center; gap: 4px; min-width: 0; }
|
|
5
|
+
.dim-aliasName h3 { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
6
|
+
.dim-aliasName h3:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: 2px; border-radius: 3px; }
|
|
7
|
+
.dim-botNameTooltip { position: fixed; z-index: 1000; box-sizing: border-box; width: max-content; max-width: min(320px, calc(100vw - 16px)); padding: 6px 9px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 7px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-3, #fff); box-shadow: 0 8px 24px rgb(31 35 41 / 14%); font: 500 12px/18px -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; white-space: normal; overflow-wrap: anywhere; pointer-events: none; animation: dim-botNameTooltip-in .15s ease; }
|
|
8
|
+
@keyframes dim-botNameTooltip-in { from { opacity: 0; } to { opacity: 1; } }
|
|
9
|
+
@media (prefers-reduced-motion: reduce) { .dim-botNameTooltip { animation: none; } }
|
|
10
|
+
.dim-aliasEntry { display: inline-flex; flex: none; }
|
|
11
|
+
.dim-aliasEdit { display: grid; place-items: center; width: 28px; height: 28px; padding: 4px; border: 0; border-radius: 5px; color: var(--dsw-alias-label-tertiary, #8f959e); background: transparent; cursor: pointer; }
|
|
12
|
+
.dim-aliasEdit svg { opacity: .55; transition: opacity .15s ease; }
|
|
13
|
+
.dim-aliasName:hover .dim-aliasEdit:not(:disabled) svg, .dim-aliasEdit:focus-visible svg { opacity: 1; }
|
|
14
|
+
.dim-aliasEdit:hover:not(:disabled), .dim-aliasEdit:focus-visible { color: var(--dsw-alias-state-business-primary, #3370ff); background: var(--dsw-alias-interactive-bg-hover, #f7f8fa); }
|
|
15
|
+
.dim-aliasDialog { box-sizing: border-box; width: min(380px, calc(100% - 32px)); max-height: calc(100dvh - 32px); overflow-y: auto; padding: 22px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 12px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-3, #fff); box-shadow: 0 12px 36px rgb(0 0 0 / 18%); font: 13px/1.5 -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; }
|
|
16
|
+
.dim-aliasDialog * { box-sizing: border-box; }
|
|
17
|
+
.dim-aliasDialog::backdrop { background: rgb(15 17 21 / 30%); }
|
|
18
|
+
.dim-aliasHeader { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 18px; }
|
|
19
|
+
.dim-aliasHeader h3 { margin: 0; font-size: 16px; }
|
|
20
|
+
.dim-aliasDialog button { font: inherit; cursor: pointer; }
|
|
21
|
+
.dim-aliasDialog .dim-aliasClose { width: 28px; height: 28px; padding: 0; border: 0; border-radius: 5px; background: transparent; color: var(--dsw-alias-label-secondary, #646a73); font-size: 20px; }
|
|
22
|
+
.dim-aliasOriginal { display: flex; flex-wrap: wrap; gap: 6px 14px; padding: 10px 12px; margin-bottom: 18px; border-radius: 6px; background: var(--dsw-alias-bg-layer-2, #f5f6f7); overflow-wrap: anywhere; }
|
|
23
|
+
.dim-aliasOriginal > span:first-child { flex: none; color: var(--dsw-alias-label-secondary, #646a73); }
|
|
24
|
+
.dim-aliasDialog label { display: block; margin-bottom: 7px; }
|
|
25
|
+
.dim-aliasDialog input { width: 100%; min-height: 38px; padding: 8px 10px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 6px; color: inherit; background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; }
|
|
26
|
+
.dim-aliasHelp { margin: 8px 0 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; }
|
|
27
|
+
.dim-aliasError { color: var(--dsw-alias-state-danger-primary, #c53030); overflow-wrap: anywhere; }
|
|
28
|
+
.dim-aliasFooter { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 14px; margin-top: 24px; }
|
|
29
|
+
.dim-aliasRestore { padding: 4px 0; border: 0; color: var(--dsw-alias-state-business-primary, #3370ff); background: transparent; }
|
|
30
|
+
.dim-aliasActions { display: flex; gap: 8px; margin-left: auto; }
|
|
31
|
+
.dim-aliasActions button { padding: 7px 14px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 6px; color: inherit; background: var(--dsw-alias-bg-layer-1, #fff); }
|
|
32
|
+
.dim-aliasActions .dim-aliasSave { color: #fff; border-color: var(--dsw-alias-state-business-primary, #3370ff); background: var(--dsw-alias-state-business-primary, #3370ff); }
|
|
33
|
+
.dim-aliasEdit:disabled, .dim-aliasDialog button:disabled, .dim-aliasDialog input:disabled { opacity: .55; cursor: not-allowed; }
|
|
34
|
+
.dim-aliasEdit:focus-visible, .dim-aliasDialog button:focus-visible, .dim-aliasDialog input:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: 2px; }
|
|
35
|
+
@media (pointer: coarse) { .dim-aliasEdit, .dim-aliasDialog button { min-width: 44px; min-height: 44px; } .dim-aliasDialog input { font-size: 16px; } }
|
|
4
36
|
.dim-page {
|
|
5
37
|
--dim-blue: var(--dsw-alias-state-business-primary, #3370ff);
|
|
6
38
|
--dim-blue-soft: color-mix(in srgb, var(--dim-blue) 9%, transparent);
|
|
@@ -375,9 +407,9 @@ const CSS = String.raw`
|
|
|
375
407
|
/* Header tooltips may extend beyond the card; the collapsible body clips its own content. */
|
|
376
408
|
.dim-panel .dim-botCard { position: relative; min-width: 0; width: 100%; max-width: 100%; overflow: visible; border: 1px solid var(--dsw-alias-border-l2, #e5e6eb); border-radius: 14px; background: var(--dsw-alias-bg-layer-1, #fff); box-shadow: 0 1px 2px rgb(31 35 41 / 3%); }
|
|
377
409
|
.dim-panel .dim-botCard::before { display: none; }
|
|
378
|
-
.dim-panel .dim-botCardBody { position: relative; min-width: 0; width: 100%; max-width: 100%; padding: 12px; }
|
|
410
|
+
.dim-panel .dim-botCardBody { position: relative; min-width: 0; width: 100%; max-width: 100%; padding: 12px 8px; }
|
|
379
411
|
.dim-collapsibleAccount { min-width: 0; display: flex; flex-direction: column; }
|
|
380
|
-
.dim-collapsibleHead { min-width: 0; display: flex; align-items: center; gap:
|
|
412
|
+
.dim-collapsibleHead { min-width: 0; display: flex; align-items: center; gap: 4px; cursor: pointer; user-select: none; -webkit-user-select: none; }
|
|
381
413
|
.dim-collapsibleHead:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: 2px; border-radius: 8px; }
|
|
382
414
|
.dim-collapsibleHeaderContent { min-width: 0; flex: 1 1 auto; display: flex; align-items: center; }
|
|
383
415
|
.dim-collapsibleChevron { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: 9px; height: 9px; border-right: 1.6px solid var(--dsw-alias-label-tertiary, #8f959e); border-bottom: 1.6px solid var(--dsw-alias-label-tertiary, #8f959e); transform: rotate(-45deg); transition: transform .22s cubic-bezier(.4, 0, .2, 1); transform-origin: 50% 50%; }
|
|
@@ -387,14 +419,17 @@ const CSS = String.raw`
|
|
|
387
419
|
.dim-collapsibleAccount.is-open > .dim-collapsibleBody { grid-template-rows: 1fr; }
|
|
388
420
|
.dim-collapsibleBodyInner { min-height: 0; overflow: hidden; }
|
|
389
421
|
.dim-collapsibleAccount:not(.is-open) .dim-collapsibleBodyInner { visibility: hidden; }
|
|
390
|
-
|
|
391
|
-
|
|
422
|
+
/* Reclaim horizontal spacing for names while keeping status on the same row,
|
|
423
|
+
including when a channel's mobile stylesheet requests a column layout. */
|
|
424
|
+
.dim-panel .dim-botCardTop { min-width: 0; width: 100%; max-width: 100%; display: flex; flex-direction: row; flex-wrap: nowrap; align-items: flex-start; justify-content: space-between; gap: 6px; }
|
|
425
|
+
.dim-panel .dim-botIdentity { min-width: 0; flex: 1 1 0; display: flex; align-items: center; gap: 6px; }
|
|
392
426
|
.dim-panel .dim-botAvatar { flex: none; width: 38px; height: 38px; display: grid; place-items: center; overflow: hidden; border-radius: 11px; box-shadow: none; }
|
|
393
427
|
.dim-panel .dim-botAvatar svg { width: 27px; height: 27px; }
|
|
394
|
-
.dim-panel .dim-botName { min-width: 0; }
|
|
428
|
+
.dim-panel .dim-botName { min-width: 0; flex: 1; }
|
|
429
|
+
.dim-panel .dim-aliasName { gap: 2px; }
|
|
395
430
|
.dim-panel .dim-botName h3 { overflow: hidden; margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 15px; font-weight: 650; line-height: normal; text-overflow: ellipsis; white-space: nowrap; }
|
|
396
431
|
.dim-panel .dim-botName p { overflow: hidden; margin: 4px 0 0; color: var(--dsw-alias-label-secondary, #646a73); font: 12px ui-monospace, SFMono-Regular, monospace; line-height: normal; text-overflow: ellipsis; white-space: nowrap; }
|
|
397
|
-
.dim-panel .dim-botCardTools { flex: none; display: flex; align-items: flex-start; gap:
|
|
432
|
+
.dim-panel .dim-botCardTools { flex: none; display: flex; align-items: flex-start; gap: 4px; }
|
|
398
433
|
.dim-panel .dim-botHealthGroup { min-width: 0; max-width: 100%; flex: none; display: grid; justify-items: end; gap: 5px; }
|
|
399
434
|
.dim-panel .dim-botCard .dim-botHealth { flex: none; min-height: 0; display: inline-flex; align-items: center; gap: 7px; padding: 0; border: 0; border-radius: 0; color: var(--dsw-alias-label-secondary, #646a73); background: transparent; font: inherit; font-size: 12px; font-weight: 400; line-height: normal; white-space: nowrap; }
|
|
400
435
|
.dim-panel .dim-lastChecked { display: inline-flex; align-items: baseline; gap: 4px; color: var(--dsw-alias-label-tertiary, #8f959e); font: inherit; font-size: 11px; font-weight: 400; line-height: normal; white-space: nowrap; }
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SET_ALIAS_ENDPOINT, validAliasPayload } from '../shared/bot-alias-rpc.mjs';
|
|
1
2
|
import { registerManagementRpc } from '../../../management-rpc.mjs';
|
|
2
3
|
import QRCode from 'qrcode';
|
|
3
4
|
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
|
|
@@ -25,6 +26,7 @@ export const DINGTALK_ENDPOINTS = Object.freeze({
|
|
|
25
26
|
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
26
27
|
setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
|
|
27
28
|
setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
|
|
29
|
+
setAlias: SET_ALIAS_ENDPOINT,
|
|
28
30
|
approveSender: 'bot.sender.approve',
|
|
29
31
|
revokeSender: 'bot.sender.revoke',
|
|
30
32
|
});
|
|
@@ -112,6 +114,10 @@ function payloadFailure(endpoint, payload) {
|
|
|
112
114
|
return validAccessPolicyPayload(payload)
|
|
113
115
|
? null : '请提交有效的访问设置。';
|
|
114
116
|
}
|
|
117
|
+
if (endpoint === DINGTALK_ENDPOINTS.setAlias) {
|
|
118
|
+
return validAliasPayload(payload)
|
|
119
|
+
? null : '请输入有效的别名(最多 80 个字符)。';
|
|
120
|
+
}
|
|
115
121
|
if (endpoint === DINGTALK_ENDPOINTS.approveSender) {
|
|
116
122
|
return exactKeys(payload, ['botId', 'requestId', 'confirm'])
|
|
117
123
|
&& validId(payload.botId)
|
|
@@ -301,6 +307,11 @@ export function createDingtalkRpcHandler(controller, { encodeQr = qrDataUrl } =
|
|
|
301
307
|
value = await controller.updateContextEnhancement(
|
|
302
308
|
payload.botId, payload.config, (status) => publicStatus(status, cachedEncode),
|
|
303
309
|
);
|
|
310
|
+
} else if (endpoint === DINGTALK_ENDPOINTS.setAlias) {
|
|
311
|
+
if (typeof controller.updateAlias !== 'function') throw new Error('Alias update is unavailable');
|
|
312
|
+
value = await controller.updateAlias(
|
|
313
|
+
payload.botId, payload.alias, (status) => publicStatus(status, cachedEncode),
|
|
314
|
+
);
|
|
304
315
|
} else if (endpoint === DINGTALK_ENDPOINTS.setAccessPolicy) {
|
|
305
316
|
if (typeof controller.updateAccessPolicy !== 'function') throw new Error('Access policy update is unavailable');
|
|
306
317
|
value = await controller.updateAccessPolicy(
|
|
@@ -162,6 +162,27 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
162
162
|
if (!botConfig) throw new Error('Unknown Feishu bot');
|
|
163
163
|
return stateFor(botConfig);
|
|
164
164
|
};
|
|
165
|
+
// Session-sync mirror targets: which synced DMs (openId) belong to a
|
|
166
|
+
// Session. Resolved lazily per turn start so rebinding a delivery target
|
|
167
|
+
// takes effect without a restart.
|
|
168
|
+
const sessionSyncTargetsFor = async (sessionId) => {
|
|
169
|
+
if (typeof workspaces.listSessionSyncTargets !== 'function') return [];
|
|
170
|
+
const matches = [];
|
|
171
|
+
for (const entry of workspaces.listSessionSyncTargets()) {
|
|
172
|
+
let state;
|
|
173
|
+
try {
|
|
174
|
+
state = await stateForBotId(entry.botId);
|
|
175
|
+
} catch {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
const bound = state?.snapshot?.().sessions?.[entry.conversationKey];
|
|
179
|
+
if (bound !== sessionId) continue;
|
|
180
|
+
const target = workspaces.deliveryTargetFor(entry.botId, entry.targetId);
|
|
181
|
+
const openId = target?.route?.openId;
|
|
182
|
+
if (openId) matches.push({ openId, botId: entry.botId, targetId: entry.targetId });
|
|
183
|
+
}
|
|
184
|
+
return matches;
|
|
185
|
+
};
|
|
165
186
|
const commandExecutor = createHarnessCommandExecutor(ctx, internals.commandExecutor);
|
|
166
187
|
const inboundTtl = internals.inboundTtl ?? getInboundTtlRuntime(ctx, config);
|
|
167
188
|
const inboundTtlService = inboundTtl?.service ?? inboundTtl;
|
|
@@ -220,6 +241,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
220
241
|
groupTopicReply: botConfig.groupTopicReply,
|
|
221
242
|
stepPush: botConfig.stepPush,
|
|
222
243
|
stepPushMode: botConfig.stepPushMode,
|
|
244
|
+
sessionSyncTargetsFor: sessionSyncTargetsFor,
|
|
223
245
|
ownerOpenIds: botConfig.ownerOpenIds ?? [botConfig.ownerOpenId],
|
|
224
246
|
harness: workspaceScope.harness,
|
|
225
247
|
state: workspaceScope.state,
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { normalizeBotAlias } from '../../../../src/channels/shared/bot-alias.mjs';
|
|
2
|
+
import { SET_ALIAS_ENDPOINT, validAliasPayload } from '../shared/bot-alias-rpc.mjs';
|
|
1
3
|
import { registerManagementRpc } from '../../../management-rpc.mjs';
|
|
2
4
|
import QRCode from 'qrcode';
|
|
3
5
|
import {
|
|
@@ -34,6 +36,7 @@ import {
|
|
|
34
36
|
export const FEISHU_ENDPOINTS = Object.freeze({
|
|
35
37
|
...FEISHU_CLIENT_ENDPOINTS,
|
|
36
38
|
setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
|
|
39
|
+
setAlias: SET_ALIAS_ENDPOINT,
|
|
37
40
|
});
|
|
38
41
|
export { FEISHU_RPC_CHANNEL };
|
|
39
42
|
export const FEISHU_MULTI_ENDPOINTS = Object.freeze({
|
|
@@ -214,6 +217,7 @@ function connectionFacts(connection) {
|
|
|
214
217
|
function publicBot(bot) {
|
|
215
218
|
const source = bot && typeof bot === 'object' ? bot : {};
|
|
216
219
|
const result = {
|
|
220
|
+
...normalizeBotAlias(source),
|
|
217
221
|
name: typeof source.name === 'string' && source.name.length > 0 ? source.name : '飞书机器人',
|
|
218
222
|
};
|
|
219
223
|
if (typeof source.avatarUrl === 'string') result.avatarUrl = source.avatarUrl;
|
|
@@ -451,6 +455,10 @@ function validPayload(endpoint, payload) {
|
|
|
451
455
|
return validAccessPolicyPayload(payload)
|
|
452
456
|
? null : '请提交有效的访问设置。';
|
|
453
457
|
}
|
|
458
|
+
if (endpoint === FEISHU_ENDPOINTS.setAlias) {
|
|
459
|
+
return validAliasPayload(payload)
|
|
460
|
+
? null : '请输入有效的别名(最多 80 个字符)。';
|
|
461
|
+
}
|
|
454
462
|
if (endpoint === FEISHU_ENDPOINTS.setGroupResponseMode) {
|
|
455
463
|
return hasOnlyKeys(payload, new Set(['botId', 'groupResponseMode']))
|
|
456
464
|
&& safeOpaqueId(payload.botId)
|
|
@@ -735,6 +743,12 @@ export function createFeishuRpcHandler(controller, { encodeQr = qrCodeDataUrl }
|
|
|
735
743
|
payload.botId, payload.config,
|
|
736
744
|
(status) => toPublicFeishuStatus(status, { encodeQr: cachedEncodeQr }),
|
|
737
745
|
);
|
|
746
|
+
} else if (endpoint === FEISHU_ENDPOINTS.setAlias) {
|
|
747
|
+
if (typeof controller.updateAlias !== 'function') throw new Error('Alias update is unavailable');
|
|
748
|
+
value = await controller.updateAlias(
|
|
749
|
+
payload.botId, payload.alias,
|
|
750
|
+
(status) => toPublicFeishuStatus(status, { encodeQr: cachedEncodeQr }),
|
|
751
|
+
);
|
|
738
752
|
} else if (endpoint === FEISHU_ENDPOINTS.setAccessPolicy) {
|
|
739
753
|
if (typeof controller.updateAccessPolicy !== 'function') throw new Error('Access policy update is unavailable');
|
|
740
754
|
value = await controller.updateAccessPolicy(
|
|
@@ -15,6 +15,7 @@ export const IMESSAGE_ENDPOINTS = Object.freeze({
|
|
|
15
15
|
setAgentPreset: 'bot.agent-preset.set',
|
|
16
16
|
setContextEnhancement: 'bot.context-enhancement.set',
|
|
17
17
|
setAccessPolicy: 'bot.access-policy.set',
|
|
18
|
+
setAlias: 'bot.alias.set',
|
|
18
19
|
});
|
|
19
20
|
export const IMESSAGE_RPC_ENDPOINTS = Object.freeze(Object.values(IMESSAGE_ENDPOINTS));
|
|
20
21
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SET_ALIAS_ENDPOINT, validAliasPayload } from '../shared/bot-alias-rpc.mjs';
|
|
1
2
|
import { registerManagementRpc } from '../../../management-rpc.mjs';
|
|
2
3
|
import QRCode from 'qrcode';
|
|
3
4
|
import {
|
|
@@ -25,6 +26,7 @@ export const QQ_ENDPOINTS = Object.freeze({
|
|
|
25
26
|
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
26
27
|
setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
|
|
27
28
|
setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
|
|
29
|
+
setAlias: SET_ALIAS_ENDPOINT,
|
|
28
30
|
});
|
|
29
31
|
export const QQ_RPC_ENDPOINTS = Object.freeze(Object.values(QQ_ENDPOINTS));
|
|
30
32
|
|
|
@@ -94,6 +96,10 @@ function payloadFailure(endpoint, payload) {
|
|
|
94
96
|
return validAccessPolicyPayload(payload)
|
|
95
97
|
? null : '请提交有效的访问设置。';
|
|
96
98
|
}
|
|
99
|
+
if (endpoint === QQ_ENDPOINTS.setAlias) {
|
|
100
|
+
return validAliasPayload(payload)
|
|
101
|
+
? null : '请输入有效的别名(最多 80 个字符)。';
|
|
102
|
+
}
|
|
97
103
|
return 'Unknown QQ endpoint.';
|
|
98
104
|
}
|
|
99
105
|
|
|
@@ -200,6 +206,11 @@ export function createQqRpcHandler(controller, { encodeQr = qrDataUrl } = {}) {
|
|
|
200
206
|
value = await controller.updateContextEnhancement(
|
|
201
207
|
payload.botId, payload.config, (status) => publicStatus(status, cachedEncode),
|
|
202
208
|
);
|
|
209
|
+
} else if (endpoint === QQ_ENDPOINTS.setAlias) {
|
|
210
|
+
if (typeof controller.updateAlias !== 'function') throw new Error('Alias update is unavailable');
|
|
211
|
+
value = await controller.updateAlias(
|
|
212
|
+
payload.botId, payload.alias, (status) => publicStatus(status, cachedEncode),
|
|
213
|
+
);
|
|
203
214
|
} else if (endpoint === QQ_ENDPOINTS.setAccessPolicy) {
|
|
204
215
|
if (typeof controller.updateAccessPolicy !== 'function') throw new Error('Access policy update is unavailable');
|
|
205
216
|
value = await controller.updateAccessPolicy(
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { validateBotAlias } from '../../../../src/channels/shared/bot-alias.mjs';
|
|
2
|
+
export { SET_ALIAS_ENDPOINT } from '../../../../src/channels/shared/bot-alias.mjs';
|
|
3
|
+
|
|
4
|
+
export function validAliasPayload(payload) {
|
|
5
|
+
try {
|
|
6
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)
|
|
7
|
+
|| Object.keys(payload).length !== 2
|
|
8
|
+
|| typeof payload.botId !== 'string'
|
|
9
|
+
|| !/^[A-Za-z0-9_-]{1,128}$/.test(payload.botId)) return false;
|
|
10
|
+
validateBotAlias(payload.alias);
|
|
11
|
+
return true;
|
|
12
|
+
} catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SET_ALIAS_ENDPOINT, validAliasPayload } from './bot-alias-rpc.mjs';
|
|
1
2
|
import { registerManagementRpc } from '../../../management-rpc.mjs';
|
|
2
3
|
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from './context-enhancement-rpc.mjs';
|
|
3
4
|
import { SET_ACCESS_POLICY_ENDPOINT, validAccessPolicyPayload } from './access-policy-rpc.mjs';
|
|
@@ -24,6 +25,7 @@ export const TOKEN_BOT_ENDPOINTS = Object.freeze({
|
|
|
24
25
|
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
25
26
|
setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
|
|
26
27
|
setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
|
|
28
|
+
setAlias: SET_ALIAS_ENDPOINT,
|
|
27
29
|
});
|
|
28
30
|
|
|
29
31
|
const ENDPOINTS = Object.freeze(Object.values(TOKEN_BOT_ENDPOINTS));
|
|
@@ -90,6 +92,10 @@ function payloadFailure(endpoint, payload) {
|
|
|
90
92
|
return validAccessPolicyPayload(payload)
|
|
91
93
|
? null : '请提交有效的访问设置。';
|
|
92
94
|
}
|
|
95
|
+
if (endpoint === TOKEN_BOT_ENDPOINTS.setAlias) {
|
|
96
|
+
return validAliasPayload(payload)
|
|
97
|
+
? null : '请输入有效的别名(最多 80 个字符)。';
|
|
98
|
+
}
|
|
93
99
|
return 'Unknown bot endpoint.';
|
|
94
100
|
}
|
|
95
101
|
|
|
@@ -177,6 +183,9 @@ export function createTokenBotRpcHandler(controller, { channel }) {
|
|
|
177
183
|
} else if (endpoint === TOKEN_BOT_ENDPOINTS.setContextEnhancement) {
|
|
178
184
|
if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
|
|
179
185
|
value = await controller.updateContextEnhancement(payload.botId, payload.config);
|
|
186
|
+
} else if (endpoint === TOKEN_BOT_ENDPOINTS.setAlias) {
|
|
187
|
+
if (typeof controller.updateAlias !== 'function') throw new Error('Alias update is unavailable');
|
|
188
|
+
value = await controller.updateAlias(payload.botId, payload.alias);
|
|
180
189
|
} else if (endpoint === TOKEN_BOT_ENDPOINTS.setAccessPolicy) {
|
|
181
190
|
if (typeof controller.updateAccessPolicy !== 'function') throw new Error('Access policy update is unavailable');
|
|
182
191
|
value = await controller.updateAccessPolicy(payload.botId, payload.policy);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SET_ALIAS_ENDPOINT, validAliasPayload } from '../shared/bot-alias-rpc.mjs';
|
|
1
2
|
import { registerManagementRpc } from '../../../management-rpc.mjs';
|
|
2
3
|
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
|
|
3
4
|
import { SET_ACCESS_POLICY_ENDPOINT, validAccessPolicyPayload } from '../shared/access-policy-rpc.mjs';
|
|
@@ -25,6 +26,7 @@ export const SLACK_ENDPOINTS = Object.freeze({
|
|
|
25
26
|
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
26
27
|
setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
|
|
27
28
|
setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
|
|
29
|
+
setAlias: SET_ALIAS_ENDPOINT,
|
|
28
30
|
});
|
|
29
31
|
export const SLACK_RPC_ENDPOINTS = Object.freeze(Object.values(SLACK_ENDPOINTS));
|
|
30
32
|
|
|
@@ -93,6 +95,10 @@ function payloadFailure(endpoint, payload) {
|
|
|
93
95
|
return validAccessPolicyPayload(payload)
|
|
94
96
|
? null : '请提交有效的访问设置。';
|
|
95
97
|
}
|
|
98
|
+
if (endpoint === SLACK_ENDPOINTS.setAlias) {
|
|
99
|
+
return validAliasPayload(payload)
|
|
100
|
+
? null : '请输入有效的别名(最多 80 个字符)。';
|
|
101
|
+
}
|
|
96
102
|
return 'Unknown Slack endpoint.';
|
|
97
103
|
}
|
|
98
104
|
|
|
@@ -180,6 +186,10 @@ export function createSlackRpcHandler(controller) {
|
|
|
180
186
|
if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
|
|
181
187
|
value = await controller.updateContextEnhancement(payload.botId, payload.config);
|
|
182
188
|
}
|
|
189
|
+
else if (endpoint === SLACK_ENDPOINTS.setAlias) {
|
|
190
|
+
if (typeof controller.updateAlias !== 'function') throw new Error('Alias update is unavailable');
|
|
191
|
+
value = await controller.updateAlias(payload.botId, payload.alias);
|
|
192
|
+
}
|
|
183
193
|
else if (endpoint === SLACK_ENDPOINTS.setAccessPolicy) {
|
|
184
194
|
if (typeof controller.updateAccessPolicy !== 'function') throw new Error('Access policy update is unavailable');
|
|
185
195
|
value = await controller.updateAccessPolicy(payload.botId, payload.policy);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SET_ALIAS_ENDPOINT, validAliasPayload } from '../shared/bot-alias-rpc.mjs';
|
|
1
2
|
import { registerManagementRpc } from '../../../management-rpc.mjs';
|
|
2
3
|
import QRCode from 'qrcode';
|
|
3
4
|
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
|
|
@@ -25,6 +26,7 @@ export const WECOM_ENDPOINTS = Object.freeze({
|
|
|
25
26
|
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
26
27
|
setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
|
|
27
28
|
setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
|
|
29
|
+
setAlias: SET_ALIAS_ENDPOINT,
|
|
28
30
|
});
|
|
29
31
|
export const WECOM_RPC_ENDPOINTS = Object.freeze(Object.values(WECOM_ENDPOINTS));
|
|
30
32
|
|
|
@@ -94,6 +96,10 @@ function payloadFailure(endpoint, payload) {
|
|
|
94
96
|
return validAccessPolicyPayload(payload)
|
|
95
97
|
? null : '请提交有效的访问设置。';
|
|
96
98
|
}
|
|
99
|
+
if (endpoint === WECOM_ENDPOINTS.setAlias) {
|
|
100
|
+
return validAliasPayload(payload)
|
|
101
|
+
? null : '请输入有效的别名(最多 80 个字符)。';
|
|
102
|
+
}
|
|
97
103
|
return 'Unknown Enterprise WeChat endpoint.';
|
|
98
104
|
}
|
|
99
105
|
|
|
@@ -201,6 +207,11 @@ export function createWecomRpcHandler(controller, { encodeQr = qrDataUrl } = {})
|
|
|
201
207
|
value = await controller.updateContextEnhancement(
|
|
202
208
|
payload.botId, payload.config, (status) => publicStatus(status, cachedEncode),
|
|
203
209
|
);
|
|
210
|
+
} else if (endpoint === WECOM_ENDPOINTS.setAlias) {
|
|
211
|
+
if (typeof controller.updateAlias !== 'function') throw new Error('Alias update is unavailable');
|
|
212
|
+
value = await controller.updateAlias(
|
|
213
|
+
payload.botId, payload.alias, (status) => publicStatus(status, cachedEncode),
|
|
214
|
+
);
|
|
204
215
|
} else if (endpoint === WECOM_ENDPOINTS.setAccessPolicy) {
|
|
205
216
|
if (typeof controller.updateAccessPolicy !== 'function') throw new Error('Access policy update is unavailable');
|
|
206
217
|
value = await controller.updateAccessPolicy(
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SET_ALIAS_ENDPOINT, validAliasPayload } from '../shared/bot-alias-rpc.mjs';
|
|
1
2
|
import { registerManagementRpc } from '../../../management-rpc.mjs';
|
|
2
3
|
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
|
|
3
4
|
import { SET_ACCESS_POLICY_ENDPOINT, validAccessPolicyPayload } from '../shared/access-policy-rpc.mjs';
|
|
@@ -23,6 +24,7 @@ export const WECOM_APP_ENDPOINTS = Object.freeze({
|
|
|
23
24
|
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
24
25
|
setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
|
|
25
26
|
setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
|
|
27
|
+
setAlias: SET_ALIAS_ENDPOINT,
|
|
26
28
|
});
|
|
27
29
|
export const WECOM_APP_RPC_ENDPOINTS = Object.freeze(Object.values(WECOM_APP_ENDPOINTS));
|
|
28
30
|
|
|
@@ -113,6 +115,9 @@ function payloadFailure(endpoint, payload) {
|
|
|
113
115
|
if (endpoint === WECOM_APP_ENDPOINTS.setAccessPolicy) {
|
|
114
116
|
return validAccessPolicyPayload(payload) ? null : '请提交有效的访问设置。';
|
|
115
117
|
}
|
|
118
|
+
if (endpoint === WECOM_APP_ENDPOINTS.setAlias) {
|
|
119
|
+
return validAliasPayload(payload) ? null : '请输入有效的别名(最多 80 个字符)。';
|
|
120
|
+
}
|
|
116
121
|
return 'Unknown Enterprise WeChat app endpoint.';
|
|
117
122
|
}
|
|
118
123
|
|
|
@@ -189,6 +194,11 @@ export function createWecomAppRpcHandler(controller) {
|
|
|
189
194
|
value = await controller.updateContextEnhancement(
|
|
190
195
|
payload.botId, payload.config, (status) => publicStatus(status),
|
|
191
196
|
);
|
|
197
|
+
} else if (endpoint === WECOM_APP_ENDPOINTS.setAlias) {
|
|
198
|
+
if (typeof controller.updateAlias !== 'function') throw new Error('Alias update is unavailable');
|
|
199
|
+
value = await controller.updateAlias(
|
|
200
|
+
payload.botId, payload.alias, (status) => publicStatus(status),
|
|
201
|
+
);
|
|
192
202
|
} else if (endpoint === WECOM_APP_ENDPOINTS.setAccessPolicy) {
|
|
193
203
|
if (typeof controller.updateAccessPolicy !== 'function') throw new Error('Access policy update is unavailable');
|
|
194
204
|
value = await controller.updateAccessPolicy(
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SET_ALIAS_ENDPOINT, validAliasPayload } from '../shared/bot-alias-rpc.mjs';
|
|
1
2
|
import { registerManagementRpc } from '../../../management-rpc.mjs';
|
|
2
3
|
import QRCode from 'qrcode';
|
|
3
4
|
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
|
|
@@ -32,6 +33,7 @@ export const WEIXIN_ENDPOINTS = Object.freeze({
|
|
|
32
33
|
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
33
34
|
setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
|
|
34
35
|
setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
|
|
36
|
+
setAlias: SET_ALIAS_ENDPOINT,
|
|
35
37
|
});
|
|
36
38
|
export const WEIXIN_RPC_ENDPOINTS = Object.freeze(Object.values(WEIXIN_ENDPOINTS));
|
|
37
39
|
|
|
@@ -101,6 +103,10 @@ function payloadFailure(endpoint, payload) {
|
|
|
101
103
|
return validAccessPolicyPayload(payload)
|
|
102
104
|
? null : '请提交有效的访问设置。';
|
|
103
105
|
}
|
|
106
|
+
if (endpoint === WEIXIN_ENDPOINTS.setAlias) {
|
|
107
|
+
return validAliasPayload(payload)
|
|
108
|
+
? null : '请输入有效的别名(最多 80 个字符)。';
|
|
109
|
+
}
|
|
104
110
|
return 'Unknown Weixin endpoint.';
|
|
105
111
|
}
|
|
106
112
|
|
|
@@ -236,6 +242,11 @@ export function createWeixinRpcHandler(controller, { encodeQr = qrDataUrl } = {}
|
|
|
236
242
|
value = await controller.updateContextEnhancement(
|
|
237
243
|
payload.botId, payload.config, (status) => publicStatus(status, cachedEncode),
|
|
238
244
|
);
|
|
245
|
+
} else if (endpoint === WEIXIN_ENDPOINTS.setAlias) {
|
|
246
|
+
if (typeof controller.updateAlias !== 'function') throw new Error('Alias update is unavailable');
|
|
247
|
+
value = await controller.updateAlias(
|
|
248
|
+
payload.botId, payload.alias, (status) => publicStatus(status, cachedEncode),
|
|
249
|
+
);
|
|
239
250
|
} else if (endpoint === WEIXIN_ENDPOINTS.setAccessPolicy) {
|
|
240
251
|
if (typeof controller.updateAccessPolicy !== 'function') throw new Error('Access policy update is unavailable');
|
|
241
252
|
value = await controller.updateAccessPolicy(
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SET_ALIAS_ENDPOINT, validAliasPayload } from '../shared/bot-alias-rpc.mjs';
|
|
1
2
|
import { registerManagementRpc } from '../../../management-rpc.mjs';
|
|
2
3
|
import QRCode from 'qrcode';
|
|
3
4
|
|
|
@@ -18,6 +19,7 @@ export const WHATSAPP_ENDPOINTS = Object.freeze({
|
|
|
18
19
|
reconnectBot: 'bot.reconnect',
|
|
19
20
|
deleteBot: 'bot.delete',
|
|
20
21
|
setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
|
|
22
|
+
setAlias: SET_ALIAS_ENDPOINT,
|
|
21
23
|
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
|
22
24
|
setModel: SET_MODEL_ENDPOINT,
|
|
23
25
|
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
@@ -61,6 +63,10 @@ function payloadFailure(endpoint, payload) {
|
|
|
61
63
|
return validAccessPolicyPayload(payload)
|
|
62
64
|
? null : '请提交有效的访问设置。';
|
|
63
65
|
}
|
|
66
|
+
if (endpoint === WHATSAPP_ENDPOINTS.setAlias) {
|
|
67
|
+
return validAliasPayload(payload)
|
|
68
|
+
? null : '请输入有效的别名(最多 80 个字符)。';
|
|
69
|
+
}
|
|
64
70
|
if (endpoint === WHATSAPP_ENDPOINTS.setWorkspace) {
|
|
65
71
|
return validWorkspacePayload(payload)
|
|
66
72
|
? null : '请输入工作区绝对路径。';
|
|
@@ -195,6 +201,13 @@ export function createWhatsappRpcHandler(controller, { encodeQr = qrDataUrl } =
|
|
|
195
201
|
await controller.updateAgentPreset(payload.botId, payload.agentPreset),
|
|
196
202
|
cachedEncode,
|
|
197
203
|
);
|
|
204
|
+
} else if (endpoint === WHATSAPP_ENDPOINTS.setAlias) {
|
|
205
|
+
if (typeof controller.updateAlias !== 'function') throw new Error('Alias update is unavailable');
|
|
206
|
+
value = await controller.updateAlias(
|
|
207
|
+
payload.botId,
|
|
208
|
+
payload.alias,
|
|
209
|
+
(status) => publicStatus(status, cachedEncode),
|
|
210
|
+
);
|
|
198
211
|
} else if (endpoint === WHATSAPP_ENDPOINTS.setAccessPolicy) {
|
|
199
212
|
if (typeof controller.updateAccessPolicy !== 'function') throw new Error('Access policy update is unavailable');
|
|
200
213
|
value = await controller.updateAccessPolicy(
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
consumeDshImInputOrigin,
|
|
4
4
|
textFromHarnessContent,
|
|
5
5
|
} from '../../src/channels/shared/harness-client.mjs';
|
|
6
|
+
import { deliverSessionSyncMirror } from '../../src/channels/shared/session-sync-registry.mjs';
|
|
6
7
|
|
|
7
8
|
const DSH_USER_PREFIX = '[来自 DSH]\n';
|
|
8
9
|
const DSH_ASSISTANT_PREFIX = '[DSH 助手]\n';
|
|
@@ -111,6 +112,10 @@ export function createSessionSyncCoordinator({ deliveryService, logger = console
|
|
|
111
112
|
if (event.surfaceOp !== 'append') return;
|
|
112
113
|
if (state.origin === 'unknown') state.origin = origin;
|
|
113
114
|
if (state.origin !== 'dsh' || origin !== 'dsh') return;
|
|
115
|
+
// The user echo stays as plain text even when the mirror owns the
|
|
116
|
+
// turn: the card also quotes the question, but keeping the echo here
|
|
117
|
+
// guarantees recipients are established so a failed mirror can still
|
|
118
|
+
// fall back to the final-answer text delivery.
|
|
114
119
|
const text = textFromHarnessContent(event.data?.content);
|
|
115
120
|
if (!text) return;
|
|
116
121
|
|
|
@@ -151,9 +156,23 @@ export function createSessionSyncCoordinator({ deliveryService, logger = console
|
|
|
151
156
|
turns.delete(sessionId);
|
|
152
157
|
if (state.origin !== 'dsh' || !completedTurn(event.data?.reason)
|
|
153
158
|
|| !state.recipients?.size || !state.assistant.text) return;
|
|
159
|
+
// A pending card is not proof of delivery. Only suppress this target's
|
|
160
|
+
// text after its renderer confirms the complete final answer is visible.
|
|
161
|
+
// Targets without a renderer retain the original text delivery path.
|
|
162
|
+
const recipients = [...state.recipients.values()];
|
|
163
|
+
const rendered = await Promise.all(recipients.map(async (target) => {
|
|
164
|
+
try {
|
|
165
|
+
return await deliverSessionSyncMirror(target, sessionId, state.turn, state.assistant.text);
|
|
166
|
+
} catch (error) {
|
|
167
|
+
logFailure('card delivery', target, error);
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
}));
|
|
171
|
+
const plain = recipients.filter((_target, index) => !rendered[index]);
|
|
172
|
+
if (plain.length === 0) return;
|
|
154
173
|
await deliver(
|
|
155
174
|
sessionId,
|
|
156
|
-
|
|
175
|
+
plain,
|
|
157
176
|
`${DSH_ASSISTANT_PREFIX}${state.assistant.text}`,
|
|
158
177
|
'assistant delivery',
|
|
159
178
|
);
|