@xmanrui/dsh-im 0.17.1 → 0.18.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.
Files changed (46) hide show
  1. package/README.en.md +3 -2
  2. package/README.md +3 -2
  3. package/lib/client.js +931 -538
  4. package/lib/index.js +189 -144
  5. package/package.json +1 -1
  6. package/plugin-src/client/agent-preset.js +110 -0
  7. package/plugin-src/client/channels/dingtalk/api.js +5 -0
  8. package/plugin-src/client/channels/dingtalk/index.js +48 -2
  9. package/plugin-src/client/channels/feishu/api.js +5 -0
  10. package/plugin-src/client/channels/feishu/index.js +39 -2
  11. package/plugin-src/client/channels/qq/api.js +5 -0
  12. package/plugin-src/client/channels/qq/index.js +37 -6
  13. package/plugin-src/client/channels/shared/token-api.js +5 -0
  14. package/plugin-src/client/channels/shared/token-channel.js +34 -6
  15. package/plugin-src/client/channels/wecom/api.js +5 -0
  16. package/plugin-src/client/channels/wecom/index.js +37 -6
  17. package/plugin-src/client/channels/weixin/api.js +5 -0
  18. package/plugin-src/client/channels/weixin/index.js +50 -4
  19. package/plugin-src/client/channels/whatsapp/api.js +5 -0
  20. package/plugin-src/client/channels/whatsapp/index.js +30 -4
  21. package/plugin-src/client/i18n.js +9 -0
  22. package/plugin-src/client/styles.js +8 -0
  23. package/plugin-src/host/channels/dingtalk/production.mjs +10 -4
  24. package/plugin-src/host/channels/dingtalk/rpc.mjs +12 -0
  25. package/plugin-src/host/channels/feishu/connection-supervisor.mjs +1 -1
  26. package/plugin-src/host/channels/feishu/production.mjs +6 -3
  27. package/plugin-src/host/channels/feishu/rpc.mjs +17 -0
  28. package/plugin-src/host/channels/qq/production.mjs +10 -4
  29. package/plugin-src/host/channels/qq/rpc.mjs +12 -0
  30. package/plugin-src/host/channels/shared/agent-preset-rpc.mjs +25 -0
  31. package/plugin-src/host/channels/shared/production.mjs +10 -4
  32. package/plugin-src/host/channels/shared/rpc.mjs +12 -0
  33. package/plugin-src/host/channels/shared/workspace-rpc.mjs +2 -0
  34. package/plugin-src/host/channels/slack/production.mjs +10 -4
  35. package/plugin-src/host/channels/slack/rpc.mjs +13 -0
  36. package/plugin-src/host/channels/wecom/production.mjs +10 -4
  37. package/plugin-src/host/channels/wecom/rpc.mjs +12 -0
  38. package/plugin-src/host/channels/weixin/production.mjs +10 -4
  39. package/plugin-src/host/channels/weixin/rpc.mjs +15 -0
  40. package/plugin-src/host/channels/whatsapp/production.mjs +10 -4
  41. package/plugin-src/host/channels/whatsapp/rpc.mjs +12 -0
  42. package/src/channels/discord/discord-api.mjs +1 -1
  43. package/src/channels/shared/agent-preset.mjs +74 -0
  44. package/src/channels/shared/bot-workspace-store.mjs +141 -14
  45. package/src/channels/shared/harness-client.mjs +6 -4
  46. package/src/channels/weixin/weixin-api.mjs +1 -1
package/lib/client.js CHANGED
@@ -40,7 +40,7 @@ __export(index_exports, {
40
40
  name: () => name
41
41
  });
42
42
  module.exports = __toCommonJS(index_exports);
43
- var React18 = __toESM(require("react"), 1);
43
+ var React19 = __toESM(require("react"), 1);
44
44
 
45
45
  // plugin-src/client/channel-logos.js
46
46
  var React = __toESM(require("react"), 1);
@@ -186,207 +186,7 @@ function OfficeLogoGlyph({ size } = {}) {
186
186
  );
187
187
  }
188
188
 
189
- // plugin-src/client/channels/dingtalk/api.js
190
- var DINGTALK_RPC_CHANNEL = "/dingtalk";
191
- var DINGTALK_ENDPOINTS = Object.freeze({
192
- status: "connection.status",
193
- beginProvisioning: "provision.begin",
194
- pollProvisioning: "provision.poll",
195
- cancelProvisioning: "provision.cancel",
196
- bindCredentials: "bot.bind-credentials",
197
- reconnectBot: "bot.reconnect",
198
- deleteBot: "bot.delete",
199
- setWorkspace: "bot.workspace.set"
200
- });
201
- var ACCOUNT_STATES = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]);
202
- var SNAPSHOT_STATES = /* @__PURE__ */ new Set(["disconnected", "offline", "provisioning", "connected", "degraded"]);
203
- var PROVISION_STATES = /* @__PURE__ */ new Set([
204
- "starting",
205
- "pending",
206
- "scanned",
207
- "authorizing",
208
- "creating",
209
- "connecting",
210
- "connected",
211
- "expired",
212
- "failed",
213
- "cancelled"
214
- ]);
215
- var HEALTH_STATES = /* @__PURE__ */ new Set(["healthy", "checking", "degraded", "offline"]);
216
- var FORBIDDEN_ERROR_FIELDS = /(client[_-]?secret|secret[_-]?ref|device[_-]?code|app[_-]?secret|access[_-]?token|token)/i;
217
- var QR_DATA_URL = /^data:image\/(?:png|webp);base64,[a-z\d+/]+={0,2}$/i;
218
- var MAX_QR_SOURCE_LENGTH = 2 * 1024 * 1024;
219
- function isRecord(value) {
220
- return value !== null && typeof value === "object" && !Array.isArray(value);
221
- }
222
- function optionalString(value, maxLength = 240) {
223
- if (typeof value !== "string") return void 0;
224
- const trimmed = value.trim();
225
- return trimmed ? trimmed.slice(0, maxLength) : void 0;
226
- }
227
- function opaqueId(value) {
228
- const id5 = optionalString(value, 128);
229
- return id5 && /^[a-z\d_-]+$/i.test(id5) ? id5 : void 0;
230
- }
231
- function timestamp(value) {
232
- if (typeof value === "number" && Number.isFinite(value)) return value;
233
- if (typeof value === "string" && value.trim()) {
234
- const parsed = Date.parse(value);
235
- return Number.isNaN(parsed) ? void 0 : parsed;
236
- }
237
- return void 0;
238
- }
239
- function nonNegativeInteger(value) {
240
- const number = Number(value);
241
- return Number.isSafeInteger(number) && number >= 0 ? number : 0;
242
- }
243
- function clamp(value, min, max, fallback) {
244
- const number = Number(value);
245
- return Number.isFinite(number) ? Math.min(max, Math.max(min, number)) : fallback;
246
- }
247
- function safeErrorCode(value, fallback) {
248
- const code = optionalString(value, 80);
249
- return code && /^[a-z][a-z\d_.:-]*$/i.test(code) && !FORBIDDEN_ERROR_FIELDS.test(code) ? code : fallback;
250
- }
251
- function sanitizeMessage(value, fallback) {
252
- const message = optionalString(value, 480) ?? fallback;
253
- if (FORBIDDEN_ERROR_FIELDS.test(message)) return fallback;
254
- return message.replace(/([=:]\s*)[^\s,;,。]+/g, "$1\u2022\u2022\u2022\u2022\u2022\u2022").slice(0, 240);
255
- }
256
- function normalizeError(value, fallbackCode, fallbackMessage) {
257
- if (!isRecord(value)) return void 0;
258
- return {
259
- code: safeErrorCode(value.code, fallbackCode),
260
- message: sanitizeMessage(value.message, fallbackMessage)
261
- };
262
- }
263
- function normalizeTestMessage(value) {
264
- if (!isRecord(value)) return null;
265
- if (value.sent === true) return { sent: true };
266
- if (value.sent !== false) return null;
267
- const code = value.code === "test-target-unavailable" ? "test-target-unavailable" : "test-message-failed";
268
- return { sent: false, code };
269
- }
270
- function unwrapRpcResult(result) {
271
- if (!isRecord(result) || typeof result.ok !== "boolean") {
272
- throw new Error("\u9489\u9489\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94");
273
- }
274
- if (!result.ok) {
275
- const error = new Error(sanitizeMessage(result.error?.message, "\u9489\u9489\u64CD\u4F5C\u5931\u8D25"));
276
- error.code = safeErrorCode(result.error?.code, "DINGTALK_RPC_ERROR");
277
- throw error;
278
- }
279
- return result.value;
280
- }
281
- function safeQrSource(value) {
282
- if (typeof value !== "string" || value.length > MAX_QR_SOURCE_LENGTH) return void 0;
283
- return QR_DATA_URL.test(value) ? value : void 0;
284
- }
285
- function normalizeProvisioning(value, now = Date.now()) {
286
- const source = isRecord(value?.provisioning) ? value.provisioning : value;
287
- if (!isRecord(source)) throw new Error("\u9489\u9489\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u626B\u7801\u7ED1\u5B9A\u8FDB\u5EA6");
288
- const attemptId = opaqueId(source.attemptId);
289
- if (!attemptId) throw new Error("\u9489\u9489\u626B\u7801\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u7ED1\u5B9A\u4EFB\u52A1");
290
- const reportedStatus = optionalString(source.status, 32) ?? optionalString(source.state, 32);
291
- const status = PROVISION_STATES.has(reportedStatus) ? reportedStatus : "failed";
292
- const expiresAt = timestamp(source.expiresAt) ?? now + clamp(source.expiresIn, 1, 2 * 60 * 60, 10 * 60) * 1e3;
293
- const result = {
294
- attemptId,
295
- status,
296
- expiresAt,
297
- pollIntervalMs: clamp(source.pollIntervalMs, 1e3, 1e4, 3e3)
298
- };
299
- const qrCodeDataUrl = safeQrSource(source.qrCodeDataUrl);
300
- if (qrCodeDataUrl) result.qrCodeDataUrl = qrCodeDataUrl;
301
- if (opaqueId(source.botId)) result.botId = opaqueId(source.botId);
302
- if (source.alreadyConnected === true) result.alreadyConnected = true;
303
- const error = normalizeError(
304
- source.error,
305
- "DINGTALK_PROVISION_FAILED",
306
- "\u9489\u9489\u673A\u5668\u4EBA\u6CA1\u6709\u63A5\u5165\u5B8C\u6210"
307
- );
308
- if (error) result.error = error;
309
- return result;
310
- }
311
- function normalizeBot(value) {
312
- if (!isRecord(value)) return void 0;
313
- const botId = opaqueId(value.botId);
314
- if (!botId) return void 0;
315
- const bot = isRecord(value.bot) ? value.bot : {};
316
- const connected = value.connected === true;
317
- const reportedState = ACCOUNT_STATES.has(value.state) ? value.state : "offline";
318
- const state = connected ? "connected" : reportedState === "connected" ? "connecting" : reportedState;
319
- const health = isRecord(value.health) ? value.health : {};
320
- const stats = isRecord(value.stats) ? value.stats : {};
321
- return {
322
- botId,
323
- state,
324
- connected,
325
- configured: value.configured !== false,
326
- workspace: optionalString(value.workspace, 4096) ?? "",
327
- bot: {
328
- name: optionalString(bot.name, 100) ?? "\u9489\u9489\u673A\u5668\u4EBA",
329
- clientIdMasked: optionalString(bot.clientIdMasked, 140) ?? "\u5DF2\u5B89\u5168\u4FDD\u5B58"
330
- },
331
- health: {
332
- status: HEALTH_STATES.has(health.status) ? health.status : connected ? "healthy" : "offline",
333
- summary: optionalString(health.summary, 200) ?? (connected ? "\u9489\u9489 Stream \u957F\u8FDE\u63A5\u8FD0\u884C\u6B63\u5E38" : "\u9489\u9489\u8FDE\u63A5\u5C1A\u672A\u5C31\u7EEA"),
334
- lastCheckedAt: timestamp(health.lastCheckedAt),
335
- lastConnectedAt: timestamp(health.lastConnectedAt)
336
- },
337
- stats: {
338
- messagesReceived: nonNegativeInteger(stats.messagesReceived),
339
- messagesReplied: nonNegativeInteger(stats.messagesReplied)
340
- },
341
- error: normalizeError(value.error, "DINGTALK_ACCOUNT_ERROR", "\u9489\u9489\u8FDE\u63A5\u5C1A\u672A\u5C31\u7EEA") ?? null
342
- };
343
- }
344
- function normalizeSnapshot(value) {
345
- const source = isRecord(value?.snapshot) ? value.snapshot : value;
346
- if (!isRecord(source) || !Array.isArray(source.bots)) {
347
- throw new Error("\u9489\u9489\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u673A\u5668\u4EBA\u5217\u8868");
348
- }
349
- const seen = /* @__PURE__ */ new Set();
350
- const bots = source.bots.map(normalizeBot).filter((bot) => {
351
- if (!bot || seen.has(bot.botId)) return false;
352
- seen.add(bot.botId);
353
- return true;
354
- });
355
- return {
356
- schemaVersion: Number.isSafeInteger(source.schemaVersion) ? source.schemaVersion : 1,
357
- revision: nonNegativeInteger(source.revision),
358
- state: SNAPSHOT_STATES.has(source.state) ? source.state : "offline",
359
- bots,
360
- totals: {
361
- configured: bots.length,
362
- connected: bots.filter((bot) => bot.connected).length
363
- },
364
- provisioning: source.provisioning ? normalizeProvisioning(source.provisioning) : null,
365
- testMessage: normalizeTestMessage(source.testMessage)
366
- };
367
- }
368
- function connectionTestFeedback(result) {
369
- if (result?.sent === true) return "\u9489\u9489\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\u3002";
370
- if (result?.code === "test-target-unavailable") {
371
- return "\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002\u673A\u5668\u4EBA\u5C1A\u672A\u6536\u5230\u53EF\u7528\u4E8E\u6D4B\u8BD5\u7684\u79C1\u804A\u6D88\u606F\u3002";
372
- }
373
- return result ? "\u9489\u9489\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u5931\u8D25\u3002" : null;
374
- }
375
- function presentError(error) {
376
- return {
377
- code: safeErrorCode(error?.code, "DINGTALK_ERROR"),
378
- message: sanitizeMessage(error?.message, "\u9489\u9489\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5")
379
- };
380
- }
381
- function formatRemaining(milliseconds) {
382
- const seconds = Math.max(0, Math.ceil(Number(milliseconds) / 1e3) || 0);
383
- return `${String(Math.floor(seconds / 60)).padStart(2, "0")}:${String(seconds % 60).padStart(2, "0")}`;
384
- }
385
-
386
- // plugin-src/client/channels/dingtalk/index.js
387
- var React7 = __toESM(require("react"), 1);
388
-
389
- // plugin-src/client/credential-binding.js
189
+ // plugin-src/client/agent-preset.js
390
190
  var React3 = __toESM(require("react"), 1);
391
191
 
392
192
  // plugin-src/client/i18n.js
@@ -507,6 +307,15 @@ var EN = Object.freeze({
507
307
  "\u5DE5\u4F5C\u533A\u8DEF\u5F84\u4E0D\u5B58\u5728\u3002": "The workspace path does not exist.",
508
308
  "\u5DE5\u4F5C\u533A\u8DEF\u5F84\u5FC5\u987B\u6307\u5411\u4E00\u4E2A\u76EE\u5F55\u3002": "The workspace path must point to a directory.",
509
309
  "\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002": "The bot could not be found.",
310
+ "Agent Preset": "Agent Preset",
311
+ "\u8DDF\u968F Host \u9ED8\u8BA4": "Follow the Host default",
312
+ "\uFF08\u5DF2\u4E0D\u53EF\u7528\uFF09": " (unavailable)",
313
+ "\u53EA\u5F71\u54CD\u65B0\u5EFA\u4F1A\u8BDD\uFF1B\u82E5\u5F53\u524D\u804A\u5929\u5DF2\u6709\u4F1A\u8BDD\uFF0C\u5148\u53D1\u9001 /new\uFF0C\u518D\u53D1\u9001\u666E\u901A\u6D88\u606F\u751F\u6548\u3002": "This affects only new sessions. If the current chat already has a session, send /new, then send a regular message to apply it.",
314
+ "\u5F53\u524D Agent Preset \u5DF2\u4E0D\u53EF\u7528\uFF0C\u8BF7\u9009\u62E9\u5176\u4ED6 Preset \u6216\u8DDF\u968F Host \u9ED8\u8BA4\u3002": "The current Agent Preset is unavailable. Choose another preset or follow the Host default.",
315
+ "Agent Preset \u4FEE\u6539\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002": "Could not update the Agent Preset. Try again.",
316
+ "\u8BF7\u9009\u62E9 Agent Preset\u3002": "Choose an Agent Preset.",
317
+ "Agent Preset \u65E0\u6548\u3002": "The Agent Preset is invalid.",
318
+ "Agent Preset \u4E0D\u5B58\u5728\u6216\u4E0D\u53EF\u7528\u3002": "The Agent Preset does not exist or is unavailable.",
510
319
  "\u5C1A\u672A\u68C0\u67E5": "Not checked yet",
511
320
  "\u521A\u521A": "Just now",
512
321
  "\u68C0\u67E5\u8FDE\u63A5": "Check connection",
@@ -1005,7 +814,310 @@ function h2(type, props, ...children) {
1005
814
  return React2.createElement(type, localizedProps, ...children.map(localizeChild));
1006
815
  }
1007
816
 
817
+ // plugin-src/client/agent-preset.js
818
+ var SET_AGENT_PRESET_ENDPOINT = "bot.preset.set";
819
+ var PRESET_ID = /^[a-z0-9][a-z0-9-]*$/;
820
+ var EMPTY_AGENT_PRESET_CATALOG = Object.freeze({
821
+ defaultId: "",
822
+ items: Object.freeze([])
823
+ });
824
+ var AgentPresetCatalogContext = React3.createContext(EMPTY_AGENT_PRESET_CATALOG);
825
+ function normalizeAgentPresetId(value) {
826
+ if (typeof value !== "string") return "";
827
+ const id5 = value.trim();
828
+ return PRESET_ID.test(id5) ? id5 : "";
829
+ }
830
+ function normalizeAgentPresetCatalog(value) {
831
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
832
+ return { defaultId: "", items: [] };
833
+ }
834
+ const items = [];
835
+ const seen = /* @__PURE__ */ new Set();
836
+ for (const entry of Array.isArray(value.items) ? value.items : []) {
837
+ const id5 = typeof entry === "string" ? normalizeAgentPresetId(entry) : normalizeAgentPresetId(entry?.id);
838
+ if (!id5 || seen.has(id5)) continue;
839
+ seen.add(id5);
840
+ const label = typeof entry?.label === "string" && entry.label.trim() ? entry.label.trim().slice(0, 128) : typeof entry?.name === "string" && entry.name.trim() ? entry.name.trim().slice(0, 128) : id5;
841
+ items.push({ id: id5, label });
842
+ }
843
+ return {
844
+ defaultId: normalizeAgentPresetId(value.defaultId),
845
+ items
846
+ };
847
+ }
848
+ function AgentPresetEditor({ agentPreset = "", disabled = false, onSave }) {
849
+ const catalog = React3.useContext(AgentPresetCatalogContext) ?? EMPTY_AGENT_PRESET_CATALOG;
850
+ const current = normalizeAgentPresetId(agentPreset);
851
+ const [saving, setSaving] = React3.useState(false);
852
+ const [error, setError] = React3.useState(null);
853
+ const items = [];
854
+ const seen = /* @__PURE__ */ new Set();
855
+ for (const item of Array.isArray(catalog.items) ? catalog.items : []) {
856
+ if (!item?.id || seen.has(item.id)) continue;
857
+ seen.add(item.id);
858
+ items.push(item);
859
+ }
860
+ const currentUnavailable = Boolean(current && !seen.has(current));
861
+ if (currentUnavailable) items.push({ id: current, label: current, unavailable: true });
862
+ const inheritLabel = "\u8DDF\u968F Host \u9ED8\u8BA4";
863
+ const change = async (event) => {
864
+ const next = event.target.value;
865
+ if (next === current || saving || disabled) return;
866
+ setSaving(true);
867
+ setError(null);
868
+ try {
869
+ await onSave?.(next || null);
870
+ } catch (cause) {
871
+ setError(cause?.message ?? "Agent Preset \u4FEE\u6539\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002");
872
+ } finally {
873
+ setSaving(false);
874
+ }
875
+ };
876
+ return h2(
877
+ "div",
878
+ { className: "dim-preset" },
879
+ h2(
880
+ "div",
881
+ { className: "dim-presetHeader" },
882
+ h2("span", null, "Agent Preset"),
883
+ saving ? h2("span", { className: "dim-presetStatus" }, "\u4FDD\u5B58\u4E2D\u2026") : null
884
+ ),
885
+ React3.createElement(
886
+ "select",
887
+ {
888
+ className: "dim-presetSelect",
889
+ value: current,
890
+ disabled: disabled || saving,
891
+ "aria-label": "Agent Preset",
892
+ onChange: (event) => {
893
+ void change(event);
894
+ }
895
+ },
896
+ h2("option", { value: "" }, inheritLabel),
897
+ ...items.map((item) => h2(
898
+ "option",
899
+ { key: item.id, value: item.id },
900
+ item.unavailable ? [item.id, "\uFF08\u5DF2\u4E0D\u53EF\u7528\uFF09"] : item.label && item.label !== item.id ? `${item.label}\uFF08${item.id}\uFF09` : item.id
901
+ ))
902
+ ),
903
+ h2(
904
+ "small",
905
+ { className: "dim-presetHelp" },
906
+ "\u53EA\u5F71\u54CD\u65B0\u5EFA\u4F1A\u8BDD\uFF1B\u82E5\u5F53\u524D\u804A\u5929\u5DF2\u6709\u4F1A\u8BDD\uFF0C\u5148\u53D1\u9001 /new\uFF0C\u518D\u53D1\u9001\u666E\u901A\u6D88\u606F\u751F\u6548\u3002"
907
+ ),
908
+ error || currentUnavailable ? h2(
909
+ "p",
910
+ { className: "dim-presetError", role: error ? "alert" : "status" },
911
+ error ?? "\u5F53\u524D Agent Preset \u5DF2\u4E0D\u53EF\u7528\uFF0C\u8BF7\u9009\u62E9\u5176\u4ED6 Preset \u6216\u8DDF\u968F Host \u9ED8\u8BA4\u3002"
912
+ ) : null
913
+ );
914
+ }
915
+
916
+ // plugin-src/client/channels/dingtalk/api.js
917
+ var DINGTALK_RPC_CHANNEL = "/dingtalk";
918
+ var DINGTALK_ENDPOINTS = Object.freeze({
919
+ status: "connection.status",
920
+ beginProvisioning: "provision.begin",
921
+ pollProvisioning: "provision.poll",
922
+ cancelProvisioning: "provision.cancel",
923
+ bindCredentials: "bot.bind-credentials",
924
+ reconnectBot: "bot.reconnect",
925
+ deleteBot: "bot.delete",
926
+ setWorkspace: "bot.workspace.set",
927
+ setAgentPreset: SET_AGENT_PRESET_ENDPOINT
928
+ });
929
+ var ACCOUNT_STATES = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]);
930
+ var SNAPSHOT_STATES = /* @__PURE__ */ new Set(["disconnected", "offline", "provisioning", "connected", "degraded"]);
931
+ var PROVISION_STATES = /* @__PURE__ */ new Set([
932
+ "starting",
933
+ "pending",
934
+ "scanned",
935
+ "authorizing",
936
+ "creating",
937
+ "connecting",
938
+ "connected",
939
+ "expired",
940
+ "failed",
941
+ "cancelled"
942
+ ]);
943
+ var HEALTH_STATES = /* @__PURE__ */ new Set(["healthy", "checking", "degraded", "offline"]);
944
+ var FORBIDDEN_ERROR_FIELDS = /(client[_-]?secret|secret[_-]?ref|device[_-]?code|app[_-]?secret|access[_-]?token|token)/i;
945
+ var QR_DATA_URL = /^data:image\/(?:png|webp);base64,[a-z\d+/]+={0,2}$/i;
946
+ var MAX_QR_SOURCE_LENGTH = 2 * 1024 * 1024;
947
+ function isRecord(value) {
948
+ return value !== null && typeof value === "object" && !Array.isArray(value);
949
+ }
950
+ function optionalString(value, maxLength = 240) {
951
+ if (typeof value !== "string") return void 0;
952
+ const trimmed = value.trim();
953
+ return trimmed ? trimmed.slice(0, maxLength) : void 0;
954
+ }
955
+ function opaqueId(value) {
956
+ const id5 = optionalString(value, 128);
957
+ return id5 && /^[a-z\d_-]+$/i.test(id5) ? id5 : void 0;
958
+ }
959
+ function timestamp(value) {
960
+ if (typeof value === "number" && Number.isFinite(value)) return value;
961
+ if (typeof value === "string" && value.trim()) {
962
+ const parsed = Date.parse(value);
963
+ return Number.isNaN(parsed) ? void 0 : parsed;
964
+ }
965
+ return void 0;
966
+ }
967
+ function nonNegativeInteger(value) {
968
+ const number = Number(value);
969
+ return Number.isSafeInteger(number) && number >= 0 ? number : 0;
970
+ }
971
+ function clamp(value, min, max, fallback) {
972
+ const number = Number(value);
973
+ return Number.isFinite(number) ? Math.min(max, Math.max(min, number)) : fallback;
974
+ }
975
+ function safeErrorCode(value, fallback) {
976
+ const code = optionalString(value, 80);
977
+ return code && /^[a-z][a-z\d_.:-]*$/i.test(code) && !FORBIDDEN_ERROR_FIELDS.test(code) ? code : fallback;
978
+ }
979
+ function sanitizeMessage(value, fallback) {
980
+ const message = optionalString(value, 480) ?? fallback;
981
+ if (FORBIDDEN_ERROR_FIELDS.test(message)) return fallback;
982
+ return message.replace(/([=:]\s*)[^\s,;,。]+/g, "$1\u2022\u2022\u2022\u2022\u2022\u2022").slice(0, 240);
983
+ }
984
+ function normalizeError(value, fallbackCode, fallbackMessage) {
985
+ if (!isRecord(value)) return void 0;
986
+ return {
987
+ code: safeErrorCode(value.code, fallbackCode),
988
+ message: sanitizeMessage(value.message, fallbackMessage)
989
+ };
990
+ }
991
+ function normalizeTestMessage(value) {
992
+ if (!isRecord(value)) return null;
993
+ if (value.sent === true) return { sent: true };
994
+ if (value.sent !== false) return null;
995
+ const code = value.code === "test-target-unavailable" ? "test-target-unavailable" : "test-message-failed";
996
+ return { sent: false, code };
997
+ }
998
+ function unwrapRpcResult(result) {
999
+ if (!isRecord(result) || typeof result.ok !== "boolean") {
1000
+ throw new Error("\u9489\u9489\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94");
1001
+ }
1002
+ if (!result.ok) {
1003
+ const error = new Error(sanitizeMessage(result.error?.message, "\u9489\u9489\u64CD\u4F5C\u5931\u8D25"));
1004
+ error.code = safeErrorCode(result.error?.code, "DINGTALK_RPC_ERROR");
1005
+ throw error;
1006
+ }
1007
+ return result.value;
1008
+ }
1009
+ function safeQrSource(value) {
1010
+ if (typeof value !== "string" || value.length > MAX_QR_SOURCE_LENGTH) return void 0;
1011
+ return QR_DATA_URL.test(value) ? value : void 0;
1012
+ }
1013
+ function normalizeProvisioning(value, now = Date.now()) {
1014
+ const source = isRecord(value?.provisioning) ? value.provisioning : value;
1015
+ if (!isRecord(source)) throw new Error("\u9489\u9489\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u626B\u7801\u7ED1\u5B9A\u8FDB\u5EA6");
1016
+ const attemptId = opaqueId(source.attemptId);
1017
+ if (!attemptId) throw new Error("\u9489\u9489\u626B\u7801\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u7ED1\u5B9A\u4EFB\u52A1");
1018
+ const reportedStatus = optionalString(source.status, 32) ?? optionalString(source.state, 32);
1019
+ const status = PROVISION_STATES.has(reportedStatus) ? reportedStatus : "failed";
1020
+ const expiresAt = timestamp(source.expiresAt) ?? now + clamp(source.expiresIn, 1, 2 * 60 * 60, 10 * 60) * 1e3;
1021
+ const result = {
1022
+ attemptId,
1023
+ status,
1024
+ expiresAt,
1025
+ pollIntervalMs: clamp(source.pollIntervalMs, 1e3, 1e4, 3e3)
1026
+ };
1027
+ const qrCodeDataUrl = safeQrSource(source.qrCodeDataUrl);
1028
+ if (qrCodeDataUrl) result.qrCodeDataUrl = qrCodeDataUrl;
1029
+ if (opaqueId(source.botId)) result.botId = opaqueId(source.botId);
1030
+ if (source.alreadyConnected === true) result.alreadyConnected = true;
1031
+ const error = normalizeError(
1032
+ source.error,
1033
+ "DINGTALK_PROVISION_FAILED",
1034
+ "\u9489\u9489\u673A\u5668\u4EBA\u6CA1\u6709\u63A5\u5165\u5B8C\u6210"
1035
+ );
1036
+ if (error) result.error = error;
1037
+ return result;
1038
+ }
1039
+ function normalizeBot(value) {
1040
+ if (!isRecord(value)) return void 0;
1041
+ const botId = opaqueId(value.botId);
1042
+ if (!botId) return void 0;
1043
+ const bot = isRecord(value.bot) ? value.bot : {};
1044
+ const connected = value.connected === true;
1045
+ const reportedState = ACCOUNT_STATES.has(value.state) ? value.state : "offline";
1046
+ const state = connected ? "connected" : reportedState === "connected" ? "connecting" : reportedState;
1047
+ const health = isRecord(value.health) ? value.health : {};
1048
+ const stats = isRecord(value.stats) ? value.stats : {};
1049
+ return {
1050
+ botId,
1051
+ state,
1052
+ connected,
1053
+ configured: value.configured !== false,
1054
+ workspace: optionalString(value.workspace, 4096) ?? "",
1055
+ agentPreset: normalizeAgentPresetId(value.agentPreset),
1056
+ bot: {
1057
+ name: optionalString(bot.name, 100) ?? "\u9489\u9489\u673A\u5668\u4EBA",
1058
+ clientIdMasked: optionalString(bot.clientIdMasked, 140) ?? "\u5DF2\u5B89\u5168\u4FDD\u5B58"
1059
+ },
1060
+ health: {
1061
+ status: HEALTH_STATES.has(health.status) ? health.status : connected ? "healthy" : "offline",
1062
+ summary: optionalString(health.summary, 200) ?? (connected ? "\u9489\u9489 Stream \u957F\u8FDE\u63A5\u8FD0\u884C\u6B63\u5E38" : "\u9489\u9489\u8FDE\u63A5\u5C1A\u672A\u5C31\u7EEA"),
1063
+ lastCheckedAt: timestamp(health.lastCheckedAt),
1064
+ lastConnectedAt: timestamp(health.lastConnectedAt)
1065
+ },
1066
+ stats: {
1067
+ messagesReceived: nonNegativeInteger(stats.messagesReceived),
1068
+ messagesReplied: nonNegativeInteger(stats.messagesReplied)
1069
+ },
1070
+ error: normalizeError(value.error, "DINGTALK_ACCOUNT_ERROR", "\u9489\u9489\u8FDE\u63A5\u5C1A\u672A\u5C31\u7EEA") ?? null
1071
+ };
1072
+ }
1073
+ function normalizeSnapshot(value) {
1074
+ const source = isRecord(value?.snapshot) ? value.snapshot : value;
1075
+ if (!isRecord(source) || !Array.isArray(source.bots)) {
1076
+ throw new Error("\u9489\u9489\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u673A\u5668\u4EBA\u5217\u8868");
1077
+ }
1078
+ const seen = /* @__PURE__ */ new Set();
1079
+ const bots = source.bots.map(normalizeBot).filter((bot) => {
1080
+ if (!bot || seen.has(bot.botId)) return false;
1081
+ seen.add(bot.botId);
1082
+ return true;
1083
+ });
1084
+ return {
1085
+ schemaVersion: Number.isSafeInteger(source.schemaVersion) ? source.schemaVersion : 1,
1086
+ revision: nonNegativeInteger(source.revision),
1087
+ state: SNAPSHOT_STATES.has(source.state) ? source.state : "offline",
1088
+ bots,
1089
+ totals: {
1090
+ configured: bots.length,
1091
+ connected: bots.filter((bot) => bot.connected).length
1092
+ },
1093
+ provisioning: source.provisioning ? normalizeProvisioning(source.provisioning) : null,
1094
+ testMessage: normalizeTestMessage(source.testMessage),
1095
+ agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog)
1096
+ };
1097
+ }
1098
+ function connectionTestFeedback(result) {
1099
+ if (result?.sent === true) return "\u9489\u9489\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\u3002";
1100
+ if (result?.code === "test-target-unavailable") {
1101
+ return "\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002\u673A\u5668\u4EBA\u5C1A\u672A\u6536\u5230\u53EF\u7528\u4E8E\u6D4B\u8BD5\u7684\u79C1\u804A\u6D88\u606F\u3002";
1102
+ }
1103
+ return result ? "\u9489\u9489\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u5931\u8D25\u3002" : null;
1104
+ }
1105
+ function presentError(error) {
1106
+ return {
1107
+ code: safeErrorCode(error?.code, "DINGTALK_ERROR"),
1108
+ message: sanitizeMessage(error?.message, "\u9489\u9489\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5")
1109
+ };
1110
+ }
1111
+ function formatRemaining(milliseconds) {
1112
+ const seconds = Math.max(0, Math.ceil(Number(milliseconds) / 1e3) || 0);
1113
+ return `${String(Math.floor(seconds / 60)).padStart(2, "0")}:${String(seconds % 60).padStart(2, "0")}`;
1114
+ }
1115
+
1116
+ // plugin-src/client/channels/dingtalk/index.js
1117
+ var React8 = __toESM(require("react"), 1);
1118
+
1008
1119
  // plugin-src/client/credential-binding.js
1120
+ var React4 = __toESM(require("react"), 1);
1009
1121
  function ActionIcon({ children }) {
1010
1122
  return h2("svg", {
1011
1123
  className: "dim-actionIcon",
@@ -1065,9 +1177,9 @@ function CredentialBindingPanel({
1065
1177
  onSubmit,
1066
1178
  onCancel
1067
1179
  }) {
1068
- const [identity, setIdentity] = React3.useState("");
1069
- const [secret, setSecret] = React3.useState("");
1070
- const headingId = React3.useId();
1180
+ const [identity, setIdentity] = React4.useState("");
1181
+ const [secret, setSecret] = React4.useState("");
1182
+ const headingId = React4.useId();
1071
1183
  const hasIdentity = Boolean(identityLabel);
1072
1184
  const submit = (event) => {
1073
1185
  event.preventDefault();
@@ -1146,10 +1258,10 @@ function CredentialBindingPanel({
1146
1258
  }
1147
1259
 
1148
1260
  // plugin-src/client/workspace-editor.js
1149
- var React5 = __toESM(require("react"), 1);
1261
+ var React6 = __toESM(require("react"), 1);
1150
1262
 
1151
1263
  // plugin-src/client/workspace-directory-picker.js
1152
- var React4 = __toESM(require("react"), 1);
1264
+ var React5 = __toESM(require("react"), 1);
1153
1265
  var import_react_dom = require("react-dom");
1154
1266
  function pickerErrorCode(error) {
1155
1267
  return error?.rpcError?.code ?? error?.code;
@@ -1161,7 +1273,7 @@ function pickerErrorMessage(error) {
1161
1273
  return error?.rpcError?.message ?? error?.message ?? "\u65E0\u6CD5\u8BFB\u53D6\u76EE\u5F55\uFF0C\u8BF7\u91CD\u8BD5\u3002";
1162
1274
  }
1163
1275
  function FolderIcon() {
1164
- return React4.createElement(
1276
+ return React5.createElement(
1165
1277
  "svg",
1166
1278
  {
1167
1279
  viewBox: "0 0 24 24",
@@ -1172,11 +1284,11 @@ function FolderIcon() {
1172
1284
  strokeLinejoin: "round",
1173
1285
  "aria-hidden": "true"
1174
1286
  },
1175
- React4.createElement("path", { d: "M3.5 7.25A2.25 2.25 0 0 1 5.75 5h4.1l1.8 2h6.6a2.25 2.25 0 0 1 2.25 2.25v7A2.75 2.75 0 0 1 17.75 19h-12A2.25 2.25 0 0 1 3.5 16.75v-9.5Z" })
1287
+ React5.createElement("path", { d: "M3.5 7.25A2.25 2.25 0 0 1 5.75 5h4.1l1.8 2h6.6a2.25 2.25 0 0 1 2.25 2.25v7A2.75 2.75 0 0 1 17.75 19h-12A2.25 2.25 0 0 1 3.5 16.75v-9.5Z" })
1176
1288
  );
1177
1289
  }
1178
1290
  function ChevronIcon() {
1179
- return React4.createElement("svg", {
1291
+ return React5.createElement("svg", {
1180
1292
  viewBox: "0 0 20 20",
1181
1293
  fill: "none",
1182
1294
  stroke: "currentColor",
@@ -1184,7 +1296,7 @@ function ChevronIcon() {
1184
1296
  strokeLinecap: "round",
1185
1297
  strokeLinejoin: "round",
1186
1298
  "aria-hidden": "true"
1187
- }, React4.createElement("path", { d: "m7.5 4.5 5 5.5-5 5.5" }));
1299
+ }, React5.createElement("path", { d: "m7.5 4.5 5 5.5-5 5.5" }));
1188
1300
  }
1189
1301
  function displayCrumbs(listing) {
1190
1302
  const homeIndex = listing.crumbs.findIndex((crumb) => crumb.path === listing.home);
@@ -1200,25 +1312,25 @@ function WorkspaceDirectoryPicker({
1200
1312
  onPicked,
1201
1313
  onCancel
1202
1314
  }) {
1203
- const [listing, setListing] = React4.useState(null);
1204
- const [loading, setLoading] = React4.useState(false);
1205
- const [error, setError] = React4.useState(null);
1206
- const [showHidden, setShowHidden] = React4.useState(false);
1207
- const [retryKey, setRetryKey] = React4.useState(0);
1208
- const requestRef = React4.useRef(0);
1209
- const controllerRef = React4.useRef(null);
1210
- const dialogRef = React4.useRef(null);
1211
- const bodyRef = React4.useRef(null);
1212
- const titleId = React4.useId();
1213
- const noticeId = React4.useId();
1214
- const initialPathRef = React4.useRef(startPath);
1215
- const onPickedRef = React4.useRef(onPicked);
1216
- const onCancelRef = React4.useRef(onCancel);
1217
- const busyRef = React4.useRef(busy);
1315
+ const [listing, setListing] = React5.useState(null);
1316
+ const [loading, setLoading] = React5.useState(false);
1317
+ const [error, setError] = React5.useState(null);
1318
+ const [showHidden, setShowHidden] = React5.useState(false);
1319
+ const [retryKey, setRetryKey] = React5.useState(0);
1320
+ const requestRef = React5.useRef(0);
1321
+ const controllerRef = React5.useRef(null);
1322
+ const dialogRef = React5.useRef(null);
1323
+ const bodyRef = React5.useRef(null);
1324
+ const titleId = React5.useId();
1325
+ const noticeId = React5.useId();
1326
+ const initialPathRef = React5.useRef(startPath);
1327
+ const onPickedRef = React5.useRef(onPicked);
1328
+ const onCancelRef = React5.useRef(onCancel);
1329
+ const busyRef = React5.useRef(busy);
1218
1330
  onPickedRef.current = onPicked;
1219
1331
  onCancelRef.current = onCancel;
1220
1332
  busyRef.current = busy;
1221
- const loadDirectory = React4.useCallback(async (path, { reportError = true } = {}) => {
1333
+ const loadDirectory = React5.useCallback(async (path, { reportError = true } = {}) => {
1222
1334
  const request = requestRef.current + 1;
1223
1335
  requestRef.current = request;
1224
1336
  controllerRef.current?.abort();
@@ -1241,7 +1353,7 @@ function WorkspaceDirectoryPicker({
1241
1353
  if (request === requestRef.current) setLoading(false);
1242
1354
  }
1243
1355
  }, [picker]);
1244
- React4.useEffect(() => {
1356
+ React5.useEffect(() => {
1245
1357
  if (!open) return void 0;
1246
1358
  let active = true;
1247
1359
  setListing(null);
@@ -1319,10 +1431,10 @@ function WorkspaceDirectoryPicker({
1319
1431
  "nav",
1320
1432
  { className: "dim-directoryCrumbs", "aria-label": "\u5F53\u524D\u76EE\u5F55" },
1321
1433
  crumbs.map((crumb, index) => h2(
1322
- React4.Fragment,
1434
+ React5.Fragment,
1323
1435
  { key: crumb.path },
1324
1436
  index > 0 ? h2("span", { className: "dim-directoryCrumbSeparator", "aria-hidden": "true" }, "\u203A") : null,
1325
- React4.createElement("button", {
1437
+ React5.createElement("button", {
1326
1438
  type: "button",
1327
1439
  title: crumb.path,
1328
1440
  disabled: loading || busy,
@@ -1343,7 +1455,7 @@ function WorkspaceDirectoryPicker({
1343
1455
  ) : listing ? entries.length > 0 ? h2("ul", { className: "dim-directoryList" }, entries.map((entry) => h2(
1344
1456
  "li",
1345
1457
  { key: entry.path },
1346
- React4.createElement(
1458
+ React5.createElement(
1347
1459
  "button",
1348
1460
  {
1349
1461
  type: "button",
@@ -1352,7 +1464,7 @@ function WorkspaceDirectoryPicker({
1352
1464
  onClick: () => void loadDirectory(entry.path)
1353
1465
  },
1354
1466
  h2("span", { className: "dim-directoryFolder" }, h2(FolderIcon)),
1355
- React4.createElement("span", { className: "dim-directoryName" }, entry.name),
1467
+ React5.createElement("span", { className: "dim-directoryName" }, entry.name),
1356
1468
  h2("span", { className: "dim-directoryChevron" }, h2(ChevronIcon))
1357
1469
  )
1358
1470
  ))) : h2(
@@ -1405,21 +1517,21 @@ function WorkspaceDirectoryPicker({
1405
1517
  }
1406
1518
 
1407
1519
  // plugin-src/client/workspace-editor.js
1408
- var WorkspaceDirectoryPickerContext = React5.createContext(null);
1520
+ var WorkspaceDirectoryPickerContext = React6.createContext(null);
1409
1521
  function WorkspaceEditor({ workspace, directoryPicker, disabled = false, onSave }) {
1410
- const sharedDirectoryPicker = React5.useContext(WorkspaceDirectoryPickerContext);
1522
+ const sharedDirectoryPicker = React6.useContext(WorkspaceDirectoryPickerContext);
1411
1523
  const activeDirectoryPicker = directoryPicker ?? sharedDirectoryPicker;
1412
- const [open, setOpen] = React5.useState(false);
1413
- const [saving, setSaving] = React5.useState(false);
1414
- const [error, setError] = React5.useState(null);
1415
- const editButtonRef = React5.useRef(null);
1416
- const savingRef = React5.useRef(false);
1417
- const close = React5.useCallback(() => {
1524
+ const [open, setOpen] = React6.useState(false);
1525
+ const [saving, setSaving] = React6.useState(false);
1526
+ const [error, setError] = React6.useState(null);
1527
+ const editButtonRef = React6.useRef(null);
1528
+ const savingRef = React6.useRef(false);
1529
+ const close = React6.useCallback(() => {
1418
1530
  setOpen(false);
1419
1531
  setError(null);
1420
1532
  queueMicrotask(() => editButtonRef.current?.focus?.());
1421
1533
  }, []);
1422
- const pick = React5.useCallback(async (value) => {
1534
+ const pick = React6.useCallback(async (value) => {
1423
1535
  if (!value || savingRef.current || disabled) return;
1424
1536
  if (value === workspace) {
1425
1537
  close();
@@ -1456,7 +1568,7 @@ function WorkspaceEditor({ workspace, directoryPicker, disabled = false, onSave
1456
1568
  disabled: disabled || !activeDirectoryPicker
1457
1569
  }, "\u9009\u62E9\u76EE\u5F55")
1458
1570
  ),
1459
- workspace ? React5.createElement("code", {
1571
+ workspace ? React6.createElement("code", {
1460
1572
  className: "dim-workspacePath",
1461
1573
  title: workspace
1462
1574
  }, workspace) : h2("code", { className: "dim-workspacePath" }, "\u672A\u8BBE\u7F6E"),
@@ -1473,10 +1585,10 @@ function WorkspaceEditor({ workspace, directoryPicker, disabled = false, onSave
1473
1585
  }
1474
1586
 
1475
1587
  // plugin-src/client/workspace-snapshot-fence.js
1476
- var React6 = __toESM(require("react"), 1);
1588
+ var React7 = __toESM(require("react"), 1);
1477
1589
  function useWorkspaceSnapshotFence() {
1478
- const state = React6.useRef({ version: 0, pendingMutations: 0 });
1479
- return React6.useMemo(() => Object.freeze({
1590
+ const state = React7.useRef({ version: 0, pendingMutations: 0 });
1591
+ return React7.useMemo(() => Object.freeze({
1480
1592
  beginStatus() {
1481
1593
  return state.current.pendingMutations === 0 ? state.current.version : null;
1482
1594
  },
@@ -1656,7 +1768,7 @@ function DingtalkIcon({ size = 28 }) {
1656
1768
  d: "M37.05 22.783c-6.758-5.216-14.378-12.128-22.73-19.538-.655-.585-1.242-.354-1.536.42-1.88 4.973-.058 9.386 2.889 11.932s7.368 4.912 10.058 6.155c.105.049.013.203-.093.163-4.953-2.182-8.397-3.765-13.07-7.368-.497-.388-1.01-.242-1.07.521-.384 4.748 2.657 8.483 6.058 9.745 2.1.781 4.398 1.212 6.53 1.474.109.015.084.178-.027.178-2.747.01-6.058-.654-8.935-1.751-.606-.233-.818.25-.722.633.491 2.008 2.974 5.076 6.926 5.73a12 12 0 0 0 2.228.115c.164 0 .208.089.154.217q-2.685 4.6-2.803 4.797c-.091.152-.036.275.156.275h3.543c.164 0 .264.106.18.246l-4.958 8.196c-.191.328.035.565.395.301s15.212-11.133 15.636-11.448c.195-.142.148-.327-.124-.327h-3.18c-.206 0-.252-.14-.111-.28.14-.141 3.602-3.594 4.837-4.888 1.283-1.35 1.938-3.825-.231-5.498"
1657
1769
  }));
1658
1770
  }
1659
- var Button = React7.forwardRef(function Button2({ children, kind = "secondary", className = "", ...props }, ref) {
1771
+ var Button = React8.forwardRef(function Button2({ children, kind = "secondary", className = "", ...props }, ref) {
1660
1772
  return h2("button", {
1661
1773
  ...props,
1662
1774
  ref,
@@ -1752,13 +1864,13 @@ function EmptyView({ busy, onStart }) {
1752
1864
  );
1753
1865
  }
1754
1866
  function QrPanel({ provision, now, busy, onRefresh, onCancel }) {
1755
- const [imageFailed, setImageFailed] = React7.useState(false);
1867
+ const [imageFailed, setImageFailed] = React8.useState(false);
1756
1868
  const source = safeQrSource(provision.qrCodeDataUrl);
1757
1869
  const remaining = Math.max(0, provision.expiresAt - now);
1758
1870
  const expired = remaining === 0 || provision.status === "expired";
1759
1871
  const duration = Math.max(1, provision.durationMs ?? 10 * 6e4);
1760
1872
  const progress = Math.round(Math.min(1, remaining / duration) * 100);
1761
- React7.useEffect(() => setImageFailed(false), [source]);
1873
+ React8.useEffect(() => setImageFailed(false), [source]);
1762
1874
  return h2(
1763
1875
  "div",
1764
1876
  { className: "ddt-card dim-surfaceCard" },
@@ -1875,8 +1987,8 @@ function checkedTime(value) {
1875
1987
  }
1876
1988
  }
1877
1989
  function RemoveConfirmation({ account, busy, onConfirm, onCancel }) {
1878
- const cancelRef = React7.useRef(null);
1879
- React7.useEffect(() => cancelRef.current?.focus(), []);
1990
+ const cancelRef = React8.useRef(null);
1991
+ React8.useEffect(() => cancelRef.current?.focus(), []);
1880
1992
  return h2(
1881
1993
  "div",
1882
1994
  {
@@ -1908,6 +2020,7 @@ function AccountCard({
1908
2020
  removing,
1909
2021
  onReconnect,
1910
2022
  onWorkspaceSave,
2023
+ onAgentPresetSave,
1911
2024
  onRequestRemove,
1912
2025
  onConfirmRemove,
1913
2026
  onCancelRemove
@@ -1964,6 +2077,11 @@ function AccountCard({
1964
2077
  disabled: Boolean(busy),
1965
2078
  onSave: onWorkspaceSave
1966
2079
  }),
2080
+ h2(AgentPresetEditor, {
2081
+ agentPreset: account.agentPreset,
2082
+ disabled: Boolean(busy),
2083
+ onSave: onAgentPresetSave
2084
+ }),
1967
2085
  h2(
1968
2086
  "div",
1969
2087
  { className: "ddt-accountFooter dim-cardFooter" },
@@ -2015,6 +2133,7 @@ function AccountList(props) {
2015
2133
  removing: props.removeTarget === account.botId,
2016
2134
  onReconnect: () => props.onReconnect(account),
2017
2135
  onWorkspaceSave: (workspace) => props.onWorkspaceSave(account, workspace),
2136
+ onAgentPresetSave: (agentPreset) => props.onAgentPresetSave(account, agentPreset),
2018
2137
  onRequestRemove: () => props.onRequestRemove(account),
2019
2138
  onConfirmRemove: () => props.onConfirmRemove(account),
2020
2139
  onCancelRemove: props.onCancelRemove
@@ -2024,29 +2143,30 @@ function AccountList(props) {
2024
2143
  }
2025
2144
  var EMPTY_TOTALS = Object.freeze({ configured: 0, connected: 0 });
2026
2145
  function DingtalkSettingsTab({ rpcCall }) {
2027
- const [model, setModel] = React7.useState({
2146
+ const [model, setModel] = React8.useState({
2028
2147
  phase: "loading",
2029
2148
  bots: [],
2030
2149
  totals: EMPTY_TOTALS,
2031
2150
  revision: 0,
2032
- error: null
2151
+ error: null,
2152
+ agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
2033
2153
  });
2034
- const [provision, setProvision] = React7.useState(null);
2035
- const [busy, setBusy] = React7.useState(false);
2036
- const [busyByBot, setBusyByBot] = React7.useState({});
2037
- const [feedbackByBot, setFeedbackByBot] = React7.useState({});
2038
- const [removeTarget, setRemoveTarget] = React7.useState(null);
2039
- const [credentialOpen, setCredentialOpen] = React7.useState(false);
2040
- const [credentialError, setCredentialError] = React7.useState(null);
2041
- const [notice, setNotice] = React7.useState("");
2042
- const [now, setNow] = React7.useState(() => Date.now());
2043
- const addButtonRef = React7.useRef(null);
2044
- const mountedRef = React7.useRef(true);
2045
- const statusRequestRef = React7.useRef(0);
2154
+ const [provision, setProvision] = React8.useState(null);
2155
+ const [busy, setBusy] = React8.useState(false);
2156
+ const [busyByBot, setBusyByBot] = React8.useState({});
2157
+ const [feedbackByBot, setFeedbackByBot] = React8.useState({});
2158
+ const [removeTarget, setRemoveTarget] = React8.useState(null);
2159
+ const [credentialOpen, setCredentialOpen] = React8.useState(false);
2160
+ const [credentialError, setCredentialError] = React8.useState(null);
2161
+ const [notice, setNotice] = React8.useState("");
2162
+ const [now, setNow] = React8.useState(() => Date.now());
2163
+ const addButtonRef = React8.useRef(null);
2164
+ const mountedRef = React8.useRef(true);
2165
+ const statusRequestRef = React8.useRef(0);
2046
2166
  const workspaceFence = useWorkspaceSnapshotFence();
2047
- const noticeFrameRef = React7.useRef(null);
2048
- const focusFrameRef = React7.useRef(null);
2049
- React7.useEffect(() => {
2167
+ const noticeFrameRef = React8.useRef(null);
2168
+ const focusFrameRef = React8.useRef(null);
2169
+ React8.useEffect(() => {
2050
2170
  mountedRef.current = true;
2051
2171
  return () => {
2052
2172
  mountedRef.current = false;
@@ -2061,8 +2181,8 @@ function DingtalkSettingsTab({ rpcCall }) {
2061
2181
  }
2062
2182
  };
2063
2183
  }, []);
2064
- React7.useEffect(() => installDingtalkStyles(), []);
2065
- const announce = React7.useCallback((message) => {
2184
+ React8.useEffect(() => installDingtalkStyles(), []);
2185
+ const announce = React8.useCallback((message) => {
2066
2186
  if (!mountedRef.current) return;
2067
2187
  if (noticeFrameRef.current !== null) {
2068
2188
  window.cancelAnimationFrame(noticeFrameRef.current);
@@ -2076,7 +2196,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2076
2196
  });
2077
2197
  }
2078
2198
  }, []);
2079
- const discardStaleFeedback = React7.useCallback((snapshot) => {
2199
+ const discardStaleFeedback = React8.useCallback((snapshot) => {
2080
2200
  const botsById = new Map(snapshot.bots.map((bot) => [bot.botId, bot]));
2081
2201
  setFeedbackByBot((current) => {
2082
2202
  let changed = false;
@@ -2091,7 +2211,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2091
2211
  return changed ? next : current;
2092
2212
  });
2093
2213
  }, []);
2094
- const focusAddButton = React7.useCallback(() => {
2214
+ const focusAddButton = React8.useCallback(() => {
2095
2215
  if (!mountedRef.current) return;
2096
2216
  if (focusFrameRef.current !== null) window.cancelAnimationFrame(focusFrameRef.current);
2097
2217
  focusFrameRef.current = window.requestAnimationFrame(() => {
@@ -2099,11 +2219,11 @@ function DingtalkSettingsTab({ rpcCall }) {
2099
2219
  if (mountedRef.current) addButtonRef.current?.focus();
2100
2220
  });
2101
2221
  }, []);
2102
- const invoke = React7.useCallback(async (endpoint, payload = {}, signal) => {
2222
+ const invoke = React8.useCallback(async (endpoint, payload = {}, signal) => {
2103
2223
  if (typeof rpcCall !== "function") throw new TypeError("\u9489\u9489\u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
2104
2224
  return unwrapRpcResult(await rpcCall(endpoint, payload, signal));
2105
2225
  }, [rpcCall]);
2106
- const loadStatus = React7.useCallback(async ({
2226
+ const loadStatus = React8.useCallback(async ({
2107
2227
  signal,
2108
2228
  silent = false,
2109
2229
  restoreProvisioning = false
@@ -2125,7 +2245,8 @@ function DingtalkSettingsTab({ rpcCall }) {
2125
2245
  bots: snapshot.bots,
2126
2246
  totals: snapshot.totals,
2127
2247
  revision: snapshot.revision,
2128
- error: null
2248
+ error: null,
2249
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
2129
2250
  });
2130
2251
  discardStaleFeedback(snapshot);
2131
2252
  if (restoreProvisioning && snapshot.provisioning) {
@@ -2146,12 +2267,12 @@ function DingtalkSettingsTab({ rpcCall }) {
2146
2267
  return void 0;
2147
2268
  }
2148
2269
  }, [discardStaleFeedback, invoke, workspaceFence]);
2149
- React7.useEffect(() => {
2270
+ React8.useEffect(() => {
2150
2271
  const controller = new AbortController();
2151
2272
  void loadStatus({ signal: controller.signal, restoreProvisioning: true });
2152
2273
  return () => controller.abort();
2153
2274
  }, [loadStatus]);
2154
- React7.useEffect(() => {
2275
+ React8.useEffect(() => {
2155
2276
  if (model.phase !== "ready") return void 0;
2156
2277
  const controller = new AbortController();
2157
2278
  let running = false;
@@ -2170,14 +2291,14 @@ function DingtalkSettingsTab({ rpcCall }) {
2170
2291
  window.clearInterval(timer);
2171
2292
  };
2172
2293
  }, [loadStatus, model.phase]);
2173
- React7.useEffect(() => {
2294
+ React8.useEffect(() => {
2174
2295
  if (!provision || !ACTIVE_PROVISION_STATES.has(provision.status)) return void 0;
2175
2296
  const timer = window.setInterval(() => {
2176
2297
  if (mountedRef.current) setNow(Date.now());
2177
2298
  }, 1e3);
2178
2299
  return () => window.clearInterval(timer);
2179
2300
  }, [provision?.attemptId, provision?.status]);
2180
- const startProvisioning = React7.useCallback(async ({ replace = false } = {}) => {
2301
+ const startProvisioning = React8.useCallback(async ({ replace = false } = {}) => {
2181
2302
  if (!mountedRef.current) return;
2182
2303
  setCredentialOpen(false);
2183
2304
  setCredentialError(null);
@@ -2215,7 +2336,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2215
2336
  if (mountedRef.current) setBusy(false);
2216
2337
  }
2217
2338
  }, [announce, invoke, provision?.attemptId]);
2218
- const bindCredentials = React7.useCallback(async ({ identity, secret }) => {
2339
+ const bindCredentials = React8.useCallback(async ({ identity, secret }) => {
2219
2340
  if (!mountedRef.current) return;
2220
2341
  const snapshotVersion = workspaceFence.beginMutation();
2221
2342
  setBusy(true);
@@ -2232,7 +2353,8 @@ function DingtalkSettingsTab({ rpcCall }) {
2232
2353
  bots: snapshot.bots,
2233
2354
  totals: snapshot.totals,
2234
2355
  revision: snapshot.revision,
2235
- error: null
2356
+ error: null,
2357
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
2236
2358
  });
2237
2359
  discardStaleFeedback(snapshot);
2238
2360
  }
@@ -2246,7 +2368,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2246
2368
  if (mountedRef.current) setBusy(false);
2247
2369
  }
2248
2370
  }, [announce, discardStaleFeedback, invoke, loadStatus, workspaceFence]);
2249
- const cancelProvisioning = React7.useCallback(async () => {
2371
+ const cancelProvisioning = React8.useCallback(async () => {
2250
2372
  if (!mountedRef.current) return;
2251
2373
  setBusy(true);
2252
2374
  try {
@@ -2264,7 +2386,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2264
2386
  if (mountedRef.current) setBusy(false);
2265
2387
  }
2266
2388
  }, [announce, focusAddButton, invoke, provision?.attemptId, provision?.status]);
2267
- React7.useEffect(() => {
2389
+ React8.useEffect(() => {
2268
2390
  const attemptId = provision?.attemptId;
2269
2391
  if (!attemptId || !ACTIVE_PROVISION_STATES.has(provision.status)) return void 0;
2270
2392
  const controller = new AbortController();
@@ -2323,7 +2445,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2323
2445
  timer = null;
2324
2446
  };
2325
2447
  }, [announce, invoke, loadStatus, provision?.attemptId, provision?.pollIntervalMs, provision?.status]);
2326
- const setBotBusy = React7.useCallback((botId, operation) => {
2448
+ const setBotBusy = React8.useCallback((botId, operation) => {
2327
2449
  if (!mountedRef.current) return;
2328
2450
  setBusyByBot((current) => {
2329
2451
  const next = { ...current };
@@ -2332,7 +2454,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2332
2454
  return next;
2333
2455
  });
2334
2456
  }, []);
2335
- const runBotAction = React7.useCallback(async ({ account, operation, endpoint, payload, success }) => {
2457
+ const runBotAction = React8.useCallback(async ({ account, operation, endpoint, payload, success }) => {
2336
2458
  if (!mountedRef.current) return void 0;
2337
2459
  const snapshotVersion = workspaceFence.beginMutation();
2338
2460
  setBotBusy(account.botId, operation);
@@ -2352,7 +2474,8 @@ function DingtalkSettingsTab({ rpcCall }) {
2352
2474
  bots: snapshot.bots,
2353
2475
  totals: snapshot.totals,
2354
2476
  revision: snapshot.revision,
2355
- error: null
2477
+ error: null,
2478
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
2356
2479
  });
2357
2480
  discardStaleFeedback(snapshot);
2358
2481
  }
@@ -2385,7 +2508,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2385
2508
  if (mountedRef.current) setBotBusy(account.botId, null);
2386
2509
  }
2387
2510
  }, [announce, discardStaleFeedback, invoke, loadStatus, setBotBusy, workspaceFence]);
2388
- const reconnect = React7.useCallback((account) => runBotAction({
2511
+ const reconnect = React8.useCallback((account) => runBotAction({
2389
2512
  account,
2390
2513
  operation: "reconnect",
2391
2514
  endpoint: DINGTALK_ENDPOINTS.reconnectBot,
@@ -2396,7 +2519,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2396
2519
  return connectionTestFeedback(snapshot.testMessage) ?? "\u9489\u9489\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002";
2397
2520
  }
2398
2521
  }), [runBotAction]);
2399
- const saveWorkspace = React7.useCallback(async (account, workspace) => {
2522
+ const saveWorkspace = React8.useCallback(async (account, workspace) => {
2400
2523
  const workspaceVersion = workspaceFence.beginMutation();
2401
2524
  setBotBusy(account.botId, "workspace");
2402
2525
  try {
@@ -2410,7 +2533,8 @@ function DingtalkSettingsTab({ rpcCall }) {
2410
2533
  bots: snapshot.bots,
2411
2534
  totals: snapshot.totals,
2412
2535
  revision: snapshot.revision,
2413
- error: null
2536
+ error: null,
2537
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
2414
2538
  });
2415
2539
  discardStaleFeedback(snapshot);
2416
2540
  }
@@ -2420,7 +2544,32 @@ function DingtalkSettingsTab({ rpcCall }) {
2420
2544
  if (mountedRef.current) setBotBusy(account.botId, null);
2421
2545
  }
2422
2546
  }, [discardStaleFeedback, invoke, loadStatus, setBotBusy, workspaceFence]);
2423
- const remove = React7.useCallback(async (account) => {
2547
+ const saveAgentPreset = React8.useCallback(async (account, agentPreset) => {
2548
+ const snapshotVersion = workspaceFence.beginMutation();
2549
+ setBotBusy(account.botId, "preset");
2550
+ try {
2551
+ const snapshot = normalizeSnapshot(await invoke(
2552
+ DINGTALK_ENDPOINTS.setAgentPreset,
2553
+ { botId: account.botId, agentPreset }
2554
+ ));
2555
+ if (mountedRef.current && workspaceFence.canCommitMutation(snapshotVersion)) {
2556
+ setModel({
2557
+ phase: "ready",
2558
+ bots: snapshot.bots,
2559
+ totals: snapshot.totals,
2560
+ revision: snapshot.revision,
2561
+ error: null,
2562
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
2563
+ });
2564
+ discardStaleFeedback(snapshot);
2565
+ }
2566
+ } finally {
2567
+ const shouldRefresh = workspaceFence.endMutation();
2568
+ if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true });
2569
+ if (mountedRef.current) setBotBusy(account.botId, null);
2570
+ }
2571
+ }, [discardStaleFeedback, invoke, loadStatus, setBotBusy, workspaceFence]);
2572
+ const remove = React8.useCallback(async (account) => {
2424
2573
  const snapshot = await runBotAction({
2425
2574
  account,
2426
2575
  operation: "delete",
@@ -2474,7 +2623,9 @@ function DingtalkSettingsTab({ rpcCall }) {
2474
2623
  setCredentialError(null);
2475
2624
  }
2476
2625
  }) : null;
2477
- return h2(
2626
+ return h2(AgentPresetCatalogContext.Provider, {
2627
+ value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
2628
+ }, h2(
2478
2629
  "section",
2479
2630
  { className: "ddt-page dim-channelPage", "aria-label": "\u9489\u9489\u8BBE\u7F6E" },
2480
2631
  h2(Heading, {
@@ -2502,7 +2653,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2502
2653
  h2(Button, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
2503
2654
  )
2504
2655
  ) : h2(
2505
- React7.Fragment,
2656
+ React8.Fragment,
2506
2657
  null,
2507
2658
  credentialView,
2508
2659
  provisionView,
@@ -2514,12 +2665,13 @@ function DingtalkSettingsTab({ rpcCall }) {
2514
2665
  removeTarget,
2515
2666
  onReconnect: (account) => void reconnect(account),
2516
2667
  onWorkspaceSave: saveWorkspace,
2668
+ onAgentPresetSave: saveAgentPreset,
2517
2669
  onRequestRemove: (account) => setRemoveTarget(account.botId),
2518
2670
  onConfirmRemove: (account) => void remove(account),
2519
2671
  onCancelRemove: () => setRemoveTarget(null)
2520
2672
  }) : null
2521
2673
  )
2522
- );
2674
+ ));
2523
2675
  }
2524
2676
 
2525
2677
  // plugin-src/client/channels/shared/token-api.js
@@ -2544,7 +2696,8 @@ var TOKEN_BOT_ENDPOINTS = Object.freeze({
2544
2696
  bindCredentials: "bot.bind-credentials",
2545
2697
  reconnectBot: "bot.reconnect",
2546
2698
  deleteBot: "bot.delete",
2547
- setWorkspace: "bot.workspace.set"
2699
+ setWorkspace: "bot.workspace.set",
2700
+ setAgentPreset: SET_AGENT_PRESET_ENDPOINT
2548
2701
  });
2549
2702
  function createTokenChannelApi(channel4, connectionSummary, {
2550
2703
  normalizeBotExtension = () => ({})
@@ -2570,6 +2723,7 @@ function createTokenChannelApi(channel4, connectionSummary, {
2570
2723
  connected,
2571
2724
  state: connected ? "connected" : state,
2572
2725
  workspace: text(value.workspace, "", 4096),
2726
+ agentPreset: normalizeAgentPresetId(value.agentPreset),
2573
2727
  bot: {
2574
2728
  name: text(value.bot?.name, `${channel4}\u673A\u5668\u4EBA`, 100),
2575
2729
  username: text(value.bot?.username, "", 100),
@@ -2598,7 +2752,8 @@ function createTokenChannelApi(channel4, connectionSummary, {
2598
2752
  return {
2599
2753
  revision: Number.isSafeInteger(source.revision) ? source.revision : 0,
2600
2754
  bots,
2601
- totals: { configured: bots.length, connected: bots.filter((bot) => bot.connected).length }
2755
+ totals: { configured: bots.length, connected: bots.filter((bot) => bot.connected).length },
2756
+ agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog)
2602
2757
  };
2603
2758
  };
2604
2759
  const presentError10 = (error) => ({
@@ -2617,8 +2772,8 @@ var normalizeSnapshot2 = api.normalizeSnapshot;
2617
2772
  var presentError2 = api.presentError;
2618
2773
 
2619
2774
  // plugin-src/client/channels/shared/token-channel.js
2620
- var React8 = __toESM(require("react"), 1);
2621
- var Button3 = React8.forwardRef(function Button4({ children, kind = "secondary", className = "", ...props }, ref) {
2775
+ var React9 = __toESM(require("react"), 1);
2776
+ var Button3 = React9.forwardRef(function Button4({ children, kind = "secondary", className = "", ...props }, ref) {
2622
2777
  return h2("button", {
2623
2778
  ...props,
2624
2779
  ref,
@@ -2670,7 +2825,7 @@ function createTokenChannelSettings(definition) {
2670
2825
  AccountSettings = null,
2671
2826
  accountSettingsEndpoint = null
2672
2827
  } = definition;
2673
- function AccountCard5({ account, busy, testNotice, removing, onReconnect, onWorkspaceSave, onAccountSettingsSave, onRequestRemove, onConfirmRemove, onCancelRemove }) {
2828
+ function AccountCard5({ account, busy, testNotice, removing, onReconnect, onWorkspaceSave, onAgentPresetSave, onAccountSettingsSave, onRequestRemove, onConfirmRemove, onCancelRemove }) {
2674
2829
  const state = busy === "reconnect" ? "connecting" : account.state;
2675
2830
  const tone = account.connected ? "success" : state === "error" ? "error" : "warning";
2676
2831
  const stateLabel2 = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA";
@@ -2728,6 +2883,11 @@ function createTokenChannelSettings(definition) {
2728
2883
  disabled: Boolean(busy),
2729
2884
  onSave: onWorkspaceSave
2730
2885
  }),
2886
+ h2(AgentPresetEditor, {
2887
+ agentPreset: account.agentPreset,
2888
+ disabled: Boolean(busy),
2889
+ onSave: onAgentPresetSave
2890
+ }),
2731
2891
  AccountSettings ? h2(AccountSettings, {
2732
2892
  account,
2733
2893
  busy: Boolean(busy),
@@ -2774,21 +2934,22 @@ function createTokenChannelSettings(definition) {
2774
2934
  );
2775
2935
  }
2776
2936
  function SettingsTab({ rpcCall }) {
2777
- const [model, setModel] = React8.useState({
2937
+ const [model, setModel] = React9.useState({
2778
2938
  phase: "loading",
2779
2939
  bots: [],
2780
2940
  totals: { configured: 0, connected: 0 },
2781
- error: null
2941
+ error: null,
2942
+ agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
2782
2943
  });
2783
- const [credentialOpen, setCredentialOpen] = React8.useState(false);
2784
- const [credentialError, setCredentialError] = React8.useState(null);
2785
- const [busy, setBusy] = React8.useState(false);
2786
- const [busyByBot, setBusyByBot] = React8.useState({});
2787
- const [testNoticeByBot, setTestNoticeByBot] = React8.useState({});
2788
- const [removeTarget, setRemoveTarget] = React8.useState(null);
2789
- const mounted = React8.useRef(true);
2944
+ const [credentialOpen, setCredentialOpen] = React9.useState(false);
2945
+ const [credentialError, setCredentialError] = React9.useState(null);
2946
+ const [busy, setBusy] = React9.useState(false);
2947
+ const [busyByBot, setBusyByBot] = React9.useState({});
2948
+ const [testNoticeByBot, setTestNoticeByBot] = React9.useState({});
2949
+ const [removeTarget, setRemoveTarget] = React9.useState(null);
2950
+ const mounted = React9.useRef(true);
2790
2951
  const workspaceFence = useWorkspaceSnapshotFence();
2791
- React8.useEffect(() => {
2952
+ React9.useEffect(() => {
2792
2953
  const disposeDingtalk = installDingtalkStyles();
2793
2954
  const disposeChannel = installStyles();
2794
2955
  mounted.current = true;
@@ -2798,18 +2959,24 @@ function createTokenChannelSettings(definition) {
2798
2959
  disposeDingtalk();
2799
2960
  };
2800
2961
  }, []);
2801
- const invoke = React8.useCallback(async (endpoint, payload = {}, signal) => {
2962
+ const invoke = React9.useCallback(async (endpoint, payload = {}, signal) => {
2802
2963
  if (typeof rpcCall !== "function") throw new TypeError(`${channel4} \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5`);
2803
2964
  return api4.unwrapRpcResult(await rpcCall(endpoint, payload, signal));
2804
2965
  }, [rpcCall]);
2805
- const loadStatus = React8.useCallback(async ({ signal, silent = false } = {}) => {
2966
+ const loadStatus = React9.useCallback(async ({ signal, silent = false } = {}) => {
2806
2967
  const workspaceVersion = workspaceFence.beginStatus();
2807
2968
  if (workspaceVersion === null) return;
2808
2969
  if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
2809
2970
  try {
2810
2971
  const snapshot = api4.normalizeSnapshot(await invoke(endpoints.status, {}, signal));
2811
2972
  if (!mounted.current || signal?.aborted || !workspaceFence.canCommitStatus(workspaceVersion)) return;
2812
- setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
2973
+ setModel({
2974
+ phase: "ready",
2975
+ bots: snapshot.bots,
2976
+ totals: snapshot.totals,
2977
+ error: null,
2978
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
2979
+ });
2813
2980
  } catch (error) {
2814
2981
  if (error?.name !== "AbortError" && mounted.current && !signal?.aborted && workspaceFence.canCommitStatus(workspaceVersion)) {
2815
2982
  setModel((current) => ({
@@ -2820,12 +2987,12 @@ function createTokenChannelSettings(definition) {
2820
2987
  }
2821
2988
  }
2822
2989
  }, [invoke, workspaceFence]);
2823
- React8.useEffect(() => {
2990
+ React9.useEffect(() => {
2824
2991
  const controller = new AbortController();
2825
2992
  void loadStatus({ signal: controller.signal });
2826
2993
  return () => controller.abort();
2827
2994
  }, [loadStatus]);
2828
- React8.useEffect(() => {
2995
+ React9.useEffect(() => {
2829
2996
  if (model.phase !== "ready") return void 0;
2830
2997
  const controller = new AbortController();
2831
2998
  const timer = window.setInterval(
@@ -2837,7 +3004,7 @@ function createTokenChannelSettings(definition) {
2837
3004
  window.clearInterval(timer);
2838
3005
  };
2839
3006
  }, [loadStatus, model.phase]);
2840
- const bindCredentials = React8.useCallback(async (values) => {
3007
+ const bindCredentials = React9.useCallback(async (values) => {
2841
3008
  const snapshotVersion = workspaceFence.beginMutation();
2842
3009
  setBusy(true);
2843
3010
  setCredentialError(null);
@@ -2848,7 +3015,13 @@ function createTokenChannelSettings(definition) {
2848
3015
  ));
2849
3016
  if (!mounted.current) return;
2850
3017
  if (workspaceFence.canCommitMutation(snapshotVersion)) {
2851
- setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
3018
+ setModel({
3019
+ phase: "ready",
3020
+ bots: snapshot.bots,
3021
+ totals: snapshot.totals,
3022
+ error: null,
3023
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
3024
+ });
2852
3025
  }
2853
3026
  setCredentialOpen(false);
2854
3027
  } catch (error) {
@@ -2859,14 +3032,20 @@ function createTokenChannelSettings(definition) {
2859
3032
  if (mounted.current) setBusy(false);
2860
3033
  }
2861
3034
  }, [invoke, loadStatus, workspaceFence]);
2862
- const botAction = React8.useCallback(async (account, operation, endpoint, payload) => {
3035
+ const botAction = React9.useCallback(async (account, operation, endpoint, payload) => {
2863
3036
  const snapshotVersion = workspaceFence.beginMutation();
2864
3037
  setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
2865
3038
  try {
2866
3039
  const value = await invoke(endpoint, payload);
2867
3040
  const snapshot = api4.normalizeSnapshot(value);
2868
3041
  if (mounted.current && workspaceFence.canCommitMutation(snapshotVersion)) {
2869
- setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
3042
+ setModel({
3043
+ phase: "ready",
3044
+ bots: snapshot.bots,
3045
+ totals: snapshot.totals,
3046
+ error: null,
3047
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
3048
+ });
2870
3049
  }
2871
3050
  if (mounted.current && operation === "reconnect") {
2872
3051
  setTestNoticeByBot((current) => ({
@@ -2917,6 +3096,12 @@ function createTokenChannelSettings(definition) {
2917
3096
  endpoints.setWorkspace,
2918
3097
  { botId: account.botId, workspace }
2919
3098
  ),
3099
+ onAgentPresetSave: (agentPreset) => botAction(
3100
+ account,
3101
+ "preset",
3102
+ endpoints.setAgentPreset,
3103
+ { botId: account.botId, agentPreset }
3104
+ ),
2920
3105
  onAccountSettingsSave: AccountSettings && accountSettingsEndpoint ? (payload) => botAction(
2921
3106
  account,
2922
3107
  "settings",
@@ -2934,7 +3119,9 @@ function createTokenChannelSettings(definition) {
2934
3119
  }
2935
3120
  }))))
2936
3121
  ) : null;
2937
- return h2(
3122
+ return h2(AgentPresetCatalogContext.Provider, {
3123
+ value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
3124
+ }, h2(
2938
3125
  "section",
2939
3126
  {
2940
3127
  className: `ddt-page ${pageClass} dim-channelPage`,
@@ -2982,7 +3169,7 @@ function createTokenChannelSettings(definition) {
2982
3169
  h2(Button3, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
2983
3170
  )
2984
3171
  ) : h2(
2985
- React8.Fragment,
3172
+ React9.Fragment,
2986
3173
  null,
2987
3174
  credentialOpen ? CredentialPanel ? h2(CredentialPanel, {
2988
3175
  busy,
@@ -3038,7 +3225,7 @@ function createTokenChannelSettings(definition) {
3038
3225
  ) : null,
3039
3226
  botList
3040
3227
  )
3041
- );
3228
+ ));
3042
3229
  }
3043
3230
  return { SettingsTab, AccountCard: AccountCard5 };
3044
3231
  }
@@ -3083,7 +3270,7 @@ var DiscordSettingsTab = channel.SettingsTab;
3083
3270
  var DiscordAccountCard = channel.AccountCard;
3084
3271
 
3085
3272
  // plugin-src/client/channels/feishu/index.js
3086
- var React10 = __toESM(require("react"), 1);
3273
+ var React11 = __toESM(require("react"), 1);
3087
3274
 
3088
3275
  // plugin-src/client/channels/feishu/api.js
3089
3276
  var FEISHU_RPC_CHANNEL = "/feishu";
@@ -3098,6 +3285,7 @@ var FEISHU_ENDPOINTS = Object.freeze({
3098
3285
  disconnectBot: "bot.disconnect",
3099
3286
  deleteBot: "bot.delete",
3100
3287
  setWorkspace: "bot.workspace.set",
3288
+ setAgentPreset: "bot.preset.set",
3101
3289
  // Kept for rolling upgrades. The multi-bot UI never calls these endpoints.
3102
3290
  testConnection: "connection.test",
3103
3291
  disconnect: "connection.disconnect"
@@ -3231,6 +3419,7 @@ function normalizeBotConnection(value, fallbackBotId) {
3231
3419
  connected,
3232
3420
  configured: value.configured !== false,
3233
3421
  workspace: optionalString2(value.workspace)?.slice(0, 4096) ?? "",
3422
+ agentPreset: normalizeAgentPresetId(value.agentPreset),
3234
3423
  bot: normalizeBot2(value.bot),
3235
3424
  health: normalizeHealth(value.health, connected),
3236
3425
  error: normalizeError2(value.error)
@@ -3271,7 +3460,8 @@ function normalizeBotsSnapshot(value) {
3271
3460
  // make the UI claim that an unavailable bot is online.
3272
3461
  totals: { configured, connected },
3273
3462
  provisioning: value.provisioning ? normalizeProvisioning2(value.provisioning) : void 0,
3274
- error: normalizeError2(value.error)
3463
+ error: normalizeError2(value.error),
3464
+ agentPresetCatalog: normalizeAgentPresetCatalog(value.agentPresetCatalog)
3275
3465
  };
3276
3466
  }
3277
3467
  function normalizeConnectionSnapshot(value) {
@@ -3321,7 +3511,7 @@ function formatRemaining2(milliseconds) {
3321
3511
  }
3322
3512
 
3323
3513
  // plugin-src/client/lifecycle.js
3324
- var React9 = __toESM(require("react"), 1);
3514
+ var React10 = __toESM(require("react"), 1);
3325
3515
  function createPollScheduler({ setTimeoutFn, clearTimeoutFn }) {
3326
3516
  let disposed = false;
3327
3517
  let timer;
@@ -3383,8 +3573,8 @@ function createAnimationFrameScheduler({ requestFrame, cancelFrame }) {
3383
3573
  };
3384
3574
  }
3385
3575
  function useAnimationFrameScheduler() {
3386
- const schedulerRef = React9.useRef(null);
3387
- React9.useEffect(() => {
3576
+ const schedulerRef = React10.useRef(null);
3577
+ React10.useEffect(() => {
3388
3578
  const scheduler = createAnimationFrameScheduler({
3389
3579
  requestFrame: (callback) => window.requestAnimationFrame(callback),
3390
3580
  cancelFrame: (frame) => window.cancelAnimationFrame(frame)
@@ -3395,7 +3585,7 @@ function useAnimationFrameScheduler() {
3395
3585
  if (schedulerRef.current === scheduler) schedulerRef.current = null;
3396
3586
  };
3397
3587
  }, []);
3398
- return React9.useCallback(
3588
+ return React10.useCallback(
3399
3589
  (callback, key) => schedulerRef.current?.schedule(callback, key) ?? false,
3400
3590
  []
3401
3591
  );
@@ -3969,7 +4159,7 @@ function QrIcon({ size = 58 }) {
3969
4159
  fill: "currentColor"
3970
4160
  }));
3971
4161
  }
3972
- var Button5 = React10.forwardRef(function Button6({ children, kind = "secondary", size, icon, className = "", ...props }, ref) {
4162
+ var Button5 = React11.forwardRef(function Button6({ children, kind = "secondary", size, icon, className = "", ...props }, ref) {
3973
4163
  return h2("button", {
3974
4164
  ...props,
3975
4165
  ref,
@@ -4086,7 +4276,7 @@ function safeQrSource2(value) {
4086
4276
  return /^data:image\/(?:png|webp|svg\+xml)(?:;charset=[^;,]+)?;base64,/i.test(value) ? value : void 0;
4087
4277
  }
4088
4278
  function QrPane({ provision, now, onRefresh, onCancel, busy }) {
4089
- const [imageFailed, setImageFailed] = React10.useState(false);
4279
+ const [imageFailed, setImageFailed] = React11.useState(false);
4090
4280
  const qrSource = safeQrSource2(provision.qrCodeDataUrl);
4091
4281
  const href = safeVerificationHref(provision.verificationUrl);
4092
4282
  const remaining = Math.max(0, provision.expiresAt - now);
@@ -4094,7 +4284,7 @@ function QrPane({ provision, now, onRefresh, onCancel, busy }) {
4094
4284
  const progress = Math.min(1, remaining / Math.max(1, provision.durationMs ?? remaining));
4095
4285
  const repairing = isCallbackRepair(provision);
4096
4286
  const botName = provision.botName ?? "\u6B64\u673A\u5668\u4EBA";
4097
- React10.useEffect(() => setImageFailed(false), [qrSource]);
4287
+ React11.useEffect(() => setImageFailed(false), [qrSource]);
4098
4288
  return h2(
4099
4289
  "div",
4100
4290
  { className: "bxf-card bxf-provisionCard dim-surfaceCard" },
@@ -4255,11 +4445,11 @@ function connectionTestNotice2(value) {
4255
4445
  return value?.testMessage ? "\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u5931\u8D25\u3002" : null;
4256
4446
  }
4257
4447
  function RemoveConfirmation2({ bot, busy, onConfirm, onCancel }) {
4258
- const cancelRef = React10.useRef(null);
4448
+ const cancelRef = React11.useRef(null);
4259
4449
  const idPart = bot.botId.replace(/[^a-zA-Z0-9_-]/g, "-");
4260
4450
  const titleId = `bxf-remove-title-${idPart}`;
4261
4451
  const descriptionId = `bxf-remove-description-${idPart}`;
4262
- React10.useEffect(() => cancelRef.current?.focus(), []);
4452
+ React11.useEffect(() => cancelRef.current?.focus(), []);
4263
4453
  return h2(
4264
4454
  "div",
4265
4455
  {
@@ -4302,6 +4492,7 @@ function BotCard({
4302
4492
  onReconnect,
4303
4493
  onRepairCallback,
4304
4494
  onWorkspaceSave,
4495
+ onAgentPresetSave,
4305
4496
  onRequestRemove,
4306
4497
  onConfirmRemove,
4307
4498
  onCancelRemove,
@@ -4371,6 +4562,11 @@ function BotCard({
4371
4562
  disabled: Boolean(busy),
4372
4563
  onSave: onWorkspaceSave
4373
4564
  }),
4565
+ h2(AgentPresetEditor, {
4566
+ agentPreset: connection.agentPreset,
4567
+ disabled: Boolean(busy),
4568
+ onSave: onAgentPresetSave
4569
+ }),
4374
4570
  h2(
4375
4571
  "div",
4376
4572
  { className: "bxf-connectedFooter dim-cardFooter" },
@@ -4444,6 +4640,7 @@ function BotList(props) {
4444
4640
  onReconnect: () => props.onReconnect(bot),
4445
4641
  onRepairCallback: () => props.onRepairCallback(bot),
4446
4642
  onWorkspaceSave: (workspace) => props.onWorkspaceSave(bot, workspace),
4643
+ onAgentPresetSave: (agentPreset) => props.onAgentPresetSave(bot, agentPreset),
4447
4644
  onRequestRemove: () => props.onRequestRemove(bot),
4448
4645
  onConfirmRemove: () => props.onConfirmRemove(bot),
4449
4646
  onCancelRemove: props.onCancelRemove,
@@ -4501,53 +4698,55 @@ function mergeFeishuSnapshotState(current, snapshot, { restoreProvisioning = fal
4501
4698
  totals: snapshot.totals,
4502
4699
  provisioning,
4503
4700
  pageError: null,
4504
- statusError: null
4701
+ statusError: null,
4702
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? current.agentPresetCatalog
4505
4703
  };
4506
4704
  }
4507
4705
  function FeishuSettingsTab({ rpcCall }) {
4508
- const [model, setModel] = React10.useState({
4706
+ const [model, setModel] = React11.useState({
4509
4707
  phase: "loading",
4510
4708
  revision: 0,
4511
4709
  bots: [],
4512
4710
  totals: EMPTY_TOTALS2,
4513
4711
  provisioning: null,
4514
4712
  pageError: null,
4515
- statusError: null
4713
+ statusError: null,
4714
+ agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
4516
4715
  });
4517
- const [pageBusy, setPageBusy] = React10.useState(false);
4518
- const [provisionBusy, setProvisionBusy] = React10.useState(false);
4519
- const [credentialOpen, setCredentialOpen] = React10.useState(false);
4520
- const [credentialBusy, setCredentialBusy] = React10.useState(false);
4521
- const [credentialError, setCredentialError] = React10.useState(null);
4522
- const [busyByBot, setBusyByBot] = React10.useState({});
4523
- const [errorsByBot, setErrorsByBot] = React10.useState({});
4524
- const [testNoticesByBot, setTestNoticesByBot] = React10.useState({});
4525
- const [removeTargetId, setRemoveTargetId] = React10.useState(null);
4526
- const [announcement, setAnnouncement] = React10.useState("");
4527
- const [now, setNow] = React10.useState(() => Date.now());
4528
- const [focusBotId, setFocusBotId] = React10.useState(null);
4529
- const cardRefs = React10.useRef(/* @__PURE__ */ new Map());
4530
- const removeButtonRefs = React10.useRef(/* @__PURE__ */ new Map());
4531
- const addButtonRef = React10.useRef(null);
4532
- const mountedRef = React10.useRef(true);
4716
+ const [pageBusy, setPageBusy] = React11.useState(false);
4717
+ const [provisionBusy, setProvisionBusy] = React11.useState(false);
4718
+ const [credentialOpen, setCredentialOpen] = React11.useState(false);
4719
+ const [credentialBusy, setCredentialBusy] = React11.useState(false);
4720
+ const [credentialError, setCredentialError] = React11.useState(null);
4721
+ const [busyByBot, setBusyByBot] = React11.useState({});
4722
+ const [errorsByBot, setErrorsByBot] = React11.useState({});
4723
+ const [testNoticesByBot, setTestNoticesByBot] = React11.useState({});
4724
+ const [removeTargetId, setRemoveTargetId] = React11.useState(null);
4725
+ const [announcement, setAnnouncement] = React11.useState("");
4726
+ const [now, setNow] = React11.useState(() => Date.now());
4727
+ const [focusBotId, setFocusBotId] = React11.useState(null);
4728
+ const cardRefs = React11.useRef(/* @__PURE__ */ new Map());
4729
+ const removeButtonRefs = React11.useRef(/* @__PURE__ */ new Map());
4730
+ const addButtonRef = React11.useRef(null);
4731
+ const mountedRef = React11.useRef(true);
4533
4732
  const workspaceFence = useWorkspaceSnapshotFence();
4534
4733
  const scheduleAnimationFrame = useAnimationFrameScheduler();
4535
- React10.useEffect(() => {
4734
+ React11.useEffect(() => {
4536
4735
  mountedRef.current = true;
4537
4736
  return () => {
4538
4737
  mountedRef.current = false;
4539
4738
  };
4540
4739
  }, []);
4541
- const announce = React10.useCallback((message) => {
4740
+ const announce = React11.useCallback((message) => {
4542
4741
  setAnnouncement("");
4543
4742
  scheduleAnimationFrame(() => {
4544
4743
  if (message) setAnnouncement(message);
4545
4744
  }, "announcement");
4546
4745
  }, [scheduleAnimationFrame]);
4547
- const invoke = React10.useCallback(async (endpoint, payload = {}, signal) => {
4746
+ const invoke = React11.useCallback(async (endpoint, payload = {}, signal) => {
4548
4747
  return unwrapRpcResult3(await rpcCall(endpoint, payload, signal));
4549
4748
  }, [rpcCall]);
4550
- const mergeSnapshot = React10.useCallback((snapshot, { restoreProvisioning = false } = {}) => {
4749
+ const mergeSnapshot = React11.useCallback((snapshot, { restoreProvisioning = false } = {}) => {
4551
4750
  const now2 = Date.now();
4552
4751
  setModel((current) => mergeFeishuSnapshotState(
4553
4752
  current,
@@ -4555,7 +4754,7 @@ function FeishuSettingsTab({ rpcCall }) {
4555
4754
  { restoreProvisioning, now: now2 }
4556
4755
  ));
4557
4756
  }, []);
4558
- const loadStatus = React10.useCallback(async ({ signal, silent = false, restoreProvisioning = false } = {}) => {
4757
+ const loadStatus = React11.useCallback(async ({ signal, silent = false, restoreProvisioning = false } = {}) => {
4559
4758
  const workspaceVersion = workspaceFence.beginStatus();
4560
4759
  if (workspaceVersion === null || !mountedRef.current) return void 0;
4561
4760
  if (!silent) setPageBusy(true);
@@ -4573,12 +4772,12 @@ function FeishuSettingsTab({ rpcCall }) {
4573
4772
  if (!silent && !signal?.aborted && mountedRef.current) setPageBusy(false);
4574
4773
  }
4575
4774
  }, [invoke, mergeSnapshot, workspaceFence]);
4576
- React10.useEffect(() => {
4775
+ React11.useEffect(() => {
4577
4776
  const controller = new AbortController();
4578
4777
  void loadStatus({ signal: controller.signal, restoreProvisioning: true });
4579
4778
  return () => controller.abort();
4580
4779
  }, [loadStatus]);
4581
- React10.useEffect(() => {
4780
+ React11.useEffect(() => {
4582
4781
  if (model.phase !== "ready") return void 0;
4583
4782
  const controller = new AbortController();
4584
4783
  let inFlight = false;
@@ -4597,7 +4796,7 @@ function FeishuSettingsTab({ rpcCall }) {
4597
4796
  window.clearInterval(timer);
4598
4797
  };
4599
4798
  }, [loadStatus, model.phase]);
4600
- React10.useEffect(() => {
4799
+ React11.useEffect(() => {
4601
4800
  if (!focusBotId) return;
4602
4801
  const node = cardRefs.current.get(focusBotId);
4603
4802
  if (!node) return;
@@ -4605,7 +4804,7 @@ function FeishuSettingsTab({ rpcCall }) {
4605
4804
  node.focus({ preventScroll: true });
4606
4805
  setFocusBotId(null);
4607
4806
  }, [focusBotId, model.bots]);
4608
- const startProvisioning = React10.useCallback(async ({
4807
+ const startProvisioning = React11.useCallback(async ({
4609
4808
  replace = false,
4610
4809
  operation = FEISHU_REGISTRATION_OPERATIONS.PROVISION,
4611
4810
  bot
@@ -4678,7 +4877,7 @@ function FeishuSettingsTab({ rpcCall }) {
4678
4877
  model.provisioning?.botId,
4679
4878
  model.provisioning?.botName
4680
4879
  ]);
4681
- const bindCredentials = React10.useCallback(async ({ identity, secret }) => {
4880
+ const bindCredentials = React11.useCallback(async ({ identity, secret }) => {
4682
4881
  const snapshotVersion = workspaceFence.beginMutation();
4683
4882
  setCredentialBusy(true);
4684
4883
  setCredentialError(null);
@@ -4700,7 +4899,7 @@ function FeishuSettingsTab({ rpcCall }) {
4700
4899
  setCredentialBusy(false);
4701
4900
  }
4702
4901
  }, [announce, invoke, loadStatus, mergeSnapshot, workspaceFence]);
4703
- const cancelProvisioning = React10.useCallback(async () => {
4902
+ const cancelProvisioning = React11.useCallback(async () => {
4704
4903
  const activeProvision = model.provisioning;
4705
4904
  const attemptId = activeProvision?.attemptId;
4706
4905
  const repairing = isCallbackRepair(activeProvision);
@@ -4763,7 +4962,7 @@ function FeishuSettingsTab({ rpcCall }) {
4763
4962
  const countdownPhase = model.provisioning?.phase;
4764
4963
  const countdownExpiresAt = model.provisioning?.expiresAt;
4765
4964
  const countdownExpired = model.provisioning?.expired;
4766
- React10.useEffect(() => {
4965
+ React11.useEffect(() => {
4767
4966
  if (!countdownAttemptId || countdownPhase !== "qr" || countdownExpired) return void 0;
4768
4967
  const tick = () => {
4769
4968
  const timestamp7 = Date.now();
@@ -4776,7 +4975,7 @@ function FeishuSettingsTab({ rpcCall }) {
4776
4975
  const timer = window.setInterval(tick, 1e3);
4777
4976
  return () => window.clearInterval(timer);
4778
4977
  }, [countdownAttemptId, countdownPhase, countdownExpiresAt, countdownExpired]);
4779
- React10.useEffect(() => {
4978
+ React11.useEffect(() => {
4780
4979
  const provision2 = model.provisioning;
4781
4980
  if (!provision2 || !["qr", "connecting"].includes(provision2.phase) || !provision2.attemptId || provision2.expired) return void 0;
4782
4981
  const controller = new AbortController();
@@ -4844,7 +5043,7 @@ function FeishuSettingsTab({ rpcCall }) {
4844
5043
  window.clearTimeout(timer);
4845
5044
  };
4846
5045
  }, [announce, invoke, loadStatus, model.provisioning]);
4847
- const setBotBusy = React10.useCallback((botId, value) => {
5046
+ const setBotBusy = React11.useCallback((botId, value) => {
4848
5047
  setBusyByBot((current) => {
4849
5048
  const next = { ...current };
4850
5049
  if (value) next[botId] = value;
@@ -4852,7 +5051,7 @@ function FeishuSettingsTab({ rpcCall }) {
4852
5051
  return next;
4853
5052
  });
4854
5053
  }, []);
4855
- const setBotError = React10.useCallback((botId, error) => {
5054
+ const setBotError = React11.useCallback((botId, error) => {
4856
5055
  setErrorsByBot((current) => {
4857
5056
  const next = { ...current };
4858
5057
  if (error) next[botId] = presentError3(error);
@@ -4860,7 +5059,7 @@ function FeishuSettingsTab({ rpcCall }) {
4860
5059
  return next;
4861
5060
  });
4862
5061
  }, []);
4863
- const repairCallback = React10.useCallback((connection) => {
5062
+ const repairCallback = React11.useCallback((connection) => {
4864
5063
  if (model.provisioning) return;
4865
5064
  setRemoveTargetId(null);
4866
5065
  setBotError(connection.botId, null);
@@ -4874,7 +5073,7 @@ function FeishuSettingsTab({ rpcCall }) {
4874
5073
  bot: connection
4875
5074
  });
4876
5075
  }, [model.provisioning, setBotError, startProvisioning]);
4877
- const reconnectOneBot = React10.useCallback(async (connection) => {
5076
+ const reconnectOneBot = React11.useCallback(async (connection) => {
4878
5077
  const { botId, bot } = connection;
4879
5078
  const snapshotVersion = workspaceFence.beginMutation();
4880
5079
  setBotBusy(botId, "reconnect");
@@ -4914,7 +5113,7 @@ function FeishuSettingsTab({ rpcCall }) {
4914
5113
  setBotBusy(botId, null);
4915
5114
  }
4916
5115
  }, [announce, invoke, loadStatus, mergeSnapshot, setBotBusy, setBotError, workspaceFence]);
4917
- const saveWorkspace = React10.useCallback(async (connection, workspace) => {
5116
+ const saveWorkspace = React11.useCallback(async (connection, workspace) => {
4918
5117
  const { botId } = connection;
4919
5118
  const workspaceVersion = workspaceFence.beginMutation();
4920
5119
  setBotBusy(botId, "workspace");
@@ -4933,15 +5132,34 @@ function FeishuSettingsTab({ rpcCall }) {
4933
5132
  if (mountedRef.current) setBotBusy(botId, null);
4934
5133
  }
4935
5134
  }, [invoke, loadStatus, mergeSnapshot, setBotBusy, setBotError, workspaceFence]);
4936
- const requestRemove = React10.useCallback((connection) => {
5135
+ const saveAgentPreset = React11.useCallback(async (connection, agentPreset) => {
5136
+ const { botId } = connection;
5137
+ const snapshotVersion = workspaceFence.beginMutation();
5138
+ setBotBusy(botId, "preset");
5139
+ setBotError(botId, null);
5140
+ try {
5141
+ const snapshot = normalizeBotsSnapshot(await invoke(
5142
+ FEISHU_ENDPOINTS.setAgentPreset,
5143
+ { botId, agentPreset }
5144
+ ));
5145
+ if (mountedRef.current && workspaceFence.canCommitMutation(snapshotVersion)) {
5146
+ mergeSnapshot(snapshot);
5147
+ }
5148
+ } finally {
5149
+ const shouldRefresh = workspaceFence.endMutation();
5150
+ if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true });
5151
+ if (mountedRef.current) setBotBusy(botId, null);
5152
+ }
5153
+ }, [invoke, loadStatus, mergeSnapshot, setBotBusy, setBotError, workspaceFence]);
5154
+ const requestRemove = React11.useCallback((connection) => {
4937
5155
  setRemoveTargetId(connection.botId);
4938
5156
  }, []);
4939
- const cancelRemove = React10.useCallback(() => {
5157
+ const cancelRemove = React11.useCallback(() => {
4940
5158
  const botId = removeTargetId;
4941
5159
  setRemoveTargetId(null);
4942
5160
  scheduleAnimationFrame(() => removeButtonRefs.current.get(botId)?.focus(), "focus");
4943
5161
  }, [removeTargetId, scheduleAnimationFrame]);
4944
- const confirmRemove = React10.useCallback(async (connection) => {
5162
+ const confirmRemove = React11.useCallback(async (connection) => {
4945
5163
  const { botId, bot } = connection;
4946
5164
  const snapshotVersion = workspaceFence.beginMutation();
4947
5165
  setBotBusy(botId, "delete");
@@ -5026,15 +5244,17 @@ function FeishuSettingsTab({ rpcCall }) {
5026
5244
  setCredentialError(null);
5027
5245
  }
5028
5246
  }) : null;
5029
- const setCardRef = React10.useCallback((botId, node) => {
5247
+ const setCardRef = React11.useCallback((botId, node) => {
5030
5248
  if (node) cardRefs.current.set(botId, node);
5031
5249
  else cardRefs.current.delete(botId);
5032
5250
  }, []);
5033
- const setRemoveButtonRef = React10.useCallback((botId, node) => {
5251
+ const setRemoveButtonRef = React11.useCallback((botId, node) => {
5034
5252
  if (node) removeButtonRefs.current.set(botId, node);
5035
5253
  else removeButtonRefs.current.delete(botId);
5036
5254
  }, []);
5037
- return h2(
5255
+ return h2(AgentPresetCatalogContext.Provider, {
5256
+ value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
5257
+ }, h2(
5038
5258
  "section",
5039
5259
  { className: "bxf-page dim-channelPage", "aria-label": "\u98DE\u4E66\u673A\u5668\u4EBA\u8BBE\u7F6E" },
5040
5260
  h2(Heading2, {
@@ -5067,7 +5287,7 @@ function FeishuSettingsTab({ rpcCall }) {
5067
5287
  onRetry: () => void loadStatus(),
5068
5288
  busy: pageBusy
5069
5289
  }) : h2(
5070
- React10.Fragment,
5290
+ React11.Fragment,
5071
5291
  null,
5072
5292
  credentialContent,
5073
5293
  provisionContent,
@@ -5082,6 +5302,7 @@ function FeishuSettingsTab({ rpcCall }) {
5082
5302
  onReconnect: (bot) => void reconnectOneBot(bot),
5083
5303
  onRepairCallback: repairCallback,
5084
5304
  onWorkspaceSave: saveWorkspace,
5305
+ onAgentPresetSave: saveAgentPreset,
5085
5306
  onRequestRemove: requestRemove,
5086
5307
  onConfirmRemove: (bot) => void confirmRemove(bot),
5087
5308
  onCancelRemove: cancelRemove,
@@ -5089,7 +5310,7 @@ function FeishuSettingsTab({ rpcCall }) {
5089
5310
  setRemoveButtonRef
5090
5311
  }) : null
5091
5312
  )
5092
- );
5313
+ ));
5093
5314
  }
5094
5315
 
5095
5316
  // plugin-src/client/channels/qq/api.js
@@ -5102,7 +5323,8 @@ var QQ_ENDPOINTS = Object.freeze({
5102
5323
  bindCredentials: "bot.bind-credentials",
5103
5324
  reconnectBot: "bot.reconnect",
5104
5325
  deleteBot: "bot.delete",
5105
- setWorkspace: "bot.workspace.set"
5326
+ setWorkspace: "bot.workspace.set",
5327
+ setAgentPreset: SET_AGENT_PRESET_ENDPOINT
5106
5328
  });
5107
5329
  var PROVISION_STATES2 = /* @__PURE__ */ new Set(["starting", "pending", "refreshing", "connecting", "connected", "failed", "cancelled"]);
5108
5330
  var ACCOUNT_STATES3 = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]);
@@ -5166,6 +5388,7 @@ function normalizeBot3(value) {
5166
5388
  connected,
5167
5389
  state: connected ? "connected" : state,
5168
5390
  workspace: text2(value.workspace, "", 4096),
5391
+ agentPreset: normalizeAgentPresetId(value.agentPreset),
5169
5392
  bot: {
5170
5393
  name: text2(value.bot?.name, "QQ\u673A\u5668\u4EBA", 100),
5171
5394
  appIdMasked: text2(value.bot?.appIdMasked, "\u5E94\u7528\u6807\u8BC6\u5DF2\u5B89\u5168\u4FDD\u5B58", 140)
@@ -5199,6 +5422,7 @@ function normalizeSnapshot3(value) {
5199
5422
  bots,
5200
5423
  totals: { configured: bots.length, connected: bots.filter((bot) => bot.connected).length },
5201
5424
  provisioning: source.provisioning ? normalizeProvisioning3(source.provisioning) : null,
5425
+ agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog),
5202
5426
  ...testMessage ? { testMessage } : {}
5203
5427
  };
5204
5428
  }
@@ -5221,7 +5445,7 @@ function formatRemaining3(milliseconds) {
5221
5445
  }
5222
5446
 
5223
5447
  // plugin-src/client/channels/qq/index.js
5224
- var React11 = __toESM(require("react"), 1);
5448
+ var React12 = __toESM(require("react"), 1);
5225
5449
 
5226
5450
  // plugin-src/client/channels/qq/styles.js
5227
5451
  var QQ_STYLE_ID = "xmanrui-dsh-im-qq-settings";
@@ -5246,7 +5470,7 @@ function installQqStyles() {
5246
5470
 
5247
5471
  // plugin-src/client/channels/qq/index.js
5248
5472
  var ACTIVE_STATES = /* @__PURE__ */ new Set(["pending", "refreshing", "connecting"]);
5249
- var Button7 = React11.forwardRef(function Button8({ children, kind = "secondary", className = "", ...props }, ref) {
5473
+ var Button7 = React12.forwardRef(function Button8({ children, kind = "secondary", className = "", ...props }, ref) {
5250
5474
  return h2("button", {
5251
5475
  ...props,
5252
5476
  ref,
@@ -5460,6 +5684,7 @@ function AccountCard2({
5460
5684
  removing,
5461
5685
  onReconnect,
5462
5686
  onWorkspaceSave,
5687
+ onAgentPresetSave,
5463
5688
  onRequestRemove,
5464
5689
  onConfirmRemove,
5465
5690
  onCancelRemove
@@ -5505,6 +5730,11 @@ function AccountCard2({
5505
5730
  disabled: Boolean(busy),
5506
5731
  onSave: onWorkspaceSave
5507
5732
  }),
5733
+ h2(AgentPresetEditor, {
5734
+ agentPreset: account.agentPreset,
5735
+ disabled: Boolean(busy),
5736
+ onSave: onAgentPresetSave
5737
+ }),
5508
5738
  h2(
5509
5739
  "div",
5510
5740
  { className: "ddt-accountFooter dim-cardFooter" },
@@ -5531,19 +5761,25 @@ function AccountCard2({
5531
5761
  );
5532
5762
  }
5533
5763
  function QqSettingsTab({ rpcCall }) {
5534
- const [model, setModel] = React11.useState({ phase: "loading", bots: [], totals: { configured: 0, connected: 0 }, error: null });
5535
- const [provision, setProvision] = React11.useState(null);
5536
- const [busy, setBusy] = React11.useState(false);
5537
- const [busyByBot, setBusyByBot] = React11.useState({});
5538
- const [feedbackByBot, setFeedbackByBot] = React11.useState({});
5539
- const [removeTarget, setRemoveTarget] = React11.useState(null);
5540
- const [credentialOpen, setCredentialOpen] = React11.useState(false);
5541
- const [credentialError, setCredentialError] = React11.useState(null);
5542
- const [now, setNow] = React11.useState(Date.now());
5543
- const mounted = React11.useRef(true);
5764
+ const [model, setModel] = React12.useState({
5765
+ phase: "loading",
5766
+ bots: [],
5767
+ totals: { configured: 0, connected: 0 },
5768
+ error: null,
5769
+ agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
5770
+ });
5771
+ const [provision, setProvision] = React12.useState(null);
5772
+ const [busy, setBusy] = React12.useState(false);
5773
+ const [busyByBot, setBusyByBot] = React12.useState({});
5774
+ const [feedbackByBot, setFeedbackByBot] = React12.useState({});
5775
+ const [removeTarget, setRemoveTarget] = React12.useState(null);
5776
+ const [credentialOpen, setCredentialOpen] = React12.useState(false);
5777
+ const [credentialError, setCredentialError] = React12.useState(null);
5778
+ const [now, setNow] = React12.useState(Date.now());
5779
+ const mounted = React12.useRef(true);
5544
5780
  const workspaceFence = useWorkspaceSnapshotFence();
5545
- const addButtonRef = React11.useRef(null);
5546
- React11.useEffect(() => {
5781
+ const addButtonRef = React12.useRef(null);
5782
+ React12.useEffect(() => {
5547
5783
  const disposeDingtalk = installDingtalkStyles();
5548
5784
  const disposeQq = installQqStyles();
5549
5785
  mounted.current = true;
@@ -5553,18 +5789,24 @@ function QqSettingsTab({ rpcCall }) {
5553
5789
  disposeDingtalk();
5554
5790
  };
5555
5791
  }, []);
5556
- const invoke = React11.useCallback(async (endpoint, payload = {}, signal) => {
5792
+ const invoke = React12.useCallback(async (endpoint, payload = {}, signal) => {
5557
5793
  if (typeof rpcCall !== "function") throw new TypeError("QQ \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
5558
5794
  return unwrapRpcResult4(await rpcCall(endpoint, payload, signal));
5559
5795
  }, [rpcCall]);
5560
- const loadStatus = React11.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
5796
+ const loadStatus = React12.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
5561
5797
  const workspaceVersion = workspaceFence.beginStatus();
5562
5798
  if (workspaceVersion === null) return void 0;
5563
5799
  if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
5564
5800
  try {
5565
5801
  const snapshot = normalizeSnapshot3(await invoke(QQ_ENDPOINTS.status, {}, signal));
5566
5802
  if (!mounted.current || signal?.aborted || !workspaceFence.canCommitStatus(workspaceVersion)) return void 0;
5567
- setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
5803
+ setModel({
5804
+ phase: "ready",
5805
+ bots: snapshot.bots,
5806
+ totals: snapshot.totals,
5807
+ error: null,
5808
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
5809
+ });
5568
5810
  if (restore && snapshot.provisioning) setProvision({
5569
5811
  ...snapshot.provisioning,
5570
5812
  durationMs: Math.max(1, snapshot.provisioning.expiresAt - Date.now())
@@ -5577,12 +5819,12 @@ function QqSettingsTab({ rpcCall }) {
5577
5819
  return void 0;
5578
5820
  }
5579
5821
  }, [invoke, workspaceFence]);
5580
- React11.useEffect(() => {
5822
+ React12.useEffect(() => {
5581
5823
  const controller = new AbortController();
5582
5824
  void loadStatus({ signal: controller.signal, restore: true });
5583
5825
  return () => controller.abort();
5584
5826
  }, [loadStatus]);
5585
- React11.useEffect(() => {
5827
+ React12.useEffect(() => {
5586
5828
  if (model.phase !== "ready") return void 0;
5587
5829
  const controller = new AbortController();
5588
5830
  const timer = window.setInterval(() => void loadStatus({ signal: controller.signal, silent: true }), 15e3);
@@ -5591,12 +5833,12 @@ function QqSettingsTab({ rpcCall }) {
5591
5833
  window.clearInterval(timer);
5592
5834
  };
5593
5835
  }, [loadStatus, model.phase]);
5594
- React11.useEffect(() => {
5836
+ React12.useEffect(() => {
5595
5837
  if (!provision || !ACTIVE_STATES.has(provision.status)) return void 0;
5596
5838
  const timer = window.setInterval(() => mounted.current && setNow(Date.now()), 1e3);
5597
5839
  return () => window.clearInterval(timer);
5598
5840
  }, [provision?.attemptId, provision?.status]);
5599
- const startProvisioning = React11.useCallback(async (replace = false) => {
5841
+ const startProvisioning = React12.useCallback(async (replace = false) => {
5600
5842
  setCredentialOpen(false);
5601
5843
  setCredentialError(null);
5602
5844
  setBusy(true);
@@ -5614,7 +5856,7 @@ function QqSettingsTab({ rpcCall }) {
5614
5856
  if (mounted.current) setBusy(false);
5615
5857
  }
5616
5858
  }, [invoke, provision?.attemptId]);
5617
- const bindCredentials = React11.useCallback(async ({ identity, secret }) => {
5859
+ const bindCredentials = React12.useCallback(async ({ identity, secret }) => {
5618
5860
  const snapshotVersion = workspaceFence.beginMutation();
5619
5861
  setBusy(true);
5620
5862
  setCredentialError(null);
@@ -5625,7 +5867,13 @@ function QqSettingsTab({ rpcCall }) {
5625
5867
  ));
5626
5868
  if (!mounted.current) return;
5627
5869
  if (workspaceFence.canCommitMutation(snapshotVersion)) {
5628
- setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
5870
+ setModel({
5871
+ phase: "ready",
5872
+ bots: snapshot.bots,
5873
+ totals: snapshot.totals,
5874
+ error: null,
5875
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
5876
+ });
5629
5877
  }
5630
5878
  setCredentialOpen(false);
5631
5879
  } catch (error) {
@@ -5636,7 +5884,7 @@ function QqSettingsTab({ rpcCall }) {
5636
5884
  if (mounted.current) setBusy(false);
5637
5885
  }
5638
5886
  }, [invoke, loadStatus, workspaceFence]);
5639
- const closeProvision = React11.useCallback(async () => {
5887
+ const closeProvision = React12.useCallback(async () => {
5640
5888
  setBusy(true);
5641
5889
  try {
5642
5890
  if (provision?.attemptId && ACTIVE_STATES.has(provision.status)) {
@@ -5647,7 +5895,7 @@ function QqSettingsTab({ rpcCall }) {
5647
5895
  if (mounted.current) setBusy(false);
5648
5896
  }
5649
5897
  }, [invoke, provision?.attemptId, provision?.status]);
5650
- React11.useEffect(() => {
5898
+ React12.useEffect(() => {
5651
5899
  const attemptId = provision?.attemptId;
5652
5900
  if (!attemptId || !ACTIVE_STATES.has(provision.status)) return void 0;
5653
5901
  const controller = new AbortController();
@@ -5677,13 +5925,19 @@ function QqSettingsTab({ rpcCall }) {
5677
5925
  window.clearTimeout(timer);
5678
5926
  };
5679
5927
  }, [invoke, loadStatus, provision?.attemptId, provision?.pollIntervalMs, provision?.status]);
5680
- const botAction = React11.useCallback(async (account, operation, endpoint, payload) => {
5928
+ const botAction = React12.useCallback(async (account, operation, endpoint, payload) => {
5681
5929
  const snapshotVersion = workspaceFence.beginMutation();
5682
5930
  setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
5683
5931
  try {
5684
5932
  const snapshot = normalizeSnapshot3(await invoke(endpoint, payload));
5685
5933
  if (mounted.current && workspaceFence.canCommitMutation(snapshotVersion)) {
5686
- setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
5934
+ setModel({
5935
+ phase: "ready",
5936
+ bots: snapshot.bots,
5937
+ totals: snapshot.totals,
5938
+ error: null,
5939
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
5940
+ });
5687
5941
  }
5688
5942
  return snapshot;
5689
5943
  } finally {
@@ -5696,7 +5950,7 @@ function QqSettingsTab({ rpcCall }) {
5696
5950
  });
5697
5951
  }
5698
5952
  }, [invoke, loadStatus, workspaceFence]);
5699
- const reconnect = React11.useCallback(async (account) => {
5953
+ const reconnect = React12.useCallback(async (account) => {
5700
5954
  setFeedbackByBot((current) => {
5701
5955
  const next = { ...current };
5702
5956
  delete next[account.botId];
@@ -5759,6 +6013,12 @@ function QqSettingsTab({ rpcCall }) {
5759
6013
  QQ_ENDPOINTS.setWorkspace,
5760
6014
  { botId: account.botId, workspace }
5761
6015
  ),
6016
+ onAgentPresetSave: (agentPreset) => botAction(
6017
+ account,
6018
+ "preset",
6019
+ QQ_ENDPOINTS.setAgentPreset,
6020
+ { botId: account.botId, agentPreset }
6021
+ ),
5762
6022
  onRequestRemove: () => setRemoveTarget(account.botId),
5763
6023
  onCancelRemove: () => setRemoveTarget(null),
5764
6024
  onConfirmRemove: async () => {
@@ -5781,7 +6041,9 @@ function QqSettingsTab({ rpcCall }) {
5781
6041
  setCredentialError(null);
5782
6042
  }
5783
6043
  }) : null;
5784
- return h2(
6044
+ return h2(AgentPresetCatalogContext.Provider, {
6045
+ value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
6046
+ }, h2(
5785
6047
  "section",
5786
6048
  { className: "ddt-page dqq-page dim-channelPage", "aria-label": "QQ \u8BBE\u7F6E" },
5787
6049
  h2(Heading3, {
@@ -5797,14 +6059,14 @@ function QqSettingsTab({ rpcCall }) {
5797
6059
  addButtonRef
5798
6060
  }),
5799
6061
  model.phase === "loading" ? h2(LoadingView3) : model.phase === "error" ? h2("div", { className: "ddt-card dim-surfaceCard" }, h2("div", { className: "ddt-inlineError dim-inlineError" }, h2("h3", null, "\u65E0\u6CD5\u8BFB\u53D6 QQ \u673A\u5668\u4EBA\u72B6\u6001"), h2("p", null, model.error?.message), h2(Button7, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6"))) : h2(
5800
- React11.Fragment,
6062
+ React12.Fragment,
5801
6063
  null,
5802
6064
  credentialView,
5803
6065
  provisionView,
5804
6066
  model.bots.length === 0 && !provision && !credentialOpen ? h2(EmptyView3, { busy, onStart: () => void startProvisioning() }) : null,
5805
6067
  botList
5806
6068
  )
5807
- );
6069
+ ));
5808
6070
  }
5809
6071
 
5810
6072
  // src/channels/office/protocol.mjs
@@ -5886,7 +6148,7 @@ function normalizeOfficeStatus(value) {
5886
6148
  }
5887
6149
 
5888
6150
  // plugin-src/client/channels/office/index.js
5889
- var React12 = __toESM(require("react"), 1);
6151
+ var React13 = __toESM(require("react"), 1);
5890
6152
  function Button9({ children, kind = "secondary", ...props }) {
5891
6153
  return h2("button", { ...props, type: "button", className: "ddt-button", "data-kind": kind }, children);
5892
6154
  }
@@ -5915,12 +6177,12 @@ function stateLabel(model) {
5915
6177
  return "\u5DF2\u914D\u7F6E";
5916
6178
  }
5917
6179
  function OfficeSettingsTab({ rpcCall, initialStatus }) {
5918
- const [model, setModel] = React12.useState(normalizeOfficeStatus(initialStatus));
5919
- const [phase, setPhase] = React12.useState(initialStatus === void 0 ? "loading" : "ready");
5920
- const [busy, setBusy] = React12.useState("");
5921
- const [error, setError] = React12.useState("");
5922
- const [notice, setNotice] = React12.useState("");
5923
- const [form, setForm] = React12.useState({
6180
+ const [model, setModel] = React13.useState(normalizeOfficeStatus(initialStatus));
6181
+ const [phase, setPhase] = React13.useState(initialStatus === void 0 ? "loading" : "ready");
6182
+ const [busy, setBusy] = React13.useState("");
6183
+ const [error, setError] = React13.useState("");
6184
+ const [notice, setNotice] = React13.useState("");
6185
+ const [form, setForm] = React13.useState({
5924
6186
  baseUrl: "",
5925
6187
  deviceId: "local-harness",
5926
6188
  deviceToken: "",
@@ -5929,11 +6191,11 @@ function OfficeSettingsTab({ rpcCall, initialStatus }) {
5929
6191
  workspaces: "",
5930
6192
  instructionPresets: ""
5931
6193
  });
5932
- const invoke = React12.useCallback(async (endpoint, payload = {}) => {
6194
+ const invoke = React13.useCallback(async (endpoint, payload = {}) => {
5933
6195
  if (typeof rpcCall !== "function") throw new Error("AI Office \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
5934
6196
  return unwrapOfficeRpc(await rpcCall(endpoint, payload));
5935
6197
  }, [rpcCall]);
5936
- const adopt = React12.useCallback((value) => {
6198
+ const adopt = React13.useCallback((value) => {
5937
6199
  const next = normalizeOfficeStatus(value?.snapshot ?? value);
5938
6200
  setModel(next);
5939
6201
  if (next.config) setForm((current) => ({
@@ -5948,7 +6210,7 @@ function OfficeSettingsTab({ rpcCall, initialStatus }) {
5948
6210
  }));
5949
6211
  return next;
5950
6212
  }, []);
5951
- const load = React12.useCallback(async () => {
6213
+ const load = React13.useCallback(async () => {
5952
6214
  try {
5953
6215
  adopt(await invoke(OFFICE_RPC_ENDPOINTS.status));
5954
6216
  setPhase("ready");
@@ -5958,7 +6220,7 @@ function OfficeSettingsTab({ rpcCall, initialStatus }) {
5958
6220
  setError(caught.message);
5959
6221
  }
5960
6222
  }, [adopt, invoke]);
5961
- React12.useEffect(() => {
6223
+ React13.useEffect(() => {
5962
6224
  void load();
5963
6225
  }, [load]);
5964
6226
  const run = async (name2, operation) => {
@@ -5975,7 +6237,7 @@ function OfficeSettingsTab({ rpcCall, initialStatus }) {
5975
6237
  setBusy("");
5976
6238
  }
5977
6239
  };
5978
- const hooks = React12.useMemo(() => {
6240
+ const hooks = React13.useMemo(() => {
5979
6241
  try {
5980
6242
  return officeHookUrls(form.baseUrl);
5981
6243
  } catch {
@@ -6160,7 +6422,7 @@ var normalizeSnapshot4 = api2.normalizeSnapshot;
6160
6422
  var presentError5 = api2.presentError;
6161
6423
 
6162
6424
  // plugin-src/client/channels/slack/index.js
6163
- var React13 = __toESM(require("react"), 1);
6425
+ var React14 = __toESM(require("react"), 1);
6164
6426
 
6165
6427
  // src/channels/slack/manifest.mjs
6166
6428
  var SLACK_APP_MANIFEST_YAML = `_metadata:
@@ -6233,10 +6495,10 @@ function installSlackStyles() {
6233
6495
 
6234
6496
  // plugin-src/client/channels/slack/index.js
6235
6497
  function SlackCredentialPanel({ busy, error, onSubmit, onCancel }) {
6236
- const [botToken, setBotToken] = React13.useState("");
6237
- const [appToken, setAppToken] = React13.useState("");
6238
- const [copied, setCopied] = React13.useState(false);
6239
- const headingId = React13.useId();
6498
+ const [botToken, setBotToken] = React14.useState("");
6499
+ const [appToken, setAppToken] = React14.useState("");
6500
+ const [copied, setCopied] = React14.useState(false);
6501
+ const headingId = React14.useId();
6240
6502
  const copyManifest = async () => {
6241
6503
  try {
6242
6504
  await navigator.clipboard.writeText(SLACK_APP_MANIFEST_YAML);
@@ -6392,7 +6654,7 @@ var normalizeSnapshot5 = api3.normalizeSnapshot;
6392
6654
  var presentError6 = api3.presentError;
6393
6655
 
6394
6656
  // plugin-src/client/channels/telegram/index.js
6395
- var React14 = __toESM(require("react"), 1);
6657
+ var React15 = __toESM(require("react"), 1);
6396
6658
 
6397
6659
  // plugin-src/client/channels/telegram/styles.js
6398
6660
  var TELEGRAM_STYLE_ID = "xmanrui-dsh-im-telegram-settings";
@@ -6458,11 +6720,11 @@ function allowedUsersFromText(value) {
6458
6720
  function TelegramAccessSettings({ account, busy = false, onSave }) {
6459
6721
  const policy = policyFor(account);
6460
6722
  const sourceUsers = policy.allowedUsers.join("\n");
6461
- const accessHelpId = React14.useId();
6462
- const [accessMode, setAccessMode] = React14.useState(policy.accessMode);
6463
- const [allowedUsers, setAllowedUsers] = React14.useState(sourceUsers);
6464
- const [error, setError] = React14.useState(null);
6465
- React14.useEffect(() => {
6723
+ const accessHelpId = React15.useId();
6724
+ const [accessMode, setAccessMode] = React15.useState(policy.accessMode);
6725
+ const [allowedUsers, setAllowedUsers] = React15.useState(sourceUsers);
6726
+ const [error, setError] = React15.useState(null);
6727
+ React15.useEffect(() => {
6466
6728
  setAccessMode(policy.accessMode);
6467
6729
  setAllowedUsers(sourceUsers);
6468
6730
  setError(null);
@@ -6611,7 +6873,8 @@ var WECOM_ENDPOINTS = Object.freeze({
6611
6873
  bindCredentials: "bot.bind-credentials",
6612
6874
  reconnectBot: "bot.reconnect",
6613
6875
  deleteBot: "bot.delete",
6614
- setWorkspace: "bot.workspace.set"
6876
+ setWorkspace: "bot.workspace.set",
6877
+ setAgentPreset: SET_AGENT_PRESET_ENDPOINT
6615
6878
  });
6616
6879
  var PROVISION_STATES3 = /* @__PURE__ */ new Set(["starting", "pending", "refreshing", "connecting", "connected", "failed", "cancelled"]);
6617
6880
  var ACCOUNT_STATES4 = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]);
@@ -6681,6 +6944,7 @@ function normalizeBot4(value) {
6681
6944
  connected,
6682
6945
  state: connected ? "connected" : state,
6683
6946
  workspace: text3(value.workspace, "", 4096),
6947
+ agentPreset: normalizeAgentPresetId(value.agentPreset),
6684
6948
  bot: {
6685
6949
  name: text3(value.bot?.name, "\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA", 100),
6686
6950
  appIdMasked: text3(value.bot?.appIdMasked, "\u5E94\u7528\u6807\u8BC6\u5DF2\u5B89\u5168\u4FDD\u5B58", 140)
@@ -6704,7 +6968,8 @@ function normalizeSnapshot6(value) {
6704
6968
  bots,
6705
6969
  totals: { configured: bots.length, connected: bots.filter((bot) => bot.connected).length },
6706
6970
  provisioning: source.provisioning ? normalizeProvisioning4(source.provisioning) : null,
6707
- testMessage: normalizeTestMessage3(source.testMessage)
6971
+ testMessage: normalizeTestMessage3(source.testMessage),
6972
+ agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog)
6708
6973
  };
6709
6974
  }
6710
6975
  function presentError7(error) {
@@ -6719,7 +6984,7 @@ function formatRemaining4(milliseconds) {
6719
6984
  }
6720
6985
 
6721
6986
  // plugin-src/client/channels/wecom/index.js
6722
- var React15 = __toESM(require("react"), 1);
6987
+ var React16 = __toESM(require("react"), 1);
6723
6988
 
6724
6989
  // plugin-src/client/channels/wecom/styles.js
6725
6990
  var WECOM_STYLE_ID = "xmanrui-dsh-im-wecom-settings";
@@ -6744,7 +7009,7 @@ function installWecomStyles() {
6744
7009
 
6745
7010
  // plugin-src/client/channels/wecom/index.js
6746
7011
  var ACTIVE_STATES2 = /* @__PURE__ */ new Set(["pending", "refreshing", "connecting"]);
6747
- var Button10 = React15.forwardRef(function Button11({ children, kind = "secondary", className = "", ...props }, ref) {
7012
+ var Button10 = React16.forwardRef(function Button11({ children, kind = "secondary", className = "", ...props }, ref) {
6748
7013
  return h2("button", {
6749
7014
  ...props,
6750
7015
  ref,
@@ -6958,6 +7223,7 @@ function AccountCard3({
6958
7223
  removing,
6959
7224
  onReconnect,
6960
7225
  onWorkspaceSave,
7226
+ onAgentPresetSave,
6961
7227
  onRequestRemove,
6962
7228
  onConfirmRemove,
6963
7229
  onCancelRemove
@@ -7003,6 +7269,11 @@ function AccountCard3({
7003
7269
  disabled: Boolean(busy),
7004
7270
  onSave: onWorkspaceSave
7005
7271
  }),
7272
+ h2(AgentPresetEditor, {
7273
+ agentPreset: account.agentPreset,
7274
+ disabled: Boolean(busy),
7275
+ onSave: onAgentPresetSave
7276
+ }),
7006
7277
  h2(
7007
7278
  "div",
7008
7279
  { className: "ddt-accountFooter dim-cardFooter" },
@@ -7029,21 +7300,27 @@ function AccountCard3({
7029
7300
  );
7030
7301
  }
7031
7302
  function WecomSettingsTab({ rpcCall }) {
7032
- const [model, setModel] = React15.useState({ phase: "loading", bots: [], totals: { configured: 0, connected: 0 }, error: null });
7033
- const [provision, setProvision] = React15.useState(null);
7034
- const [busy, setBusy] = React15.useState(false);
7035
- const [busyByBot, setBusyByBot] = React15.useState({});
7036
- const [feedbackByBot, setFeedbackByBot] = React15.useState({});
7037
- const [removeTarget, setRemoveTarget] = React15.useState(null);
7038
- const [credentialOpen, setCredentialOpen] = React15.useState(false);
7039
- const [credentialError, setCredentialError] = React15.useState(null);
7040
- const [notice, setNotice] = React15.useState("");
7041
- const [now, setNow] = React15.useState(Date.now());
7042
- const mounted = React15.useRef(true);
7303
+ const [model, setModel] = React16.useState({
7304
+ phase: "loading",
7305
+ bots: [],
7306
+ totals: { configured: 0, connected: 0 },
7307
+ error: null,
7308
+ agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
7309
+ });
7310
+ const [provision, setProvision] = React16.useState(null);
7311
+ const [busy, setBusy] = React16.useState(false);
7312
+ const [busyByBot, setBusyByBot] = React16.useState({});
7313
+ const [feedbackByBot, setFeedbackByBot] = React16.useState({});
7314
+ const [removeTarget, setRemoveTarget] = React16.useState(null);
7315
+ const [credentialOpen, setCredentialOpen] = React16.useState(false);
7316
+ const [credentialError, setCredentialError] = React16.useState(null);
7317
+ const [notice, setNotice] = React16.useState("");
7318
+ const [now, setNow] = React16.useState(Date.now());
7319
+ const mounted = React16.useRef(true);
7043
7320
  const workspaceFence = useWorkspaceSnapshotFence();
7044
- const addButtonRef = React15.useRef(null);
7045
- const noticeFrameRef = React15.useRef(null);
7046
- const announce = React15.useCallback((message) => {
7321
+ const addButtonRef = React16.useRef(null);
7322
+ const noticeFrameRef = React16.useRef(null);
7323
+ const announce = React16.useCallback((message) => {
7047
7324
  if (!mounted.current) return;
7048
7325
  if (noticeFrameRef.current !== null) {
7049
7326
  window.cancelAnimationFrame(noticeFrameRef.current);
@@ -7057,7 +7334,7 @@ function WecomSettingsTab({ rpcCall }) {
7057
7334
  });
7058
7335
  }
7059
7336
  }, []);
7060
- React15.useEffect(() => {
7337
+ React16.useEffect(() => {
7061
7338
  const disposeDingtalk = installDingtalkStyles();
7062
7339
  const disposeWecom = installWecomStyles();
7063
7340
  mounted.current = true;
@@ -7071,18 +7348,24 @@ function WecomSettingsTab({ rpcCall }) {
7071
7348
  disposeDingtalk();
7072
7349
  };
7073
7350
  }, []);
7074
- const invoke = React15.useCallback(async (endpoint, payload = {}, signal) => {
7351
+ const invoke = React16.useCallback(async (endpoint, payload = {}, signal) => {
7075
7352
  if (typeof rpcCall !== "function") throw new TypeError("\u4F01\u4E1A\u5FAE\u4FE1\u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
7076
7353
  return unwrapRpcResult7(await rpcCall(endpoint, payload, signal));
7077
7354
  }, [rpcCall]);
7078
- const loadStatus = React15.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
7355
+ const loadStatus = React16.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
7079
7356
  const workspaceVersion = workspaceFence.beginStatus();
7080
7357
  if (workspaceVersion === null) return void 0;
7081
7358
  if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
7082
7359
  try {
7083
7360
  const snapshot = normalizeSnapshot6(await invoke(WECOM_ENDPOINTS.status, {}, signal));
7084
7361
  if (!mounted.current || signal?.aborted || !workspaceFence.canCommitStatus(workspaceVersion)) return void 0;
7085
- setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
7362
+ setModel({
7363
+ phase: "ready",
7364
+ bots: snapshot.bots,
7365
+ totals: snapshot.totals,
7366
+ error: null,
7367
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
7368
+ });
7086
7369
  if (restore && snapshot.provisioning) setProvision({
7087
7370
  ...snapshot.provisioning,
7088
7371
  durationMs: Math.max(1, snapshot.provisioning.expiresAt - Date.now())
@@ -7095,12 +7378,12 @@ function WecomSettingsTab({ rpcCall }) {
7095
7378
  return void 0;
7096
7379
  }
7097
7380
  }, [invoke, workspaceFence]);
7098
- React15.useEffect(() => {
7381
+ React16.useEffect(() => {
7099
7382
  const controller = new AbortController();
7100
7383
  void loadStatus({ signal: controller.signal, restore: true });
7101
7384
  return () => controller.abort();
7102
7385
  }, [loadStatus]);
7103
- React15.useEffect(() => {
7386
+ React16.useEffect(() => {
7104
7387
  if (model.phase !== "ready") return void 0;
7105
7388
  const controller = new AbortController();
7106
7389
  const timer = window.setInterval(() => void loadStatus({ signal: controller.signal, silent: true }), 15e3);
@@ -7109,12 +7392,12 @@ function WecomSettingsTab({ rpcCall }) {
7109
7392
  window.clearInterval(timer);
7110
7393
  };
7111
7394
  }, [loadStatus, model.phase]);
7112
- React15.useEffect(() => {
7395
+ React16.useEffect(() => {
7113
7396
  if (!provision || !ACTIVE_STATES2.has(provision.status)) return void 0;
7114
7397
  const timer = window.setInterval(() => mounted.current && setNow(Date.now()), 1e3);
7115
7398
  return () => window.clearInterval(timer);
7116
7399
  }, [provision?.attemptId, provision?.status]);
7117
- const startProvisioning = React15.useCallback(async (replace = false) => {
7400
+ const startProvisioning = React16.useCallback(async (replace = false) => {
7118
7401
  setCredentialOpen(false);
7119
7402
  setCredentialError(null);
7120
7403
  setBusy(true);
@@ -7132,7 +7415,7 @@ function WecomSettingsTab({ rpcCall }) {
7132
7415
  if (mounted.current) setBusy(false);
7133
7416
  }
7134
7417
  }, [invoke, provision?.attemptId]);
7135
- const bindCredentials = React15.useCallback(async ({ identity, secret }) => {
7418
+ const bindCredentials = React16.useCallback(async ({ identity, secret }) => {
7136
7419
  const snapshotVersion = workspaceFence.beginMutation();
7137
7420
  setBusy(true);
7138
7421
  setCredentialError(null);
@@ -7143,7 +7426,13 @@ function WecomSettingsTab({ rpcCall }) {
7143
7426
  ));
7144
7427
  if (!mounted.current) return;
7145
7428
  if (workspaceFence.canCommitMutation(snapshotVersion)) {
7146
- setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
7429
+ setModel({
7430
+ phase: "ready",
7431
+ bots: snapshot.bots,
7432
+ totals: snapshot.totals,
7433
+ error: null,
7434
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
7435
+ });
7147
7436
  }
7148
7437
  setCredentialOpen(false);
7149
7438
  } catch (error) {
@@ -7154,7 +7443,7 @@ function WecomSettingsTab({ rpcCall }) {
7154
7443
  if (mounted.current) setBusy(false);
7155
7444
  }
7156
7445
  }, [invoke, loadStatus, workspaceFence]);
7157
- const closeProvision = React15.useCallback(async () => {
7446
+ const closeProvision = React16.useCallback(async () => {
7158
7447
  setBusy(true);
7159
7448
  try {
7160
7449
  if (provision?.attemptId && ACTIVE_STATES2.has(provision.status)) {
@@ -7165,7 +7454,7 @@ function WecomSettingsTab({ rpcCall }) {
7165
7454
  if (mounted.current) setBusy(false);
7166
7455
  }
7167
7456
  }, [invoke, provision?.attemptId, provision?.status]);
7168
- React15.useEffect(() => {
7457
+ React16.useEffect(() => {
7169
7458
  const attemptId = provision?.attemptId;
7170
7459
  if (!attemptId || !ACTIVE_STATES2.has(provision.status)) return void 0;
7171
7460
  const controller = new AbortController();
@@ -7195,13 +7484,19 @@ function WecomSettingsTab({ rpcCall }) {
7195
7484
  window.clearTimeout(timer);
7196
7485
  };
7197
7486
  }, [invoke, loadStatus, provision?.attemptId, provision?.pollIntervalMs, provision?.status]);
7198
- const botAction = React15.useCallback(async (account, operation, endpoint, payload) => {
7487
+ const botAction = React16.useCallback(async (account, operation, endpoint, payload) => {
7199
7488
  const snapshotVersion = workspaceFence.beginMutation();
7200
7489
  setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
7201
7490
  try {
7202
7491
  const snapshot = normalizeSnapshot6(await invoke(endpoint, payload));
7203
7492
  if (mounted.current && workspaceFence.canCommitMutation(snapshotVersion)) {
7204
- setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
7493
+ setModel({
7494
+ phase: "ready",
7495
+ bots: snapshot.bots,
7496
+ totals: snapshot.totals,
7497
+ error: null,
7498
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
7499
+ });
7205
7500
  }
7206
7501
  return snapshot;
7207
7502
  } finally {
@@ -7214,7 +7509,7 @@ function WecomSettingsTab({ rpcCall }) {
7214
7509
  });
7215
7510
  }
7216
7511
  }, [invoke, loadStatus, workspaceFence]);
7217
- const reconnect = React15.useCallback(async (account) => {
7512
+ const reconnect = React16.useCallback(async (account) => {
7218
7513
  setFeedbackByBot((current) => {
7219
7514
  const next = { ...current };
7220
7515
  delete next[account.botId];
@@ -7288,6 +7583,12 @@ function WecomSettingsTab({ rpcCall }) {
7288
7583
  WECOM_ENDPOINTS.setWorkspace,
7289
7584
  { botId: account.botId, workspace }
7290
7585
  ),
7586
+ onAgentPresetSave: (agentPreset) => botAction(
7587
+ account,
7588
+ "preset",
7589
+ WECOM_ENDPOINTS.setAgentPreset,
7590
+ { botId: account.botId, agentPreset }
7591
+ ),
7291
7592
  onRequestRemove: () => setRemoveTarget(account.botId),
7292
7593
  onCancelRemove: () => setRemoveTarget(null),
7293
7594
  onConfirmRemove: async () => {
@@ -7310,7 +7611,9 @@ function WecomSettingsTab({ rpcCall }) {
7310
7611
  setCredentialError(null);
7311
7612
  }
7312
7613
  }) : null;
7313
- return h2(
7614
+ return h2(AgentPresetCatalogContext.Provider, {
7615
+ value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
7616
+ }, h2(
7314
7617
  "section",
7315
7618
  { className: "ddt-page dwecom-page dim-channelPage", "aria-label": "\u4F01\u4E1A\u5FAE\u4FE1\u8BBE\u7F6E" },
7316
7619
  h2(Heading4, {
@@ -7327,18 +7630,18 @@ function WecomSettingsTab({ rpcCall }) {
7327
7630
  }),
7328
7631
  h2("div", { className: "ddt-visuallyHidden", role: "status", "aria-live": "polite" }, notice),
7329
7632
  model.phase === "loading" ? h2(LoadingView4) : model.phase === "error" ? h2("div", { className: "ddt-card dim-surfaceCard" }, h2("div", { className: "ddt-inlineError dim-inlineError" }, h2("h3", null, "\u65E0\u6CD5\u8BFB\u53D6\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA\u72B6\u6001"), h2("p", null, model.error?.message), h2(Button10, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6"))) : h2(
7330
- React15.Fragment,
7633
+ React16.Fragment,
7331
7634
  null,
7332
7635
  credentialView,
7333
7636
  provisionView,
7334
7637
  model.bots.length === 0 && !provision && !credentialOpen ? h2(EmptyView4, { busy, onStart: () => void startProvisioning() }) : null,
7335
7638
  botList
7336
7639
  )
7337
- );
7640
+ ));
7338
7641
  }
7339
7642
 
7340
7643
  // plugin-src/client/channels/weixin/index.js
7341
- var React16 = __toESM(require("react"), 1);
7644
+ var React17 = __toESM(require("react"), 1);
7342
7645
 
7343
7646
  // plugin-src/client/channels/weixin/api.js
7344
7647
  var WEIXIN_RPC_CHANNEL = "/weixin";
@@ -7350,7 +7653,8 @@ var WEIXIN_ENDPOINTS = Object.freeze({
7350
7653
  cancelProvisioning: "provision.cancel",
7351
7654
  reconnectBot: "bot.reconnect",
7352
7655
  deleteBot: "bot.delete",
7353
- setWorkspace: "bot.workspace.set"
7656
+ setWorkspace: "bot.workspace.set",
7657
+ setAgentPreset: SET_AGENT_PRESET_ENDPOINT
7354
7658
  });
7355
7659
  var ACCOUNT_STATES5 = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]);
7356
7660
  var PROVISION_STATES4 = /* @__PURE__ */ new Set([
@@ -7448,6 +7752,7 @@ function normalizeBot5(value) {
7448
7752
  connected,
7449
7753
  configured: value.configured === true,
7450
7754
  workspace: string(value.workspace).slice(0, 4096),
7755
+ agentPreset: normalizeAgentPresetId(value.agentPreset),
7451
7756
  bot: {
7452
7757
  name: string(value.bot.name, "\u5FAE\u4FE1\u673A\u5668\u4EBA"),
7453
7758
  accountIdMasked: string(value.bot.accountIdMasked, "\u5DF2\u5B89\u5168\u4FDD\u5B58")
@@ -7483,7 +7788,8 @@ function normalizeSnapshot7(value) {
7483
7788
  connected: bots.filter((bot) => bot.connected).length
7484
7789
  },
7485
7790
  provisioning: value.provisioning ? normalizeProvisioning5(value.provisioning) : null,
7486
- testMessage: normalizeTestMessage4(value.testMessage)
7791
+ testMessage: normalizeTestMessage4(value.testMessage),
7792
+ agentPresetCatalog: normalizeAgentPresetCatalog(value.agentPresetCatalog)
7487
7793
  };
7488
7794
  }
7489
7795
  function presentError8(error) {
@@ -7620,7 +7926,7 @@ function installWeixinStyles() {
7620
7926
  }
7621
7927
 
7622
7928
  // plugin-src/client/channels/weixin/index.js
7623
- var Button12 = React16.forwardRef(function Button13({ children, kind = "secondary", className = "", ...props }, ref) {
7929
+ var Button12 = React17.forwardRef(function Button13({ children, kind = "secondary", className = "", ...props }, ref) {
7624
7930
  return h2("button", {
7625
7931
  ...props,
7626
7932
  ref,
@@ -7693,14 +7999,14 @@ function EmptyView5({ onStart, busy }) {
7693
7999
  );
7694
8000
  }
7695
8001
  function QrPanel4({ provision, now, busy, onRefresh, onCancel }) {
7696
- const [imageFailed, setImageFailed] = React16.useState(false);
8002
+ const [imageFailed, setImageFailed] = React17.useState(false);
7697
8003
  const source = safeQrSource5(provision.qrCodeDataUrl);
7698
8004
  const href = safeVerificationUrl(provision.verificationUrl);
7699
8005
  const remaining = Math.max(0, provision.expiresAt - now);
7700
8006
  const expired = remaining === 0 || provision.status === "expired";
7701
8007
  const duration = Math.max(1, provision.durationMs ?? 5 * 6e4);
7702
8008
  const progress = Math.round(Math.min(1, remaining / duration) * 100);
7703
- React16.useEffect(() => setImageFailed(false), [source]);
8009
+ React17.useEffect(() => setImageFailed(false), [source]);
7704
8010
  return h2(
7705
8011
  "div",
7706
8012
  { className: "dxw-card dim-surfaceCard" },
@@ -7767,9 +8073,9 @@ function QrPanel4({ provision, now, busy, onRefresh, onCancel }) {
7767
8073
  );
7768
8074
  }
7769
8075
  function VerificationPanel({ provision, busy, onSubmit, onCancel }) {
7770
- const [code, setCode] = React16.useState("");
8076
+ const [code, setCode] = React17.useState("");
7771
8077
  const valid = /^\d{4,8}$/.test(code);
7772
- React16.useEffect(() => setCode(""), [provision.attemptId]);
8078
+ React17.useEffect(() => setCode(""), [provision.attemptId]);
7773
8079
  return h2(
7774
8080
  "div",
7775
8081
  { className: "dxw-card dim-surfaceCard" },
@@ -7867,6 +8173,7 @@ function AccountCard4({
7867
8173
  removing,
7868
8174
  onReconnect,
7869
8175
  onWorkspaceSave,
8176
+ onAgentPresetSave,
7870
8177
  onRequestRemove,
7871
8178
  onConfirmRemove,
7872
8179
  onCancelRemove
@@ -7917,6 +8224,11 @@ function AccountCard4({
7917
8224
  disabled: Boolean(busy),
7918
8225
  onSave: onWorkspaceSave
7919
8226
  }),
8227
+ h2(AgentPresetEditor, {
8228
+ agentPreset: account.agentPreset,
8229
+ disabled: Boolean(busy),
8230
+ onSave: onAgentPresetSave
8231
+ }),
7920
8232
  h2(
7921
8233
  "div",
7922
8234
  { className: "dxw-accountFooter dim-cardFooter" },
@@ -7975,6 +8287,7 @@ function AccountList2(props) {
7975
8287
  removing: props.removeTarget === account.botId,
7976
8288
  onReconnect: () => props.onReconnect(account),
7977
8289
  onWorkspaceSave: (workspace) => props.onWorkspaceSave(account, workspace),
8290
+ onAgentPresetSave: (agentPreset) => props.onAgentPresetSave(account, agentPreset),
7978
8291
  onRequestRemove: () => props.onRequestRemove(account),
7979
8292
  onConfirmRemove: () => props.onConfirmRemove(account),
7980
8293
  onCancelRemove: props.onCancelRemove
@@ -7993,40 +8306,41 @@ function mergeWeixinProvisioningSnapshot(current, incoming, { restoreProvisionin
7993
8306
  };
7994
8307
  }
7995
8308
  function WeixinSettingsTab({ rpcCall }) {
7996
- const [model, setModel] = React16.useState({
8309
+ const [model, setModel] = React17.useState({
7997
8310
  phase: "loading",
7998
8311
  bots: [],
7999
8312
  totals: EMPTY_TOTALS3,
8000
8313
  revision: 0,
8001
- error: null
8314
+ error: null,
8315
+ agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
8002
8316
  });
8003
- const [provision, setProvision] = React16.useState(null);
8004
- const [busy, setBusy] = React16.useState(false);
8005
- const [busyByBot, setBusyByBot] = React16.useState({});
8006
- const [feedbackByBot, setFeedbackByBot] = React16.useState({});
8007
- const [removeTarget, setRemoveTarget] = React16.useState(null);
8008
- const [notice, setNotice] = React16.useState("");
8009
- const [now, setNow] = React16.useState(() => Date.now());
8010
- const addButtonRef = React16.useRef(null);
8011
- const mountedRef = React16.useRef(true);
8317
+ const [provision, setProvision] = React17.useState(null);
8318
+ const [busy, setBusy] = React17.useState(false);
8319
+ const [busyByBot, setBusyByBot] = React17.useState({});
8320
+ const [feedbackByBot, setFeedbackByBot] = React17.useState({});
8321
+ const [removeTarget, setRemoveTarget] = React17.useState(null);
8322
+ const [notice, setNotice] = React17.useState("");
8323
+ const [now, setNow] = React17.useState(() => Date.now());
8324
+ const addButtonRef = React17.useRef(null);
8325
+ const mountedRef = React17.useRef(true);
8012
8326
  const workspaceFence = useWorkspaceSnapshotFence();
8013
8327
  const scheduleAnimationFrame = useAnimationFrameScheduler();
8014
- React16.useEffect(() => {
8328
+ React17.useEffect(() => {
8015
8329
  mountedRef.current = true;
8016
8330
  return () => {
8017
8331
  mountedRef.current = false;
8018
8332
  };
8019
8333
  }, []);
8020
- const announce = React16.useCallback((value) => {
8334
+ const announce = React17.useCallback((value) => {
8021
8335
  setNotice("");
8022
8336
  scheduleAnimationFrame(() => {
8023
8337
  if (value) setNotice(value);
8024
8338
  }, "announcement");
8025
8339
  }, [scheduleAnimationFrame]);
8026
- const invoke = React16.useCallback(async (endpoint, payload = {}, signal) => {
8340
+ const invoke = React17.useCallback(async (endpoint, payload = {}, signal) => {
8027
8341
  return unwrapRpcResult8(await rpcCall(endpoint, payload, signal));
8028
8342
  }, [rpcCall]);
8029
- const loadStatus = React16.useCallback(async ({
8343
+ const loadStatus = React17.useCallback(async ({
8030
8344
  signal,
8031
8345
  silent = false,
8032
8346
  restoreProvisioning = false
@@ -8042,7 +8356,8 @@ function WeixinSettingsTab({ rpcCall }) {
8042
8356
  bots: snapshot.bots,
8043
8357
  totals: snapshot.totals,
8044
8358
  revision: snapshot.revision,
8045
- error: null
8359
+ error: null,
8360
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
8046
8361
  });
8047
8362
  if (snapshot.provisioning) {
8048
8363
  setProvision((current) => mergeWeixinProvisioningSnapshot(
@@ -8062,12 +8377,12 @@ function WeixinSettingsTab({ rpcCall }) {
8062
8377
  return void 0;
8063
8378
  }
8064
8379
  }, [invoke, workspaceFence]);
8065
- React16.useEffect(() => {
8380
+ React17.useEffect(() => {
8066
8381
  const controller = new AbortController();
8067
8382
  void loadStatus({ signal: controller.signal, restoreProvisioning: true });
8068
8383
  return () => controller.abort();
8069
8384
  }, [loadStatus]);
8070
- React16.useEffect(() => {
8385
+ React17.useEffect(() => {
8071
8386
  if (model.phase !== "ready") return void 0;
8072
8387
  const controller = new AbortController();
8073
8388
  let running = false;
@@ -8086,12 +8401,12 @@ function WeixinSettingsTab({ rpcCall }) {
8086
8401
  window.clearInterval(timer);
8087
8402
  };
8088
8403
  }, [loadStatus, model.phase]);
8089
- React16.useEffect(() => {
8404
+ React17.useEffect(() => {
8090
8405
  if (!provision || !["pending", "scanned"].includes(provision.status)) return void 0;
8091
8406
  const timer = window.setInterval(() => setNow(Date.now()), 1e3);
8092
8407
  return () => window.clearInterval(timer);
8093
8408
  }, [provision?.attemptId, provision?.status]);
8094
- const startProvisioning = React16.useCallback(async ({ replace = false } = {}) => {
8409
+ const startProvisioning = React17.useCallback(async ({ replace = false } = {}) => {
8095
8410
  setBusy(true);
8096
8411
  try {
8097
8412
  if (replace && provision?.attemptId) {
@@ -8112,7 +8427,7 @@ function WeixinSettingsTab({ rpcCall }) {
8112
8427
  setBusy(false);
8113
8428
  }
8114
8429
  }, [announce, invoke, provision?.attemptId]);
8115
- const cancelProvisioning = React16.useCallback(async () => {
8430
+ const cancelProvisioning = React17.useCallback(async () => {
8116
8431
  setBusy(true);
8117
8432
  try {
8118
8433
  if (provision?.attemptId && !["failed", "expired", "cancelled"].includes(provision.status)) {
@@ -8127,7 +8442,7 @@ function WeixinSettingsTab({ rpcCall }) {
8127
8442
  setBusy(false);
8128
8443
  }
8129
8444
  }, [announce, invoke, provision?.attemptId, provision?.status, scheduleAnimationFrame]);
8130
- const submitVerification = React16.useCallback(async (verifyCode) => {
8445
+ const submitVerification = React17.useCallback(async (verifyCode) => {
8131
8446
  if (!provision?.attemptId) return;
8132
8447
  setBusy(true);
8133
8448
  try {
@@ -8143,7 +8458,7 @@ function WeixinSettingsTab({ rpcCall }) {
8143
8458
  setBusy(false);
8144
8459
  }
8145
8460
  }, [announce, invoke, provision?.attemptId]);
8146
- React16.useEffect(() => {
8461
+ React17.useEffect(() => {
8147
8462
  const attemptId = provision?.attemptId;
8148
8463
  if (!attemptId || !["pending", "scanned", "connecting"].includes(provision.status)) return void 0;
8149
8464
  const controller = new AbortController();
@@ -8191,7 +8506,7 @@ function WeixinSettingsTab({ rpcCall }) {
8191
8506
  controller.abort();
8192
8507
  };
8193
8508
  }, [announce, invoke, loadStatus, provision?.attemptId, provision?.status, provision?.pollIntervalMs]);
8194
- const setBotBusy = React16.useCallback((botId, value) => {
8509
+ const setBotBusy = React17.useCallback((botId, value) => {
8195
8510
  setBusyByBot((current) => {
8196
8511
  const next = { ...current };
8197
8512
  if (value) next[botId] = value;
@@ -8199,7 +8514,7 @@ function WeixinSettingsTab({ rpcCall }) {
8199
8514
  return next;
8200
8515
  });
8201
8516
  }, []);
8202
- const reconnect = React16.useCallback(async (account) => {
8517
+ const reconnect = React17.useCallback(async (account) => {
8203
8518
  const snapshotVersion = workspaceFence.beginMutation();
8204
8519
  setBotBusy(account.botId, "reconnect");
8205
8520
  setFeedbackByBot((current) => {
@@ -8213,7 +8528,13 @@ function WeixinSettingsTab({ rpcCall }) {
8213
8528
  { botId: account.botId, sendTest: true }
8214
8529
  ));
8215
8530
  if (mountedRef.current && workspaceFence.canCommitMutation(snapshotVersion)) {
8216
- setModel((current) => ({ ...current, bots: snapshot.bots, totals: snapshot.totals, revision: snapshot.revision }));
8531
+ setModel((current) => ({
8532
+ ...current,
8533
+ bots: snapshot.bots,
8534
+ totals: snapshot.totals,
8535
+ revision: snapshot.revision,
8536
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? current.agentPresetCatalog
8537
+ }));
8217
8538
  }
8218
8539
  const refreshed = snapshot.bots.find((bot) => bot.botId === account.botId);
8219
8540
  let feedback;
@@ -8244,7 +8565,7 @@ function WeixinSettingsTab({ rpcCall }) {
8244
8565
  setBotBusy(account.botId, null);
8245
8566
  }
8246
8567
  }, [announce, invoke, loadStatus, setBotBusy, workspaceFence]);
8247
- const saveWorkspace = React16.useCallback(async (account, workspace) => {
8568
+ const saveWorkspace = React17.useCallback(async (account, workspace) => {
8248
8569
  const workspaceVersion = workspaceFence.beginMutation();
8249
8570
  setBotBusy(account.botId, "workspace");
8250
8571
  try {
@@ -8258,7 +8579,32 @@ function WeixinSettingsTab({ rpcCall }) {
8258
8579
  bots: snapshot.bots,
8259
8580
  totals: snapshot.totals,
8260
8581
  revision: snapshot.revision,
8261
- error: null
8582
+ error: null,
8583
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
8584
+ });
8585
+ }
8586
+ } finally {
8587
+ const shouldRefresh = workspaceFence.endMutation();
8588
+ if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true });
8589
+ if (mountedRef.current) setBotBusy(account.botId, null);
8590
+ }
8591
+ }, [invoke, loadStatus, setBotBusy, workspaceFence]);
8592
+ const saveAgentPreset = React17.useCallback(async (account, agentPreset) => {
8593
+ const snapshotVersion = workspaceFence.beginMutation();
8594
+ setBotBusy(account.botId, "preset");
8595
+ try {
8596
+ const snapshot = normalizeSnapshot7(await invoke(
8597
+ WEIXIN_ENDPOINTS.setAgentPreset,
8598
+ { botId: account.botId, agentPreset }
8599
+ ));
8600
+ if (mountedRef.current && workspaceFence.canCommitMutation(snapshotVersion)) {
8601
+ setModel({
8602
+ phase: "ready",
8603
+ bots: snapshot.bots,
8604
+ totals: snapshot.totals,
8605
+ revision: snapshot.revision,
8606
+ error: null,
8607
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
8262
8608
  });
8263
8609
  }
8264
8610
  } finally {
@@ -8267,7 +8613,7 @@ function WeixinSettingsTab({ rpcCall }) {
8267
8613
  if (mountedRef.current) setBotBusy(account.botId, null);
8268
8614
  }
8269
8615
  }, [invoke, loadStatus, setBotBusy, workspaceFence]);
8270
- const remove = React16.useCallback(async (account) => {
8616
+ const remove = React17.useCallback(async (account) => {
8271
8617
  const snapshotVersion = workspaceFence.beginMutation();
8272
8618
  setBotBusy(account.botId, "delete");
8273
8619
  try {
@@ -8276,7 +8622,13 @@ function WeixinSettingsTab({ rpcCall }) {
8276
8622
  confirm: true
8277
8623
  }));
8278
8624
  if (mountedRef.current && workspaceFence.canCommitMutation(snapshotVersion)) {
8279
- setModel((current) => ({ ...current, bots: snapshot.bots, totals: snapshot.totals, revision: snapshot.revision }));
8625
+ setModel((current) => ({
8626
+ ...current,
8627
+ bots: snapshot.bots,
8628
+ totals: snapshot.totals,
8629
+ revision: snapshot.revision,
8630
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? current.agentPresetCatalog
8631
+ }));
8280
8632
  }
8281
8633
  setRemoveTarget(null);
8282
8634
  announce("\u5FAE\u4FE1\u8D26\u53F7\u53CA\u672C\u673A\u51ED\u636E\u5DF2\u79FB\u9664\u3002");
@@ -8320,7 +8672,9 @@ function WeixinSettingsTab({ rpcCall }) {
8320
8672
  onClose: () => void cancelProvisioning()
8321
8673
  });
8322
8674
  }
8323
- return h2(
8675
+ return h2(AgentPresetCatalogContext.Provider, {
8676
+ value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
8677
+ }, h2(
8324
8678
  "section",
8325
8679
  { className: "dxw-page dim-channelPage", "aria-label": "\u5FAE\u4FE1\u8BBE\u7F6E" },
8326
8680
  h2(Heading5, {
@@ -8343,7 +8697,7 @@ function WeixinSettingsTab({ rpcCall }) {
8343
8697
  h2(Button12, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
8344
8698
  )
8345
8699
  ) : h2(
8346
- React16.Fragment,
8700
+ React17.Fragment,
8347
8701
  null,
8348
8702
  provisionView,
8349
8703
  model.bots.length === 0 && !provision ? h2(EmptyView5, { onStart: () => void startProvisioning(), busy }) : null,
@@ -8354,12 +8708,13 @@ function WeixinSettingsTab({ rpcCall }) {
8354
8708
  removeTarget,
8355
8709
  onReconnect: (account) => void reconnect(account),
8356
8710
  onWorkspaceSave: saveWorkspace,
8711
+ onAgentPresetSave: saveAgentPreset,
8357
8712
  onRequestRemove: (account) => setRemoveTarget(account.botId),
8358
8713
  onConfirmRemove: (account) => void remove(account),
8359
8714
  onCancelRemove: () => setRemoveTarget(null)
8360
8715
  }) : null
8361
8716
  )
8362
- );
8717
+ ));
8363
8718
  }
8364
8719
 
8365
8720
  // plugin-src/client/channels/whatsapp/api.js
@@ -8371,7 +8726,8 @@ var WHATSAPP_ENDPOINTS = Object.freeze({
8371
8726
  cancelProvisioning: "provision.cancel",
8372
8727
  reconnectBot: "bot.reconnect",
8373
8728
  deleteBot: "bot.delete",
8374
- setWorkspace: "bot.workspace.set"
8729
+ setWorkspace: "bot.workspace.set",
8730
+ setAgentPreset: SET_AGENT_PRESET_ENDPOINT
8375
8731
  });
8376
8732
  var PROVISION_STATES5 = /* @__PURE__ */ new Set(["starting", "pending", "connecting", "connected", "failed", "cancelled"]);
8377
8733
  var BOT_STATES = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]);
@@ -8436,6 +8792,7 @@ function normalizeBot6(value) {
8436
8792
  connected,
8437
8793
  state: connected ? "connected" : state,
8438
8794
  workspace: text4(value.workspace, "", 4096),
8795
+ agentPreset: normalizeAgentPresetId(value.agentPreset),
8439
8796
  bot: {
8440
8797
  name: text4(value.bot?.name, "WhatsApp\u673A\u5668\u4EBA", 100),
8441
8798
  idMasked: text4(value.bot?.idMasked, "WhatsApp\u8D26\u53F7", 140)
@@ -8460,7 +8817,8 @@ function normalizeSnapshot8(value) {
8460
8817
  revision: Number.isSafeInteger(source.revision) ? source.revision : 0,
8461
8818
  bots,
8462
8819
  totals: { configured: bots.length, connected: bots.filter((bot) => bot.connected).length },
8463
- provisioning: source.provisioning ? normalizeProvisioning6(source.provisioning) : null
8820
+ provisioning: source.provisioning ? normalizeProvisioning6(source.provisioning) : null,
8821
+ agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog)
8464
8822
  };
8465
8823
  }
8466
8824
  function presentError9(error) {
@@ -8475,7 +8833,7 @@ function formatRemaining6(milliseconds) {
8475
8833
  }
8476
8834
 
8477
8835
  // plugin-src/client/channels/whatsapp/index.js
8478
- var React17 = __toESM(require("react"), 1);
8836
+ var React18 = __toESM(require("react"), 1);
8479
8837
 
8480
8838
  // plugin-src/client/channels/whatsapp/styles.js
8481
8839
  var WHATSAPP_STYLE_ID = "xmanrui-dsh-im-whatsapp-settings";
@@ -8500,7 +8858,7 @@ function installWhatsappStyles() {
8500
8858
 
8501
8859
  // plugin-src/client/channels/whatsapp/index.js
8502
8860
  var ACTIVE_STATES3 = /* @__PURE__ */ new Set(["pending", "connecting"]);
8503
- var Button14 = React17.forwardRef(function Button15({ children, kind = "secondary", className = "", ...props }, ref) {
8861
+ var Button14 = React18.forwardRef(function Button15({ children, kind = "secondary", className = "", ...props }, ref) {
8504
8862
  return h2("button", {
8505
8863
  ...props,
8506
8864
  ref,
@@ -8722,6 +9080,7 @@ function WhatsappAccountCard({
8722
9080
  removing,
8723
9081
  onReconnect,
8724
9082
  onWorkspaceSave,
9083
+ onAgentPresetSave,
8725
9084
  onRequestRemove,
8726
9085
  onConfirmRemove,
8727
9086
  onCancelRemove
@@ -8781,6 +9140,11 @@ function WhatsappAccountCard({
8781
9140
  disabled: Boolean(busy),
8782
9141
  onSave: onWorkspaceSave
8783
9142
  }),
9143
+ h2(AgentPresetEditor, {
9144
+ agentPreset: account.agentPreset,
9145
+ disabled: Boolean(busy),
9146
+ onSave: onAgentPresetSave
9147
+ }),
8784
9148
  h2(
8785
9149
  "div",
8786
9150
  { className: "ddt-accountFooter dim-cardFooter" },
@@ -8815,22 +9179,23 @@ function WhatsappAccountCard({
8815
9179
  );
8816
9180
  }
8817
9181
  function WhatsappSettingsTab({ rpcCall }) {
8818
- const [model, setModel] = React17.useState({
9182
+ const [model, setModel] = React18.useState({
8819
9183
  phase: "loading",
8820
9184
  bots: [],
8821
9185
  totals: { configured: 0, connected: 0 },
8822
- error: null
9186
+ error: null,
9187
+ agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
8823
9188
  });
8824
- const [provision, setProvision] = React17.useState(null);
8825
- const [busy, setBusy] = React17.useState(false);
8826
- const [busyByBot, setBusyByBot] = React17.useState({});
8827
- const [testNoticeByBot, setTestNoticeByBot] = React17.useState({});
8828
- const [removeTarget, setRemoveTarget] = React17.useState(null);
8829
- const [now, setNow] = React17.useState(Date.now());
8830
- const mounted = React17.useRef(true);
9189
+ const [provision, setProvision] = React18.useState(null);
9190
+ const [busy, setBusy] = React18.useState(false);
9191
+ const [busyByBot, setBusyByBot] = React18.useState({});
9192
+ const [testNoticeByBot, setTestNoticeByBot] = React18.useState({});
9193
+ const [removeTarget, setRemoveTarget] = React18.useState(null);
9194
+ const [now, setNow] = React18.useState(Date.now());
9195
+ const mounted = React18.useRef(true);
8831
9196
  const workspaceFence = useWorkspaceSnapshotFence();
8832
- const addButtonRef = React17.useRef(null);
8833
- React17.useEffect(() => {
9197
+ const addButtonRef = React18.useRef(null);
9198
+ React18.useEffect(() => {
8834
9199
  const disposeDingtalk = installDingtalkStyles();
8835
9200
  const disposeWhatsapp = installWhatsappStyles();
8836
9201
  mounted.current = true;
@@ -8840,18 +9205,24 @@ function WhatsappSettingsTab({ rpcCall }) {
8840
9205
  disposeDingtalk();
8841
9206
  };
8842
9207
  }, []);
8843
- const invoke = React17.useCallback(async (endpoint, payload = {}, signal) => {
9208
+ const invoke = React18.useCallback(async (endpoint, payload = {}, signal) => {
8844
9209
  if (typeof rpcCall !== "function") throw new TypeError("WhatsApp \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
8845
9210
  return unwrapRpcResult9(await rpcCall(endpoint, payload, signal));
8846
9211
  }, [rpcCall]);
8847
- const loadStatus = React17.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
9212
+ const loadStatus = React18.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
8848
9213
  const workspaceVersion = workspaceFence.beginStatus();
8849
9214
  if (workspaceVersion === null) return void 0;
8850
9215
  if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
8851
9216
  try {
8852
9217
  const snapshot = normalizeSnapshot8(await invoke(WHATSAPP_ENDPOINTS.status, {}, signal));
8853
9218
  if (!mounted.current || signal?.aborted || !workspaceFence.canCommitStatus(workspaceVersion)) return void 0;
8854
- setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
9219
+ setModel({
9220
+ phase: "ready",
9221
+ bots: snapshot.bots,
9222
+ totals: snapshot.totals,
9223
+ error: null,
9224
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
9225
+ });
8855
9226
  if (restore && snapshot.provisioning) setProvision({
8856
9227
  ...snapshot.provisioning,
8857
9228
  durationMs: Math.max(1, snapshot.provisioning.expiresAt - Date.now())
@@ -8868,12 +9239,12 @@ function WhatsappSettingsTab({ rpcCall }) {
8868
9239
  return void 0;
8869
9240
  }
8870
9241
  }, [invoke, workspaceFence]);
8871
- React17.useEffect(() => {
9242
+ React18.useEffect(() => {
8872
9243
  const controller = new AbortController();
8873
9244
  void loadStatus({ signal: controller.signal, restore: true });
8874
9245
  return () => controller.abort();
8875
9246
  }, [loadStatus]);
8876
- React17.useEffect(() => {
9247
+ React18.useEffect(() => {
8877
9248
  if (model.phase !== "ready") return void 0;
8878
9249
  const controller = new AbortController();
8879
9250
  const timer = window.setInterval(
@@ -8885,12 +9256,12 @@ function WhatsappSettingsTab({ rpcCall }) {
8885
9256
  window.clearInterval(timer);
8886
9257
  };
8887
9258
  }, [loadStatus, model.phase]);
8888
- React17.useEffect(() => {
9259
+ React18.useEffect(() => {
8889
9260
  if (!provision || !ACTIVE_STATES3.has(provision.status)) return void 0;
8890
9261
  const timer = window.setInterval(() => mounted.current && setNow(Date.now()), 1e3);
8891
9262
  return () => window.clearInterval(timer);
8892
9263
  }, [provision?.attemptId, provision?.status]);
8893
- const startProvisioning = React17.useCallback(async (replace = false) => {
9264
+ const startProvisioning = React18.useCallback(async (replace = false) => {
8894
9265
  setBusy(true);
8895
9266
  try {
8896
9267
  if (replace && provision?.attemptId) {
@@ -8908,7 +9279,7 @@ function WhatsappSettingsTab({ rpcCall }) {
8908
9279
  if (mounted.current) setBusy(false);
8909
9280
  }
8910
9281
  }, [invoke, provision?.attemptId]);
8911
- const closeProvision = React17.useCallback(async () => {
9282
+ const closeProvision = React18.useCallback(async () => {
8912
9283
  setBusy(true);
8913
9284
  try {
8914
9285
  if (provision?.attemptId && ACTIVE_STATES3.has(provision.status)) {
@@ -8919,7 +9290,7 @@ function WhatsappSettingsTab({ rpcCall }) {
8919
9290
  if (mounted.current) setBusy(false);
8920
9291
  }
8921
9292
  }, [invoke, provision?.attemptId, provision?.status]);
8922
- React17.useEffect(() => {
9293
+ React18.useEffect(() => {
8923
9294
  const attemptId = provision?.attemptId;
8924
9295
  if (!attemptId || !ACTIVE_STATES3.has(provision.status)) return void 0;
8925
9296
  const controller = new AbortController();
@@ -8960,7 +9331,7 @@ function WhatsappSettingsTab({ rpcCall }) {
8960
9331
  if (timer) window.clearTimeout(timer);
8961
9332
  };
8962
9333
  }, [invoke, loadStatus, provision?.attemptId, provision?.status]);
8963
- const botAction = React17.useCallback(async (account, operation, endpoint, payload) => {
9334
+ const botAction = React18.useCallback(async (account, operation, endpoint, payload) => {
8964
9335
  const snapshotVersion = workspaceFence.beginMutation();
8965
9336
  setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
8966
9337
  if (operation === "reconnect") {
@@ -8974,7 +9345,13 @@ function WhatsappSettingsTab({ rpcCall }) {
8974
9345
  const value = await invoke(endpoint, payload);
8975
9346
  const snapshot = normalizeSnapshot8(value);
8976
9347
  if (mounted.current && workspaceFence.canCommitMutation(snapshotVersion)) {
8977
- setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
9348
+ setModel({
9349
+ phase: "ready",
9350
+ bots: snapshot.bots,
9351
+ totals: snapshot.totals,
9352
+ error: null,
9353
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
9354
+ });
8978
9355
  if (operation === "reconnect") {
8979
9356
  setTestNoticeByBot((current) => ({
8980
9357
  ...current,
@@ -9025,6 +9402,12 @@ function WhatsappSettingsTab({ rpcCall }) {
9025
9402
  WHATSAPP_ENDPOINTS.setWorkspace,
9026
9403
  { botId: account.botId, workspace }
9027
9404
  ),
9405
+ onAgentPresetSave: (agentPreset) => botAction(
9406
+ account,
9407
+ "preset",
9408
+ WHATSAPP_ENDPOINTS.setAgentPreset,
9409
+ { botId: account.botId, agentPreset }
9410
+ ),
9028
9411
  onRequestRemove: () => setRemoveTarget(account.botId),
9029
9412
  onCancelRemove: () => setRemoveTarget(null),
9030
9413
  onConfirmRemove: async () => {
@@ -9036,7 +9419,9 @@ function WhatsappSettingsTab({ rpcCall }) {
9036
9419
  }
9037
9420
  }))))
9038
9421
  ) : null;
9039
- return h2(
9422
+ return h2(AgentPresetCatalogContext.Provider, {
9423
+ value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
9424
+ }, h2(
9040
9425
  "section",
9041
9426
  {
9042
9427
  className: "ddt-page dwa-page dim-channelPage",
@@ -9059,7 +9444,7 @@ function WhatsappSettingsTab({ rpcCall }) {
9059
9444
  h2(Button14, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
9060
9445
  )
9061
9446
  ) : h2(
9062
- React17.Fragment,
9447
+ React18.Fragment,
9063
9448
  null,
9064
9449
  provision?.status === "pending" ? h2(QrPanel5, {
9065
9450
  provision,
@@ -9075,7 +9460,7 @@ function WhatsappSettingsTab({ rpcCall }) {
9075
9460
  }) : model.bots.length === 0 ? h2(EmptyView6, { busy, onStart: () => void startProvisioning(false) }) : null,
9076
9461
  botList
9077
9462
  )
9078
- );
9463
+ ));
9079
9464
  }
9080
9465
 
9081
9466
  // plugin-src/client/styles.js
@@ -9222,6 +9607,14 @@ var CSS11 = String.raw`
9222
9607
  .dim-panel .dim-workspaceEdit { grid-column: 2; grid-row: 1; padding: 0; border: 0; color: #1677ff; background: transparent; font: inherit; font-weight: 560; white-space: nowrap; cursor: pointer; }
9223
9608
  .dim-panel .dim-workspaceEdit:disabled { cursor: not-allowed; opacity: .55; }
9224
9609
  .dim-panel .dim-workspacePath { min-width: 0; max-width: 100%; grid-column: 1 / -1; grid-row: 2; display: block; overflow-x: auto; overflow-y: hidden; color: var(--dsw-alias-label-primary, #1f2329); font: 12px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; white-space: nowrap; }
9610
+ .dim-panel .dim-preset { min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr) max-content; align-items: center; column-gap: 10px; row-gap: 4px; margin-top: 6px; padding: 6px 10px; border: 1px solid var(--dsw-alias-border-l1, #eef0f3); border-radius: 9px; background: var(--dsw-alias-bg-module-platform, #f7f8fa); }
9611
+ .dim-panel .dim-presetHeader { display: contents; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: normal; }
9612
+ .dim-panel .dim-presetHeader > span { grid-column: 1; grid-row: 1; white-space: nowrap; }
9613
+ .dim-panel .dim-presetStatus { grid-column: 2; grid-row: 1; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 12px; white-space: nowrap; }
9614
+ .dim-panel .dim-presetSelect { min-width: 0; max-width: 100%; grid-column: 1 / -1; grid-row: 2; height: 30px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 7px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 12px; }
9615
+ .dim-panel .dim-presetSelect:disabled { cursor: not-allowed; opacity: .55; }
9616
+ .dim-panel .dim-presetHelp { grid-column: 1 / -1; grid-row: 3; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; line-height: 1.4; }
9617
+ .dim-panel .dim-presetError { grid-column: 1 / -1; grid-row: 4; margin: 0; color: var(--dsw-alias-state-error-primary, #d54941); font-size: 12px; line-height: 1.4; }
9225
9618
  .dim-directoryPickerBackdrop { --dim-blue: var(--dsw-alias-state-business-primary, #3370ff); --dim-blue-soft: color-mix(in srgb, var(--dim-blue) 9%, transparent); position: fixed; inset: 0; z-index: 1000; display: grid; place-items: center; padding: 24px; background: rgb(15 17 21 / 42%); backdrop-filter: blur(3px); }
9226
9619
  .dim-directoryPickerBackdrop, .dim-directoryPickerBackdrop *, .dim-directoryPickerBackdrop *::before, .dim-directoryPickerBackdrop *::after { box-sizing: border-box; }
9227
9620
  .dim-directoryPicker { width: min(720px, 100%); height: min(620px, calc(100vh - 48px)); min-height: 420px; display: grid; grid-template-rows: auto minmax(0, 1fr) auto; overflow: hidden; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 18px; outline: none; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-1, #fff); box-shadow: 0 24px 72px rgb(15 17 21 / 24%); }
@@ -9452,8 +9845,8 @@ function IMSettingsTab({
9452
9845
  officeRpcCall,
9453
9846
  workspaceDirectoryPicker
9454
9847
  }) {
9455
- const [selected, setSelected] = React18.useState("weixin");
9456
- const githubTooltipId = React18.useId();
9848
+ const [selected, setSelected] = React19.useState("weixin");
9849
+ const githubTooltipId = React19.useId();
9457
9850
  const active = CHANNELS.find((channel4) => channel4.id === selected) ?? CHANNELS[0];
9458
9851
  return h2(
9459
9852
  WorkspaceDirectoryPickerContext.Provider,