@xmanrui/dsh-im 4.22.0 → 4.24.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.
- package/README.en.md +5 -4
- package/README.md +5 -4
- package/lib/client.js +2584 -1153
- package/lib/index.js +290 -298
- package/package.json +14 -2
- package/plugin-src/client/channel-card-meta.js +2 -0
- package/plugin-src/client/channel-logos.js +11 -0
- package/plugin-src/client/channels/dingtalk/api.js +5 -1
- package/plugin-src/client/channels/dingtalk/index.js +15 -13
- package/plugin-src/client/channels/email/api.js +52 -0
- package/plugin-src/client/channels/email/index.js +728 -0
- package/plugin-src/client/channels/email/styles.js +51 -0
- package/plugin-src/client/channels/feishu/api.js +4 -2
- package/plugin-src/client/channels/feishu/index.js +23 -7
- package/plugin-src/client/channels/imessage/index.js +2 -1
- package/plugin-src/client/channels/office/api.js +2 -0
- package/plugin-src/client/channels/office/index.js +6 -5
- package/plugin-src/client/channels/qq/api.js +7 -0
- package/plugin-src/client/channels/qq/index.js +16 -3
- package/plugin-src/client/channels/shared/token-api.js +8 -1
- package/plugin-src/client/channels/shared/token-channel.js +28 -3
- package/plugin-src/client/channels/slack/index.js +2 -1
- package/plugin-src/client/channels/telegram/index.js +3 -0
- package/plugin-src/client/channels/telegram/styles.js +12 -0
- package/plugin-src/client/channels/telegram/thinking-traces.js +25 -0
- package/plugin-src/client/channels/wecom/api.js +7 -1
- package/plugin-src/client/channels/wecom/index.js +16 -3
- package/plugin-src/client/channels/wecom-app/api.js +6 -1
- package/plugin-src/client/channels/wecom-app/index.js +16 -3
- package/plugin-src/client/channels/weixin/api.js +2 -1
- package/plugin-src/client/channels/weixin/connection-error.js +1 -71
- package/plugin-src/client/channels/whatsapp/api.js +5 -0
- package/plugin-src/client/channels/whatsapp/index.js +16 -3
- package/plugin-src/client/connection-error.js +87 -0
- package/plugin-src/client/global-settings.js +81 -1
- package/plugin-src/client/i18n.js +69 -1
- package/plugin-src/client/index.js +40 -4
- package/plugin-src/client/last-message-error.js +2 -1
- package/plugin-src/client/styles.js +8 -2
- package/plugin-src/host/build.mjs +4 -0
- package/plugin-src/host/channels/dingtalk/production.mjs +2 -0
- package/plugin-src/host/channels/dingtalk/rpc.mjs +7 -4
- package/plugin-src/host/channels/email/availability.mjs +60 -0
- package/plugin-src/host/channels/email/index.mjs +36 -0
- package/plugin-src/host/channels/email/production.mjs +58 -0
- package/plugin-src/host/channels/email/rpc.mjs +163 -0
- package/plugin-src/host/channels/feishu/production.mjs +3 -0
- package/plugin-src/host/channels/feishu/rpc.mjs +7 -4
- package/plugin-src/host/channels/imessage/rpc.mjs +13 -6
- package/plugin-src/host/channels/office/rpc.mjs +3 -1
- package/plugin-src/host/channels/qq/production.mjs +2 -0
- package/plugin-src/host/channels/qq/rpc.mjs +5 -3
- package/plugin-src/host/channels/shared/production.mjs +52 -5
- package/plugin-src/host/channels/shared/rpc.mjs +14 -3
- package/plugin-src/host/channels/shared/startup-error.mjs +4 -3
- package/plugin-src/host/channels/shared/startup.mjs +11 -6
- package/plugin-src/host/channels/shared/thinking-traces-rpc.mjs +11 -0
- package/plugin-src/host/channels/slack/production.mjs +2 -0
- package/plugin-src/host/channels/slack/rpc.mjs +5 -3
- package/plugin-src/host/channels/wecom/production.mjs +2 -0
- package/plugin-src/host/channels/wecom/rpc.mjs +5 -3
- package/plugin-src/host/channels/wecom-app/production.mjs +2 -0
- package/plugin-src/host/channels/wecom-app/rpc.mjs +5 -3
- package/plugin-src/host/channels/weixin/production.mjs +2 -0
- package/plugin-src/host/channels/whatsapp/production.mjs +2 -0
- package/plugin-src/host/channels/whatsapp/rpc.mjs +5 -3
- package/plugin-src/host/delivery-adapter.mjs +11 -0
- package/plugin-src/host/image-input-rpc.mjs +24 -0
- package/plugin-src/host/inbound-ttl-rpc.mjs +6 -1
- package/plugin-src/host/index.mjs +3 -0
- package/plugin-src/host/modern-harness-api.mjs +7 -2
- package/plugin-src/management-rpc.mjs +12 -2
- package/scripts/verify-package.mjs +13 -5
- package/src/channels/dingtalk/connection-error.mjs +5 -12
- package/src/channels/dingtalk/device-auth.mjs +4 -1
- package/src/channels/dingtalk/dingtalk-bridge.mjs +6 -4
- package/src/channels/dingtalk/dingtalk-controller.mjs +60 -45
- package/src/channels/dingtalk/dingtalk-runtime.mjs +20 -4
- package/src/channels/discord/discord-api.mjs +3 -3
- package/src/channels/discord/discord-runtime.mjs +18 -12
- package/src/channels/email/config-store.mjs +194 -0
- package/src/channels/email/email-api.mjs +20 -0
- package/src/channels/email/email-bridge.mjs +17 -0
- package/src/channels/email/email-controller.mjs +824 -0
- package/src/channels/email/email-runtime.mjs +699 -0
- package/src/channels/email/harness-client.mjs +7 -0
- package/src/channels/email/mail-format.mjs +91 -0
- package/src/channels/email/state-store.mjs +176 -0
- package/src/channels/email/transport.mjs +64 -0
- package/src/channels/email/transports/agent-mail.mjs +604 -0
- package/src/channels/email/transports/agently-cli.mjs +288 -0
- package/src/channels/email/transports/imap-smtp.mjs +165 -0
- package/src/channels/feishu/bridge.mjs +39 -11
- package/src/channels/feishu/feishu-channel.mjs +35 -0
- package/src/channels/feishu/feishu-runtime.mjs +15 -8
- package/src/channels/feishu/group-message-permission-manager.mjs +2 -1
- package/src/channels/feishu/live-cot.mjs +260 -0
- package/src/channels/feishu/message-utils.mjs +3 -2
- package/src/channels/feishu/multi-bot-controller.mjs +56 -47
- package/src/channels/feishu/registration-manager.mjs +8 -4
- package/src/channels/feishu/repair-manager.mjs +2 -1
- package/src/channels/feishu/slash-command-registry.mjs +17 -0
- package/src/channels/feishu/step-push-mode.mjs +10 -4
- package/src/channels/imessage/runtime.mjs +9 -5
- package/src/channels/office/office-controller.mjs +23 -12
- package/src/channels/office/office-runtime.mjs +6 -3
- package/src/channels/office/office-transport.mjs +1 -0
- package/src/channels/qq/qq-bridge.mjs +7 -5
- package/src/channels/qq/qq-controller.mjs +56 -42
- package/src/channels/qq/qq-runtime.mjs +16 -11
- package/src/channels/shared/bot-workspace-store.mjs +29 -10
- package/src/channels/shared/connection-error.mjs +191 -0
- package/src/channels/shared/connection-test.mjs +3 -1
- package/src/channels/shared/conversation-state-store.mjs +46 -1
- package/src/channels/shared/diagnostic-details.mjs +95 -0
- package/src/channels/shared/harness-client.mjs +287 -57
- package/src/channels/shared/i18n-en/diagnostics.mjs +55 -0
- package/src/channels/shared/i18n-en/email.mjs +25 -0
- package/src/channels/shared/i18n-en/image-input.mjs +17 -0
- package/src/channels/shared/i18n-en/shared-a.mjs +1 -0
- package/src/channels/shared/i18n-en/shared-c.mjs +1 -1
- package/src/channels/shared/i18n-en.mjs +6 -0
- package/src/channels/shared/image-input-policy.mjs +35 -0
- package/src/channels/shared/image-input-settings-store.mjs +60 -0
- package/src/channels/shared/image-input.mjs +124 -0
- package/src/channels/shared/image-prompt.mjs +15 -7
- package/src/channels/shared/message-failure.mjs +12 -0
- package/src/channels/shared/semantic/reply-reference.mjs +3 -3
- package/src/channels/shared/text-harness-bridge.mjs +150 -25
- package/src/channels/shared/token-bot-controller.mjs +52 -47
- package/src/channels/slack/slack-api.mjs +3 -3
- package/src/channels/slack/slack-controller.mjs +59 -51
- package/src/channels/slack/slack-runtime.mjs +18 -12
- package/src/channels/telegram/config-store.mjs +4 -1
- package/src/channels/telegram/telegram-api.mjs +2 -2
- package/src/channels/telegram/telegram-controller.mjs +13 -1
- package/src/channels/telegram/telegram-runtime.mjs +215 -14
- package/src/channels/wecom/wecom-bridge.mjs +15 -6
- package/src/channels/wecom/wecom-controller.mjs +51 -37
- package/src/channels/wecom/wecom-runtime.mjs +10 -6
- package/src/channels/wecom-app/wecom-app-bridge.mjs +7 -5
- package/src/channels/wecom-app/wecom-app-controller.mjs +50 -37
- package/src/channels/wecom-app/wecom-app-runtime.mjs +2 -1
- package/src/channels/weixin/connection-error.mjs +26 -92
- package/src/channels/weixin/diagnostic-details.mjs +1 -63
- package/src/channels/weixin/weixin-bridge.mjs +7 -5
- package/src/channels/whatsapp/whatsapp-controller.mjs +34 -17
- package/src/channels/whatsapp/whatsapp-runtime.mjs +35 -8
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getImageInputSettingsStore } from '../../../../src/channels/shared/image-input-settings-store.mjs';
|
|
1
2
|
import { unlink } from 'node:fs/promises';
|
|
2
3
|
import { homedir } from 'node:os';
|
|
3
4
|
import { join, resolve } from 'node:path';
|
|
@@ -107,6 +108,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
107
108
|
...(controlExecutor ? { controlExecutor } : {}),
|
|
108
109
|
...(sessionMaintenanceExecutor ? { sessionMaintenanceExecutor } : {}),
|
|
109
110
|
...(fileIngressExecutor ? { fileIngressExecutor } : {}),
|
|
111
|
+
imageInputPolicy: () => getImageInputSettingsStore(config).get(),
|
|
110
112
|
});
|
|
111
113
|
const modelCatalog = () => listModelCatalog(harness);
|
|
112
114
|
const coreController = new Controller({
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createConnectionDiagnostics, diagnosticRpcResult } from '../../../../src/channels/shared/connection-error.mjs';
|
|
1
2
|
import { SET_ALIAS_ENDPOINT, validAliasPayload } from '../shared/bot-alias-rpc.mjs';
|
|
2
3
|
import { registerManagementRpc } from '../../../management-rpc.mjs';
|
|
3
4
|
import QRCode from 'qrcode';
|
|
@@ -131,6 +132,7 @@ async function publicStatus(status, encodeQr) {
|
|
|
131
132
|
}
|
|
132
133
|
|
|
133
134
|
export function createWecomRpcHandler(controller, { encodeQr = qrDataUrl } = {}) {
|
|
135
|
+
const diagnostics = controller.diagnostics ?? createConnectionDiagnostics({ channel: 'wecom' });
|
|
134
136
|
for (const method of ['status', 'startProvisioning', 'registrationStatus', 'cancelProvisioning', 'bindCredentials', 'reconnectBot', 'deleteBot']) {
|
|
135
137
|
if (typeof controller?.[method] !== 'function') {
|
|
136
138
|
throw new TypeError(`A complete Enterprise WeChat controller is required (${method})`);
|
|
@@ -185,7 +187,7 @@ export function createWecomRpcHandler(controller, { encodeQr = qrDataUrl } = {})
|
|
|
185
187
|
await controller.sendConnectionTest(payload.botId);
|
|
186
188
|
testMessage = publicConnectionTestResult();
|
|
187
189
|
} catch (error) {
|
|
188
|
-
testMessage = publicConnectionTestResult(error);
|
|
190
|
+
testMessage = publicConnectionTestResult(error, { diagnostics, botId: payload.botId });
|
|
189
191
|
}
|
|
190
192
|
}
|
|
191
193
|
}
|
|
@@ -231,10 +233,10 @@ export function createWecomRpcHandler(controller, { encodeQr = qrDataUrl } = {})
|
|
|
231
233
|
: { ok: true, value };
|
|
232
234
|
} catch (error) {
|
|
233
235
|
const workspaceError = publicWorkspaceError(error);
|
|
234
|
-
return signal?.aborted
|
|
236
|
+
return diagnosticRpcResult(diagnostics, error, signal?.aborted
|
|
235
237
|
? { ok: false, error: { code: 'cancelled', message: 'The request was cancelled.' } }
|
|
236
238
|
: { ok: false, error: workspaceError
|
|
237
|
-
?? { code: 'wecom-operation-failed', message: '企业微信操作失败,请稍后重试。' } };
|
|
239
|
+
?? { code: 'wecom-operation-failed', message: '企业微信操作失败,请稍后重试。' } }, { operation: endpoint, botId: payload?.botId });
|
|
238
240
|
}
|
|
239
241
|
};
|
|
240
242
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getImageInputSettingsStore } from '../../../../src/channels/shared/image-input-settings-store.mjs';
|
|
1
2
|
import { unlink } from 'node:fs/promises';
|
|
2
3
|
import { homedir } from 'node:os';
|
|
3
4
|
import { join, resolve } from 'node:path';
|
|
@@ -141,6 +142,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
141
142
|
...(controlExecutor ? { controlExecutor } : {}),
|
|
142
143
|
...(sessionMaintenanceExecutor ? { sessionMaintenanceExecutor } : {}),
|
|
143
144
|
...(fileIngressExecutor ? { fileIngressExecutor } : {}),
|
|
145
|
+
imageInputPolicy: () => getImageInputSettingsStore(config).get(),
|
|
144
146
|
});
|
|
145
147
|
const modelCatalog = () => listModelCatalog(harness);
|
|
146
148
|
const coreController = new Controller({
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createConnectionDiagnostics, diagnosticRpcResult } from '../../../../src/channels/shared/connection-error.mjs';
|
|
1
2
|
import { SET_ALIAS_ENDPOINT, validAliasPayload } from '../shared/bot-alias-rpc.mjs';
|
|
2
3
|
import { registerManagementRpc } from '../../../management-rpc.mjs';
|
|
3
4
|
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
|
|
@@ -136,6 +137,7 @@ function publicStatus(status) {
|
|
|
136
137
|
}
|
|
137
138
|
|
|
138
139
|
export function createWecomAppRpcHandler(controller) {
|
|
140
|
+
const diagnostics = controller.diagnostics ?? createConnectionDiagnostics({ channel: 'wecom-app' });
|
|
139
141
|
for (const method of ['status', 'bindApp', 'updateAppSettings', 'resetCallbackSecret', 'reconnectBot', 'deleteBot']) {
|
|
140
142
|
if (typeof controller?.[method] !== 'function') {
|
|
141
143
|
throw new TypeError(`A complete Enterprise WeChat app controller is required (${method})`);
|
|
@@ -178,7 +180,7 @@ export function createWecomAppRpcHandler(controller) {
|
|
|
178
180
|
await controller.sendConnectionTest(payload.botId);
|
|
179
181
|
testMessage = publicConnectionTestResult();
|
|
180
182
|
} catch (error) {
|
|
181
|
-
testMessage = publicConnectionTestResult(error);
|
|
183
|
+
testMessage = publicConnectionTestResult(error, { diagnostics, botId: payload.botId });
|
|
182
184
|
}
|
|
183
185
|
}
|
|
184
186
|
}
|
|
@@ -215,11 +217,11 @@ export function createWecomAppRpcHandler(controller) {
|
|
|
215
217
|
: { ok: true, value };
|
|
216
218
|
} catch (error) {
|
|
217
219
|
const workspaceError = publicWorkspaceError(error);
|
|
218
|
-
return signal?.aborted
|
|
220
|
+
return diagnosticRpcResult(diagnostics, error, signal?.aborted
|
|
219
221
|
? { ok: false, error: { code: 'cancelled', message: 'The request was cancelled.', details: {} } }
|
|
220
222
|
: { ok: false, error: workspaceError
|
|
221
223
|
? { ...workspaceError, details: {} }
|
|
222
|
-
: { code: 'wecom-app-operation-failed', message: '企业微信应用操作失败,请稍后重试。', details: {} } };
|
|
224
|
+
: { code: 'wecom-app-operation-failed', message: '企业微信应用操作失败,请稍后重试。', details: {} } }, { operation: endpoint, botId: payload?.botId });
|
|
223
225
|
}
|
|
224
226
|
};
|
|
225
227
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getImageInputSettingsStore } from '../../../../src/channels/shared/image-input-settings-store.mjs';
|
|
1
2
|
import { unlink } from 'node:fs/promises';
|
|
2
3
|
import { homedir } from 'node:os';
|
|
3
4
|
import { join, resolve } from 'node:path';
|
|
@@ -139,6 +140,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
139
140
|
...(controlExecutor ? { controlExecutor } : {}),
|
|
140
141
|
...(sessionMaintenanceExecutor ? { sessionMaintenanceExecutor } : {}),
|
|
141
142
|
...(fileIngressExecutor ? { fileIngressExecutor } : {}),
|
|
143
|
+
imageInputPolicy: () => getImageInputSettingsStore(config).get(),
|
|
142
144
|
});
|
|
143
145
|
const modelCatalog = () => listModelCatalog(harness);
|
|
144
146
|
const coreController = new Controller({
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getImageInputSettingsStore } from '../../../../src/channels/shared/image-input-settings-store.mjs';
|
|
1
2
|
import { rm, unlink } from 'node:fs/promises';
|
|
2
3
|
import { homedir } from 'node:os';
|
|
3
4
|
import { join, resolve } from 'node:path';
|
|
@@ -113,6 +114,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
113
114
|
...(controlExecutor ? { controlExecutor } : {}),
|
|
114
115
|
...(sessionMaintenanceExecutor ? { sessionMaintenanceExecutor } : {}),
|
|
115
116
|
...(fileIngressExecutor ? { fileIngressExecutor } : {}),
|
|
117
|
+
imageInputPolicy: () => getImageInputSettingsStore(config).get(),
|
|
116
118
|
});
|
|
117
119
|
const modelCatalog = () => listModelCatalog(harness);
|
|
118
120
|
const coreController = new Controller({
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createConnectionDiagnostics, diagnosticRpcResult } from '../../../../src/channels/shared/connection-error.mjs';
|
|
1
2
|
import { SET_ALIAS_ENDPOINT, validAliasPayload } from '../shared/bot-alias-rpc.mjs';
|
|
2
3
|
import { registerManagementRpc } from '../../../management-rpc.mjs';
|
|
3
4
|
import QRCode from 'qrcode';
|
|
@@ -116,6 +117,7 @@ async function publicStatus(value, encodeQr) {
|
|
|
116
117
|
}
|
|
117
118
|
|
|
118
119
|
export function createWhatsappRpcHandler(controller, { encodeQr = qrDataUrl } = {}) {
|
|
120
|
+
const diagnostics = controller.diagnostics ?? createConnectionDiagnostics({ channel: 'whatsapp' });
|
|
119
121
|
for (const method of ['status', 'startProvisioning', 'registrationStatus', 'cancelProvisioning', 'reconnectBot', 'deleteBot']) {
|
|
120
122
|
if (typeof controller?.[method] !== 'function') {
|
|
121
123
|
throw new TypeError(`A complete WhatsApp controller is required (${method})`);
|
|
@@ -176,7 +178,7 @@ export function createWhatsappRpcHandler(controller, { encodeQr = qrDataUrl } =
|
|
|
176
178
|
testError = error;
|
|
177
179
|
}
|
|
178
180
|
}
|
|
179
|
-
value = { ...value, testMessage: publicConnectionTestResult(testError) };
|
|
181
|
+
value = { ...value, testMessage: publicConnectionTestResult(testError, { diagnostics, botId: payload.botId }) };
|
|
180
182
|
}
|
|
181
183
|
} else if (endpoint === WHATSAPP_ENDPOINTS.setWorkspace) {
|
|
182
184
|
if (typeof controller.updateWorkspace !== 'function') throw new Error('Workspace update is unavailable');
|
|
@@ -223,10 +225,10 @@ export function createWhatsappRpcHandler(controller, { encodeQr = qrDataUrl } =
|
|
|
223
225
|
: { ok: true, value };
|
|
224
226
|
} catch (error) {
|
|
225
227
|
const workspaceError = publicWorkspaceError(error);
|
|
226
|
-
return signal?.aborted
|
|
228
|
+
return diagnosticRpcResult(diagnostics, error, signal?.aborted
|
|
227
229
|
? { ok: false, error: { code: 'cancelled', message: 'The request was cancelled.' } }
|
|
228
230
|
: { ok: false, error: workspaceError
|
|
229
|
-
?? { code: 'whatsapp-operation-failed', message: 'WhatsApp 操作失败,请稍后重试。' } };
|
|
231
|
+
?? { code: 'whatsapp-operation-failed', message: 'WhatsApp 操作失败,请稍后重试。' } }, { operation: endpoint, botId: payload?.botId });
|
|
230
232
|
}
|
|
231
233
|
};
|
|
232
234
|
}
|
|
@@ -16,6 +16,7 @@ const CHANNELS = new Set([
|
|
|
16
16
|
'discord',
|
|
17
17
|
'whatsapp',
|
|
18
18
|
'imessage',
|
|
19
|
+
'email',
|
|
19
20
|
]);
|
|
20
21
|
|
|
21
22
|
export function supportsDeliveryChannel(channel) {
|
|
@@ -126,6 +127,16 @@ function normalizeRoute(channel, kind, route) {
|
|
|
126
127
|
case 'imessage':
|
|
127
128
|
oneOf(kind, ['user']);
|
|
128
129
|
return routeWithStrings(route, ['chatGuid']);
|
|
130
|
+
case 'email': {
|
|
131
|
+
oneOf(kind, ['user']);
|
|
132
|
+
const normalized = routeWithStrings(route, ['address']);
|
|
133
|
+
// Mail addresses are normalized to lower case everywhere so a target
|
|
134
|
+
// created from a mixed-case address still matches.
|
|
135
|
+
if (!/^[^\s@<>]+@[^\s@<>]+\.[^\s@<>]+$/.test(normalized.address)) {
|
|
136
|
+
throw invalidTarget('route.address must be a valid email address');
|
|
137
|
+
}
|
|
138
|
+
return { address: normalized.address.toLowerCase() };
|
|
139
|
+
}
|
|
129
140
|
default:
|
|
130
141
|
throw new TypeError(`Unsupported delivery channel: ${channel}`);
|
|
131
142
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { normalizeImageInputSettings } from '../../src/channels/shared/image-input-policy.mjs';
|
|
2
|
+
|
|
3
|
+
export const IMAGE_INPUT_ENDPOINTS = Object.freeze({
|
|
4
|
+
get: 'settings.image-input.get',
|
|
5
|
+
set: 'settings.image-input.set',
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export function createImageInputRpcHandler(store) {
|
|
9
|
+
return async (endpoint, payload, signal) => {
|
|
10
|
+
const object = payload && typeof payload === 'object' && !Array.isArray(payload);
|
|
11
|
+
const valid = object && (endpoint === IMAGE_INPUT_ENDPOINTS.get
|
|
12
|
+
? Object.keys(payload).length === 0
|
|
13
|
+
: endpoint === IMAGE_INPUT_ENDPOINTS.set && Object.keys(payload).length === 4
|
|
14
|
+
&& normalizeImageInputSettings(payload));
|
|
15
|
+
if (!valid) return { ok: false, error: { code: 'bad-request', message: '图片限制无效:请输入正整数,原图接收上限和总量上限不能小于单图上限。' } };
|
|
16
|
+
if (signal?.aborted) return { ok: false, error: { code: 'cancelled', message: 'Request cancelled.' } };
|
|
17
|
+
try {
|
|
18
|
+
const value = endpoint === IMAGE_INPUT_ENDPOINTS.get ? await store.get() : await store.set(payload);
|
|
19
|
+
return { ok: true, value };
|
|
20
|
+
} catch {
|
|
21
|
+
return { ok: false, error: { code: 'image-input-settings-unavailable', message: '无法读取或保存图片设置,请稍后重试。' } };
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { registerManagementRpc } from '../management-rpc.mjs';
|
|
2
2
|
import { normalizeInboundTtlHours } from '../../src/channels/shared/inbound-ttl.mjs';
|
|
3
3
|
import { getInboundTtlRuntime } from './inbound-ttl-runtime.mjs';
|
|
4
|
+
import { getImageInputSettingsStore } from '../../src/channels/shared/image-input-settings-store.mjs';
|
|
5
|
+
import { createImageInputRpcHandler, IMAGE_INPUT_ENDPOINTS } from './image-input-rpc.mjs';
|
|
4
6
|
|
|
5
7
|
export const INBOUND_TTL_RPC_CHANNEL = '/dsh-im-settings';
|
|
6
8
|
export const INBOUND_TTL_ENDPOINTS = Object.freeze({
|
|
@@ -73,9 +75,12 @@ export function installInboundTtlRpc(ctx, options = {}) {
|
|
|
73
75
|
const runtime = options.runtime ?? getInboundTtlRuntime(ctx, options.config);
|
|
74
76
|
const logger = typeof ctx?.logger === 'function'
|
|
75
77
|
? ctx.logger('dsh-im:inbound-ttl') : (ctx?.logger ?? null);
|
|
78
|
+
const ttl = createInboundTtlRpcHandler({ ...runtime, logger });
|
|
79
|
+
const image = createImageInputRpcHandler(getImageInputSettingsStore(options.config));
|
|
76
80
|
return registerManagementRpc(ctx,
|
|
77
81
|
INBOUND_TTL_RPC_CHANNEL,
|
|
78
|
-
|
|
82
|
+
(endpoint, payload, signal) => Object.values(IMAGE_INPUT_ENDPOINTS).includes(endpoint)
|
|
83
|
+
? image(endpoint, payload, signal) : ttl(endpoint, payload, signal),
|
|
79
84
|
{ authority: 'loopback' },
|
|
80
85
|
);
|
|
81
86
|
}
|
|
@@ -10,6 +10,7 @@ import { apply as applyWecomApp } from './channels/wecom-app/index.mjs';
|
|
|
10
10
|
import { apply as applyWeixin } from './channels/weixin/index.mjs';
|
|
11
11
|
import { apply as applyWhatsapp } from './channels/whatsapp/index.mjs';
|
|
12
12
|
import { apply as applyIMessage } from './channels/imessage/index.mjs';
|
|
13
|
+
import { apply as applyEmail } from './channels/email/index.mjs';
|
|
13
14
|
import { installOutboundArtifactTool } from '../../src/channels/shared/semantic/artifact.mjs';
|
|
14
15
|
import { installHostLanguage } from './host-language.mjs';
|
|
15
16
|
import { installHostLanguageRpc } from './host-language-rpc.mjs';
|
|
@@ -60,6 +61,7 @@ export function createImHostPlugin(internals = {}) {
|
|
|
60
61
|
const startOffice = internals.applyOffice ?? applyOffice;
|
|
61
62
|
const startWhatsapp = internals.applyWhatsapp ?? applyWhatsapp;
|
|
62
63
|
const startIMessage = internals.applyIMessage ?? applyIMessage;
|
|
64
|
+
const startEmail = internals.applyEmail ?? applyEmail;
|
|
63
65
|
const channels = [
|
|
64
66
|
['feishu', startFeishu],
|
|
65
67
|
['weixin', startWeixin],
|
|
@@ -72,6 +74,7 @@ export function createImHostPlugin(internals = {}) {
|
|
|
72
74
|
['discord', startDiscord],
|
|
73
75
|
['whatsapp', startWhatsapp],
|
|
74
76
|
['imessage', startIMessage],
|
|
77
|
+
['email', startEmail],
|
|
75
78
|
['office', startOffice],
|
|
76
79
|
];
|
|
77
80
|
return Object.freeze({
|
|
@@ -382,7 +382,7 @@ class ModernHarnessApi {
|
|
|
382
382
|
return;
|
|
383
383
|
}
|
|
384
384
|
if (frame.type !== 'chunk' || frame.index !== stream.entries.length) return;
|
|
385
|
-
|
|
385
|
+
const entry = {
|
|
386
386
|
type: 'transient',
|
|
387
387
|
event: {
|
|
388
388
|
type: 'assistant/chunk',
|
|
@@ -392,7 +392,12 @@ class ModernHarnessApi {
|
|
|
392
392
|
time: frame.time,
|
|
393
393
|
data: { turn: stream.turn, step: stream.step, chunk: frame.chunk },
|
|
394
394
|
},
|
|
395
|
-
}
|
|
395
|
+
};
|
|
396
|
+
stream.entries.push(entry);
|
|
397
|
+
// History only exposes these transient chunks while the attempt is open.
|
|
398
|
+
// Broadcast each one as it arrives so live IM renderers cannot miss a
|
|
399
|
+
// short reasoning phase that starts and commits between two history polls.
|
|
400
|
+
this.#broadcast({ type: 'session/event', sessionId, event: entry.event });
|
|
396
401
|
}
|
|
397
402
|
|
|
398
403
|
#withAssistantStream(sessionId, entries) {
|
|
@@ -72,6 +72,16 @@ export function registerManagementRpc(ctx, channel, handler, { authority } = {})
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
/** Keep channel UI callers unchanged while using DSH's native correlation and response validation. */
|
|
75
|
-
export function callManagementRpc(connection, channel, method, payload, signal) {
|
|
76
|
-
|
|
75
|
+
export async function callManagementRpc(connection, channel, method, payload, signal) {
|
|
76
|
+
try {
|
|
77
|
+
return await connection.rpc.call('/api', rpcEndpoint(channel), { method, payload }, signal);
|
|
78
|
+
} catch (cause) {
|
|
79
|
+
if (signal?.aborted || cause?.name === 'AbortError') throw cause;
|
|
80
|
+
const error = new Error('无法访问 DSH 管理接口,请检查 DSH 连接或重新登录后重试。', { cause });
|
|
81
|
+
error.code = 'management-unreachable';
|
|
82
|
+
error.details = { operation: method, stage: 'management.request', occurredAt: new Date().toISOString(),
|
|
83
|
+
...(Number.isInteger(cause?.status) && cause.status >= 100 && cause.status <= 599 ? { httpStatus: cause.status } : {}),
|
|
84
|
+
};
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
77
87
|
}
|
|
@@ -142,20 +142,28 @@ if (client.includes('settings.plugins.tab') || clientSources.includes('settings.
|
|
|
142
142
|
throw new Error('client source or bundle still contains the legacy Plugins-tab settings entry');
|
|
143
143
|
}
|
|
144
144
|
// Connections still have no channel-enable toggle. Checkable inputs are owned
|
|
145
|
-
// only by the shared context editor
|
|
145
|
+
// only by the shared context editor, the saved-target Session sync row, the
|
|
146
|
+
// email settings, and the Telegram thinking-traces block.
|
|
146
147
|
// The context editor contains one switch template and one mapped field-input
|
|
147
|
-
// template; the delivery target adds one ordinary checkbox template
|
|
148
|
+
// template; the delivery target adds one ordinary checkbox template; the
|
|
149
|
+
// Telegram thinking-traces block adds one ordinary checkbox template.
|
|
148
150
|
const contextEditorSource = await readFile(resolve(root, 'plugin-src/client/context-enhancement.js'), 'utf8');
|
|
149
151
|
const deliverySettingsSource = await readFile(resolve(root, 'plugin-src/client/delivery-settings.js'), 'utf8');
|
|
152
|
+
const emailSettingsSource = await readFile(resolve(root, 'plugin-src/client/channels/email/index.js'), 'utf8');
|
|
153
|
+
const thinkingTracesSource = await readFile(resolve(root, 'plugin-src/client/channels/telegram/thinking-traces.js'), 'utf8');
|
|
150
154
|
const otherClientSources = clientSources
|
|
151
155
|
.replace(contextEditorSource, '')
|
|
152
|
-
.replace(deliverySettingsSource, '')
|
|
156
|
+
.replace(deliverySettingsSource, '')
|
|
157
|
+
.replace(emailSettingsSource, '')
|
|
158
|
+
.replace(thinkingTracesSource, '');
|
|
153
159
|
if (/role:\s*["']switch|type:\s*["']checkbox/.test(otherClientSources)
|
|
154
160
|
|| (deliverySettingsSource.match(/type:\s*["']checkbox["']/g) ?? []).length !== 1
|
|
155
161
|
|| /role:\s*["']switch["']/u.test(deliverySettingsSource)
|
|
162
|
+
|| (thinkingTracesSource.match(/type:\s*["']checkbox["']/g) ?? []).length !== 1
|
|
163
|
+
|| /role:\s*["']switch["']/u.test(thinkingTracesSource)
|
|
156
164
|
|| (client.match(/role:\s*["']switch["']/g) ?? []).length !== 1
|
|
157
|
-
|| (client.match(/type:\s*["']checkbox["']/g) ?? []).length !==
|
|
158
|
-
throw new Error('checkable inputs must be limited to context enhancement and
|
|
165
|
+
|| (client.match(/type:\s*["']checkbox["']/g) ?? []).length !== 4) {
|
|
166
|
+
throw new Error('checkable inputs must be limited to context enhancement, Session sync, email settings, and the Telegram thinking-traces toggle');
|
|
159
167
|
}
|
|
160
168
|
for (const marker of ['bot.context-enhancement.set', '<dsh_im_source>', '<dsh_im_source_guidance>']) {
|
|
161
169
|
if (!host.includes(marker) || !client.includes(marker)) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { connectionErrorChain, connectionStageError } from '../shared/connection-error.mjs';
|
|
1
2
|
import { randomUUID } from 'node:crypto';
|
|
2
3
|
import { createRequire } from 'node:module';
|
|
3
4
|
|
|
@@ -62,17 +63,7 @@ export function installedDingtalkConnectionDependencies() {
|
|
|
62
63
|
return installedDependencyVersions;
|
|
63
64
|
}
|
|
64
65
|
|
|
65
|
-
function errorChain(error) {
|
|
66
|
-
const chain = [];
|
|
67
|
-
const seen = new Set();
|
|
68
|
-
let current = error;
|
|
69
|
-
while (current && typeof current === 'object' && chain.length < 4 && !seen.has(current)) {
|
|
70
|
-
seen.add(current);
|
|
71
|
-
chain.push(current);
|
|
72
|
-
current = current.cause;
|
|
73
|
-
}
|
|
74
|
-
return chain;
|
|
75
|
-
}
|
|
66
|
+
function errorChain(error) { return connectionErrorChain(error).chain; }
|
|
76
67
|
|
|
77
68
|
function statusFrom(error) {
|
|
78
69
|
if (Number.isInteger(error?.status)) return error.status;
|
|
@@ -153,7 +144,9 @@ export function dingtalkRuntimeStartError(code, cause) {
|
|
|
153
144
|
);
|
|
154
145
|
error.name = 'DingtalkRuntimeStartError';
|
|
155
146
|
error.code = STAGE_CODES.has(code) ? code : 'dingtalk-runtime-prepare-failed';
|
|
156
|
-
return error
|
|
147
|
+
return connectionStageError(error, ({ 'dingtalk-harness-connect-failed': 'harness.check',
|
|
148
|
+
'dingtalk-runtime-prepare-failed': 'runtime.prepare', 'dingtalk-stream-client-load-failed': 'sdk.load',
|
|
149
|
+
'dingtalk-stream-listener-failed': 'runtime.prepare', 'dingtalk-stream-connect-failed': 'connection.start' })[error.code]);
|
|
157
150
|
}
|
|
158
151
|
|
|
159
152
|
/** Creates browser-safe guidance plus a redacted Host-log diagnostic for one connection failure. */
|
|
@@ -68,6 +68,8 @@ export class DingtalkDeviceAuthError extends Error {
|
|
|
68
68
|
this.name = 'DingtalkDeviceAuthError';
|
|
69
69
|
this.code = code;
|
|
70
70
|
this.action = action;
|
|
71
|
+
if (Number.isInteger(options.status)) this.status = options.status;
|
|
72
|
+
if (Number.isFinite(options.timeoutMs)) this.timeoutMs = options.timeoutMs;
|
|
71
73
|
}
|
|
72
74
|
}
|
|
73
75
|
|
|
@@ -199,7 +201,7 @@ export class DingtalkDeviceAuth {
|
|
|
199
201
|
'timeout',
|
|
200
202
|
`DingTalk ${action} request timed out`,
|
|
201
203
|
action,
|
|
202
|
-
{ cause: error },
|
|
204
|
+
{ cause: error, timeoutMs: this.#timeoutMs },
|
|
203
205
|
);
|
|
204
206
|
}
|
|
205
207
|
if (error?.name === 'AbortError') throw error;
|
|
@@ -215,6 +217,7 @@ export class DingtalkDeviceAuth {
|
|
|
215
217
|
'http-error',
|
|
216
218
|
`DingTalk ${action} request failed`,
|
|
217
219
|
action,
|
|
220
|
+
{ status: response?.status },
|
|
218
221
|
);
|
|
219
222
|
}
|
|
220
223
|
let value;
|
|
@@ -65,6 +65,7 @@ import {
|
|
|
65
65
|
import { recoverAssistantTextByTimestamp } from '../shared/session-reply-recovery.mjs';
|
|
66
66
|
import { DINGTALK_RECENT_OUTBOUND_MATCH_TOLERANCE_MS } from './state-store.mjs';
|
|
67
67
|
import {
|
|
68
|
+
messageFailureDiagnostic,
|
|
68
69
|
channelDeliveryFailure,
|
|
69
70
|
clearLastMessageFailure,
|
|
70
71
|
messageFailureText,
|
|
@@ -740,7 +741,7 @@ export class DingtalkHarnessBridge {
|
|
|
740
741
|
const failure = setLastMessageFailure(this.#status, error);
|
|
741
742
|
this.#logger.error?.(
|
|
742
743
|
`[dsh-dingtalk] failed to process a command [${failure.referenceId}]`,
|
|
743
|
-
|
|
744
|
+
messageFailureDiagnostic(error, failure),
|
|
744
745
|
);
|
|
745
746
|
return this.#send(sessionWebhook, messageFailureText(failure), this.#atUsersFor(message)).catch(() => undefined);
|
|
746
747
|
}).finally(() => {
|
|
@@ -1207,7 +1208,7 @@ export class DingtalkHarnessBridge {
|
|
|
1207
1208
|
const failure = setLastMessageFailure(this.#status, error);
|
|
1208
1209
|
this.#logger.error?.(
|
|
1209
1210
|
`[dsh-dingtalk] failed to process a batch input message [${failure.referenceId}]`,
|
|
1210
|
-
|
|
1211
|
+
messageFailureDiagnostic(error, failure),
|
|
1211
1212
|
);
|
|
1212
1213
|
await this.#send(sessionWebhook, messageFailureText(failure)).catch(() => undefined);
|
|
1213
1214
|
}).finally(() => {
|
|
@@ -1338,7 +1339,7 @@ export class DingtalkHarnessBridge {
|
|
|
1338
1339
|
signal: this.#signal,
|
|
1339
1340
|
});
|
|
1340
1341
|
let content = hasInboundImages(modelMessage) || hasReply
|
|
1341
|
-
? await promptContentForInboundMessage(modelMessage, { signal: this.#signal })
|
|
1342
|
+
? await promptContentForInboundMessage(modelMessage, { signal: this.#signal, deferImages: true })
|
|
1342
1343
|
: undefined;
|
|
1343
1344
|
const snapshot = this.#acceptedMessageIds.get(messageId);
|
|
1344
1345
|
let contextEnhanced = false;
|
|
@@ -1395,6 +1396,7 @@ export class DingtalkHarnessBridge {
|
|
|
1395
1396
|
}),
|
|
1396
1397
|
onInteractionResolved: (resolution) => this.#handleInteractionResolved(resolution),
|
|
1397
1398
|
files: modelMessage.files,
|
|
1399
|
+
images: modelMessage.images,
|
|
1398
1400
|
},
|
|
1399
1401
|
});
|
|
1400
1402
|
if (batchSubmission) {
|
|
@@ -1486,7 +1488,7 @@ export class DingtalkHarnessBridge {
|
|
|
1486
1488
|
});
|
|
1487
1489
|
this.#logger.error?.(
|
|
1488
1490
|
`[dsh-dingtalk] failed to process an inbound message [${failure.referenceId}]`,
|
|
1489
|
-
|
|
1491
|
+
messageFailureDiagnostic(error, failure),
|
|
1490
1492
|
);
|
|
1491
1493
|
try {
|
|
1492
1494
|
const errorText = messageFailureText(failure);
|