@xmanrui/dsh-im 4.9.1 → 4.11.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/PROACTIVE_DELIVERY.en.md +23 -1
- package/PROACTIVE_DELIVERY.md +23 -1
- package/README.en.md +5 -1
- package/README.md +6 -2
- package/lib/client.js +2077 -673
- package/lib/index.js +277 -272
- package/package.json +13 -1
- package/plugin-src/client/channel-card-meta.js +9 -1
- package/plugin-src/client/channels/dingtalk/api.js +4 -0
- package/plugin-src/client/channels/dingtalk/index.js +25 -2
- package/plugin-src/client/channels/feishu/api.js +6 -0
- package/plugin-src/client/channels/feishu/group-settings.js +581 -0
- package/plugin-src/client/channels/feishu/index.js +26 -150
- package/plugin-src/client/channels/qq/api.js +4 -0
- package/plugin-src/client/channels/qq/index.js +25 -2
- package/plugin-src/client/channels/shared/token-api.js +4 -0
- package/plugin-src/client/channels/shared/token-channel.js +25 -3
- package/plugin-src/client/channels/wecom/api.js +4 -0
- package/plugin-src/client/channels/wecom/index.js +25 -2
- package/plugin-src/client/channels/weixin/api.js +4 -0
- package/plugin-src/client/channels/weixin/index.js +25 -2
- package/plugin-src/client/channels/whatsapp/api.js +4 -0
- package/plugin-src/client/channels/whatsapp/index.js +24 -2
- package/plugin-src/client/delivery-settings.js +73 -5
- package/plugin-src/client/global-settings.js +341 -0
- package/plugin-src/client/i18n.js +49 -0
- package/plugin-src/client/index.js +47 -6
- package/plugin-src/client/model-setting.js +104 -0
- package/plugin-src/client/styles.js +99 -0
- package/plugin-src/host/channels/dingtalk/production.mjs +15 -0
- package/plugin-src/host/channels/dingtalk/rpc.mjs +11 -0
- package/plugin-src/host/channels/feishu/production.mjs +17 -0
- package/plugin-src/host/channels/feishu/rpc.mjs +32 -0
- package/plugin-src/host/channels/qq/production.mjs +15 -0
- package/plugin-src/host/channels/qq/rpc.mjs +11 -0
- package/plugin-src/host/channels/shared/model-setting-rpc.mjs +19 -0
- package/plugin-src/host/channels/shared/production.mjs +15 -0
- package/plugin-src/host/channels/shared/rpc.mjs +9 -0
- package/plugin-src/host/channels/shared/workspace-rpc.mjs +2 -0
- package/plugin-src/host/channels/slack/production.mjs +15 -0
- package/plugin-src/host/channels/slack/rpc.mjs +9 -0
- package/plugin-src/host/channels/wecom/production.mjs +15 -0
- package/plugin-src/host/channels/wecom/rpc.mjs +11 -0
- package/plugin-src/host/channels/weixin/production.mjs +15 -0
- package/plugin-src/host/channels/weixin/rpc.mjs +11 -0
- package/plugin-src/host/channels/whatsapp/production.mjs +15 -0
- package/plugin-src/host/channels/whatsapp/rpc.mjs +11 -0
- package/plugin-src/host/delivery-adapter.mjs +142 -3
- package/plugin-src/host/delivery-rpc.mjs +12 -0
- package/plugin-src/host/delivery-service.mjs +125 -3
- package/plugin-src/host/delivery-suggestions.mjs +46 -0
- package/plugin-src/host/harness-session-coordinator.mjs +32 -10
- package/plugin-src/host/inbound-ttl-rpc.mjs +83 -0
- package/plugin-src/host/inbound-ttl-runtime.mjs +85 -0
- package/plugin-src/host/inbound-ttl-service.mjs +179 -0
- package/plugin-src/host/index.mjs +26 -1
- package/plugin-src/host/modern-harness-api.mjs +58 -2
- package/plugin-src/host/session-sync-coordinator.mjs +217 -0
- package/scripts/verify-package.mjs +12 -6
- package/src/channels/feishu/bridge.mjs +164 -4
- package/src/channels/feishu/feishu-channel.mjs +45 -10
- package/src/channels/feishu/feishu-runtime.mjs +9 -0
- package/src/channels/feishu/message-utils.mjs +13 -0
- package/src/channels/feishu/multi-bot-controller.mjs +16 -0
- package/src/channels/feishu/plugin-config-store.mjs +1 -0
- package/src/channels/feishu/state-store.mjs +25 -0
- package/src/channels/shared/bot-workspace-store.mjs +291 -38
- package/src/channels/shared/harness-client.mjs +124 -43
- package/src/channels/shared/inbound-file.mjs +117 -2
- package/src/channels/shared/inbound-ttl-store.mjs +110 -0
- package/src/channels/shared/inbound-ttl.mjs +22 -0
- package/src/channels/shared/model-setting.mjs +110 -0
|
@@ -13,12 +13,17 @@ import {
|
|
|
13
13
|
observeBotWorkspaceRemovals,
|
|
14
14
|
} from '../../../../src/channels/shared/bot-workspace-store.mjs';
|
|
15
15
|
import { listAgentPresetCatalog } from '../../../../src/channels/shared/agent-preset.mjs';
|
|
16
|
+
import { listModelCatalog } from '../../../../src/channels/shared/model-setting.mjs';
|
|
16
17
|
import { createDeliveryAdapter } from '../../delivery-adapter.mjs';
|
|
17
18
|
import { createTokenConnectionSupervisor } from '../shared/connection-supervisor.mjs';
|
|
18
19
|
import { pluginPaths } from '../shared/production.mjs';
|
|
19
20
|
import { createHarnessCommandExecutor } from '../../harness-command-executor.mjs';
|
|
20
21
|
import { harnessConnection } from '../../harness-connection.mjs';
|
|
21
22
|
import { createHarnessSessionExecutors } from '../../harness-session-coordinator.mjs';
|
|
23
|
+
import {
|
|
24
|
+
getInboundTtlRuntime,
|
|
25
|
+
registerInboundTtlWorkspaces,
|
|
26
|
+
} from '../../inbound-ttl-runtime.mjs';
|
|
22
27
|
import {
|
|
23
28
|
accessPolicyProvider,
|
|
24
29
|
initialAccessPolicyFor,
|
|
@@ -63,10 +68,18 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
63
68
|
return state;
|
|
64
69
|
};
|
|
65
70
|
const commandExecutor = createHarnessCommandExecutor(ctx, internals.commandExecutor);
|
|
71
|
+
const inboundTtl = internals.inboundTtl ?? getInboundTtlRuntime(ctx, config);
|
|
72
|
+
const inboundTtlService = inboundTtl?.service ?? inboundTtl;
|
|
73
|
+
registerInboundTtlWorkspaces(ctx, inboundTtlService, {
|
|
74
|
+
workspaces,
|
|
75
|
+
configStore: observedConfigStore,
|
|
76
|
+
defaultWorkspace,
|
|
77
|
+
});
|
|
66
78
|
const { controlExecutor, sessionMaintenanceExecutor, fileIngressExecutor } = createHarnessSessionExecutors(ctx, {
|
|
67
79
|
controlExecutor: internals.controlExecutor,
|
|
68
80
|
sessionMaintenanceExecutor: internals.sessionMaintenanceExecutor,
|
|
69
81
|
fileIngressExecutor: internals.fileIngressExecutor,
|
|
82
|
+
inboundTtlService,
|
|
70
83
|
});
|
|
71
84
|
const harness = new ResolvedHarness({
|
|
72
85
|
...connection,
|
|
@@ -78,6 +91,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
78
91
|
...(sessionMaintenanceExecutor ? { sessionMaintenanceExecutor } : {}),
|
|
79
92
|
...(fileIngressExecutor ? { fileIngressExecutor } : {}),
|
|
80
93
|
});
|
|
94
|
+
const modelCatalog = () => listModelCatalog(harness);
|
|
81
95
|
const coreController = new ResolvedController({
|
|
82
96
|
credentials: ctx.credentials,
|
|
83
97
|
configStore: observedConfigStore,
|
|
@@ -130,6 +144,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
130
144
|
workspaces,
|
|
131
145
|
stateFor,
|
|
132
146
|
agentPresetCatalog,
|
|
147
|
+
modelCatalog,
|
|
133
148
|
});
|
|
134
149
|
const supervisor = createSupervisor({
|
|
135
150
|
channel: 'slack',
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
SET_AGENT_PRESET_ENDPOINT,
|
|
12
12
|
validAgentPresetPayload,
|
|
13
13
|
} from '../shared/agent-preset-rpc.mjs';
|
|
14
|
+
import { SET_MODEL_ENDPOINT, validModelPayload } from '../shared/model-setting-rpc.mjs';
|
|
14
15
|
|
|
15
16
|
export const SLACK_RPC_CHANNEL = '/slack';
|
|
16
17
|
export const SLACK_ENDPOINTS = Object.freeze({
|
|
@@ -19,6 +20,7 @@ export const SLACK_ENDPOINTS = Object.freeze({
|
|
|
19
20
|
reconnectBot: 'bot.reconnect',
|
|
20
21
|
deleteBot: 'bot.delete',
|
|
21
22
|
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
|
23
|
+
setModel: SET_MODEL_ENDPOINT,
|
|
22
24
|
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
23
25
|
setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
|
|
24
26
|
setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
|
|
@@ -75,6 +77,9 @@ function payloadFailure(endpoint, payload) {
|
|
|
75
77
|
return validWorkspacePayload(payload)
|
|
76
78
|
? null : '请输入工作区绝对路径。';
|
|
77
79
|
}
|
|
80
|
+
if (endpoint === SLACK_ENDPOINTS.setModel) {
|
|
81
|
+
return validModelPayload(payload) ? null : '请选择有效模型。';
|
|
82
|
+
}
|
|
78
83
|
if (endpoint === SLACK_ENDPOINTS.setAgentPreset) {
|
|
79
84
|
return validAgentPresetPayload(payload)
|
|
80
85
|
? null : '请选择 Agent Preset。';
|
|
@@ -166,6 +171,10 @@ export function createSlackRpcHandler(controller) {
|
|
|
166
171
|
if (typeof controller.updateWorkspace !== 'function') throw new Error('Workspace update is unavailable');
|
|
167
172
|
value = await controller.updateWorkspace(payload.botId, payload.workspace);
|
|
168
173
|
}
|
|
174
|
+
else if (endpoint === SLACK_ENDPOINTS.setModel) {
|
|
175
|
+
if (typeof controller.updateModel !== 'function') throw new Error('Model update is unavailable');
|
|
176
|
+
value = await controller.updateModel(payload.botId, payload.model);
|
|
177
|
+
}
|
|
169
178
|
else if (endpoint === SLACK_ENDPOINTS.setContextEnhancement) {
|
|
170
179
|
if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
|
|
171
180
|
value = await controller.updateContextEnhancement(payload.botId, payload.config);
|
|
@@ -15,11 +15,16 @@ import {
|
|
|
15
15
|
observeBotWorkspaceRemovals,
|
|
16
16
|
} from '../../../../src/channels/shared/bot-workspace-store.mjs';
|
|
17
17
|
import { listAgentPresetCatalog } from '../../../../src/channels/shared/agent-preset.mjs';
|
|
18
|
+
import { listModelCatalog } from '../../../../src/channels/shared/model-setting.mjs';
|
|
18
19
|
import { createDeliveryAdapter } from '../../delivery-adapter.mjs';
|
|
19
20
|
import { createConnectionSupervisor } from './connection-supervisor.mjs';
|
|
20
21
|
import { createHarnessCommandExecutor } from '../../harness-command-executor.mjs';
|
|
21
22
|
import { harnessConnection } from '../../harness-connection.mjs';
|
|
22
23
|
import { createHarnessSessionExecutors } from '../../harness-session-coordinator.mjs';
|
|
24
|
+
import {
|
|
25
|
+
getInboundTtlRuntime,
|
|
26
|
+
registerInboundTtlWorkspaces,
|
|
27
|
+
} from '../../inbound-ttl-runtime.mjs';
|
|
23
28
|
import {
|
|
24
29
|
accessPolicyProvider,
|
|
25
30
|
initialAccessPolicyFor,
|
|
@@ -78,10 +83,18 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
78
83
|
return state;
|
|
79
84
|
};
|
|
80
85
|
const commandExecutor = createHarnessCommandExecutor(ctx, internals.commandExecutor);
|
|
86
|
+
const inboundTtl = internals.inboundTtl ?? getInboundTtlRuntime(ctx, config);
|
|
87
|
+
const inboundTtlService = inboundTtl?.service ?? inboundTtl;
|
|
88
|
+
registerInboundTtlWorkspaces(ctx, inboundTtlService, {
|
|
89
|
+
workspaces,
|
|
90
|
+
configStore: observedConfigStore,
|
|
91
|
+
defaultWorkspace,
|
|
92
|
+
});
|
|
81
93
|
const { controlExecutor, sessionMaintenanceExecutor, fileIngressExecutor } = createHarnessSessionExecutors(ctx, {
|
|
82
94
|
controlExecutor: internals.controlExecutor,
|
|
83
95
|
sessionMaintenanceExecutor: internals.sessionMaintenanceExecutor,
|
|
84
96
|
fileIngressExecutor: internals.fileIngressExecutor,
|
|
97
|
+
inboundTtlService,
|
|
85
98
|
});
|
|
86
99
|
const harness = new Harness({
|
|
87
100
|
...connection,
|
|
@@ -93,6 +106,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
93
106
|
...(sessionMaintenanceExecutor ? { sessionMaintenanceExecutor } : {}),
|
|
94
107
|
...(fileIngressExecutor ? { fileIngressExecutor } : {}),
|
|
95
108
|
});
|
|
109
|
+
const modelCatalog = () => listModelCatalog(harness);
|
|
96
110
|
const coreController = new Controller({
|
|
97
111
|
qrAuth,
|
|
98
112
|
credentials: ctx.credentials,
|
|
@@ -145,6 +159,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
145
159
|
workspaces,
|
|
146
160
|
stateFor,
|
|
147
161
|
agentPresetCatalog,
|
|
162
|
+
modelCatalog,
|
|
148
163
|
});
|
|
149
164
|
const supervisor = createSupervisor({
|
|
150
165
|
controller,
|
|
@@ -4,6 +4,7 @@ import { SET_ACCESS_POLICY_ENDPOINT, validAccessPolicyPayload } from '../shared/
|
|
|
4
4
|
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
|
5
5
|
import { publicWorkspaceError, SET_WORKSPACE_ENDPOINT, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
|
|
6
6
|
import { SET_AGENT_PRESET_ENDPOINT, validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
|
|
7
|
+
import { SET_MODEL_ENDPOINT, validModelPayload } from '../shared/model-setting-rpc.mjs';
|
|
7
8
|
import {
|
|
8
9
|
connectionTestTargetUnavailable,
|
|
9
10
|
publicConnectionTestResult,
|
|
@@ -19,6 +20,7 @@ export const WECOM_ENDPOINTS = Object.freeze({
|
|
|
19
20
|
reconnectBot: 'bot.reconnect',
|
|
20
21
|
deleteBot: 'bot.delete',
|
|
21
22
|
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
|
23
|
+
setModel: SET_MODEL_ENDPOINT,
|
|
22
24
|
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
23
25
|
setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
|
|
24
26
|
setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
|
|
@@ -76,6 +78,9 @@ function payloadFailure(endpoint, payload) {
|
|
|
76
78
|
return validWorkspacePayload(payload)
|
|
77
79
|
? null : '请输入工作区绝对路径。';
|
|
78
80
|
}
|
|
81
|
+
if (endpoint === WECOM_ENDPOINTS.setModel) {
|
|
82
|
+
return validModelPayload(payload) ? null : '请选择有效模型。';
|
|
83
|
+
}
|
|
79
84
|
if (endpoint === WECOM_ENDPOINTS.setAgentPreset) {
|
|
80
85
|
return validAgentPresetPayload(payload)
|
|
81
86
|
? null : '请选择 Agent Preset。';
|
|
@@ -184,6 +189,12 @@ export function createWecomRpcHandler(controller, { encodeQr = qrDataUrl } = {})
|
|
|
184
189
|
await controller.updateWorkspace(payload.botId, payload.workspace),
|
|
185
190
|
cachedEncode,
|
|
186
191
|
);
|
|
192
|
+
} else if (endpoint === WECOM_ENDPOINTS.setModel) {
|
|
193
|
+
if (typeof controller.updateModel !== 'function') throw new Error('Model update is unavailable');
|
|
194
|
+
value = await publicStatus(
|
|
195
|
+
await controller.updateModel(payload.botId, payload.model),
|
|
196
|
+
cachedEncode,
|
|
197
|
+
);
|
|
187
198
|
} else if (endpoint === WECOM_ENDPOINTS.setContextEnhancement) {
|
|
188
199
|
if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
|
|
189
200
|
value = await controller.updateContextEnhancement(
|
|
@@ -18,11 +18,16 @@ import {
|
|
|
18
18
|
observeBotWorkspaceRemovals,
|
|
19
19
|
} from '../../../../src/channels/shared/bot-workspace-store.mjs';
|
|
20
20
|
import { listAgentPresetCatalog } from '../../../../src/channels/shared/agent-preset.mjs';
|
|
21
|
+
import { listModelCatalog } from '../../../../src/channels/shared/model-setting.mjs';
|
|
21
22
|
import { createDeliveryAdapter } from '../../delivery-adapter.mjs';
|
|
22
23
|
import { createConnectionSupervisor } from './connection-supervisor.mjs';
|
|
23
24
|
import { createHarnessCommandExecutor } from '../../harness-command-executor.mjs';
|
|
24
25
|
import { harnessConnection } from '../../harness-connection.mjs';
|
|
25
26
|
import { createHarnessSessionExecutors } from '../../harness-session-coordinator.mjs';
|
|
27
|
+
import {
|
|
28
|
+
getInboundTtlRuntime,
|
|
29
|
+
registerInboundTtlWorkspaces,
|
|
30
|
+
} from '../../inbound-ttl-runtime.mjs';
|
|
26
31
|
import {
|
|
27
32
|
accessPolicyProvider,
|
|
28
33
|
initialAccessPolicyFor,
|
|
@@ -81,10 +86,18 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
81
86
|
return state;
|
|
82
87
|
};
|
|
83
88
|
const commandExecutor = createHarnessCommandExecutor(ctx, internals.commandExecutor);
|
|
89
|
+
const inboundTtl = internals.inboundTtl ?? getInboundTtlRuntime(ctx, config);
|
|
90
|
+
const inboundTtlService = inboundTtl?.service ?? inboundTtl;
|
|
91
|
+
registerInboundTtlWorkspaces(ctx, inboundTtlService, {
|
|
92
|
+
workspaces,
|
|
93
|
+
configStore: observedConfigStore,
|
|
94
|
+
defaultWorkspace,
|
|
95
|
+
});
|
|
84
96
|
const { controlExecutor, sessionMaintenanceExecutor, fileIngressExecutor } = createHarnessSessionExecutors(ctx, {
|
|
85
97
|
controlExecutor: internals.controlExecutor,
|
|
86
98
|
sessionMaintenanceExecutor: internals.sessionMaintenanceExecutor,
|
|
87
99
|
fileIngressExecutor: internals.fileIngressExecutor,
|
|
100
|
+
inboundTtlService,
|
|
88
101
|
});
|
|
89
102
|
const harness = new Harness({
|
|
90
103
|
...connection,
|
|
@@ -96,6 +109,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
96
109
|
...(sessionMaintenanceExecutor ? { sessionMaintenanceExecutor } : {}),
|
|
97
110
|
...(fileIngressExecutor ? { fileIngressExecutor } : {}),
|
|
98
111
|
});
|
|
112
|
+
const modelCatalog = () => listModelCatalog(harness);
|
|
99
113
|
const coreController = new Controller({
|
|
100
114
|
api,
|
|
101
115
|
credentials: ctx.credentials,
|
|
@@ -148,6 +162,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
148
162
|
workspaces,
|
|
149
163
|
stateFor,
|
|
150
164
|
agentPresetCatalog,
|
|
165
|
+
modelCatalog,
|
|
151
166
|
});
|
|
152
167
|
const supervisor = createSupervisor({
|
|
153
168
|
controller,
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
SET_AGENT_PRESET_ENDPOINT,
|
|
12
12
|
validAgentPresetPayload,
|
|
13
13
|
} from '../shared/agent-preset-rpc.mjs';
|
|
14
|
+
import { SET_MODEL_ENDPOINT, validModelPayload } from '../shared/model-setting-rpc.mjs';
|
|
14
15
|
import {
|
|
15
16
|
connectionTestTargetUnavailable,
|
|
16
17
|
publicConnectionTestResult,
|
|
@@ -26,6 +27,7 @@ export const WEIXIN_ENDPOINTS = Object.freeze({
|
|
|
26
27
|
reconnectBot: 'bot.reconnect',
|
|
27
28
|
deleteBot: 'bot.delete',
|
|
28
29
|
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
|
30
|
+
setModel: SET_MODEL_ENDPOINT,
|
|
29
31
|
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
30
32
|
setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
|
|
31
33
|
setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
|
|
@@ -83,6 +85,9 @@ function payloadFailure(endpoint, payload) {
|
|
|
83
85
|
return validWorkspacePayload(payload)
|
|
84
86
|
? null : '请输入工作区绝对路径。';
|
|
85
87
|
}
|
|
88
|
+
if (endpoint === WEIXIN_ENDPOINTS.setModel) {
|
|
89
|
+
return validModelPayload(payload) ? null : '请选择有效模型。';
|
|
90
|
+
}
|
|
86
91
|
if (endpoint === WEIXIN_ENDPOINTS.setAgentPreset) {
|
|
87
92
|
return validAgentPresetPayload(payload)
|
|
88
93
|
? null : '请选择 Agent Preset。';
|
|
@@ -219,6 +224,12 @@ export function createWeixinRpcHandler(controller, { encodeQr = qrDataUrl } = {}
|
|
|
219
224
|
await controller.updateWorkspace(payload.botId, payload.workspace),
|
|
220
225
|
cachedEncode,
|
|
221
226
|
);
|
|
227
|
+
} else if (endpoint === WEIXIN_ENDPOINTS.setModel) {
|
|
228
|
+
if (typeof controller.updateModel !== 'function') throw new Error('Model update is unavailable');
|
|
229
|
+
value = await publicStatus(
|
|
230
|
+
await controller.updateModel(payload.botId, payload.model),
|
|
231
|
+
cachedEncode,
|
|
232
|
+
);
|
|
222
233
|
} else if (endpoint === WEIXIN_ENDPOINTS.setContextEnhancement) {
|
|
223
234
|
if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
|
|
224
235
|
value = await controller.updateContextEnhancement(
|
|
@@ -18,11 +18,16 @@ import {
|
|
|
18
18
|
observeBotWorkspaceRemovals,
|
|
19
19
|
} from '../../../../src/channels/shared/bot-workspace-store.mjs';
|
|
20
20
|
import { listAgentPresetCatalog } from '../../../../src/channels/shared/agent-preset.mjs';
|
|
21
|
+
import { listModelCatalog } from '../../../../src/channels/shared/model-setting.mjs';
|
|
21
22
|
import { createDeliveryAdapter } from '../../delivery-adapter.mjs';
|
|
22
23
|
import { createTokenConnectionSupervisor } from '../shared/connection-supervisor.mjs';
|
|
23
24
|
import { createHarnessCommandExecutor } from '../../harness-command-executor.mjs';
|
|
24
25
|
import { harnessConnection } from '../../harness-connection.mjs';
|
|
25
26
|
import { createHarnessSessionExecutors } from '../../harness-session-coordinator.mjs';
|
|
27
|
+
import {
|
|
28
|
+
getInboundTtlRuntime,
|
|
29
|
+
registerInboundTtlWorkspaces,
|
|
30
|
+
} from '../../inbound-ttl-runtime.mjs';
|
|
26
31
|
import {
|
|
27
32
|
accessPolicyProvider,
|
|
28
33
|
initialAccessPolicyFor,
|
|
@@ -84,10 +89,18 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
84
89
|
return state;
|
|
85
90
|
};
|
|
86
91
|
const commandExecutor = createHarnessCommandExecutor(ctx, internals.commandExecutor);
|
|
92
|
+
const inboundTtl = internals.inboundTtl ?? getInboundTtlRuntime(ctx, config);
|
|
93
|
+
const inboundTtlService = inboundTtl?.service ?? inboundTtl;
|
|
94
|
+
registerInboundTtlWorkspaces(ctx, inboundTtlService, {
|
|
95
|
+
workspaces,
|
|
96
|
+
configStore: observedConfigStore,
|
|
97
|
+
defaultWorkspace,
|
|
98
|
+
});
|
|
87
99
|
const { controlExecutor, sessionMaintenanceExecutor, fileIngressExecutor } = createHarnessSessionExecutors(ctx, {
|
|
88
100
|
controlExecutor: internals.controlExecutor,
|
|
89
101
|
sessionMaintenanceExecutor: internals.sessionMaintenanceExecutor,
|
|
90
102
|
fileIngressExecutor: internals.fileIngressExecutor,
|
|
103
|
+
inboundTtlService,
|
|
91
104
|
});
|
|
92
105
|
const harness = new Harness({
|
|
93
106
|
...connection,
|
|
@@ -99,6 +112,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
99
112
|
...(sessionMaintenanceExecutor ? { sessionMaintenanceExecutor } : {}),
|
|
100
113
|
...(fileIngressExecutor ? { fileIngressExecutor } : {}),
|
|
101
114
|
});
|
|
115
|
+
const modelCatalog = () => listModelCatalog(harness);
|
|
102
116
|
const coreController = new Controller({
|
|
103
117
|
configStore: observedConfigStore,
|
|
104
118
|
authPath: paths.authPath,
|
|
@@ -155,6 +169,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
155
169
|
workspaces,
|
|
156
170
|
stateFor,
|
|
157
171
|
agentPresetCatalog,
|
|
172
|
+
modelCatalog,
|
|
158
173
|
});
|
|
159
174
|
const supervisor = createSupervisor({
|
|
160
175
|
channel: 'whatsapp',
|
|
@@ -6,6 +6,7 @@ import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from
|
|
|
6
6
|
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
|
7
7
|
import { publicWorkspaceError, SET_WORKSPACE_ENDPOINT, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
|
|
8
8
|
import { SET_AGENT_PRESET_ENDPOINT, validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
|
|
9
|
+
import { SET_MODEL_ENDPOINT, validModelPayload } from '../shared/model-setting-rpc.mjs';
|
|
9
10
|
|
|
10
11
|
export const WHATSAPP_RPC_CHANNEL = '/whatsapp';
|
|
11
12
|
export const WHATSAPP_ENDPOINTS = Object.freeze({
|
|
@@ -17,6 +18,7 @@ export const WHATSAPP_ENDPOINTS = Object.freeze({
|
|
|
17
18
|
deleteBot: 'bot.delete',
|
|
18
19
|
setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
|
|
19
20
|
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
|
21
|
+
setModel: SET_MODEL_ENDPOINT,
|
|
20
22
|
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
21
23
|
setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
|
|
22
24
|
});
|
|
@@ -62,6 +64,9 @@ function payloadFailure(endpoint, payload) {
|
|
|
62
64
|
return validWorkspacePayload(payload)
|
|
63
65
|
? null : '请输入工作区绝对路径。';
|
|
64
66
|
}
|
|
67
|
+
if (endpoint === WHATSAPP_ENDPOINTS.setModel) {
|
|
68
|
+
return validModelPayload(payload) ? null : '请选择有效模型。';
|
|
69
|
+
}
|
|
65
70
|
if (endpoint === WHATSAPP_ENDPOINTS.setAgentPreset) {
|
|
66
71
|
return validAgentPresetPayload(payload)
|
|
67
72
|
? null : '请选择 Agent Preset。';
|
|
@@ -172,6 +177,12 @@ export function createWhatsappRpcHandler(controller, { encodeQr = qrDataUrl } =
|
|
|
172
177
|
await controller.updateWorkspace(payload.botId, payload.workspace),
|
|
173
178
|
cachedEncode,
|
|
174
179
|
);
|
|
180
|
+
} else if (endpoint === WHATSAPP_ENDPOINTS.setModel) {
|
|
181
|
+
if (typeof controller.updateModel !== 'function') throw new Error('Model update is unavailable');
|
|
182
|
+
value = await publicStatus(
|
|
183
|
+
await controller.updateModel(payload.botId, payload.model),
|
|
184
|
+
cachedEncode,
|
|
185
|
+
);
|
|
175
186
|
} else if (endpoint === WHATSAPP_ENDPOINTS.setContextEnhancement) {
|
|
176
187
|
if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
|
|
177
188
|
value = await controller.updateContextEnhancement(
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
deliverySuggestionsFromSessions,
|
|
3
|
+
privateConversationKeyMatchesTarget,
|
|
4
|
+
resolvePrivateConversationKey,
|
|
5
|
+
} from './delivery-suggestions.mjs';
|
|
2
6
|
|
|
3
7
|
const CHANNELS = new Set([
|
|
4
8
|
'weixin',
|
|
@@ -22,6 +26,12 @@ function invalidTarget(message) {
|
|
|
22
26
|
return error;
|
|
23
27
|
}
|
|
24
28
|
|
|
29
|
+
function sessionSyncUnavailable(message = 'Session sync is unavailable for this target') {
|
|
30
|
+
const error = new Error(message);
|
|
31
|
+
error.code = 'session-sync-unavailable';
|
|
32
|
+
return error;
|
|
33
|
+
}
|
|
34
|
+
|
|
25
35
|
function isRecord(value) {
|
|
26
36
|
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
27
37
|
}
|
|
@@ -129,6 +139,49 @@ export function normalizeDeliveryTarget(channel, value, { targetIdRequired = tru
|
|
|
129
139
|
return normalized;
|
|
130
140
|
}
|
|
131
141
|
|
|
142
|
+
function targetWithoutSessionSync(target) {
|
|
143
|
+
return {
|
|
144
|
+
targetId: target.targetId,
|
|
145
|
+
...(target.name === undefined ? {} : { name: target.name }),
|
|
146
|
+
kind: target.kind,
|
|
147
|
+
route: target.route,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function sessionSyncConfigs(workspaces, botId) {
|
|
152
|
+
if (typeof workspaces.listSessionSyncTargets !== 'function') return [];
|
|
153
|
+
const targets = workspaces.listSessionSyncTargets();
|
|
154
|
+
return botId === undefined ? targets : targets.filter((target) => target.botId === botId);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function stateSessions(state) {
|
|
158
|
+
if (!state || typeof state.snapshot !== 'function') return null;
|
|
159
|
+
const sessions = state.snapshot()?.sessions;
|
|
160
|
+
return sessions && typeof sessions === 'object' && !Array.isArray(sessions) ? sessions : null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function sessionSyncView(channel, target, sessions, configured) {
|
|
164
|
+
if (configured) {
|
|
165
|
+
const valid = privateConversationKeyMatchesTarget(
|
|
166
|
+
channel,
|
|
167
|
+
configured.conversationKey,
|
|
168
|
+
target,
|
|
169
|
+
);
|
|
170
|
+
if (!valid || !sessions) return { enabled: true, state: 'unavailable' };
|
|
171
|
+
return {
|
|
172
|
+
enabled: true,
|
|
173
|
+
state: typeof sessions[configured.conversationKey] === 'string'
|
|
174
|
+
&& Boolean(sessions[configured.conversationKey])
|
|
175
|
+
? 'active'
|
|
176
|
+
: 'waiting',
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
const conversationKey = sessions
|
|
180
|
+
? resolvePrivateConversationKey(channel, sessions, target)
|
|
181
|
+
: null;
|
|
182
|
+
return { enabled: false, state: conversationKey ? 'off' : 'unavailable' };
|
|
183
|
+
}
|
|
184
|
+
|
|
132
185
|
/** Bind one channel's existing workspace store and unwrapped controller to DeliveryService. */
|
|
133
186
|
export function createDeliveryAdapter({ channel, workspaces, coreController, stateFor }) {
|
|
134
187
|
if (!supportsDeliveryChannel(channel)) throw new TypeError(`Unsupported delivery channel: ${channel}`);
|
|
@@ -144,7 +197,23 @@ export function createDeliveryAdapter({ channel, workspaces, coreController, sta
|
|
|
144
197
|
return Object.freeze({
|
|
145
198
|
channel,
|
|
146
199
|
ownsBot: (botId) => workspaces.has(botId),
|
|
147
|
-
|
|
200
|
+
listBots: () => workspaces.listBotIds(),
|
|
201
|
+
async listTargets(botId) {
|
|
202
|
+
const targets = workspaces.listDeliveryTargets(botId);
|
|
203
|
+
const configured = new Map(
|
|
204
|
+
sessionSyncConfigs(workspaces, botId).map((target) => [target.targetId, target]),
|
|
205
|
+
);
|
|
206
|
+
let sessions = null;
|
|
207
|
+
try {
|
|
208
|
+
sessions = stateSessions(await stateFor(botId));
|
|
209
|
+
} catch {
|
|
210
|
+
// Target CRUD remains usable when its persisted conversation state cannot be read.
|
|
211
|
+
}
|
|
212
|
+
return targets.map((target) => ({
|
|
213
|
+
...target,
|
|
214
|
+
sessionSync: sessionSyncView(channel, target, sessions, configured.get(target.targetId)),
|
|
215
|
+
}));
|
|
216
|
+
},
|
|
148
217
|
async listSuggestions(botId) {
|
|
149
218
|
const state = await stateFor(botId);
|
|
150
219
|
if (!state || typeof state.snapshot !== 'function') {
|
|
@@ -168,12 +237,82 @@ export function createDeliveryAdapter({ channel, workspaces, coreController, sta
|
|
|
168
237
|
),
|
|
169
238
|
deleteTarget: (botId, targetId) => workspaces.deleteDeliveryTarget(botId, targetId),
|
|
170
239
|
async sendText(botId, target, text, options = {}) {
|
|
171
|
-
const normalized = normalizeDeliveryTarget(channel, target);
|
|
240
|
+
const normalized = normalizeDeliveryTarget(channel, targetWithoutSessionSync(target));
|
|
172
241
|
if (typeof coreController.sendProactiveText !== 'function') {
|
|
173
242
|
throw new TypeError('delivery controller cannot send proactive text');
|
|
174
243
|
}
|
|
175
244
|
await coreController.sendProactiveText(botId, normalized, text, options);
|
|
176
245
|
return { sent: true };
|
|
177
246
|
},
|
|
247
|
+
async setSessionSync(botId, targetId, enabled) {
|
|
248
|
+
if (typeof workspaces.setDeliveryTargetSessionSync !== 'function') {
|
|
249
|
+
throw sessionSyncUnavailable();
|
|
250
|
+
}
|
|
251
|
+
const target = workspaces.deliveryTargetFor(botId, targetId);
|
|
252
|
+
if (!target) {
|
|
253
|
+
const error = new Error('Unknown target');
|
|
254
|
+
error.code = 'unknown-target';
|
|
255
|
+
throw error;
|
|
256
|
+
}
|
|
257
|
+
if (!enabled) {
|
|
258
|
+
await workspaces.setDeliveryTargetSessionSync(botId, targetId, null);
|
|
259
|
+
let sessions = null;
|
|
260
|
+
try {
|
|
261
|
+
sessions = stateSessions(await stateFor(botId));
|
|
262
|
+
} catch {
|
|
263
|
+
// The setting is already safely disabled; only its availability is unknown.
|
|
264
|
+
}
|
|
265
|
+
return sessionSyncView(channel, target, sessions, null);
|
|
266
|
+
}
|
|
267
|
+
const sessions = stateSessions(await stateFor(botId));
|
|
268
|
+
const conversationKey = resolvePrivateConversationKey(channel, sessions, target);
|
|
269
|
+
if (!conversationKey) throw sessionSyncUnavailable();
|
|
270
|
+
await workspaces.setDeliveryTargetSessionSync(botId, targetId, conversationKey);
|
|
271
|
+
return { enabled: true, state: 'active' };
|
|
272
|
+
},
|
|
273
|
+
async listSessionSyncTargets(sessionId) {
|
|
274
|
+
if (typeof sessionId !== 'string' || !sessionId) {
|
|
275
|
+
throw new TypeError('sessionId is required');
|
|
276
|
+
}
|
|
277
|
+
const matches = [];
|
|
278
|
+
for (const configured of sessionSyncConfigs(workspaces)) {
|
|
279
|
+
const target = workspaces.deliveryTargetFor(configured.botId, configured.targetId);
|
|
280
|
+
if (!target || !privateConversationKeyMatchesTarget(
|
|
281
|
+
channel,
|
|
282
|
+
configured.conversationKey,
|
|
283
|
+
target,
|
|
284
|
+
)) continue;
|
|
285
|
+
let sessions;
|
|
286
|
+
try {
|
|
287
|
+
sessions = stateSessions(await stateFor(configured.botId));
|
|
288
|
+
} catch {
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
if (sessions?.[configured.conversationKey] === sessionId) {
|
|
292
|
+
matches.push({ botId: configured.botId, targetId: configured.targetId });
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return matches;
|
|
296
|
+
},
|
|
297
|
+
async sendSessionSyncText(botId, targetId, sessionId, text, options = {}) {
|
|
298
|
+
if (typeof text !== 'string' || !text.trim()) throw new TypeError('text is required');
|
|
299
|
+
const configured = sessionSyncConfigs(workspaces, botId)
|
|
300
|
+
.find((target) => target.targetId === targetId);
|
|
301
|
+
const target = workspaces.deliveryTargetFor(botId, targetId);
|
|
302
|
+
if (!configured || !target || !privateConversationKeyMatchesTarget(
|
|
303
|
+
channel,
|
|
304
|
+
configured.conversationKey,
|
|
305
|
+
target,
|
|
306
|
+
)) throw sessionSyncUnavailable('Session sync target is no longer enabled');
|
|
307
|
+
const sessions = stateSessions(await stateFor(botId));
|
|
308
|
+
if (sessions?.[configured.conversationKey] !== sessionId) {
|
|
309
|
+
throw sessionSyncUnavailable('Session sync target is no longer bound to this Session');
|
|
310
|
+
}
|
|
311
|
+
if (typeof coreController.sendProactiveText !== 'function') {
|
|
312
|
+
throw new TypeError('delivery controller cannot send proactive text');
|
|
313
|
+
}
|
|
314
|
+
await coreController.sendProactiveText(botId, target, text, options);
|
|
315
|
+
return { sent: true };
|
|
316
|
+
},
|
|
178
317
|
});
|
|
179
318
|
}
|
|
@@ -9,6 +9,7 @@ export const DELIVERY_ENDPOINTS = Object.freeze({
|
|
|
9
9
|
createTarget: 'target.create',
|
|
10
10
|
updateTarget: 'target.update',
|
|
11
11
|
deleteTarget: 'target.delete',
|
|
12
|
+
setSessionSync: 'target.session-sync.set',
|
|
12
13
|
testTarget: 'target.test',
|
|
13
14
|
});
|
|
14
15
|
|
|
@@ -22,6 +23,7 @@ const PUBLIC_ERRORS = new Set([
|
|
|
22
23
|
'bot-not-connected',
|
|
23
24
|
'target-rejected',
|
|
24
25
|
'delivery-failed',
|
|
26
|
+
'session-sync-unavailable',
|
|
25
27
|
'cancelled',
|
|
26
28
|
]);
|
|
27
29
|
|
|
@@ -85,6 +87,11 @@ function validPayload(endpoint, payload) {
|
|
|
85
87
|
|| (exactKeys(payload, ['botId', 'target'])
|
|
86
88
|
&& validBotId(payload.botId) && validDraftTarget(payload.target));
|
|
87
89
|
}
|
|
90
|
+
if (endpoint === DELIVERY_ENDPOINTS.setSessionSync) {
|
|
91
|
+
return exactKeys(payload, ['botId', 'targetId', 'enabled'])
|
|
92
|
+
&& validBotId(payload.botId) && validTargetId(payload.targetId)
|
|
93
|
+
&& typeof payload.enabled === 'boolean';
|
|
94
|
+
}
|
|
88
95
|
return exactKeys(payload, ['botId', 'targetId'])
|
|
89
96
|
&& validBotId(payload.botId) && validTargetId(payload.targetId);
|
|
90
97
|
}
|
|
@@ -131,6 +138,11 @@ export function createDeliveryRpcHandler(service) {
|
|
|
131
138
|
value = await service.updateTarget(payload.botId, payload.targetId, payload.target);
|
|
132
139
|
} else if (endpoint === DELIVERY_ENDPOINTS.deleteTarget) {
|
|
133
140
|
value = await service.deleteTarget(payload.botId, payload.targetId);
|
|
141
|
+
} else if (endpoint === DELIVERY_ENDPOINTS.setSessionSync) {
|
|
142
|
+
if (typeof service.setSessionSync !== 'function') {
|
|
143
|
+
throw new TypeError('Session sync is unavailable');
|
|
144
|
+
}
|
|
145
|
+
value = await service.setSessionSync(payload.botId, payload.targetId, payload.enabled);
|
|
134
146
|
} else {
|
|
135
147
|
value = await service.send(
|
|
136
148
|
payload.botId,
|