@wenbin_wb/dsh-bridge 2.10.9 → 2.10.10
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/CHANGELOG.md +15 -0
- package/client/client.js +144 -3
- package/client/index.js +120 -0
- package/docs/telegram-usage.md +1 -1
- package/lib/bridge-rpc-constants.js +2 -0
- package/lib/bridge-rpc.js +23 -0
- package/lib/compat.js +129 -129
- package/lib/feishu/index.js +234 -225
- package/lib/feishu/node.js +439 -439
- package/lib/index.js +19 -16
- package/lib/platform/base.js +147 -147
- package/lib/platform/commands.js +235 -221
- package/lib/platform/config-restore.js +69 -0
- package/lib/platform/conversation-bridge.js +905 -821
- package/lib/platform/dsh-storage.js +117 -117
- package/lib/platform/index.js +10 -10
- package/lib/platform/message-split.js +229 -229
- package/lib/platform/session-catalog.js +394 -372
- package/lib/platform/session-config.js +52 -0
- package/lib/platform/stream-slices.js +21 -21
- package/lib/qq/index.js +322 -312
- package/lib/telegram/index.js +225 -216
- package/lib/telegram/node.js +322 -322
- package/lib/wechat/gateway.js +986 -986
- package/lib/wechat/index.js +254 -244
- package/lib/wechat/media.js +285 -285
- package/lib/wechat/node.js +352 -352
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,21 @@
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## [v2.10.10] - 2026-09-14
|
|
8
|
+
|
|
9
|
+
### ✨ 新功能
|
|
10
|
+
|
|
11
|
+
- **设置页新增「高级设置」**(每个 IM 平台卡片内):可直接配置该平台远程会话的 **Agent 预设**(下拉列出本机可用预设,留空使用 DSH 默认预设)、**工作区目录**、**模型提供方 / 模型**,以及进度摘要间隔、审批超时、单条消息上限、分块发送间隔。保存后立即生效,并随宿主重启保留——此前这四个会话级配置没有任何界面入口,只能手改 `config.json`,节奏参数的面板也已在早前重构中丢失。
|
|
12
|
+
|
|
13
|
+
### 🐞 修复
|
|
14
|
+
|
|
15
|
+
- **DSH 0.1.5 系列上远程 IM 会话工具集残缺**(issue #40):DSH 从 `0.1.5` 起把 agent 侧的工具、提示词与技能目录整体挪到了 agent preset 后面(宿主层不再挂载 `tool-fs` / `tool-bash` / `tool-jobs` / `tool-skill` 等),改由每个会话在创建时挂载预设提供。桥此前只把预设名写进会话元数据、从未真正挂载,会话因此落在「空预设层」——表现为远程 Agent 缺少工具(例如没有读文件的工具)。现在新建与恢复会话都会真实挂载预设:未配置时使用 DSH 自身的默认预设;配置的预设在本机已不存在时回退默认预设,并在会话创建提示里明确告知,不再静默降级。
|
|
16
|
+
- **宿主重启后给已有会话发消息报 `session "..." already exists`、随后卡在「当前没有活动会话」**(issue #39):判断会话是否已持久化时沿用了旧的数据结构假设,而 DSH 0.1.5 的会话列表返回的是 `{ header, revision, sizeBytes }` 快照,`entry.id` 恒为空——已存在的历史会话被误判成新会话,走 `agents.create` 建同名会话而失败。现按真实结构取会话头,重启后能正常恢复续聊。
|
|
17
|
+
- **远程会话的工作区与预设配置重启即丢**(issue #40):`cwd`、`agentPreset`、`agentProvider`、`agentModel` 写进配置后,宿主重启即失效(恢复链路只回写了白名单里的少数字段),远程会话会掉到默认目录与默认预设。现已在重启恢复与配置备份导入时一并读回;同时补上 QQ / 飞书此前遗漏的摘要间隔、审批超时、分块延时恢复。配置的工作区目录不可用(被删、换机器、写成相对路径)时回落到已注册工作区并提示,不再把会话建在不存在的目录上。
|
|
18
|
+
- **`/rename` 改名重启即失效**:此前调用的是 `sessionPersistence.update()`——DSH 的持久化服务根本没有这个方法,被可选链静默跳过,改名只改了内存对象。现改用 DSH 原生会话标题服务(追加 `session/title` 事件并落盘),重启与 Web 侧边栏都能看到新标题;会话尚未恢复时会明确提示而不是假报成功。
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
7
22
|
## [v2.10.9] - 2026-09-12
|
|
8
23
|
|
|
9
24
|
### 🐞 修复
|
package/client/client.js
CHANGED
|
@@ -1091,6 +1091,8 @@ var BRIDGE_ENDPOINTS = {
|
|
|
1091
1091
|
platformStop: "platformStop",
|
|
1092
1092
|
platformStart: "platformStart",
|
|
1093
1093
|
platformUnbind: "platformUnbind",
|
|
1094
|
+
// 本机可用的 DSH agent preset(设置页下拉用;老版本 DSH 返回 available:false)
|
|
1095
|
+
listAgentPresets: "listAgentPresets",
|
|
1094
1096
|
// 微信 Bot(v1.x 向后兼容别名,deprecated)
|
|
1095
1097
|
wechatGetStatus: "wechatGetStatus",
|
|
1096
1098
|
wechatLogin: "wechatLogin",
|
|
@@ -2652,10 +2654,29 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall }) {
|
|
|
2652
2654
|
appSecret: "",
|
|
2653
2655
|
domain: platform.config.domain ?? "feishu",
|
|
2654
2656
|
botToken: "",
|
|
2655
|
-
proxy: platform.config.proxy ?? ""
|
|
2657
|
+
proxy: platform.config.proxy ?? "",
|
|
2658
|
+
// 会话级配置:留空 = 使用 DSH 默认值
|
|
2659
|
+
agentPreset: platform.config.agentPreset ?? "",
|
|
2660
|
+
cwd: platform.config.cwd ?? "",
|
|
2661
|
+
agentProvider: platform.config.agentProvider ?? "",
|
|
2662
|
+
agentModel: platform.config.agentModel ?? ""
|
|
2656
2663
|
});
|
|
2657
2664
|
}
|
|
2658
2665
|
}, [platform?.config, platformId]);
|
|
2666
|
+
const [presetOptions, setPresetOptions] = React.useState(null);
|
|
2667
|
+
React.useEffect(() => {
|
|
2668
|
+
let cancelled = false;
|
|
2669
|
+
(async () => {
|
|
2670
|
+
try {
|
|
2671
|
+
const r = await rpcCall(BRIDGE_ENDPOINTS.listAgentPresets, {});
|
|
2672
|
+
if (!cancelled && r?.ok) setPresetOptions(r.value ?? { available: false, presets: [] });
|
|
2673
|
+
} catch {
|
|
2674
|
+
}
|
|
2675
|
+
})();
|
|
2676
|
+
return () => {
|
|
2677
|
+
cancelled = true;
|
|
2678
|
+
};
|
|
2679
|
+
}, [rpcCall]);
|
|
2659
2680
|
const loadInFlightRef = React.useRef(false);
|
|
2660
2681
|
const seqRef = React.useRef(0);
|
|
2661
2682
|
const load = React.useCallback(async (quiet = false) => {
|
|
@@ -2746,7 +2767,12 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall }) {
|
|
|
2746
2767
|
digestIntervalSec: num(cfgDraft.digestIntervalSec, platform?.config?.digestIntervalSec ?? 300),
|
|
2747
2768
|
approvalTimeoutSec: num(cfgDraft.approvalTimeoutSec, platform?.config?.approvalTimeoutSec ?? 600),
|
|
2748
2769
|
maxMessageChars: num(cfgDraft.maxMessageChars, platform?.config?.maxMessageChars ?? 2e3),
|
|
2749
|
-
sendChunkDelayMs: num(cfgDraft.sendChunkDelayMs, platform?.config?.sendChunkDelayMs ?? 1500)
|
|
2770
|
+
sendChunkDelayMs: num(cfgDraft.sendChunkDelayMs, platform?.config?.sendChunkDelayMs ?? 1500),
|
|
2771
|
+
// 会话级配置:空串表示清除该设置、回落 DSH 默认值
|
|
2772
|
+
agentPreset: (cfgDraft.agentPreset ?? "").trim(),
|
|
2773
|
+
cwd: (cfgDraft.cwd ?? "").trim(),
|
|
2774
|
+
agentProvider: (cfgDraft.agentProvider ?? "").trim(),
|
|
2775
|
+
agentModel: (cfgDraft.agentModel ?? "").trim()
|
|
2750
2776
|
};
|
|
2751
2777
|
if (platformId === "qq") {
|
|
2752
2778
|
payload.appId = cfgDraft.appId.trim();
|
|
@@ -2761,7 +2787,7 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall }) {
|
|
|
2761
2787
|
}
|
|
2762
2788
|
await act(BRIDGE_ENDPOINTS.platformSetConfig, payload);
|
|
2763
2789
|
}, [act, cfgDraft, platformId]);
|
|
2764
|
-
const cfgDirty = cfgDraft && platform?.config && (Number(cfgDraft.digestIntervalSec) !== platform.config.digestIntervalSec || Number(cfgDraft.approvalTimeoutSec) !== platform.config.approvalTimeoutSec || Number(cfgDraft.maxMessageChars) !== platform.config.maxMessageChars || Number(cfgDraft.sendChunkDelayMs) !== platform.config.sendChunkDelayMs || platformId === "qq" && (cfgDraft.appId !== (platform.config.appId ?? "") || cfgDraft.clientSecret !== (platform.config.clientSecret ?? "")) || platformId === "feishu" && (cfgDraft.appId !== (platform.config.appId ?? "") || cfgDraft.appSecret !== (platform.config.appSecret ?? "")) || platformId === "telegram" && (cfgDraft.botToken !== "" || cfgDraft.proxy !== (platform.config.proxy ?? "")));
|
|
2790
|
+
const cfgDirty = cfgDraft && platform?.config && (Number(cfgDraft.digestIntervalSec) !== platform.config.digestIntervalSec || Number(cfgDraft.approvalTimeoutSec) !== platform.config.approvalTimeoutSec || Number(cfgDraft.maxMessageChars) !== platform.config.maxMessageChars || Number(cfgDraft.sendChunkDelayMs) !== platform.config.sendChunkDelayMs || (cfgDraft.agentPreset ?? "") !== (platform.config.agentPreset ?? "") || (cfgDraft.cwd ?? "") !== (platform.config.cwd ?? "") || (cfgDraft.agentProvider ?? "") !== (platform.config.agentProvider ?? "") || (cfgDraft.agentModel ?? "") !== (platform.config.agentModel ?? "") || platformId === "qq" && (cfgDraft.appId !== (platform.config.appId ?? "") || cfgDraft.clientSecret !== (platform.config.clientSecret ?? "")) || platformId === "feishu" && (cfgDraft.appId !== (platform.config.appId ?? "") || cfgDraft.appSecret !== (platform.config.appSecret ?? "")) || platformId === "telegram" && (cfgDraft.botToken !== "" || cfgDraft.proxy !== (platform.config.proxy ?? "")));
|
|
2765
2791
|
if (!platform && !err) {
|
|
2766
2792
|
return React.createElement(
|
|
2767
2793
|
"div",
|
|
@@ -3006,6 +3032,121 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall }) {
|
|
|
3006
3032
|
}),
|
|
3007
3033
|
React.createElement("span", null, "\u7FA4\u804A\u81EA\u52A8\u6388\u6743\uFF08\u65B0\u7FA4\u9996\u6B21 @\u673A\u5668\u4EBA \u81EA\u52A8\u52A0\u5165\u767D\u540D\u5355\uFF0C\u9ED8\u8BA4\u5173\u95ED\uFF09")
|
|
3008
3034
|
),
|
|
3035
|
+
// 高级设置:会话级配置(工作区 / Agent 预设 / 模型路由)+ 会话节奏参数
|
|
3036
|
+
React.createElement(
|
|
3037
|
+
"div",
|
|
3038
|
+
{ style: { marginTop: 12 } },
|
|
3039
|
+
React.createElement("button", {
|
|
3040
|
+
style: s.btnGhost,
|
|
3041
|
+
onClick: () => setShowAdvanced((v) => !v)
|
|
3042
|
+
}, showAdvanced ? "\u6536\u8D77\u9AD8\u7EA7\u8BBE\u7F6E" : "\u2699\uFE0F \u9AD8\u7EA7\u8BBE\u7F6E"),
|
|
3043
|
+
showAdvanced && React.createElement(
|
|
3044
|
+
"div",
|
|
3045
|
+
{ style: { ...s.block, marginTop: 8, display: "flex", flexDirection: "column", gap: 10 } },
|
|
3046
|
+
React.createElement(
|
|
3047
|
+
"div",
|
|
3048
|
+
{ style: { ...s.muted, fontSize: 12, lineHeight: 1.6 } },
|
|
3049
|
+
"\u4EE5\u4E0B\u8BBE\u7F6E\u6309\u5E73\u53F0\u4FDD\u5B58\uFF0C\u53EA\u5F71\u54CD\u8BE5\u5E73\u53F0\u901A\u8FC7 Bot \u65B0\u5EFA\u7684\u4F1A\u8BDD\uFF08\u7559\u7A7A = \u4F7F\u7528 DSH \u9ED8\u8BA4\u503C\uFF09\u3002"
|
|
3050
|
+
),
|
|
3051
|
+
// Agent 预设:决定远程会话挂载哪些工具/技能
|
|
3052
|
+
React.createElement(
|
|
3053
|
+
"div",
|
|
3054
|
+
null,
|
|
3055
|
+
React.createElement("div", { style: { ...s.muted, marginBottom: 4 } }, "Agent \u9884\u8BBE \u2014 \u51B3\u5B9A\u8FDC\u7A0B\u4F1A\u8BDD\u53EF\u7528\u7684\u5DE5\u5177\u4E0E\u6280\u80FD\uFF0C\u7559\u7A7A\u7528 DSH \u9ED8\u8BA4\u9884\u8BBE"),
|
|
3056
|
+
React.createElement("input", {
|
|
3057
|
+
style: { ...s.input, width: "100%" },
|
|
3058
|
+
list: "dsh-bridge-agent-presets",
|
|
3059
|
+
placeholder: presetOptions?.default ? `\u7559\u7A7A = DSH \u9ED8\u8BA4\uFF08${presetOptions.default}\uFF09` : "\u7559\u7A7A = DSH \u9ED8\u8BA4\u9884\u8BBE",
|
|
3060
|
+
value: cfgDraft?.agentPreset ?? "",
|
|
3061
|
+
onChange: (e) => setCfgDraft((d) => ({ ...d, agentPreset: e.target.value }))
|
|
3062
|
+
}),
|
|
3063
|
+
React.createElement(
|
|
3064
|
+
"datalist",
|
|
3065
|
+
{ id: "dsh-bridge-agent-presets" },
|
|
3066
|
+
(presetOptions?.presets ?? []).map((p) => React.createElement("option", { key: p.id, value: p.id }, p.name && p.name !== p.id ? p.name : null))
|
|
3067
|
+
),
|
|
3068
|
+
React.createElement(
|
|
3069
|
+
"div",
|
|
3070
|
+
{ style: { ...s.muted, fontSize: 11, marginTop: 4 } },
|
|
3071
|
+
presetOptions == null ? "\u6B63\u5728\u8BFB\u53D6\u672C\u673A\u53EF\u7528\u9884\u8BBE\u2026" : presetOptions.available ? `\u672C\u673A\u53EF\u7528\uFF1A${(presetOptions.presets ?? []).map((p) => p.id).join(" / ") || "\uFF08\u65E0\uFF09"}` : "\u5F53\u524D DSH \u7248\u672C\u672A\u63D0\u4F9B\u9884\u8BBE\u5217\u8868\uFF0C\u8BF7\u624B\u52A8\u586B\u5199\u9884\u8BBE\u540D"
|
|
3072
|
+
)
|
|
3073
|
+
),
|
|
3074
|
+
// 工作区
|
|
3075
|
+
React.createElement(
|
|
3076
|
+
"div",
|
|
3077
|
+
null,
|
|
3078
|
+
React.createElement("div", { style: { ...s.muted, marginBottom: 4 } }, "\u5DE5\u4F5C\u533A\u76EE\u5F55 \u2014 \u8BE5\u5E73\u53F0 /new \u65B0\u5EFA\u4F1A\u8BDD\u7684\u9ED8\u8BA4\u76EE\u5F55\uFF0C\u7559\u7A7A\u7528\u9996\u4E2A\u5DF2\u6CE8\u518C\u5DE5\u4F5C\u533A"),
|
|
3079
|
+
React.createElement("input", {
|
|
3080
|
+
style: { ...s.input, width: "100%" },
|
|
3081
|
+
placeholder: "\u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u4F8B\u5982 /home/me/project \u6216 D:\\project",
|
|
3082
|
+
value: cfgDraft?.cwd ?? "",
|
|
3083
|
+
onChange: (e) => setCfgDraft((d) => ({ ...d, cwd: e.target.value }))
|
|
3084
|
+
})
|
|
3085
|
+
),
|
|
3086
|
+
// 模型路由
|
|
3087
|
+
React.createElement(
|
|
3088
|
+
"div",
|
|
3089
|
+
{ style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(160px, 1fr))", gap: 8 } },
|
|
3090
|
+
React.createElement(
|
|
3091
|
+
"div",
|
|
3092
|
+
null,
|
|
3093
|
+
React.createElement("div", { style: { ...s.muted, marginBottom: 4 } }, "\u6A21\u578B\u63D0\u4F9B\u65B9\uFF08\u53EF\u9009\uFF09"),
|
|
3094
|
+
React.createElement("input", {
|
|
3095
|
+
style: { ...s.input, width: "100%" },
|
|
3096
|
+
placeholder: "\u7559\u7A7A = DSH \u9ED8\u8BA4",
|
|
3097
|
+
value: cfgDraft?.agentProvider ?? "",
|
|
3098
|
+
onChange: (e) => setCfgDraft((d) => ({ ...d, agentProvider: e.target.value }))
|
|
3099
|
+
})
|
|
3100
|
+
),
|
|
3101
|
+
React.createElement(
|
|
3102
|
+
"div",
|
|
3103
|
+
null,
|
|
3104
|
+
React.createElement("div", { style: { ...s.muted, marginBottom: 4 } }, "\u6A21\u578B\uFF08\u53EF\u9009\uFF09"),
|
|
3105
|
+
React.createElement("input", {
|
|
3106
|
+
style: { ...s.input, width: "100%" },
|
|
3107
|
+
placeholder: "\u7559\u7A7A = DSH \u9ED8\u8BA4",
|
|
3108
|
+
value: cfgDraft?.agentModel ?? "",
|
|
3109
|
+
onChange: (e) => setCfgDraft((d) => ({ ...d, agentModel: e.target.value }))
|
|
3110
|
+
})
|
|
3111
|
+
)
|
|
3112
|
+
),
|
|
3113
|
+
// 会话节奏参数
|
|
3114
|
+
React.createElement(
|
|
3115
|
+
"div",
|
|
3116
|
+
{ style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(150px, 1fr))", gap: 8 } },
|
|
3117
|
+
[
|
|
3118
|
+
["digestIntervalSec", "\u8FDB\u5EA6\u6458\u8981\u95F4\u9694\uFF08\u79D2\uFF09"],
|
|
3119
|
+
["approvalTimeoutSec", "\u5BA1\u6279\u8D85\u65F6\uFF08\u79D2\uFF09"],
|
|
3120
|
+
["maxMessageChars", "\u5355\u6761\u6D88\u606F\u4E0A\u9650\uFF08\u5B57\u7B26\uFF09"],
|
|
3121
|
+
["sendChunkDelayMs", "\u5206\u5757\u53D1\u9001\u95F4\u9694\uFF08\u6BEB\u79D2\uFF09"]
|
|
3122
|
+
].map(([key, label]) => React.createElement(
|
|
3123
|
+
"div",
|
|
3124
|
+
{ key },
|
|
3125
|
+
React.createElement("div", { style: { ...s.muted, marginBottom: 4 } }, label),
|
|
3126
|
+
React.createElement("input", {
|
|
3127
|
+
style: { ...s.input, width: "100%" },
|
|
3128
|
+
inputMode: "numeric",
|
|
3129
|
+
value: cfgDraft?.[key] ?? "",
|
|
3130
|
+
onChange: (e) => setCfgDraft((d) => ({ ...d, [key]: e.target.value }))
|
|
3131
|
+
})
|
|
3132
|
+
))
|
|
3133
|
+
),
|
|
3134
|
+
React.createElement(
|
|
3135
|
+
"div",
|
|
3136
|
+
{ style: { display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" } },
|
|
3137
|
+
React.createElement("button", {
|
|
3138
|
+
style: { ...s.btnPri, opacity: busy || !cfgDirty ? 0.5 : 1 },
|
|
3139
|
+
onClick: saveConfig,
|
|
3140
|
+
disabled: busy || !cfgDirty
|
|
3141
|
+
}, busy ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58\u9AD8\u7EA7\u8BBE\u7F6E"),
|
|
3142
|
+
React.createElement("button", {
|
|
3143
|
+
style: { ...s.btnGhost },
|
|
3144
|
+
onClick: resetDefaults,
|
|
3145
|
+
disabled: busy
|
|
3146
|
+
}, "\u6062\u590D\u63A8\u8350\u8282\u594F\u53C2\u6570")
|
|
3147
|
+
)
|
|
3148
|
+
)
|
|
3149
|
+
),
|
|
3009
3150
|
// 飞书 / Telegram 扫码直达对话引导卡片
|
|
3010
3151
|
(platformId === "feishu" || platformId === "telegram") && platform.botQr && React.createElement(
|
|
3011
3152
|
"div",
|
package/client/index.js
CHANGED
|
@@ -1396,10 +1396,28 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall }) {
|
|
|
1396
1396
|
domain: platform.config.domain ?? 'feishu',
|
|
1397
1397
|
botToken: '',
|
|
1398
1398
|
proxy: platform.config.proxy ?? '',
|
|
1399
|
+
// 会话级配置:留空 = 使用 DSH 默认值
|
|
1400
|
+
agentPreset: platform.config.agentPreset ?? '',
|
|
1401
|
+
cwd: platform.config.cwd ?? '',
|
|
1402
|
+
agentProvider: platform.config.agentProvider ?? '',
|
|
1403
|
+
agentModel: platform.config.agentModel ?? '',
|
|
1399
1404
|
});
|
|
1400
1405
|
}
|
|
1401
1406
|
}, [platform?.config, platformId]);
|
|
1402
1407
|
|
|
1408
|
+
// 本机可用的 DSH agent preset(旧版本 DSH 返回 available:false → 退化为手填)
|
|
1409
|
+
const [presetOptions, setPresetOptions] = React.useState(null);
|
|
1410
|
+
React.useEffect(() => {
|
|
1411
|
+
let cancelled = false;
|
|
1412
|
+
(async () => {
|
|
1413
|
+
try {
|
|
1414
|
+
const r = await rpcCall(BRIDGE_ENDPOINTS.listAgentPresets, {});
|
|
1415
|
+
if (!cancelled && r?.ok) setPresetOptions(r.value ?? { available: false, presets: [] });
|
|
1416
|
+
} catch { /* 读取失败保持手填 */ }
|
|
1417
|
+
})();
|
|
1418
|
+
return () => { cancelled = true; };
|
|
1419
|
+
}, [rpcCall]);
|
|
1420
|
+
|
|
1403
1421
|
const loadInFlightRef = React.useRef(false);
|
|
1404
1422
|
const seqRef = React.useRef(0);
|
|
1405
1423
|
const load = React.useCallback(async (quiet = false) => {
|
|
@@ -1507,6 +1525,11 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall }) {
|
|
|
1507
1525
|
approvalTimeoutSec: num(cfgDraft.approvalTimeoutSec, platform?.config?.approvalTimeoutSec ?? 600),
|
|
1508
1526
|
maxMessageChars: num(cfgDraft.maxMessageChars, platform?.config?.maxMessageChars ?? 2000),
|
|
1509
1527
|
sendChunkDelayMs: num(cfgDraft.sendChunkDelayMs, platform?.config?.sendChunkDelayMs ?? 1500),
|
|
1528
|
+
// 会话级配置:空串表示清除该设置、回落 DSH 默认值
|
|
1529
|
+
agentPreset: (cfgDraft.agentPreset ?? '').trim(),
|
|
1530
|
+
cwd: (cfgDraft.cwd ?? '').trim(),
|
|
1531
|
+
agentProvider: (cfgDraft.agentProvider ?? '').trim(),
|
|
1532
|
+
agentModel: (cfgDraft.agentModel ?? '').trim(),
|
|
1510
1533
|
};
|
|
1511
1534
|
// QQ / 飞书 / Telegram 平台额外携带凭证
|
|
1512
1535
|
if (platformId === 'qq') {
|
|
@@ -1527,6 +1550,10 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall }) {
|
|
|
1527
1550
|
Number(cfgDraft.approvalTimeoutSec) !== platform.config.approvalTimeoutSec ||
|
|
1528
1551
|
Number(cfgDraft.maxMessageChars) !== platform.config.maxMessageChars ||
|
|
1529
1552
|
Number(cfgDraft.sendChunkDelayMs) !== platform.config.sendChunkDelayMs ||
|
|
1553
|
+
(cfgDraft.agentPreset ?? '') !== (platform.config.agentPreset ?? '') ||
|
|
1554
|
+
(cfgDraft.cwd ?? '') !== (platform.config.cwd ?? '') ||
|
|
1555
|
+
(cfgDraft.agentProvider ?? '') !== (platform.config.agentProvider ?? '') ||
|
|
1556
|
+
(cfgDraft.agentModel ?? '') !== (platform.config.agentModel ?? '') ||
|
|
1530
1557
|
(platformId === 'qq' && (
|
|
1531
1558
|
cfgDraft.appId !== (platform.config.appId ?? '') ||
|
|
1532
1559
|
cfgDraft.clientSecret !== (platform.config.clientSecret ?? '')
|
|
@@ -1747,6 +1774,99 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall }) {
|
|
|
1747
1774
|
}),
|
|
1748
1775
|
React.createElement('span', null, '群聊自动授权(新群首次 @机器人 自动加入白名单,默认关闭)'),
|
|
1749
1776
|
),
|
|
1777
|
+
// 高级设置:会话级配置(工作区 / Agent 预设 / 模型路由)+ 会话节奏参数
|
|
1778
|
+
React.createElement('div', { style: { marginTop: 12 } },
|
|
1779
|
+
React.createElement('button', {
|
|
1780
|
+
style: s.btnGhost,
|
|
1781
|
+
onClick: () => setShowAdvanced(v => !v),
|
|
1782
|
+
}, showAdvanced ? '收起高级设置' : '⚙️ 高级设置'),
|
|
1783
|
+
showAdvanced && React.createElement('div', { style: { ...s.block, marginTop: 8, display: 'flex', flexDirection: 'column', gap: 10 } },
|
|
1784
|
+
React.createElement('div', { style: { ...s.muted, fontSize: 12, lineHeight: 1.6 } },
|
|
1785
|
+
'以下设置按平台保存,只影响该平台通过 Bot 新建的会话(留空 = 使用 DSH 默认值)。'),
|
|
1786
|
+
// Agent 预设:决定远程会话挂载哪些工具/技能
|
|
1787
|
+
React.createElement('div', null,
|
|
1788
|
+
React.createElement('div', { style: { ...s.muted, marginBottom: 4 } }, 'Agent 预设 — 决定远程会话可用的工具与技能,留空用 DSH 默认预设'),
|
|
1789
|
+
React.createElement('input', {
|
|
1790
|
+
style: { ...s.input, width: '100%' },
|
|
1791
|
+
list: 'dsh-bridge-agent-presets',
|
|
1792
|
+
placeholder: presetOptions?.default ? `留空 = DSH 默认(${presetOptions.default})` : '留空 = DSH 默认预设',
|
|
1793
|
+
value: cfgDraft?.agentPreset ?? '',
|
|
1794
|
+
onChange: (e) => setCfgDraft(d => ({ ...d, agentPreset: e.target.value })),
|
|
1795
|
+
}),
|
|
1796
|
+
React.createElement('datalist', { id: 'dsh-bridge-agent-presets' },
|
|
1797
|
+
(presetOptions?.presets ?? []).map((p) =>
|
|
1798
|
+
React.createElement('option', { key: p.id, value: p.id }, p.name && p.name !== p.id ? p.name : null)),
|
|
1799
|
+
),
|
|
1800
|
+
React.createElement('div', { style: { ...s.muted, fontSize: 11, marginTop: 4 } },
|
|
1801
|
+
presetOptions == null
|
|
1802
|
+
? '正在读取本机可用预设…'
|
|
1803
|
+
: presetOptions.available
|
|
1804
|
+
? `本机可用:${(presetOptions.presets ?? []).map((p) => p.id).join(' / ') || '(无)'}`
|
|
1805
|
+
: '当前 DSH 版本未提供预设列表,请手动填写预设名'),
|
|
1806
|
+
),
|
|
1807
|
+
// 工作区
|
|
1808
|
+
React.createElement('div', null,
|
|
1809
|
+
React.createElement('div', { style: { ...s.muted, marginBottom: 4 } }, '工作区目录 — 该平台 /new 新建会话的默认目录,留空用首个已注册工作区'),
|
|
1810
|
+
React.createElement('input', {
|
|
1811
|
+
style: { ...s.input, width: '100%' },
|
|
1812
|
+
placeholder: '绝对路径,例如 /home/me/project 或 D:\\project',
|
|
1813
|
+
value: cfgDraft?.cwd ?? '',
|
|
1814
|
+
onChange: (e) => setCfgDraft(d => ({ ...d, cwd: e.target.value })),
|
|
1815
|
+
}),
|
|
1816
|
+
),
|
|
1817
|
+
// 模型路由
|
|
1818
|
+
React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(160px, 1fr))', gap: 8 } },
|
|
1819
|
+
React.createElement('div', null,
|
|
1820
|
+
React.createElement('div', { style: { ...s.muted, marginBottom: 4 } }, '模型提供方(可选)'),
|
|
1821
|
+
React.createElement('input', {
|
|
1822
|
+
style: { ...s.input, width: '100%' },
|
|
1823
|
+
placeholder: '留空 = DSH 默认',
|
|
1824
|
+
value: cfgDraft?.agentProvider ?? '',
|
|
1825
|
+
onChange: (e) => setCfgDraft(d => ({ ...d, agentProvider: e.target.value })),
|
|
1826
|
+
}),
|
|
1827
|
+
),
|
|
1828
|
+
React.createElement('div', null,
|
|
1829
|
+
React.createElement('div', { style: { ...s.muted, marginBottom: 4 } }, '模型(可选)'),
|
|
1830
|
+
React.createElement('input', {
|
|
1831
|
+
style: { ...s.input, width: '100%' },
|
|
1832
|
+
placeholder: '留空 = DSH 默认',
|
|
1833
|
+
value: cfgDraft?.agentModel ?? '',
|
|
1834
|
+
onChange: (e) => setCfgDraft(d => ({ ...d, agentModel: e.target.value })),
|
|
1835
|
+
}),
|
|
1836
|
+
),
|
|
1837
|
+
),
|
|
1838
|
+
// 会话节奏参数
|
|
1839
|
+
React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(150px, 1fr))', gap: 8 } },
|
|
1840
|
+
[
|
|
1841
|
+
['digestIntervalSec', '进度摘要间隔(秒)'],
|
|
1842
|
+
['approvalTimeoutSec', '审批超时(秒)'],
|
|
1843
|
+
['maxMessageChars', '单条消息上限(字符)'],
|
|
1844
|
+
['sendChunkDelayMs', '分块发送间隔(毫秒)'],
|
|
1845
|
+
].map(([key, label]) =>
|
|
1846
|
+
React.createElement('div', { key },
|
|
1847
|
+
React.createElement('div', { style: { ...s.muted, marginBottom: 4 } }, label),
|
|
1848
|
+
React.createElement('input', {
|
|
1849
|
+
style: { ...s.input, width: '100%' },
|
|
1850
|
+
inputMode: 'numeric',
|
|
1851
|
+
value: cfgDraft?.[key] ?? '',
|
|
1852
|
+
onChange: (e) => setCfgDraft(d => ({ ...d, [key]: e.target.value })),
|
|
1853
|
+
}),
|
|
1854
|
+
)),
|
|
1855
|
+
),
|
|
1856
|
+
React.createElement('div', { style: { display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' } },
|
|
1857
|
+
React.createElement('button', {
|
|
1858
|
+
style: { ...s.btnPri, opacity: (busy || !cfgDirty) ? 0.5 : 1 },
|
|
1859
|
+
onClick: saveConfig,
|
|
1860
|
+
disabled: busy || !cfgDirty,
|
|
1861
|
+
}, busy ? '保存中…' : '保存高级设置'),
|
|
1862
|
+
React.createElement('button', {
|
|
1863
|
+
style: { ...s.btnGhost },
|
|
1864
|
+
onClick: resetDefaults,
|
|
1865
|
+
disabled: busy,
|
|
1866
|
+
}, '恢复推荐节奏参数'),
|
|
1867
|
+
),
|
|
1868
|
+
),
|
|
1869
|
+
),
|
|
1750
1870
|
// 飞书 / Telegram 扫码直达对话引导卡片
|
|
1751
1871
|
(platformId === 'feishu' || platformId === 'telegram') && platform.botQr && React.createElement('div', {
|
|
1752
1872
|
style: {
|
package/docs/telegram-usage.md
CHANGED
|
@@ -90,4 +90,4 @@
|
|
|
90
90
|
2. **安全白名单拦截**:
|
|
91
91
|
- 仅白名单内的用户消息会被放行给 Agent;非白名单用户发送的消息会被直接忽略,绝不消耗 Token 或喂给模型。
|
|
92
92
|
3. **敏感操作审批**:
|
|
93
|
-
- 当 Agent 尝试执行系统命令或敏感文件读写时,Telegram 会自动下发 `[✓ 批准执行]` / `[✕ 拒绝执行]` 交互按键,10 分钟未处理自动超时拒绝。
|
|
93
|
+
- 当 Agent 尝试执行系统命令或敏感文件读写时,Telegram 会自动下发 `[✓ 批准执行]` / `[✕ 拒绝执行]` 交互按键,10 分钟未处理自动超时拒绝。
|
|
@@ -40,6 +40,8 @@ export const BRIDGE_ENDPOINTS = {
|
|
|
40
40
|
platformStop: 'platformStop',
|
|
41
41
|
platformStart: 'platformStart',
|
|
42
42
|
platformUnbind: 'platformUnbind',
|
|
43
|
+
// 本机可用的 DSH agent preset(设置页下拉用;老版本 DSH 返回 available:false)
|
|
44
|
+
listAgentPresets: 'listAgentPresets',
|
|
43
45
|
// 微信 Bot(v1.x 向后兼容别名,deprecated)
|
|
44
46
|
wechatGetStatus: 'wechatGetStatus',
|
|
45
47
|
wechatLogin: 'wechatLogin',
|
package/lib/bridge-rpc.js
CHANGED
|
@@ -378,6 +378,29 @@ export function installBridgeRpc(ctx, { service, authManager, platformManager, l
|
|
|
378
378
|
|
|
379
379
|
// ---- 平台管理器(多 IM 平台)----
|
|
380
380
|
|
|
381
|
+
// 本机可用的 DSH agent preset(设置页下拉用)。
|
|
382
|
+
// 只读、不含敏感信息:DSH 未提供 agentPresets 服务(旧版本)或名册读取失败时
|
|
383
|
+
// 返回 available:false,前端退化为手填,不影响会话创建。
|
|
384
|
+
if (endpoint === BRIDGE_ENDPOINTS.listAgentPresets) {
|
|
385
|
+
try {
|
|
386
|
+
const presets = ctx.get?.('agentPresets');
|
|
387
|
+
if (!presets?.list) return ok({ available: false, default: null, presets: [] });
|
|
388
|
+
const rows = await presets.list();
|
|
389
|
+
return ok({
|
|
390
|
+
available: true,
|
|
391
|
+
default: typeof presets.defaultId === 'string' ? presets.defaultId : null,
|
|
392
|
+
presets: (Array.isArray(rows) ? rows : [])
|
|
393
|
+
// 坏掉的预设不列出来(DSH 自己也会在挂载前拒绝)
|
|
394
|
+
.filter((p) => p && typeof p.id === 'string' && p.broken === undefined)
|
|
395
|
+
.map((p) => ({ id: p.id, name: typeof p.name === 'string' ? p.name : p.id })),
|
|
396
|
+
});
|
|
397
|
+
} catch (err) {
|
|
398
|
+
// 服务被卸载 / 名册不可读:这是设置页的辅助信息,不该让整个请求失败
|
|
399
|
+
logger?.warn?.('dsh-bridge: listAgentPresets failed: %s', err?.message ?? err);
|
|
400
|
+
return ok({ available: false, default: null, presets: [] });
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
381
404
|
if (endpoint === BRIDGE_ENDPOINTS.listPlatforms) {
|
|
382
405
|
if (!platformManager) return ok({});
|
|
383
406
|
// 每个平台的 login.qrPayload 渲染为 dataURL 后返回
|