@xmanrui/dsh-im 0.17.1 → 0.19.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 (56) hide show
  1. package/README.en.md +3 -2
  2. package/README.md +3 -2
  3. package/lib/client.js +1297 -600
  4. package/lib/index.js +190 -145
  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 +28 -5
  10. package/plugin-src/client/channels/feishu/index.js +330 -41
  11. package/plugin-src/client/channels/feishu/styles.js +46 -0
  12. package/plugin-src/client/channels/qq/api.js +5 -0
  13. package/plugin-src/client/channels/qq/index.js +37 -6
  14. package/plugin-src/client/channels/shared/token-api.js +5 -0
  15. package/plugin-src/client/channels/shared/token-channel.js +34 -6
  16. package/plugin-src/client/channels/wecom/api.js +5 -0
  17. package/plugin-src/client/channels/wecom/index.js +37 -6
  18. package/plugin-src/client/channels/weixin/api.js +5 -0
  19. package/plugin-src/client/channels/weixin/index.js +50 -4
  20. package/plugin-src/client/channels/whatsapp/api.js +5 -0
  21. package/plugin-src/client/channels/whatsapp/index.js +30 -4
  22. package/plugin-src/client/i18n.js +59 -0
  23. package/plugin-src/client/styles.js +8 -0
  24. package/plugin-src/host/channels/dingtalk/production.mjs +10 -4
  25. package/plugin-src/host/channels/dingtalk/rpc.mjs +12 -0
  26. package/plugin-src/host/channels/feishu/connection-supervisor.mjs +1 -1
  27. package/plugin-src/host/channels/feishu/production.mjs +8 -3
  28. package/plugin-src/host/channels/feishu/rpc.mjs +78 -7
  29. package/plugin-src/host/channels/qq/production.mjs +10 -4
  30. package/plugin-src/host/channels/qq/rpc.mjs +12 -0
  31. package/plugin-src/host/channels/shared/agent-preset-rpc.mjs +25 -0
  32. package/plugin-src/host/channels/shared/production.mjs +10 -4
  33. package/plugin-src/host/channels/shared/rpc.mjs +12 -0
  34. package/plugin-src/host/channels/shared/workspace-rpc.mjs +2 -0
  35. package/plugin-src/host/channels/slack/production.mjs +10 -4
  36. package/plugin-src/host/channels/slack/rpc.mjs +13 -0
  37. package/plugin-src/host/channels/wecom/production.mjs +10 -4
  38. package/plugin-src/host/channels/wecom/rpc.mjs +12 -0
  39. package/plugin-src/host/channels/weixin/production.mjs +10 -4
  40. package/plugin-src/host/channels/weixin/rpc.mjs +15 -0
  41. package/plugin-src/host/channels/whatsapp/production.mjs +10 -4
  42. package/plugin-src/host/channels/whatsapp/rpc.mjs +12 -0
  43. package/src/channels/discord/discord-api.mjs +1 -1
  44. package/src/channels/feishu/bridge.mjs +29 -4
  45. package/src/channels/feishu/feishu-runtime.mjs +14 -0
  46. package/src/channels/feishu/group-message-permission-manager.mjs +71 -0
  47. package/src/channels/feishu/group-response-mode.mjs +15 -0
  48. package/src/channels/feishu/multi-bot-controller.mjs +258 -9
  49. package/src/channels/feishu/plugin-config-store.mjs +3 -0
  50. package/src/channels/feishu/repair-manager.mjs +17 -3
  51. package/src/channels/shared/agent-preset.mjs +74 -0
  52. package/src/channels/shared/bot-workspace-store.mjs +141 -14
  53. package/src/channels/shared/harness-client.mjs +6 -4
  54. package/src/channels/telegram/telegram-api.mjs +31 -0
  55. package/src/channels/telegram/telegram-runtime.mjs +25 -1
  56. 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
@@ -458,6 +258,24 @@ var EN = Object.freeze({
458
258
  "WhatsApp\u8D26\u53F7": "WhatsApp account",
459
259
  "\u5FAE\u4FE1\u8BBE\u7F6E": "WeChat settings",
460
260
  "\u98DE\u4E66\u673A\u5668\u4EBA\u8BBE\u7F6E": "Feishu bot settings",
261
+ "\u7FA4\u804A\u54CD\u5E94\u65B9\u5F0F": "Group response mode",
262
+ "\u4EC5\u5728 @\u673A\u5668\u4EBA\u65F6\u54CD\u5E94\uFF08\u63A8\u8350\uFF09": "Only respond when @mentioned (recommended)",
263
+ "\u54CD\u5E94\u6240\u6709\u7FA4\u6D88\u606F": "Respond to all group messages",
264
+ "\u54CD\u5E94\u6240\u6709\u7FA4\u6D88\u606F\uFF08\u9700\u98DE\u4E66\u654F\u611F\u6743\u9650\uFF09": "Respond to all group messages (requires a sensitive Feishu scope)",
265
+ "\u91CD\u65B0\u6388\u6743": "Reauthorize",
266
+ "\u53BB\u6388\u6743": "Authorize",
267
+ "\u91CD\u65B0\u6388\u6743\u7FA4\u6D88\u606F\u6743\u9650": "Reauthorize group-message permission",
268
+ "\u6388\u6743\u7FA4\u6D88\u606F\u6743\u9650": "Authorize group-message permission",
269
+ "\u6B63\u5728\u51C6\u5907\u6388\u6743\u2026": "Preparing authorization\u2026",
270
+ "\u6B63\u5728\u51C6\u5907\u2026": "Preparing\u2026",
271
+ "\u79C1\u804A\u59CB\u7EC8\u54CD\u5E94\uFF1B\u7FA4\u804A\u4EC5\u5904\u7406\u660E\u786E @\u5F53\u524D\u673A\u5668\u4EBA\u7684\u6D88\u606F\u3002\u7FA4\u6D88\u606F\u6743\u9650\u5DF2\u5F00\u901A\uFF0C\u518D\u6B21\u5207\u6362\u65E0\u9700\u6388\u6743\u3002": "Direct messages always work; group chats require an explicit @mention of this bot. The group-message permission is already granted, so switching again needs no authorization.",
272
+ "\u79C1\u804A\u59CB\u7EC8\u54CD\u5E94\uFF1B\u7FA4\u804A\u4EC5\u5904\u7406\u660E\u786E @\u5F53\u524D\u673A\u5668\u4EBA\u7684\u6D88\u606F\u3002\u9009\u62E9\u5168\u90E8\u6D88\u606F\u540E\u4F1A\u6253\u5F00\u98DE\u4E66\u5B98\u65B9\u6388\u6743\u6D41\u7A0B\u3002": "Direct messages always work; group chats require an explicit @mention of this bot. Selecting all messages opens the official Feishu authorization flow.",
273
+ "\u5DF2\u5F00\u901A\u201C\u83B7\u53D6\u7FA4\u7EC4\u4E2D\u6240\u6709\u6D88\u606F\u201D\u6743\u9650\uFF08im:message.group_msg\uFF09\uFF1B\u673A\u5668\u4EBA\u4F1A\u5904\u7406\u7FA4\u804A\u4E2D\u7684\u6240\u6709\u53EF\u89C1\u6D88\u606F\u3002": "The \u201CRead all messages in associated group chat\u201D scope (im:message.group_msg) is granted; the bot processes every visible group message.",
274
+ "\u5C1A\u672A\u786E\u8BA4\u201C\u83B7\u53D6\u7FA4\u7EC4\u4E2D\u6240\u6709\u6D88\u606F\u201D\u6743\u9650\uFF0C\u8BF7\u5B8C\u6210\u98DE\u4E66\u6388\u6743\u3002": "The \u201CRead all messages in associated group chat\u201D scope has not been confirmed. Complete Feishu authorization.",
275
+ "\u79C1\u804A\u59CB\u7EC8\u54CD\u5E94\uFF1B\u7FA4\u804A\u4EC5\u5904\u7406\u660E\u786E @\u5F53\u524D\u673A\u5668\u4EBA\u7684\u6D88\u606F\u3002": "Direct messages always work; group chats require an explicit @mention of this bot.",
276
+ "\u9700\u5728\u98DE\u4E66\u4E3A\u8BE5\u673A\u5668\u4EBA\u5F00\u901A\u201C\u83B7\u53D6\u7FA4\u7EC4\u4E2D\u6240\u6709\u6D88\u606F\u201D\u6743\u9650\uFF08im:message.group_msg\uFF09\uFF1B\u5F00\u901A\u540E\uFF0C\u673A\u5668\u4EBA\u4F1A\u5904\u7406\u7FA4\u804A\u4E2D\u7684\u6240\u6709\u53EF\u89C1\u6D88\u606F\u3002": "Grant this bot the \u201CRead all messages in associated group chat\u201D Feishu scope (im:message.group_msg); once granted, it will process every visible group message.",
277
+ "\u7FA4\u804A\u54CD\u5E94\u65B9\u5F0F\u4FEE\u6539\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002": "Could not update the group response mode. Try again.",
278
+ "\u7FA4\u6D88\u606F\u6743\u9650\u6388\u6743\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002": "Could not authorize group-message permission. Try again.",
461
279
  "\u9489\u9489\u8BBE\u7F6E": "DingTalk settings",
462
280
  "\u4F01\u4E1A\u5FAE\u4FE1\u8BBE\u7F6E": "WeCom settings",
463
281
  "\u626B\u7801\u63A5\u5165\u673A\u5668\u4EBA": "Scan QR code",
@@ -507,6 +325,15 @@ var EN = Object.freeze({
507
325
  "\u5DE5\u4F5C\u533A\u8DEF\u5F84\u4E0D\u5B58\u5728\u3002": "The workspace path does not exist.",
508
326
  "\u5DE5\u4F5C\u533A\u8DEF\u5F84\u5FC5\u987B\u6307\u5411\u4E00\u4E2A\u76EE\u5F55\u3002": "The workspace path must point to a directory.",
509
327
  "\u627E\u4E0D\u5230\u8981\u4FEE\u6539\u7684\u673A\u5668\u4EBA\u3002": "The bot could not be found.",
328
+ "Agent Preset": "Agent Preset",
329
+ "\u8DDF\u968F Host \u9ED8\u8BA4": "Follow the Host default",
330
+ "\uFF08\u5DF2\u4E0D\u53EF\u7528\uFF09": " (unavailable)",
331
+ "\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.",
332
+ "\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.",
333
+ "Agent Preset \u4FEE\u6539\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002": "Could not update the Agent Preset. Try again.",
334
+ "\u8BF7\u9009\u62E9 Agent Preset\u3002": "Choose an Agent Preset.",
335
+ "Agent Preset \u65E0\u6548\u3002": "The Agent Preset is invalid.",
336
+ "Agent Preset \u4E0D\u5B58\u5728\u6216\u4E0D\u53EF\u7528\u3002": "The Agent Preset does not exist or is unavailable.",
510
337
  "\u5C1A\u672A\u68C0\u67E5": "Not checked yet",
511
338
  "\u521A\u521A": "Just now",
512
339
  "\u68C0\u67E5\u8FDE\u63A5": "Check connection",
@@ -571,6 +398,7 @@ var EN = Object.freeze({
571
398
  "\u751F\u6210 QQ \u4E8C\u7EF4\u7801": "Generate QQ QR code",
572
399
  "\u6B63\u5728\u751F\u6210\u4E8C\u7EF4\u7801\u2026": "Generating QR code\u2026",
573
400
  "\u6B63\u5728\u51C6\u5907\u6388\u6743\u4E8C\u7EF4\u7801": "Preparing authorization QR code",
401
+ "\u6B63\u5728\u51C6\u5907\u6743\u9650\u6388\u6743\u4E8C\u7EF4\u7801": "Preparing permission authorization QR code",
574
402
  "\u6B63\u5728\u51C6\u5907\u5FAE\u4FE1\u4E8C\u7EF4\u7801": "Preparing WeChat QR code",
575
403
  "\u6B63\u5728\u6DFB\u52A0\u65B0\u673A\u5668\u4EBA": "Adding a new bot",
576
404
  "\u6B63\u5728\u7533\u8BF7\u9489\u9489\u6388\u6743\u4E8C\u7EF4\u7801\u2026": "Requesting DingTalk authorization QR code\u2026",
@@ -588,6 +416,16 @@ var EN = Object.freeze({
588
416
  "\u5728\u98DE\u4E66\u4E2D\u6253\u5F00": "Open in Feishu",
589
417
  "\u53D6\u6D88\u6DFB\u52A0": "Cancel",
590
418
  "\u4F7F\u7528\u98DE\u4E66\u626B\u7801\u4FEE\u590D\u5361\u7247\u6309\u94AE": "Scan with Feishu to repair card buttons",
419
+ "\u4F7F\u7528\u98DE\u4E66\u786E\u8BA4\u7FA4\u6D88\u606F\u6743\u9650": "Confirm group-message permission with Feishu",
420
+ "\u626B\u7801\u4F1A\u66F4\u65B0\u73B0\u6709\u98DE\u4E66\u5E94\u7528\uFF0C\u53EA\u589E\u91CF\u5F00\u901A\u201C\u83B7\u53D6\u7FA4\u7EC4\u4E2D\u6240\u6709\u6D88\u606F\u201D\u6743\u9650\uFF1B\u4E0D\u4F1A\u521B\u5EFA\u65B0\u5E94\u7528\u3002\u786E\u8BA4\u540E\u4F1A\u81EA\u52A8\u542F\u7528\u201C\u54CD\u5E94\u6240\u6709\u7FA4\u6D88\u606F\u201D\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u53D7\u5F71\u54CD\u3002": "Scanning updates the existing Feishu app with only the \u201CRead all messages in associated group chat\u201D scope. It does not create a new app. After confirmation, \u201CRespond to all group messages\u201D is enabled automatically; other bots are unaffected.",
421
+ "\u6838\u5BF9\u73B0\u6709\u5E94\u7528\uFF0C\u5E76\u786E\u8BA4\u201C\u83B7\u53D6\u7FA4\u7EC4\u4E2D\u6240\u6709\u6D88\u606F\u201D\u6743\u9650": "Review the existing app and confirm the \u201CRead all messages in associated group chat\u201D permission",
422
+ "\u4FDD\u6301\u672C\u9875\u6253\u5F00\uFF0C\u7B49\u5F85\u6743\u9650\u751F\u6548\u5E76\u81EA\u52A8\u5207\u6362\u54CD\u5E94\u65B9\u5F0F": "Keep this page open while the permission takes effect and the response mode switches automatically",
423
+ "\u53D6\u6D88\u6388\u6743": "Cancel authorization",
424
+ "\u5DF2\u786E\u8BA4\uFF0C\u6B63\u5728\u542F\u7528\u5168\u90E8\u6D88\u606F\u6A21\u5F0F": "Confirmed. Enabling all-message mode",
425
+ "\u6743\u9650\u914D\u7F6E\u5DF2\u63D0\u4EA4\uFF0C\u6B63\u5728\u4FDD\u5B58\u8BBE\u7F6E\u5E76\u91CD\u8FDE\u6B64\u673A\u5668\u4EBA\uFF1B\u6B64\u9636\u6BB5\u65E0\u6CD5\u53D6\u6D88\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u4F1A\u4E2D\u65AD\u3002": "The permission update was submitted. Saving the setting and reconnecting this bot. This stage cannot be cancelled; other bots will not be interrupted.",
426
+ "\u6743\u9650\u914D\u7F6E\u5DF2\u63D0\u4EA4\uFF0C\u6B63\u5728\u542F\u7528\u5168\u90E8\u6D88\u606F\u6A21\u5F0F\u5E76\u91CD\u8FDE\u6B64\u673A\u5668\u4EBA\uFF1B\u6B64\u9636\u6BB5\u65E0\u6CD5\u53D6\u6D88\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u4F1A\u4E2D\u65AD\u3002": "The permission update was submitted. Enabling all-message mode and reconnecting this bot. This stage cannot be cancelled; other bots will not be interrupted.",
427
+ "\u6B63\u5728\u4E3A\u73B0\u6709\u98DE\u4E66\u5E94\u7528\u7533\u8BF7\u7FA4\u6D88\u606F\u6743\u9650\u4E8C\u7EF4\u7801\uFF0C\u8BF7\u7A0D\u5019\u3002": "Requesting a group-message permission QR code for the existing Feishu app\u2026",
428
+ "\u7FA4\u6D88\u606F\u6743\u9650\u6CA1\u6709\u5F00\u901A\u5B8C\u6210": "Group-message permission was not granted",
591
429
  "\u626B\u7801\u4F1A\u66F4\u65B0\u73B0\u6709\u98DE\u4E66\u5E94\u7528\uFF0C\u53EA\u589E\u91CF\u8865\u5145\u5361\u7247\u6309\u94AE\u56DE\u8C03\uFF1B\u4E0D\u4F1A\u521B\u5EFA\u65B0\u5E94\u7528\u3002\u786E\u8BA4\u540E\u6B64\u673A\u5668\u4EBA\u4F1A\u77ED\u6682\u91CD\u8FDE\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u53D7\u5F71\u54CD\u3002": "Scanning updates the existing Feishu app with only the card-button callback. It does not create a new app. This bot reconnects briefly after confirmation; other bots are not affected.",
592
430
  "\u6838\u5BF9\u73B0\u6709\u5E94\u7528\u540D\u79F0\uFF0C\u5E76\u786E\u8BA4\u53EA\u65B0\u589E\u5361\u7247\u56DE\u8C03": "Review the existing app name and confirm that only the card callback is added",
593
431
  "\u4FDD\u6301\u672C\u9875\u6253\u5F00\uFF0C\u7B49\u5F85\u5361\u7247\u6309\u94AE\u4FEE\u590D\u5B8C\u6210": "Keep this page open until card-button repair finishes",
@@ -600,14 +438,25 @@ var EN = Object.freeze({
600
438
  "\u4FEE\u590D\u5361\u7247\u6309\u94AE": "Repair card buttons",
601
439
  "\u7B49\u5F85\u626B\u7801\u2026": "Waiting for scan\u2026",
602
440
  "\u98DE\u4E66\u670D\u52A1\u8FD4\u56DE\u4E86\u4E0D\u5339\u914D\u7684\u5361\u7247\u4FEE\u590D\u4E8C\u7EF4\u7801": "Feishu returned a repair QR code for a different bot",
441
+ "\u98DE\u4E66\u670D\u52A1\u8FD4\u56DE\u4E86\u4E0D\u5339\u914D\u7684\u7FA4\u6D88\u606F\u6743\u9650\u4E8C\u7EF4\u7801": "Feishu returned a group-message permission QR code for a different bot",
603
442
  "\u98DE\u4E66\u670D\u52A1\u8FD4\u56DE\u7684\u4FEE\u590D\u4FE1\u606F\u7F3A\u5C11 botId": "Feishu repair status is missing the bot ID",
443
+ "\u98DE\u4E66\u670D\u52A1\u8FD4\u56DE\u7684\u5E94\u7528\u66F4\u65B0\u4FE1\u606F\u7F3A\u5C11 botId": "Feishu app-update status is missing the bot ID",
604
444
  "\u98DE\u4E66\u670D\u52A1\u8FD4\u56DE\u4E86\u4E0D\u5339\u914D\u7684\u6CE8\u518C\u8FDB\u5EA6": "Feishu returned registration progress for a different operation",
605
445
  "\u6B64\u673A\u5668\u4EBA": "this bot",
446
+ "\u7528\u4E8E\u4E3A${botName}\u5F00\u901A\u7FA4\u6D88\u606F\u6743\u9650\u7684\u4E00\u6B21\u6027\u6388\u6743\u4E8C\u7EF4\u7801": "One-time QR code for granting group-message permission to ${botName}",
447
+ "\u6B63\u5728\u4E3A\u300C${botName}\u300D\u5F00\u901A\u7FA4\u6D88\u606F\u6743\u9650": "Granting group-message permission to \u201C${botName}\u201D",
606
448
  '${botName ?? "\u673A\u5668\u4EBA"}\u7684\u4FEE\u590D\u4E8C\u7EF4\u7801\u5DF2\u751F\u6210\uFF0C\u8BF7\u4F7F\u7528\u98DE\u4E66\u626B\u7801\u3002': 'Repair QR code generated for ${botName ?? "bot"}. Scan it with Feishu.',
449
+ '${botName ?? "\u673A\u5668\u4EBA"}\u7684\u7FA4\u6D88\u606F\u6743\u9650\u4E8C\u7EF4\u7801\u5DF2\u751F\u6210\uFF0C\u8BF7\u4F7F\u7528\u98DE\u4E66\u786E\u8BA4\u3002': 'Group-message permission QR code generated for ${botName ?? "bot"}. Confirm it with Feishu.',
450
+ "${targetBotName}\u5DF2\u5F00\u901A\u7FA4\u6D88\u606F\u6743\u9650\uFF0C\u5E76\u542F\u7528\u201C\u54CD\u5E94\u6240\u6709\u7FA4\u6D88\u606F\u201D\u3002": "${targetBotName} now has group-message permission and \u201CRespond to all group messages\u201D is enabled.",
451
+ "${targetBot.bot.name}\u5DF2\u5F00\u901A\u7FA4\u6D88\u606F\u6743\u9650\uFF0C\u5E76\u542F\u7528\u201C\u54CD\u5E94\u6240\u6709\u7FA4\u6D88\u606F\u201D\u3002": "${targetBot.bot.name} now has group-message permission and \u201CRespond to all group messages\u201D is enabled.",
607
452
  "${targetBot.bot.name}\u5DF2\u8FDE\u63A5\uFF0C\u53EF\u4EE5\u5728\u98DE\u4E66\u4E2D\u5F00\u59CB\u804A\u5929\u3002": "${targetBot.bot.name} is connected and ready to chat in Feishu.",
608
453
  "\u5DF2\u53D6\u6D88\u5361\u7247\u6309\u94AE\u4FEE\u590D\u3002": "Card-button repair was cancelled.",
454
+ "\u5DF2\u53D6\u6D88\u7FA4\u6D88\u606F\u6743\u9650\u6388\u6743\u3002": "Group-message permission authorization was cancelled.",
609
455
  "\u5361\u7247\u6309\u94AE\u5DF2\u66F4\u65B0\uFF0C\u4F46\u6682\u65F6\u65E0\u6CD5\u786E\u8BA4\u673A\u5668\u4EBA\u8FDE\u63A5\u72B6\u6001": "The card callback was updated, but the bot connection could not be confirmed yet",
456
+ "\u7FA4\u6D88\u606F\u6743\u9650\u5DF2\u66F4\u65B0\uFF0C\u4F46\u6682\u65F6\u65E0\u6CD5\u786E\u8BA4\u673A\u5668\u4EBA\u8FDE\u63A5\u72B6\u6001": "The group-message permission was updated, but the bot connection could not be confirmed yet",
610
457
  "\u98DE\u4E66\u5361\u7247\u6309\u94AE\u4FEE\u590D\u5931\u8D25": "Could not repair the Feishu card buttons",
458
+ "\u98DE\u4E66\u7FA4\u6D88\u606F\u6743\u9650\u5F00\u901A\u5931\u8D25": "Could not grant the Feishu group-message permission",
459
+ "\u8BF7\u5148\u5B8C\u6210\u5F53\u524D\u98DE\u4E66\u6388\u6743\u64CD\u4F5C\uFF0C\u518D\u5F00\u901A\u7FA4\u6D88\u606F\u6743\u9650\u3002": "Finish the current Feishu authorization before granting group-message permission.",
611
460
  "\u5DF2\u786E\u8BA4\uFF0C\u6B63\u5728\u8FDE\u63A5\u65B0\u673A\u5668\u4EBA": "Confirmed. Connecting the new bot",
612
461
  "\u6B63\u5728\u5B89\u5168\u4FDD\u5B58\u51ED\u636E\u5E76\u68C0\u67E5\u65B0\u673A\u5668\u4EBA\u7684\u6D88\u606F\u901A\u9053\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u4F1A\u4E2D\u65AD\u3002": "Saving credentials and checking the new bot connection. Existing bots will not be interrupted.",
613
462
  "\u6B63\u5728\u5411\u98DE\u4E66\u7533\u8BF7\u4E00\u6B21\u6027\u6388\u6743\u4E8C\u7EF4\u7801\uFF0C\u8BF7\u7A0D\u5019\u3002": "Requesting a one-time authorization QR code from Feishu\u2026",
@@ -900,16 +749,26 @@ function translateDynamic(text5) {
900
749
  if (match) return `Remove \u201C${match[1]}\u201D from DeepSeek Harness?`;
901
750
  match = /^从 DeepSeek Harness 移除(.+)$/.exec(text5);
902
751
  if (match) return `Remove ${match[1]} from DeepSeek Harness`;
752
+ match = /^(.+)的飞书授权流程$/.exec(text5);
753
+ if (match) return `Feishu authorization flow for ${match[1]}`;
903
754
  match = /^用于修复(.+)卡片按钮的一次性授权二维码$/.exec(text5);
904
755
  if (match) return `One-time QR code for repairing card buttons for ${match[1]}`;
756
+ match = /^用于为(.+)开通群消息权限的一次性授权二维码$/.exec(text5);
757
+ if (match) return `One-time QR code for granting group-message permission to ${match[1]}`;
905
758
  match = /^正在修复「(.+)」$/.exec(text5);
906
759
  if (match) return `Repairing \u201C${match[1]}\u201D`;
760
+ match = /^正在为「(.+)」开通群消息权限$/.exec(text5);
761
+ if (match) return `Granting group-message permission to \u201C${match[1]}\u201D`;
907
762
  match = /^修复(.+)的卡片按钮$/.exec(text5);
908
763
  if (match) return `Repair card buttons for ${match[1]}`;
909
764
  match = /^(.+)的修复二维码已生成,请使用飞书扫码。$/.exec(text5);
910
765
  if (match) return `Repair QR code generated for ${match[1]}. Scan it with Feishu.`;
766
+ match = /^(.+)的群消息权限二维码已生成,请使用飞书确认。$/.exec(text5);
767
+ if (match) return `Group-message permission QR code generated for ${match[1]}. Confirm it with Feishu.`;
911
768
  match = /^(.+)的卡片按钮已修复。$/.exec(text5);
912
769
  if (match) return `Card buttons repaired for ${match[1]}.`;
770
+ match = /^(.+)已开通群消息权限,并启用“响应所有群消息”。$/.exec(text5);
771
+ if (match) return `${match[1]} now has group-message permission and \u201CRespond to all group messages\u201D is enabled.`;
913
772
  match = /^(检查连接|重试连接)(.+)$/.exec(text5);
914
773
  if (match) return `${localizeText(match[1])} ${match[2]}`;
915
774
  match = /^移除(.+)$/.exec(text5);
@@ -975,37 +834,340 @@ function translateDynamic(text5) {
975
834
  for (const [source, target] of phrases) output = output.replaceAll(source, target);
976
835
  return output;
977
836
  }
978
- function localizeText(value) {
979
- if (typeof value !== "string") return value;
980
- const exact = translate(value);
981
- if (exact !== value || !isEnglish()) return exact;
982
- return translateDynamic(value);
837
+ function localizeText(value) {
838
+ if (typeof value !== "string") return value;
839
+ const exact = translate(value);
840
+ if (exact !== value || !isEnglish()) return exact;
841
+ return translateDynamic(value);
842
+ }
843
+ var LOCALIZED_PROPS = Object.freeze([
844
+ "aria-label",
845
+ "alt",
846
+ "placeholder",
847
+ "title"
848
+ ]);
849
+ function localizeChild(child) {
850
+ if (typeof child === "string") return localizeText(child);
851
+ if (Array.isArray(child)) return child.map(localizeChild);
852
+ return child;
853
+ }
854
+ function h2(type, props, ...children) {
855
+ let localizedProps = props;
856
+ if (props) {
857
+ for (const key of LOCALIZED_PROPS) {
858
+ if (typeof props[key] === "string") {
859
+ localizedProps = localizedProps === props ? { ...props } : localizedProps;
860
+ localizedProps[key] = localizeText(props[key]);
861
+ }
862
+ }
863
+ }
864
+ return React2.createElement(type, localizedProps, ...children.map(localizeChild));
865
+ }
866
+
867
+ // plugin-src/client/agent-preset.js
868
+ var SET_AGENT_PRESET_ENDPOINT = "bot.preset.set";
869
+ var PRESET_ID = /^[a-z0-9][a-z0-9-]*$/;
870
+ var EMPTY_AGENT_PRESET_CATALOG = Object.freeze({
871
+ defaultId: "",
872
+ items: Object.freeze([])
873
+ });
874
+ var AgentPresetCatalogContext = React3.createContext(EMPTY_AGENT_PRESET_CATALOG);
875
+ function normalizeAgentPresetId(value) {
876
+ if (typeof value !== "string") return "";
877
+ const id5 = value.trim();
878
+ return PRESET_ID.test(id5) ? id5 : "";
879
+ }
880
+ function normalizeAgentPresetCatalog(value) {
881
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
882
+ return { defaultId: "", items: [] };
883
+ }
884
+ const items = [];
885
+ const seen = /* @__PURE__ */ new Set();
886
+ for (const entry of Array.isArray(value.items) ? value.items : []) {
887
+ const id5 = typeof entry === "string" ? normalizeAgentPresetId(entry) : normalizeAgentPresetId(entry?.id);
888
+ if (!id5 || seen.has(id5)) continue;
889
+ seen.add(id5);
890
+ 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;
891
+ items.push({ id: id5, label });
892
+ }
893
+ return {
894
+ defaultId: normalizeAgentPresetId(value.defaultId),
895
+ items
896
+ };
897
+ }
898
+ function AgentPresetEditor({ agentPreset = "", disabled = false, onSave }) {
899
+ const catalog = React3.useContext(AgentPresetCatalogContext) ?? EMPTY_AGENT_PRESET_CATALOG;
900
+ const current = normalizeAgentPresetId(agentPreset);
901
+ const [saving, setSaving] = React3.useState(false);
902
+ const [error, setError] = React3.useState(null);
903
+ const items = [];
904
+ const seen = /* @__PURE__ */ new Set();
905
+ for (const item of Array.isArray(catalog.items) ? catalog.items : []) {
906
+ if (!item?.id || seen.has(item.id)) continue;
907
+ seen.add(item.id);
908
+ items.push(item);
909
+ }
910
+ const currentUnavailable = Boolean(current && !seen.has(current));
911
+ if (currentUnavailable) items.push({ id: current, label: current, unavailable: true });
912
+ const inheritLabel = "\u8DDF\u968F Host \u9ED8\u8BA4";
913
+ const change = async (event) => {
914
+ const next = event.target.value;
915
+ if (next === current || saving || disabled) return;
916
+ setSaving(true);
917
+ setError(null);
918
+ try {
919
+ await onSave?.(next || null);
920
+ } catch (cause) {
921
+ setError(cause?.message ?? "Agent Preset \u4FEE\u6539\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002");
922
+ } finally {
923
+ setSaving(false);
924
+ }
925
+ };
926
+ return h2(
927
+ "div",
928
+ { className: "dim-preset" },
929
+ h2(
930
+ "div",
931
+ { className: "dim-presetHeader" },
932
+ h2("span", null, "Agent Preset"),
933
+ saving ? h2("span", { className: "dim-presetStatus" }, "\u4FDD\u5B58\u4E2D\u2026") : null
934
+ ),
935
+ React3.createElement(
936
+ "select",
937
+ {
938
+ className: "dim-presetSelect",
939
+ value: current,
940
+ disabled: disabled || saving,
941
+ "aria-label": "Agent Preset",
942
+ onChange: (event) => {
943
+ void change(event);
944
+ }
945
+ },
946
+ h2("option", { value: "" }, inheritLabel),
947
+ ...items.map((item) => h2(
948
+ "option",
949
+ { key: item.id, value: item.id },
950
+ item.unavailable ? [item.id, "\uFF08\u5DF2\u4E0D\u53EF\u7528\uFF09"] : item.label && item.label !== item.id ? `${item.label}\uFF08${item.id}\uFF09` : item.id
951
+ ))
952
+ ),
953
+ h2(
954
+ "small",
955
+ { className: "dim-presetHelp" },
956
+ "\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"
957
+ ),
958
+ error || currentUnavailable ? h2(
959
+ "p",
960
+ { className: "dim-presetError", role: error ? "alert" : "status" },
961
+ error ?? "\u5F53\u524D Agent Preset \u5DF2\u4E0D\u53EF\u7528\uFF0C\u8BF7\u9009\u62E9\u5176\u4ED6 Preset \u6216\u8DDF\u968F Host \u9ED8\u8BA4\u3002"
962
+ ) : null
963
+ );
964
+ }
965
+
966
+ // plugin-src/client/channels/dingtalk/api.js
967
+ var DINGTALK_RPC_CHANNEL = "/dingtalk";
968
+ var DINGTALK_ENDPOINTS = Object.freeze({
969
+ status: "connection.status",
970
+ beginProvisioning: "provision.begin",
971
+ pollProvisioning: "provision.poll",
972
+ cancelProvisioning: "provision.cancel",
973
+ bindCredentials: "bot.bind-credentials",
974
+ reconnectBot: "bot.reconnect",
975
+ deleteBot: "bot.delete",
976
+ setWorkspace: "bot.workspace.set",
977
+ setAgentPreset: SET_AGENT_PRESET_ENDPOINT
978
+ });
979
+ var ACCOUNT_STATES = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]);
980
+ var SNAPSHOT_STATES = /* @__PURE__ */ new Set(["disconnected", "offline", "provisioning", "connected", "degraded"]);
981
+ var PROVISION_STATES = /* @__PURE__ */ new Set([
982
+ "starting",
983
+ "pending",
984
+ "scanned",
985
+ "authorizing",
986
+ "creating",
987
+ "connecting",
988
+ "connected",
989
+ "expired",
990
+ "failed",
991
+ "cancelled"
992
+ ]);
993
+ var HEALTH_STATES = /* @__PURE__ */ new Set(["healthy", "checking", "degraded", "offline"]);
994
+ var FORBIDDEN_ERROR_FIELDS = /(client[_-]?secret|secret[_-]?ref|device[_-]?code|app[_-]?secret|access[_-]?token|token)/i;
995
+ var QR_DATA_URL = /^data:image\/(?:png|webp);base64,[a-z\d+/]+={0,2}$/i;
996
+ var MAX_QR_SOURCE_LENGTH = 2 * 1024 * 1024;
997
+ function isRecord(value) {
998
+ return value !== null && typeof value === "object" && !Array.isArray(value);
999
+ }
1000
+ function optionalString(value, maxLength = 240) {
1001
+ if (typeof value !== "string") return void 0;
1002
+ const trimmed = value.trim();
1003
+ return trimmed ? trimmed.slice(0, maxLength) : void 0;
1004
+ }
1005
+ function opaqueId(value) {
1006
+ const id5 = optionalString(value, 128);
1007
+ return id5 && /^[a-z\d_-]+$/i.test(id5) ? id5 : void 0;
1008
+ }
1009
+ function timestamp(value) {
1010
+ if (typeof value === "number" && Number.isFinite(value)) return value;
1011
+ if (typeof value === "string" && value.trim()) {
1012
+ const parsed = Date.parse(value);
1013
+ return Number.isNaN(parsed) ? void 0 : parsed;
1014
+ }
1015
+ return void 0;
1016
+ }
1017
+ function nonNegativeInteger(value) {
1018
+ const number = Number(value);
1019
+ return Number.isSafeInteger(number) && number >= 0 ? number : 0;
1020
+ }
1021
+ function clamp(value, min, max, fallback) {
1022
+ const number = Number(value);
1023
+ return Number.isFinite(number) ? Math.min(max, Math.max(min, number)) : fallback;
1024
+ }
1025
+ function safeErrorCode(value, fallback) {
1026
+ const code = optionalString(value, 80);
1027
+ return code && /^[a-z][a-z\d_.:-]*$/i.test(code) && !FORBIDDEN_ERROR_FIELDS.test(code) ? code : fallback;
1028
+ }
1029
+ function sanitizeMessage(value, fallback) {
1030
+ const message = optionalString(value, 480) ?? fallback;
1031
+ if (FORBIDDEN_ERROR_FIELDS.test(message)) return fallback;
1032
+ return message.replace(/([=:]\s*)[^\s,;,。]+/g, "$1\u2022\u2022\u2022\u2022\u2022\u2022").slice(0, 240);
1033
+ }
1034
+ function normalizeError(value, fallbackCode, fallbackMessage) {
1035
+ if (!isRecord(value)) return void 0;
1036
+ return {
1037
+ code: safeErrorCode(value.code, fallbackCode),
1038
+ message: sanitizeMessage(value.message, fallbackMessage)
1039
+ };
1040
+ }
1041
+ function normalizeTestMessage(value) {
1042
+ if (!isRecord(value)) return null;
1043
+ if (value.sent === true) return { sent: true };
1044
+ if (value.sent !== false) return null;
1045
+ const code = value.code === "test-target-unavailable" ? "test-target-unavailable" : "test-message-failed";
1046
+ return { sent: false, code };
1047
+ }
1048
+ function unwrapRpcResult(result) {
1049
+ if (!isRecord(result) || typeof result.ok !== "boolean") {
1050
+ throw new Error("\u9489\u9489\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94");
1051
+ }
1052
+ if (!result.ok) {
1053
+ const error = new Error(sanitizeMessage(result.error?.message, "\u9489\u9489\u64CD\u4F5C\u5931\u8D25"));
1054
+ error.code = safeErrorCode(result.error?.code, "DINGTALK_RPC_ERROR");
1055
+ throw error;
1056
+ }
1057
+ return result.value;
983
1058
  }
984
- var LOCALIZED_PROPS = Object.freeze([
985
- "aria-label",
986
- "alt",
987
- "placeholder",
988
- "title"
989
- ]);
990
- function localizeChild(child) {
991
- if (typeof child === "string") return localizeText(child);
992
- if (Array.isArray(child)) return child.map(localizeChild);
993
- return child;
1059
+ function safeQrSource(value) {
1060
+ if (typeof value !== "string" || value.length > MAX_QR_SOURCE_LENGTH) return void 0;
1061
+ return QR_DATA_URL.test(value) ? value : void 0;
994
1062
  }
995
- function h2(type, props, ...children) {
996
- let localizedProps = props;
997
- if (props) {
998
- for (const key of LOCALIZED_PROPS) {
999
- if (typeof props[key] === "string") {
1000
- localizedProps = localizedProps === props ? { ...props } : localizedProps;
1001
- localizedProps[key] = localizeText(props[key]);
1002
- }
1003
- }
1063
+ function normalizeProvisioning(value, now = Date.now()) {
1064
+ const source = isRecord(value?.provisioning) ? value.provisioning : value;
1065
+ if (!isRecord(source)) throw new Error("\u9489\u9489\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u626B\u7801\u7ED1\u5B9A\u8FDB\u5EA6");
1066
+ const attemptId = opaqueId(source.attemptId);
1067
+ if (!attemptId) throw new Error("\u9489\u9489\u626B\u7801\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u7ED1\u5B9A\u4EFB\u52A1");
1068
+ const reportedStatus = optionalString(source.status, 32) ?? optionalString(source.state, 32);
1069
+ const status = PROVISION_STATES.has(reportedStatus) ? reportedStatus : "failed";
1070
+ const expiresAt = timestamp(source.expiresAt) ?? now + clamp(source.expiresIn, 1, 2 * 60 * 60, 10 * 60) * 1e3;
1071
+ const result = {
1072
+ attemptId,
1073
+ status,
1074
+ expiresAt,
1075
+ pollIntervalMs: clamp(source.pollIntervalMs, 1e3, 1e4, 3e3)
1076
+ };
1077
+ const qrCodeDataUrl = safeQrSource(source.qrCodeDataUrl);
1078
+ if (qrCodeDataUrl) result.qrCodeDataUrl = qrCodeDataUrl;
1079
+ if (opaqueId(source.botId)) result.botId = opaqueId(source.botId);
1080
+ if (source.alreadyConnected === true) result.alreadyConnected = true;
1081
+ const error = normalizeError(
1082
+ source.error,
1083
+ "DINGTALK_PROVISION_FAILED",
1084
+ "\u9489\u9489\u673A\u5668\u4EBA\u6CA1\u6709\u63A5\u5165\u5B8C\u6210"
1085
+ );
1086
+ if (error) result.error = error;
1087
+ return result;
1088
+ }
1089
+ function normalizeBot(value) {
1090
+ if (!isRecord(value)) return void 0;
1091
+ const botId = opaqueId(value.botId);
1092
+ if (!botId) return void 0;
1093
+ const bot = isRecord(value.bot) ? value.bot : {};
1094
+ const connected = value.connected === true;
1095
+ const reportedState = ACCOUNT_STATES.has(value.state) ? value.state : "offline";
1096
+ const state = connected ? "connected" : reportedState === "connected" ? "connecting" : reportedState;
1097
+ const health = isRecord(value.health) ? value.health : {};
1098
+ const stats = isRecord(value.stats) ? value.stats : {};
1099
+ return {
1100
+ botId,
1101
+ state,
1102
+ connected,
1103
+ configured: value.configured !== false,
1104
+ workspace: optionalString(value.workspace, 4096) ?? "",
1105
+ agentPreset: normalizeAgentPresetId(value.agentPreset),
1106
+ bot: {
1107
+ name: optionalString(bot.name, 100) ?? "\u9489\u9489\u673A\u5668\u4EBA",
1108
+ clientIdMasked: optionalString(bot.clientIdMasked, 140) ?? "\u5DF2\u5B89\u5168\u4FDD\u5B58"
1109
+ },
1110
+ health: {
1111
+ status: HEALTH_STATES.has(health.status) ? health.status : connected ? "healthy" : "offline",
1112
+ summary: optionalString(health.summary, 200) ?? (connected ? "\u9489\u9489 Stream \u957F\u8FDE\u63A5\u8FD0\u884C\u6B63\u5E38" : "\u9489\u9489\u8FDE\u63A5\u5C1A\u672A\u5C31\u7EEA"),
1113
+ lastCheckedAt: timestamp(health.lastCheckedAt),
1114
+ lastConnectedAt: timestamp(health.lastConnectedAt)
1115
+ },
1116
+ stats: {
1117
+ messagesReceived: nonNegativeInteger(stats.messagesReceived),
1118
+ messagesReplied: nonNegativeInteger(stats.messagesReplied)
1119
+ },
1120
+ error: normalizeError(value.error, "DINGTALK_ACCOUNT_ERROR", "\u9489\u9489\u8FDE\u63A5\u5C1A\u672A\u5C31\u7EEA") ?? null
1121
+ };
1122
+ }
1123
+ function normalizeSnapshot(value) {
1124
+ const source = isRecord(value?.snapshot) ? value.snapshot : value;
1125
+ if (!isRecord(source) || !Array.isArray(source.bots)) {
1126
+ throw new Error("\u9489\u9489\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u673A\u5668\u4EBA\u5217\u8868");
1004
1127
  }
1005
- return React2.createElement(type, localizedProps, ...children.map(localizeChild));
1128
+ const seen = /* @__PURE__ */ new Set();
1129
+ const bots = source.bots.map(normalizeBot).filter((bot) => {
1130
+ if (!bot || seen.has(bot.botId)) return false;
1131
+ seen.add(bot.botId);
1132
+ return true;
1133
+ });
1134
+ return {
1135
+ schemaVersion: Number.isSafeInteger(source.schemaVersion) ? source.schemaVersion : 1,
1136
+ revision: nonNegativeInteger(source.revision),
1137
+ state: SNAPSHOT_STATES.has(source.state) ? source.state : "offline",
1138
+ bots,
1139
+ totals: {
1140
+ configured: bots.length,
1141
+ connected: bots.filter((bot) => bot.connected).length
1142
+ },
1143
+ provisioning: source.provisioning ? normalizeProvisioning(source.provisioning) : null,
1144
+ testMessage: normalizeTestMessage(source.testMessage),
1145
+ agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog)
1146
+ };
1147
+ }
1148
+ function connectionTestFeedback(result) {
1149
+ if (result?.sent === true) return "\u9489\u9489\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\u3002";
1150
+ if (result?.code === "test-target-unavailable") {
1151
+ 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";
1152
+ }
1153
+ return result ? "\u9489\u9489\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u5931\u8D25\u3002" : null;
1154
+ }
1155
+ function presentError(error) {
1156
+ return {
1157
+ code: safeErrorCode(error?.code, "DINGTALK_ERROR"),
1158
+ message: sanitizeMessage(error?.message, "\u9489\u9489\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5")
1159
+ };
1160
+ }
1161
+ function formatRemaining(milliseconds) {
1162
+ const seconds = Math.max(0, Math.ceil(Number(milliseconds) / 1e3) || 0);
1163
+ return `${String(Math.floor(seconds / 60)).padStart(2, "0")}:${String(seconds % 60).padStart(2, "0")}`;
1006
1164
  }
1007
1165
 
1166
+ // plugin-src/client/channels/dingtalk/index.js
1167
+ var React8 = __toESM(require("react"), 1);
1168
+
1008
1169
  // plugin-src/client/credential-binding.js
1170
+ var React4 = __toESM(require("react"), 1);
1009
1171
  function ActionIcon({ children }) {
1010
1172
  return h2("svg", {
1011
1173
  className: "dim-actionIcon",
@@ -1065,9 +1227,9 @@ function CredentialBindingPanel({
1065
1227
  onSubmit,
1066
1228
  onCancel
1067
1229
  }) {
1068
- const [identity, setIdentity] = React3.useState("");
1069
- const [secret, setSecret] = React3.useState("");
1070
- const headingId = React3.useId();
1230
+ const [identity, setIdentity] = React4.useState("");
1231
+ const [secret, setSecret] = React4.useState("");
1232
+ const headingId = React4.useId();
1071
1233
  const hasIdentity = Boolean(identityLabel);
1072
1234
  const submit = (event) => {
1073
1235
  event.preventDefault();
@@ -1146,10 +1308,10 @@ function CredentialBindingPanel({
1146
1308
  }
1147
1309
 
1148
1310
  // plugin-src/client/workspace-editor.js
1149
- var React5 = __toESM(require("react"), 1);
1311
+ var React6 = __toESM(require("react"), 1);
1150
1312
 
1151
1313
  // plugin-src/client/workspace-directory-picker.js
1152
- var React4 = __toESM(require("react"), 1);
1314
+ var React5 = __toESM(require("react"), 1);
1153
1315
  var import_react_dom = require("react-dom");
1154
1316
  function pickerErrorCode(error) {
1155
1317
  return error?.rpcError?.code ?? error?.code;
@@ -1161,7 +1323,7 @@ function pickerErrorMessage(error) {
1161
1323
  return error?.rpcError?.message ?? error?.message ?? "\u65E0\u6CD5\u8BFB\u53D6\u76EE\u5F55\uFF0C\u8BF7\u91CD\u8BD5\u3002";
1162
1324
  }
1163
1325
  function FolderIcon() {
1164
- return React4.createElement(
1326
+ return React5.createElement(
1165
1327
  "svg",
1166
1328
  {
1167
1329
  viewBox: "0 0 24 24",
@@ -1172,11 +1334,11 @@ function FolderIcon() {
1172
1334
  strokeLinejoin: "round",
1173
1335
  "aria-hidden": "true"
1174
1336
  },
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" })
1337
+ 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
1338
  );
1177
1339
  }
1178
1340
  function ChevronIcon() {
1179
- return React4.createElement("svg", {
1341
+ return React5.createElement("svg", {
1180
1342
  viewBox: "0 0 20 20",
1181
1343
  fill: "none",
1182
1344
  stroke: "currentColor",
@@ -1184,7 +1346,7 @@ function ChevronIcon() {
1184
1346
  strokeLinecap: "round",
1185
1347
  strokeLinejoin: "round",
1186
1348
  "aria-hidden": "true"
1187
- }, React4.createElement("path", { d: "m7.5 4.5 5 5.5-5 5.5" }));
1349
+ }, React5.createElement("path", { d: "m7.5 4.5 5 5.5-5 5.5" }));
1188
1350
  }
1189
1351
  function displayCrumbs(listing) {
1190
1352
  const homeIndex = listing.crumbs.findIndex((crumb) => crumb.path === listing.home);
@@ -1200,25 +1362,25 @@ function WorkspaceDirectoryPicker({
1200
1362
  onPicked,
1201
1363
  onCancel
1202
1364
  }) {
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);
1365
+ const [listing, setListing] = React5.useState(null);
1366
+ const [loading, setLoading] = React5.useState(false);
1367
+ const [error, setError] = React5.useState(null);
1368
+ const [showHidden, setShowHidden] = React5.useState(false);
1369
+ const [retryKey, setRetryKey] = React5.useState(0);
1370
+ const requestRef = React5.useRef(0);
1371
+ const controllerRef = React5.useRef(null);
1372
+ const dialogRef = React5.useRef(null);
1373
+ const bodyRef = React5.useRef(null);
1374
+ const titleId = React5.useId();
1375
+ const noticeId = React5.useId();
1376
+ const initialPathRef = React5.useRef(startPath);
1377
+ const onPickedRef = React5.useRef(onPicked);
1378
+ const onCancelRef = React5.useRef(onCancel);
1379
+ const busyRef = React5.useRef(busy);
1218
1380
  onPickedRef.current = onPicked;
1219
1381
  onCancelRef.current = onCancel;
1220
1382
  busyRef.current = busy;
1221
- const loadDirectory = React4.useCallback(async (path, { reportError = true } = {}) => {
1383
+ const loadDirectory = React5.useCallback(async (path, { reportError = true } = {}) => {
1222
1384
  const request = requestRef.current + 1;
1223
1385
  requestRef.current = request;
1224
1386
  controllerRef.current?.abort();
@@ -1241,7 +1403,7 @@ function WorkspaceDirectoryPicker({
1241
1403
  if (request === requestRef.current) setLoading(false);
1242
1404
  }
1243
1405
  }, [picker]);
1244
- React4.useEffect(() => {
1406
+ React5.useEffect(() => {
1245
1407
  if (!open) return void 0;
1246
1408
  let active = true;
1247
1409
  setListing(null);
@@ -1319,10 +1481,10 @@ function WorkspaceDirectoryPicker({
1319
1481
  "nav",
1320
1482
  { className: "dim-directoryCrumbs", "aria-label": "\u5F53\u524D\u76EE\u5F55" },
1321
1483
  crumbs.map((crumb, index) => h2(
1322
- React4.Fragment,
1484
+ React5.Fragment,
1323
1485
  { key: crumb.path },
1324
1486
  index > 0 ? h2("span", { className: "dim-directoryCrumbSeparator", "aria-hidden": "true" }, "\u203A") : null,
1325
- React4.createElement("button", {
1487
+ React5.createElement("button", {
1326
1488
  type: "button",
1327
1489
  title: crumb.path,
1328
1490
  disabled: loading || busy,
@@ -1343,7 +1505,7 @@ function WorkspaceDirectoryPicker({
1343
1505
  ) : listing ? entries.length > 0 ? h2("ul", { className: "dim-directoryList" }, entries.map((entry) => h2(
1344
1506
  "li",
1345
1507
  { key: entry.path },
1346
- React4.createElement(
1508
+ React5.createElement(
1347
1509
  "button",
1348
1510
  {
1349
1511
  type: "button",
@@ -1352,7 +1514,7 @@ function WorkspaceDirectoryPicker({
1352
1514
  onClick: () => void loadDirectory(entry.path)
1353
1515
  },
1354
1516
  h2("span", { className: "dim-directoryFolder" }, h2(FolderIcon)),
1355
- React4.createElement("span", { className: "dim-directoryName" }, entry.name),
1517
+ React5.createElement("span", { className: "dim-directoryName" }, entry.name),
1356
1518
  h2("span", { className: "dim-directoryChevron" }, h2(ChevronIcon))
1357
1519
  )
1358
1520
  ))) : h2(
@@ -1405,21 +1567,21 @@ function WorkspaceDirectoryPicker({
1405
1567
  }
1406
1568
 
1407
1569
  // plugin-src/client/workspace-editor.js
1408
- var WorkspaceDirectoryPickerContext = React5.createContext(null);
1570
+ var WorkspaceDirectoryPickerContext = React6.createContext(null);
1409
1571
  function WorkspaceEditor({ workspace, directoryPicker, disabled = false, onSave }) {
1410
- const sharedDirectoryPicker = React5.useContext(WorkspaceDirectoryPickerContext);
1572
+ const sharedDirectoryPicker = React6.useContext(WorkspaceDirectoryPickerContext);
1411
1573
  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(() => {
1574
+ const [open, setOpen] = React6.useState(false);
1575
+ const [saving, setSaving] = React6.useState(false);
1576
+ const [error, setError] = React6.useState(null);
1577
+ const editButtonRef = React6.useRef(null);
1578
+ const savingRef = React6.useRef(false);
1579
+ const close = React6.useCallback(() => {
1418
1580
  setOpen(false);
1419
1581
  setError(null);
1420
1582
  queueMicrotask(() => editButtonRef.current?.focus?.());
1421
1583
  }, []);
1422
- const pick = React5.useCallback(async (value) => {
1584
+ const pick = React6.useCallback(async (value) => {
1423
1585
  if (!value || savingRef.current || disabled) return;
1424
1586
  if (value === workspace) {
1425
1587
  close();
@@ -1456,7 +1618,7 @@ function WorkspaceEditor({ workspace, directoryPicker, disabled = false, onSave
1456
1618
  disabled: disabled || !activeDirectoryPicker
1457
1619
  }, "\u9009\u62E9\u76EE\u5F55")
1458
1620
  ),
1459
- workspace ? React5.createElement("code", {
1621
+ workspace ? React6.createElement("code", {
1460
1622
  className: "dim-workspacePath",
1461
1623
  title: workspace
1462
1624
  }, workspace) : h2("code", { className: "dim-workspacePath" }, "\u672A\u8BBE\u7F6E"),
@@ -1473,10 +1635,10 @@ function WorkspaceEditor({ workspace, directoryPicker, disabled = false, onSave
1473
1635
  }
1474
1636
 
1475
1637
  // plugin-src/client/workspace-snapshot-fence.js
1476
- var React6 = __toESM(require("react"), 1);
1638
+ var React7 = __toESM(require("react"), 1);
1477
1639
  function useWorkspaceSnapshotFence() {
1478
- const state = React6.useRef({ version: 0, pendingMutations: 0 });
1479
- return React6.useMemo(() => Object.freeze({
1640
+ const state = React7.useRef({ version: 0, pendingMutations: 0 });
1641
+ return React7.useMemo(() => Object.freeze({
1480
1642
  beginStatus() {
1481
1643
  return state.current.pendingMutations === 0 ? state.current.version : null;
1482
1644
  },
@@ -1656,7 +1818,7 @@ function DingtalkIcon({ size = 28 }) {
1656
1818
  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
1819
  }));
1658
1820
  }
1659
- var Button = React7.forwardRef(function Button2({ children, kind = "secondary", className = "", ...props }, ref) {
1821
+ var Button = React8.forwardRef(function Button2({ children, kind = "secondary", className = "", ...props }, ref) {
1660
1822
  return h2("button", {
1661
1823
  ...props,
1662
1824
  ref,
@@ -1752,13 +1914,13 @@ function EmptyView({ busy, onStart }) {
1752
1914
  );
1753
1915
  }
1754
1916
  function QrPanel({ provision, now, busy, onRefresh, onCancel }) {
1755
- const [imageFailed, setImageFailed] = React7.useState(false);
1917
+ const [imageFailed, setImageFailed] = React8.useState(false);
1756
1918
  const source = safeQrSource(provision.qrCodeDataUrl);
1757
1919
  const remaining = Math.max(0, provision.expiresAt - now);
1758
1920
  const expired = remaining === 0 || provision.status === "expired";
1759
1921
  const duration = Math.max(1, provision.durationMs ?? 10 * 6e4);
1760
1922
  const progress = Math.round(Math.min(1, remaining / duration) * 100);
1761
- React7.useEffect(() => setImageFailed(false), [source]);
1923
+ React8.useEffect(() => setImageFailed(false), [source]);
1762
1924
  return h2(
1763
1925
  "div",
1764
1926
  { className: "ddt-card dim-surfaceCard" },
@@ -1875,8 +2037,8 @@ function checkedTime(value) {
1875
2037
  }
1876
2038
  }
1877
2039
  function RemoveConfirmation({ account, busy, onConfirm, onCancel }) {
1878
- const cancelRef = React7.useRef(null);
1879
- React7.useEffect(() => cancelRef.current?.focus(), []);
2040
+ const cancelRef = React8.useRef(null);
2041
+ React8.useEffect(() => cancelRef.current?.focus(), []);
1880
2042
  return h2(
1881
2043
  "div",
1882
2044
  {
@@ -1908,6 +2070,7 @@ function AccountCard({
1908
2070
  removing,
1909
2071
  onReconnect,
1910
2072
  onWorkspaceSave,
2073
+ onAgentPresetSave,
1911
2074
  onRequestRemove,
1912
2075
  onConfirmRemove,
1913
2076
  onCancelRemove
@@ -1964,6 +2127,11 @@ function AccountCard({
1964
2127
  disabled: Boolean(busy),
1965
2128
  onSave: onWorkspaceSave
1966
2129
  }),
2130
+ h2(AgentPresetEditor, {
2131
+ agentPreset: account.agentPreset,
2132
+ disabled: Boolean(busy),
2133
+ onSave: onAgentPresetSave
2134
+ }),
1967
2135
  h2(
1968
2136
  "div",
1969
2137
  { className: "ddt-accountFooter dim-cardFooter" },
@@ -2015,6 +2183,7 @@ function AccountList(props) {
2015
2183
  removing: props.removeTarget === account.botId,
2016
2184
  onReconnect: () => props.onReconnect(account),
2017
2185
  onWorkspaceSave: (workspace) => props.onWorkspaceSave(account, workspace),
2186
+ onAgentPresetSave: (agentPreset) => props.onAgentPresetSave(account, agentPreset),
2018
2187
  onRequestRemove: () => props.onRequestRemove(account),
2019
2188
  onConfirmRemove: () => props.onConfirmRemove(account),
2020
2189
  onCancelRemove: props.onCancelRemove
@@ -2024,29 +2193,30 @@ function AccountList(props) {
2024
2193
  }
2025
2194
  var EMPTY_TOTALS = Object.freeze({ configured: 0, connected: 0 });
2026
2195
  function DingtalkSettingsTab({ rpcCall }) {
2027
- const [model, setModel] = React7.useState({
2196
+ const [model, setModel] = React8.useState({
2028
2197
  phase: "loading",
2029
2198
  bots: [],
2030
2199
  totals: EMPTY_TOTALS,
2031
2200
  revision: 0,
2032
- error: null
2201
+ error: null,
2202
+ agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
2033
2203
  });
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);
2204
+ const [provision, setProvision] = React8.useState(null);
2205
+ const [busy, setBusy] = React8.useState(false);
2206
+ const [busyByBot, setBusyByBot] = React8.useState({});
2207
+ const [feedbackByBot, setFeedbackByBot] = React8.useState({});
2208
+ const [removeTarget, setRemoveTarget] = React8.useState(null);
2209
+ const [credentialOpen, setCredentialOpen] = React8.useState(false);
2210
+ const [credentialError, setCredentialError] = React8.useState(null);
2211
+ const [notice, setNotice] = React8.useState("");
2212
+ const [now, setNow] = React8.useState(() => Date.now());
2213
+ const addButtonRef = React8.useRef(null);
2214
+ const mountedRef = React8.useRef(true);
2215
+ const statusRequestRef = React8.useRef(0);
2046
2216
  const workspaceFence = useWorkspaceSnapshotFence();
2047
- const noticeFrameRef = React7.useRef(null);
2048
- const focusFrameRef = React7.useRef(null);
2049
- React7.useEffect(() => {
2217
+ const noticeFrameRef = React8.useRef(null);
2218
+ const focusFrameRef = React8.useRef(null);
2219
+ React8.useEffect(() => {
2050
2220
  mountedRef.current = true;
2051
2221
  return () => {
2052
2222
  mountedRef.current = false;
@@ -2061,8 +2231,8 @@ function DingtalkSettingsTab({ rpcCall }) {
2061
2231
  }
2062
2232
  };
2063
2233
  }, []);
2064
- React7.useEffect(() => installDingtalkStyles(), []);
2065
- const announce = React7.useCallback((message) => {
2234
+ React8.useEffect(() => installDingtalkStyles(), []);
2235
+ const announce = React8.useCallback((message) => {
2066
2236
  if (!mountedRef.current) return;
2067
2237
  if (noticeFrameRef.current !== null) {
2068
2238
  window.cancelAnimationFrame(noticeFrameRef.current);
@@ -2076,7 +2246,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2076
2246
  });
2077
2247
  }
2078
2248
  }, []);
2079
- const discardStaleFeedback = React7.useCallback((snapshot) => {
2249
+ const discardStaleFeedback = React8.useCallback((snapshot) => {
2080
2250
  const botsById = new Map(snapshot.bots.map((bot) => [bot.botId, bot]));
2081
2251
  setFeedbackByBot((current) => {
2082
2252
  let changed = false;
@@ -2091,7 +2261,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2091
2261
  return changed ? next : current;
2092
2262
  });
2093
2263
  }, []);
2094
- const focusAddButton = React7.useCallback(() => {
2264
+ const focusAddButton = React8.useCallback(() => {
2095
2265
  if (!mountedRef.current) return;
2096
2266
  if (focusFrameRef.current !== null) window.cancelAnimationFrame(focusFrameRef.current);
2097
2267
  focusFrameRef.current = window.requestAnimationFrame(() => {
@@ -2099,11 +2269,11 @@ function DingtalkSettingsTab({ rpcCall }) {
2099
2269
  if (mountedRef.current) addButtonRef.current?.focus();
2100
2270
  });
2101
2271
  }, []);
2102
- const invoke = React7.useCallback(async (endpoint, payload = {}, signal) => {
2272
+ const invoke = React8.useCallback(async (endpoint, payload = {}, signal) => {
2103
2273
  if (typeof rpcCall !== "function") throw new TypeError("\u9489\u9489\u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
2104
2274
  return unwrapRpcResult(await rpcCall(endpoint, payload, signal));
2105
2275
  }, [rpcCall]);
2106
- const loadStatus = React7.useCallback(async ({
2276
+ const loadStatus = React8.useCallback(async ({
2107
2277
  signal,
2108
2278
  silent = false,
2109
2279
  restoreProvisioning = false
@@ -2125,7 +2295,8 @@ function DingtalkSettingsTab({ rpcCall }) {
2125
2295
  bots: snapshot.bots,
2126
2296
  totals: snapshot.totals,
2127
2297
  revision: snapshot.revision,
2128
- error: null
2298
+ error: null,
2299
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
2129
2300
  });
2130
2301
  discardStaleFeedback(snapshot);
2131
2302
  if (restoreProvisioning && snapshot.provisioning) {
@@ -2146,12 +2317,12 @@ function DingtalkSettingsTab({ rpcCall }) {
2146
2317
  return void 0;
2147
2318
  }
2148
2319
  }, [discardStaleFeedback, invoke, workspaceFence]);
2149
- React7.useEffect(() => {
2320
+ React8.useEffect(() => {
2150
2321
  const controller = new AbortController();
2151
2322
  void loadStatus({ signal: controller.signal, restoreProvisioning: true });
2152
2323
  return () => controller.abort();
2153
2324
  }, [loadStatus]);
2154
- React7.useEffect(() => {
2325
+ React8.useEffect(() => {
2155
2326
  if (model.phase !== "ready") return void 0;
2156
2327
  const controller = new AbortController();
2157
2328
  let running = false;
@@ -2170,14 +2341,14 @@ function DingtalkSettingsTab({ rpcCall }) {
2170
2341
  window.clearInterval(timer);
2171
2342
  };
2172
2343
  }, [loadStatus, model.phase]);
2173
- React7.useEffect(() => {
2344
+ React8.useEffect(() => {
2174
2345
  if (!provision || !ACTIVE_PROVISION_STATES.has(provision.status)) return void 0;
2175
2346
  const timer = window.setInterval(() => {
2176
2347
  if (mountedRef.current) setNow(Date.now());
2177
2348
  }, 1e3);
2178
2349
  return () => window.clearInterval(timer);
2179
2350
  }, [provision?.attemptId, provision?.status]);
2180
- const startProvisioning = React7.useCallback(async ({ replace = false } = {}) => {
2351
+ const startProvisioning = React8.useCallback(async ({ replace = false } = {}) => {
2181
2352
  if (!mountedRef.current) return;
2182
2353
  setCredentialOpen(false);
2183
2354
  setCredentialError(null);
@@ -2215,7 +2386,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2215
2386
  if (mountedRef.current) setBusy(false);
2216
2387
  }
2217
2388
  }, [announce, invoke, provision?.attemptId]);
2218
- const bindCredentials = React7.useCallback(async ({ identity, secret }) => {
2389
+ const bindCredentials = React8.useCallback(async ({ identity, secret }) => {
2219
2390
  if (!mountedRef.current) return;
2220
2391
  const snapshotVersion = workspaceFence.beginMutation();
2221
2392
  setBusy(true);
@@ -2232,7 +2403,8 @@ function DingtalkSettingsTab({ rpcCall }) {
2232
2403
  bots: snapshot.bots,
2233
2404
  totals: snapshot.totals,
2234
2405
  revision: snapshot.revision,
2235
- error: null
2406
+ error: null,
2407
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
2236
2408
  });
2237
2409
  discardStaleFeedback(snapshot);
2238
2410
  }
@@ -2246,7 +2418,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2246
2418
  if (mountedRef.current) setBusy(false);
2247
2419
  }
2248
2420
  }, [announce, discardStaleFeedback, invoke, loadStatus, workspaceFence]);
2249
- const cancelProvisioning = React7.useCallback(async () => {
2421
+ const cancelProvisioning = React8.useCallback(async () => {
2250
2422
  if (!mountedRef.current) return;
2251
2423
  setBusy(true);
2252
2424
  try {
@@ -2264,7 +2436,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2264
2436
  if (mountedRef.current) setBusy(false);
2265
2437
  }
2266
2438
  }, [announce, focusAddButton, invoke, provision?.attemptId, provision?.status]);
2267
- React7.useEffect(() => {
2439
+ React8.useEffect(() => {
2268
2440
  const attemptId = provision?.attemptId;
2269
2441
  if (!attemptId || !ACTIVE_PROVISION_STATES.has(provision.status)) return void 0;
2270
2442
  const controller = new AbortController();
@@ -2323,7 +2495,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2323
2495
  timer = null;
2324
2496
  };
2325
2497
  }, [announce, invoke, loadStatus, provision?.attemptId, provision?.pollIntervalMs, provision?.status]);
2326
- const setBotBusy = React7.useCallback((botId, operation) => {
2498
+ const setBotBusy = React8.useCallback((botId, operation) => {
2327
2499
  if (!mountedRef.current) return;
2328
2500
  setBusyByBot((current) => {
2329
2501
  const next = { ...current };
@@ -2332,7 +2504,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2332
2504
  return next;
2333
2505
  });
2334
2506
  }, []);
2335
- const runBotAction = React7.useCallback(async ({ account, operation, endpoint, payload, success }) => {
2507
+ const runBotAction = React8.useCallback(async ({ account, operation, endpoint, payload, success }) => {
2336
2508
  if (!mountedRef.current) return void 0;
2337
2509
  const snapshotVersion = workspaceFence.beginMutation();
2338
2510
  setBotBusy(account.botId, operation);
@@ -2352,7 +2524,8 @@ function DingtalkSettingsTab({ rpcCall }) {
2352
2524
  bots: snapshot.bots,
2353
2525
  totals: snapshot.totals,
2354
2526
  revision: snapshot.revision,
2355
- error: null
2527
+ error: null,
2528
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
2356
2529
  });
2357
2530
  discardStaleFeedback(snapshot);
2358
2531
  }
@@ -2385,7 +2558,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2385
2558
  if (mountedRef.current) setBotBusy(account.botId, null);
2386
2559
  }
2387
2560
  }, [announce, discardStaleFeedback, invoke, loadStatus, setBotBusy, workspaceFence]);
2388
- const reconnect = React7.useCallback((account) => runBotAction({
2561
+ const reconnect = React8.useCallback((account) => runBotAction({
2389
2562
  account,
2390
2563
  operation: "reconnect",
2391
2564
  endpoint: DINGTALK_ENDPOINTS.reconnectBot,
@@ -2396,7 +2569,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2396
2569
  return connectionTestFeedback(snapshot.testMessage) ?? "\u9489\u9489\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002";
2397
2570
  }
2398
2571
  }), [runBotAction]);
2399
- const saveWorkspace = React7.useCallback(async (account, workspace) => {
2572
+ const saveWorkspace = React8.useCallback(async (account, workspace) => {
2400
2573
  const workspaceVersion = workspaceFence.beginMutation();
2401
2574
  setBotBusy(account.botId, "workspace");
2402
2575
  try {
@@ -2410,7 +2583,33 @@ function DingtalkSettingsTab({ rpcCall }) {
2410
2583
  bots: snapshot.bots,
2411
2584
  totals: snapshot.totals,
2412
2585
  revision: snapshot.revision,
2413
- error: null
2586
+ error: null,
2587
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
2588
+ });
2589
+ discardStaleFeedback(snapshot);
2590
+ }
2591
+ } finally {
2592
+ const shouldRefresh = workspaceFence.endMutation();
2593
+ if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true });
2594
+ if (mountedRef.current) setBotBusy(account.botId, null);
2595
+ }
2596
+ }, [discardStaleFeedback, invoke, loadStatus, setBotBusy, workspaceFence]);
2597
+ const saveAgentPreset = React8.useCallback(async (account, agentPreset) => {
2598
+ const snapshotVersion = workspaceFence.beginMutation();
2599
+ setBotBusy(account.botId, "preset");
2600
+ try {
2601
+ const snapshot = normalizeSnapshot(await invoke(
2602
+ DINGTALK_ENDPOINTS.setAgentPreset,
2603
+ { botId: account.botId, agentPreset }
2604
+ ));
2605
+ if (mountedRef.current && workspaceFence.canCommitMutation(snapshotVersion)) {
2606
+ setModel({
2607
+ phase: "ready",
2608
+ bots: snapshot.bots,
2609
+ totals: snapshot.totals,
2610
+ revision: snapshot.revision,
2611
+ error: null,
2612
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
2414
2613
  });
2415
2614
  discardStaleFeedback(snapshot);
2416
2615
  }
@@ -2420,7 +2619,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2420
2619
  if (mountedRef.current) setBotBusy(account.botId, null);
2421
2620
  }
2422
2621
  }, [discardStaleFeedback, invoke, loadStatus, setBotBusy, workspaceFence]);
2423
- const remove = React7.useCallback(async (account) => {
2622
+ const remove = React8.useCallback(async (account) => {
2424
2623
  const snapshot = await runBotAction({
2425
2624
  account,
2426
2625
  operation: "delete",
@@ -2474,7 +2673,9 @@ function DingtalkSettingsTab({ rpcCall }) {
2474
2673
  setCredentialError(null);
2475
2674
  }
2476
2675
  }) : null;
2477
- return h2(
2676
+ return h2(AgentPresetCatalogContext.Provider, {
2677
+ value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
2678
+ }, h2(
2478
2679
  "section",
2479
2680
  { className: "ddt-page dim-channelPage", "aria-label": "\u9489\u9489\u8BBE\u7F6E" },
2480
2681
  h2(Heading, {
@@ -2502,7 +2703,7 @@ function DingtalkSettingsTab({ rpcCall }) {
2502
2703
  h2(Button, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
2503
2704
  )
2504
2705
  ) : h2(
2505
- React7.Fragment,
2706
+ React8.Fragment,
2506
2707
  null,
2507
2708
  credentialView,
2508
2709
  provisionView,
@@ -2514,12 +2715,13 @@ function DingtalkSettingsTab({ rpcCall }) {
2514
2715
  removeTarget,
2515
2716
  onReconnect: (account) => void reconnect(account),
2516
2717
  onWorkspaceSave: saveWorkspace,
2718
+ onAgentPresetSave: saveAgentPreset,
2517
2719
  onRequestRemove: (account) => setRemoveTarget(account.botId),
2518
2720
  onConfirmRemove: (account) => void remove(account),
2519
2721
  onCancelRemove: () => setRemoveTarget(null)
2520
2722
  }) : null
2521
2723
  )
2522
- );
2724
+ ));
2523
2725
  }
2524
2726
 
2525
2727
  // plugin-src/client/channels/shared/token-api.js
@@ -2544,7 +2746,8 @@ var TOKEN_BOT_ENDPOINTS = Object.freeze({
2544
2746
  bindCredentials: "bot.bind-credentials",
2545
2747
  reconnectBot: "bot.reconnect",
2546
2748
  deleteBot: "bot.delete",
2547
- setWorkspace: "bot.workspace.set"
2749
+ setWorkspace: "bot.workspace.set",
2750
+ setAgentPreset: SET_AGENT_PRESET_ENDPOINT
2548
2751
  });
2549
2752
  function createTokenChannelApi(channel4, connectionSummary, {
2550
2753
  normalizeBotExtension = () => ({})
@@ -2570,6 +2773,7 @@ function createTokenChannelApi(channel4, connectionSummary, {
2570
2773
  connected,
2571
2774
  state: connected ? "connected" : state,
2572
2775
  workspace: text(value.workspace, "", 4096),
2776
+ agentPreset: normalizeAgentPresetId(value.agentPreset),
2573
2777
  bot: {
2574
2778
  name: text(value.bot?.name, `${channel4}\u673A\u5668\u4EBA`, 100),
2575
2779
  username: text(value.bot?.username, "", 100),
@@ -2598,7 +2802,8 @@ function createTokenChannelApi(channel4, connectionSummary, {
2598
2802
  return {
2599
2803
  revision: Number.isSafeInteger(source.revision) ? source.revision : 0,
2600
2804
  bots,
2601
- totals: { configured: bots.length, connected: bots.filter((bot) => bot.connected).length }
2805
+ totals: { configured: bots.length, connected: bots.filter((bot) => bot.connected).length },
2806
+ agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog)
2602
2807
  };
2603
2808
  };
2604
2809
  const presentError10 = (error) => ({
@@ -2617,8 +2822,8 @@ var normalizeSnapshot2 = api.normalizeSnapshot;
2617
2822
  var presentError2 = api.presentError;
2618
2823
 
2619
2824
  // 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) {
2825
+ var React9 = __toESM(require("react"), 1);
2826
+ var Button3 = React9.forwardRef(function Button4({ children, kind = "secondary", className = "", ...props }, ref) {
2622
2827
  return h2("button", {
2623
2828
  ...props,
2624
2829
  ref,
@@ -2670,7 +2875,7 @@ function createTokenChannelSettings(definition) {
2670
2875
  AccountSettings = null,
2671
2876
  accountSettingsEndpoint = null
2672
2877
  } = definition;
2673
- function AccountCard5({ account, busy, testNotice, removing, onReconnect, onWorkspaceSave, onAccountSettingsSave, onRequestRemove, onConfirmRemove, onCancelRemove }) {
2878
+ function AccountCard5({ account, busy, testNotice, removing, onReconnect, onWorkspaceSave, onAgentPresetSave, onAccountSettingsSave, onRequestRemove, onConfirmRemove, onCancelRemove }) {
2674
2879
  const state = busy === "reconnect" ? "connecting" : account.state;
2675
2880
  const tone = account.connected ? "success" : state === "error" ? "error" : "warning";
2676
2881
  const stateLabel2 = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA";
@@ -2728,6 +2933,11 @@ function createTokenChannelSettings(definition) {
2728
2933
  disabled: Boolean(busy),
2729
2934
  onSave: onWorkspaceSave
2730
2935
  }),
2936
+ h2(AgentPresetEditor, {
2937
+ agentPreset: account.agentPreset,
2938
+ disabled: Boolean(busy),
2939
+ onSave: onAgentPresetSave
2940
+ }),
2731
2941
  AccountSettings ? h2(AccountSettings, {
2732
2942
  account,
2733
2943
  busy: Boolean(busy),
@@ -2774,21 +2984,22 @@ function createTokenChannelSettings(definition) {
2774
2984
  );
2775
2985
  }
2776
2986
  function SettingsTab({ rpcCall }) {
2777
- const [model, setModel] = React8.useState({
2987
+ const [model, setModel] = React9.useState({
2778
2988
  phase: "loading",
2779
2989
  bots: [],
2780
2990
  totals: { configured: 0, connected: 0 },
2781
- error: null
2991
+ error: null,
2992
+ agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
2782
2993
  });
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);
2994
+ const [credentialOpen, setCredentialOpen] = React9.useState(false);
2995
+ const [credentialError, setCredentialError] = React9.useState(null);
2996
+ const [busy, setBusy] = React9.useState(false);
2997
+ const [busyByBot, setBusyByBot] = React9.useState({});
2998
+ const [testNoticeByBot, setTestNoticeByBot] = React9.useState({});
2999
+ const [removeTarget, setRemoveTarget] = React9.useState(null);
3000
+ const mounted = React9.useRef(true);
2790
3001
  const workspaceFence = useWorkspaceSnapshotFence();
2791
- React8.useEffect(() => {
3002
+ React9.useEffect(() => {
2792
3003
  const disposeDingtalk = installDingtalkStyles();
2793
3004
  const disposeChannel = installStyles();
2794
3005
  mounted.current = true;
@@ -2798,18 +3009,24 @@ function createTokenChannelSettings(definition) {
2798
3009
  disposeDingtalk();
2799
3010
  };
2800
3011
  }, []);
2801
- const invoke = React8.useCallback(async (endpoint, payload = {}, signal) => {
3012
+ const invoke = React9.useCallback(async (endpoint, payload = {}, signal) => {
2802
3013
  if (typeof rpcCall !== "function") throw new TypeError(`${channel4} \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5`);
2803
3014
  return api4.unwrapRpcResult(await rpcCall(endpoint, payload, signal));
2804
3015
  }, [rpcCall]);
2805
- const loadStatus = React8.useCallback(async ({ signal, silent = false } = {}) => {
3016
+ const loadStatus = React9.useCallback(async ({ signal, silent = false } = {}) => {
2806
3017
  const workspaceVersion = workspaceFence.beginStatus();
2807
3018
  if (workspaceVersion === null) return;
2808
3019
  if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
2809
3020
  try {
2810
3021
  const snapshot = api4.normalizeSnapshot(await invoke(endpoints.status, {}, signal));
2811
3022
  if (!mounted.current || signal?.aborted || !workspaceFence.canCommitStatus(workspaceVersion)) return;
2812
- setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
3023
+ setModel({
3024
+ phase: "ready",
3025
+ bots: snapshot.bots,
3026
+ totals: snapshot.totals,
3027
+ error: null,
3028
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
3029
+ });
2813
3030
  } catch (error) {
2814
3031
  if (error?.name !== "AbortError" && mounted.current && !signal?.aborted && workspaceFence.canCommitStatus(workspaceVersion)) {
2815
3032
  setModel((current) => ({
@@ -2820,12 +3037,12 @@ function createTokenChannelSettings(definition) {
2820
3037
  }
2821
3038
  }
2822
3039
  }, [invoke, workspaceFence]);
2823
- React8.useEffect(() => {
3040
+ React9.useEffect(() => {
2824
3041
  const controller = new AbortController();
2825
3042
  void loadStatus({ signal: controller.signal });
2826
3043
  return () => controller.abort();
2827
3044
  }, [loadStatus]);
2828
- React8.useEffect(() => {
3045
+ React9.useEffect(() => {
2829
3046
  if (model.phase !== "ready") return void 0;
2830
3047
  const controller = new AbortController();
2831
3048
  const timer = window.setInterval(
@@ -2837,7 +3054,7 @@ function createTokenChannelSettings(definition) {
2837
3054
  window.clearInterval(timer);
2838
3055
  };
2839
3056
  }, [loadStatus, model.phase]);
2840
- const bindCredentials = React8.useCallback(async (values) => {
3057
+ const bindCredentials = React9.useCallback(async (values) => {
2841
3058
  const snapshotVersion = workspaceFence.beginMutation();
2842
3059
  setBusy(true);
2843
3060
  setCredentialError(null);
@@ -2848,7 +3065,13 @@ function createTokenChannelSettings(definition) {
2848
3065
  ));
2849
3066
  if (!mounted.current) return;
2850
3067
  if (workspaceFence.canCommitMutation(snapshotVersion)) {
2851
- setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
3068
+ setModel({
3069
+ phase: "ready",
3070
+ bots: snapshot.bots,
3071
+ totals: snapshot.totals,
3072
+ error: null,
3073
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
3074
+ });
2852
3075
  }
2853
3076
  setCredentialOpen(false);
2854
3077
  } catch (error) {
@@ -2859,14 +3082,20 @@ function createTokenChannelSettings(definition) {
2859
3082
  if (mounted.current) setBusy(false);
2860
3083
  }
2861
3084
  }, [invoke, loadStatus, workspaceFence]);
2862
- const botAction = React8.useCallback(async (account, operation, endpoint, payload) => {
3085
+ const botAction = React9.useCallback(async (account, operation, endpoint, payload) => {
2863
3086
  const snapshotVersion = workspaceFence.beginMutation();
2864
3087
  setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
2865
3088
  try {
2866
3089
  const value = await invoke(endpoint, payload);
2867
3090
  const snapshot = api4.normalizeSnapshot(value);
2868
3091
  if (mounted.current && workspaceFence.canCommitMutation(snapshotVersion)) {
2869
- setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
3092
+ setModel({
3093
+ phase: "ready",
3094
+ bots: snapshot.bots,
3095
+ totals: snapshot.totals,
3096
+ error: null,
3097
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
3098
+ });
2870
3099
  }
2871
3100
  if (mounted.current && operation === "reconnect") {
2872
3101
  setTestNoticeByBot((current) => ({
@@ -2917,6 +3146,12 @@ function createTokenChannelSettings(definition) {
2917
3146
  endpoints.setWorkspace,
2918
3147
  { botId: account.botId, workspace }
2919
3148
  ),
3149
+ onAgentPresetSave: (agentPreset) => botAction(
3150
+ account,
3151
+ "preset",
3152
+ endpoints.setAgentPreset,
3153
+ { botId: account.botId, agentPreset }
3154
+ ),
2920
3155
  onAccountSettingsSave: AccountSettings && accountSettingsEndpoint ? (payload) => botAction(
2921
3156
  account,
2922
3157
  "settings",
@@ -2934,7 +3169,9 @@ function createTokenChannelSettings(definition) {
2934
3169
  }
2935
3170
  }))))
2936
3171
  ) : null;
2937
- return h2(
3172
+ return h2(AgentPresetCatalogContext.Provider, {
3173
+ value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
3174
+ }, h2(
2938
3175
  "section",
2939
3176
  {
2940
3177
  className: `ddt-page ${pageClass} dim-channelPage`,
@@ -2982,7 +3219,7 @@ function createTokenChannelSettings(definition) {
2982
3219
  h2(Button3, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
2983
3220
  )
2984
3221
  ) : h2(
2985
- React8.Fragment,
3222
+ React9.Fragment,
2986
3223
  null,
2987
3224
  credentialOpen ? CredentialPanel ? h2(CredentialPanel, {
2988
3225
  busy,
@@ -3038,7 +3275,7 @@ function createTokenChannelSettings(definition) {
3038
3275
  ) : null,
3039
3276
  botList
3040
3277
  )
3041
- );
3278
+ ));
3042
3279
  }
3043
3280
  return { SettingsTab, AccountCard: AccountCard5 };
3044
3281
  }
@@ -3083,7 +3320,7 @@ var DiscordSettingsTab = channel.SettingsTab;
3083
3320
  var DiscordAccountCard = channel.AccountCard;
3084
3321
 
3085
3322
  // plugin-src/client/channels/feishu/index.js
3086
- var React10 = __toESM(require("react"), 1);
3323
+ var React11 = __toESM(require("react"), 1);
3087
3324
 
3088
3325
  // plugin-src/client/channels/feishu/api.js
3089
3326
  var FEISHU_RPC_CHANNEL = "/feishu";
@@ -3091,6 +3328,7 @@ var FEISHU_ENDPOINTS = Object.freeze({
3091
3328
  status: "connection.status",
3092
3329
  beginProvisioning: "provision.begin",
3093
3330
  beginCallbackRepair: "bot.callback-repair.begin",
3331
+ beginGroupMessagePermission: "bot.group-message-permission.begin",
3094
3332
  pollProvisioning: "provision.poll",
3095
3333
  cancelProvisioning: "provision.cancel",
3096
3334
  bindCredentials: "bot.bind-credentials",
@@ -3098,13 +3336,16 @@ var FEISHU_ENDPOINTS = Object.freeze({
3098
3336
  disconnectBot: "bot.disconnect",
3099
3337
  deleteBot: "bot.delete",
3100
3338
  setWorkspace: "bot.workspace.set",
3339
+ setAgentPreset: "bot.preset.set",
3340
+ setGroupResponseMode: "bot.group-response-mode.set",
3101
3341
  // Kept for rolling upgrades. The multi-bot UI never calls these endpoints.
3102
3342
  testConnection: "connection.test",
3103
3343
  disconnect: "connection.disconnect"
3104
3344
  });
3105
3345
  var FEISHU_REGISTRATION_OPERATIONS = Object.freeze({
3106
3346
  PROVISION: "provision",
3107
- CALLBACK_REPAIR: "callback_repair"
3347
+ CALLBACK_REPAIR: "callback_repair",
3348
+ GROUP_MESSAGE_PERMISSION: "group_message_permission"
3108
3349
  });
3109
3350
  var CONNECTION_STATES = /* @__PURE__ */ new Set([
3110
3351
  "disconnected",
@@ -3137,11 +3378,23 @@ function optionalTimestamp(value) {
3137
3378
  }
3138
3379
  return void 0;
3139
3380
  }
3381
+ function normalizeGroupResponseMode(value) {
3382
+ return value === "all" ? "all" : "mention";
3383
+ }
3140
3384
  function clamp2(value, min, max, fallback) {
3141
3385
  return typeof value === "number" && Number.isFinite(value) ? Math.min(max, Math.max(min, value)) : fallback;
3142
3386
  }
3143
3387
  function normalizeRegistrationOperation(value) {
3144
- return value === FEISHU_REGISTRATION_OPERATIONS.CALLBACK_REPAIR ? FEISHU_REGISTRATION_OPERATIONS.CALLBACK_REPAIR : FEISHU_REGISTRATION_OPERATIONS.PROVISION;
3388
+ if (value === FEISHU_REGISTRATION_OPERATIONS.CALLBACK_REPAIR) {
3389
+ return FEISHU_REGISTRATION_OPERATIONS.CALLBACK_REPAIR;
3390
+ }
3391
+ if (value === FEISHU_REGISTRATION_OPERATIONS.GROUP_MESSAGE_PERMISSION) {
3392
+ return FEISHU_REGISTRATION_OPERATIONS.GROUP_MESSAGE_PERMISSION;
3393
+ }
3394
+ return FEISHU_REGISTRATION_OPERATIONS.PROVISION;
3395
+ }
3396
+ function isTargetedAppUpdate(operation) {
3397
+ return operation === FEISHU_REGISTRATION_OPERATIONS.CALLBACK_REPAIR || operation === FEISHU_REGISTRATION_OPERATIONS.GROUP_MESSAGE_PERMISSION;
3145
3398
  }
3146
3399
  function unwrapRpcResult3(result) {
3147
3400
  if (!isRecord3(result) || typeof result.ok !== "boolean") {
@@ -3169,8 +3422,8 @@ function normalizeProvisioning2(value, now = Date.now()) {
3169
3422
  const expireIn = clamp2(source.expireIn, 1, 60 * 60, 5 * 60);
3170
3423
  const operation = normalizeRegistrationOperation(source.operation);
3171
3424
  const botId = optionalString2(source.botId);
3172
- if (operation === FEISHU_REGISTRATION_OPERATIONS.CALLBACK_REPAIR && !botId) {
3173
- throw new Error("\u98DE\u4E66\u670D\u52A1\u8FD4\u56DE\u7684\u4FEE\u590D\u4FE1\u606F\u7F3A\u5C11 botId");
3425
+ if (isTargetedAppUpdate(operation) && !botId) {
3426
+ throw new Error("\u98DE\u4E66\u670D\u52A1\u8FD4\u56DE\u7684\u5E94\u7528\u66F4\u65B0\u4FE1\u606F\u7F3A\u5C11 botId");
3174
3427
  }
3175
3428
  return {
3176
3429
  attemptId,
@@ -3231,6 +3484,9 @@ function normalizeBotConnection(value, fallbackBotId) {
3231
3484
  connected,
3232
3485
  configured: value.configured !== false,
3233
3486
  workspace: optionalString2(value.workspace)?.slice(0, 4096) ?? "",
3487
+ agentPreset: normalizeAgentPresetId(value.agentPreset),
3488
+ groupResponseMode: normalizeGroupResponseMode(value.groupResponseMode),
3489
+ groupMessagePermissionGranted: value.groupMessagePermissionGranted === true,
3234
3490
  bot: normalizeBot2(value.bot),
3235
3491
  health: normalizeHealth(value.health, connected),
3236
3492
  error: normalizeError2(value.error)
@@ -3271,7 +3527,8 @@ function normalizeBotsSnapshot(value) {
3271
3527
  // make the UI claim that an unavailable bot is online.
3272
3528
  totals: { configured, connected },
3273
3529
  provisioning: value.provisioning ? normalizeProvisioning2(value.provisioning) : void 0,
3274
- error: normalizeError2(value.error)
3530
+ error: normalizeError2(value.error),
3531
+ agentPresetCatalog: normalizeAgentPresetCatalog(value.agentPresetCatalog)
3275
3532
  };
3276
3533
  }
3277
3534
  function normalizeConnectionSnapshot(value) {
@@ -3321,7 +3578,7 @@ function formatRemaining2(milliseconds) {
3321
3578
  }
3322
3579
 
3323
3580
  // plugin-src/client/lifecycle.js
3324
- var React9 = __toESM(require("react"), 1);
3581
+ var React10 = __toESM(require("react"), 1);
3325
3582
  function createPollScheduler({ setTimeoutFn, clearTimeoutFn }) {
3326
3583
  let disposed = false;
3327
3584
  let timer;
@@ -3383,8 +3640,8 @@ function createAnimationFrameScheduler({ requestFrame, cancelFrame }) {
3383
3640
  };
3384
3641
  }
3385
3642
  function useAnimationFrameScheduler() {
3386
- const schedulerRef = React9.useRef(null);
3387
- React9.useEffect(() => {
3643
+ const schedulerRef = React10.useRef(null);
3644
+ React10.useEffect(() => {
3388
3645
  const scheduler = createAnimationFrameScheduler({
3389
3646
  requestFrame: (callback) => window.requestAnimationFrame(callback),
3390
3647
  cancelFrame: (frame) => window.cancelAnimationFrame(frame)
@@ -3395,7 +3652,7 @@ function useAnimationFrameScheduler() {
3395
3652
  if (schedulerRef.current === scheduler) schedulerRef.current = null;
3396
3653
  };
3397
3654
  }, []);
3398
- return React9.useCallback(
3655
+ return React10.useCallback(
3399
3656
  (callback, key) => schedulerRef.current?.schedule(callback, key) ?? false,
3400
3657
  []
3401
3658
  );
@@ -3810,6 +4067,50 @@ var CSS3 = String.raw`
3810
4067
  .bxf-metric dt { color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; line-height: 17px; }
3811
4068
  .bxf-metric dd { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-primary, #1f2329); font-size: 12px; line-height: 18px; font-weight: 550; margin: 3px 0 0; }
3812
4069
 
4070
+ .bxf-responseMode { min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr) max-content; align-items: center; column-gap: 10px; row-gap: 5px; margin-top: 6px; padding: 8px 10px; border: 1px solid var(--dsw-alias-border-l1, #eef0f3); border-radius: 9px; background: var(--dsw-alias-bg-module-platform, #f7f8fa); }
4071
+ .bxf-responseModeHeader { display: contents; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: normal; }
4072
+ .bxf-responseModeHeader > span:first-child { grid-column: 1; grid-row: 1; white-space: nowrap; }
4073
+ .bxf-responseModeStatus { grid-column: 2; grid-row: 1; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 12px; white-space: nowrap; }
4074
+ .bxf-responseModeSelect { min-width: 0; width: 100%; grid-column: 1 / -1; grid-row: 2; height: 32px; padding: 0 30px 0 9px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 7px; color: var(--dsw-alias-label-primary, #1f2329); background-color: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 12px; cursor: pointer; transition: border-color .16s ease, box-shadow .16s ease, background-color .16s ease; }
4075
+ .bxf-responseModeSelect:hover:not(:disabled) { border-color: color-mix(in srgb, var(--bxf-accent) 45%, var(--dsw-alias-border-l2, #dfe1e5)); }
4076
+ .bxf-responseModeSelect:focus-visible { outline: none; border-color: var(--bxf-accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--bxf-accent) 16%, transparent); }
4077
+ .bxf-responseModeSelect:disabled { cursor: not-allowed; opacity: .55; }
4078
+ .bxf-responseModeHelp { grid-column: 1 / -1; grid-row: 3; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; line-height: 1.45; }
4079
+ .bxf-responseModePermissionAction { grid-column: 1 / -1; grid-row: 4; display: flex; justify-content: flex-start; margin-top: 2px; }
4080
+ .bxf-responseModePermissionButton { min-height: 28px; padding: 3px 9px; color: var(--bxf-accent); border-color: color-mix(in srgb, var(--bxf-accent) 30%, var(--dsw-alias-border-l2, #dfe1e5)); background: var(--dsw-alias-bg-layer-1, #fff); }
4081
+ .bxf-responseModePermissionButton:hover:not(:disabled) { background: color-mix(in srgb, var(--bxf-accent) 7%, transparent); }
4082
+ .bxf-responseModeError { grid-column: 1 / -1; grid-row: 5; color: var(--bxf-error); font-size: 12px; line-height: 1.4; margin: 0; }
4083
+
4084
+ .bxf-botProvision {
4085
+ position: relative;
4086
+ margin-top: 14px;
4087
+ scroll-margin-block: 20px;
4088
+ animation: bxf-revealProvision .2s var(--ds-ease-out, ease-out) both;
4089
+ }
4090
+ .bxf-botProvision:focus { outline: none; }
4091
+ .bxf-botProvision:focus-visible {
4092
+ outline: 2px solid color-mix(in srgb, var(--bxf-accent) 75%, transparent);
4093
+ outline-offset: 3px;
4094
+ border-radius: 12px;
4095
+ }
4096
+ .bxf-botProvision > .bxf-provisionCard {
4097
+ border-radius: 11px;
4098
+ box-shadow: none;
4099
+ background: color-mix(in srgb, var(--bxf-accent) 2.5%, var(--dsw-alias-bg-layer-3, #fff));
4100
+ }
4101
+ .bxf-botProvision .bxf-cardBody { padding: 18px; }
4102
+ .bxf-botProvision .bxf-qrLayout {
4103
+ grid-template-columns: 184px minmax(0, 1fr);
4104
+ align-items: start;
4105
+ gap: 24px;
4106
+ }
4107
+ .bxf-botProvision .bxf-qrFrame { width: 176px; height: 176px; padding: 10px; border-radius: 11px; }
4108
+ .bxf-botProvision .bxf-countdown { width: 176px; }
4109
+ .bxf-botProvision .bxf-qrCopy h3 { font-size: 18px; line-height: 26px; }
4110
+ .bxf-botProvision .bxf-steps { gap: 8px; margin-top: 14px; }
4111
+ .bxf-botProvision .bxf-actions { margin-top: 16px; }
4112
+ .bxf-botProvision .bxf-inlineError { min-height: 160px; padding: 22px; }
4113
+
3813
4114
  .bxf-connectedFooter { display: flex; align-items: center; justify-content: space-between; gap: 15px; margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--dsw-alias-border-l1, #eef0f3); }
3814
4115
  .bxf-healthSummary { min-width: 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: 18px; }
3815
4116
  .bxf-healthSummary[data-error="true"] { color: var(--bxf-error); }
@@ -3862,6 +4163,7 @@ var CSS3 = String.raw`
3862
4163
  @keyframes bxf-rotate { to { transform: rotate(360deg); } }
3863
4164
  @keyframes bxf-pulse { 0%, 100% { transform: scale(.9); opacity: .45; } 50% { transform: scale(1.08); opacity: 1; } }
3864
4165
  @keyframes bxf-shimmer { to { background-position: -220% 0; } }
4166
+ @keyframes bxf-revealProvision { from { opacity: 0; transform: translateY(-5px); } }
3865
4167
 
3866
4168
  @container (max-width: 620px) {
3867
4169
  .bxf-headingTools { gap: 6px; }
@@ -3873,6 +4175,7 @@ var CSS3 = String.raw`
3873
4175
  .bxf-intro { grid-template-columns: minmax(0, 1fr); }
3874
4176
  .bxf-markStage { display: none; }
3875
4177
  .bxf-qrLayout { grid-template-columns: minmax(0, 1fr); justify-items: center; }
4178
+ .bxf-botProvision .bxf-qrLayout { grid-template-columns: minmax(0, 1fr); }
3876
4179
  .bxf-qrCopy { width: 100%; }
3877
4180
  .bxf-statusGrid { grid-template-columns: minmax(0, 1fr); }
3878
4181
  .bxf-connectedTop { align-items: flex-start; flex-direction: column; }
@@ -3909,9 +4212,16 @@ function installFeishuStyles() {
3909
4212
 
3910
4213
  // plugin-src/client/channels/feishu/index.js
3911
4214
  var CALLBACK_REPAIR_OPERATION = FEISHU_REGISTRATION_OPERATIONS.CALLBACK_REPAIR;
4215
+ var GROUP_MESSAGE_PERMISSION_OPERATION = FEISHU_REGISTRATION_OPERATIONS.GROUP_MESSAGE_PERMISSION;
3912
4216
  function isCallbackRepair(value) {
3913
4217
  return value?.operation === CALLBACK_REPAIR_OPERATION;
3914
4218
  }
4219
+ function isGroupMessagePermission(value) {
4220
+ return value?.operation === GROUP_MESSAGE_PERMISSION_OPERATION;
4221
+ }
4222
+ function isTargetedAppUpdate2(value) {
4223
+ return isCallbackRepair(value) || isGroupMessagePermission(value);
4224
+ }
3915
4225
  function SvgIcon({ children, size = 18, className, viewBox = "0 0 24 24" }) {
3916
4226
  return h2("svg", {
3917
4227
  width: size,
@@ -3969,7 +4279,7 @@ function QrIcon({ size = 58 }) {
3969
4279
  fill: "currentColor"
3970
4280
  }));
3971
4281
  }
3972
- var Button5 = React10.forwardRef(function Button6({ children, kind = "secondary", size, icon, className = "", ...props }, ref) {
4282
+ var Button5 = React11.forwardRef(function Button6({ children, kind = "secondary", size, icon, className = "", ...props }, ref) {
3973
4283
  return h2("button", {
3974
4284
  ...props,
3975
4285
  ref,
@@ -4086,15 +4396,16 @@ function safeQrSource2(value) {
4086
4396
  return /^data:image\/(?:png|webp|svg\+xml)(?:;charset=[^;,]+)?;base64,/i.test(value) ? value : void 0;
4087
4397
  }
4088
4398
  function QrPane({ provision, now, onRefresh, onCancel, busy }) {
4089
- const [imageFailed, setImageFailed] = React10.useState(false);
4399
+ const [imageFailed, setImageFailed] = React11.useState(false);
4090
4400
  const qrSource = safeQrSource2(provision.qrCodeDataUrl);
4091
4401
  const href = safeVerificationHref(provision.verificationUrl);
4092
4402
  const remaining = Math.max(0, provision.expiresAt - now);
4093
4403
  const expired = provision.expired === true || remaining === 0;
4094
4404
  const progress = Math.min(1, remaining / Math.max(1, provision.durationMs ?? remaining));
4095
4405
  const repairing = isCallbackRepair(provision);
4406
+ const grantingGroupMessages = isGroupMessagePermission(provision);
4096
4407
  const botName = provision.botName ?? "\u6B64\u673A\u5668\u4EBA";
4097
- React10.useEffect(() => setImageFailed(false), [qrSource]);
4408
+ React11.useEffect(() => setImageFailed(false), [qrSource]);
4098
4409
  return h2(
4099
4410
  "div",
4100
4411
  { className: "bxf-card bxf-provisionCard dim-surfaceCard" },
@@ -4109,7 +4420,7 @@ function QrPane({ provision, now, onRefresh, onCancel, busy }) {
4109
4420
  { className: "bxf-qrFrame dim-qrFrame" },
4110
4421
  qrSource && !imageFailed ? h2("img", {
4111
4422
  src: qrSource,
4112
- alt: repairing ? `\u7528\u4E8E\u4FEE\u590D${botName}\u5361\u7247\u6309\u94AE\u7684\u4E00\u6B21\u6027\u6388\u6743\u4E8C\u7EF4\u7801` : "\u7528\u4E8E\u65B0\u589E DeepSeek Harness \u98DE\u4E66\u673A\u5668\u4EBA\u7684\u4E00\u6B21\u6027\u6388\u6743\u4E8C\u7EF4\u7801",
4423
+ alt: repairing ? `\u7528\u4E8E\u4FEE\u590D${botName}\u5361\u7247\u6309\u94AE\u7684\u4E00\u6B21\u6027\u6388\u6743\u4E8C\u7EF4\u7801` : grantingGroupMessages ? `\u7528\u4E8E\u4E3A${botName}\u5F00\u901A\u7FA4\u6D88\u606F\u6743\u9650\u7684\u4E00\u6B21\u6027\u6388\u6743\u4E8C\u7EF4\u7801` : "\u7528\u4E8E\u65B0\u589E DeepSeek Harness \u98DE\u4E66\u673A\u5668\u4EBA\u7684\u4E00\u6B21\u6027\u6388\u6743\u4E8C\u7EF4\u7801",
4113
4424
  onError: () => setImageFailed(true)
4114
4425
  }) : h2(
4115
4426
  "div",
@@ -4148,16 +4459,16 @@ function QrPane({ provision, now, onRefresh, onCancel, busy }) {
4148
4459
  "div",
4149
4460
  { className: "bxf-stateLabel dim-stateLabel" },
4150
4461
  h2("span", { className: "bxf-dot dim-stateDot", "data-tone": "warning" }),
4151
- h2("span", null, repairing ? `\u6B63\u5728\u4FEE\u590D\u300C${botName}\u300D` : "\u6B63\u5728\u6DFB\u52A0\u65B0\u673A\u5668\u4EBA")
4462
+ h2("span", null, repairing ? `\u6B63\u5728\u4FEE\u590D\u300C${botName}\u300D` : grantingGroupMessages ? `\u6B63\u5728\u4E3A\u300C${botName}\u300D\u5F00\u901A\u7FA4\u6D88\u606F\u6743\u9650` : "\u6B63\u5728\u6DFB\u52A0\u65B0\u673A\u5668\u4EBA")
4152
4463
  ),
4153
- h2("h3", null, expired ? "\u5237\u65B0\u4E8C\u7EF4\u7801\u540E\u7EE7\u7EED" : repairing ? "\u4F7F\u7528\u98DE\u4E66\u626B\u7801\u4FEE\u590D\u5361\u7247\u6309\u94AE" : "\u4F7F\u7528\u98DE\u4E66\u626B\u7801\u521B\u5EFA\u673A\u5668\u4EBA"),
4154
- h2("p", null, repairing ? "\u626B\u7801\u4F1A\u66F4\u65B0\u73B0\u6709\u98DE\u4E66\u5E94\u7528\uFF0C\u53EA\u589E\u91CF\u8865\u5145\u5361\u7247\u6309\u94AE\u56DE\u8C03\uFF1B\u4E0D\u4F1A\u521B\u5EFA\u65B0\u5E94\u7528\u3002\u786E\u8BA4\u540E\u6B64\u673A\u5668\u4EBA\u4F1A\u77ED\u6682\u91CD\u8FDE\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u53D7\u5F71\u54CD\u3002" : "\u626B\u7801\u53EA\u4F1A\u65B0\u589E\u4E00\u4E2A\u673A\u5668\u4EBA\uFF0C\u5DF2\u63A5\u5165\u7684\u673A\u5668\u4EBA\u4F1A\u7EE7\u7EED\u6B63\u5E38\u6536\u53D1\u6D88\u606F\u3002"),
4464
+ h2("h3", null, expired ? "\u5237\u65B0\u4E8C\u7EF4\u7801\u540E\u7EE7\u7EED" : repairing ? "\u4F7F\u7528\u98DE\u4E66\u626B\u7801\u4FEE\u590D\u5361\u7247\u6309\u94AE" : grantingGroupMessages ? "\u4F7F\u7528\u98DE\u4E66\u786E\u8BA4\u7FA4\u6D88\u606F\u6743\u9650" : "\u4F7F\u7528\u98DE\u4E66\u626B\u7801\u521B\u5EFA\u673A\u5668\u4EBA"),
4465
+ h2("p", null, repairing ? "\u626B\u7801\u4F1A\u66F4\u65B0\u73B0\u6709\u98DE\u4E66\u5E94\u7528\uFF0C\u53EA\u589E\u91CF\u8865\u5145\u5361\u7247\u6309\u94AE\u56DE\u8C03\uFF1B\u4E0D\u4F1A\u521B\u5EFA\u65B0\u5E94\u7528\u3002\u786E\u8BA4\u540E\u6B64\u673A\u5668\u4EBA\u4F1A\u77ED\u6682\u91CD\u8FDE\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u53D7\u5F71\u54CD\u3002" : grantingGroupMessages ? "\u626B\u7801\u4F1A\u66F4\u65B0\u73B0\u6709\u98DE\u4E66\u5E94\u7528\uFF0C\u53EA\u589E\u91CF\u5F00\u901A\u201C\u83B7\u53D6\u7FA4\u7EC4\u4E2D\u6240\u6709\u6D88\u606F\u201D\u6743\u9650\uFF1B\u4E0D\u4F1A\u521B\u5EFA\u65B0\u5E94\u7528\u3002\u786E\u8BA4\u540E\u4F1A\u81EA\u52A8\u542F\u7528\u201C\u54CD\u5E94\u6240\u6709\u7FA4\u6D88\u606F\u201D\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u53D7\u5F71\u54CD\u3002" : "\u626B\u7801\u53EA\u4F1A\u65B0\u589E\u4E00\u4E2A\u673A\u5668\u4EBA\uFF0C\u5DF2\u63A5\u5165\u7684\u673A\u5668\u4EBA\u4F1A\u7EE7\u7EED\u6B63\u5E38\u6536\u53D1\u6D88\u606F\u3002"),
4155
4466
  h2(
4156
4467
  "ol",
4157
4468
  { className: "bxf-steps dim-steps" },
4158
4469
  h2("li", null, "\u6253\u5F00\u98DE\u4E66\u79FB\u52A8\u7AEF\uFF0C\u4F7F\u7528\u626B\u4E00\u626B\u8BFB\u53D6\u4E8C\u7EF4\u7801"),
4159
- h2("li", null, repairing ? "\u6838\u5BF9\u73B0\u6709\u5E94\u7528\u540D\u79F0\uFF0C\u5E76\u786E\u8BA4\u53EA\u65B0\u589E\u5361\u7247\u56DE\u8C03" : "\u6838\u5BF9\u5E94\u7528\u540D\u79F0\u4E0E\u6743\u9650\u8303\u56F4\uFF0C\u5E76\u786E\u8BA4\u521B\u5EFA"),
4160
- h2("li", null, repairing ? "\u4FDD\u6301\u672C\u9875\u6253\u5F00\uFF0C\u7B49\u5F85\u5361\u7247\u6309\u94AE\u4FEE\u590D\u5B8C\u6210" : "\u4FDD\u6301\u672C\u9875\u6253\u5F00\uFF0C\u7B49\u5F85\u65B0\u673A\u5668\u4EBA\u7684\u957F\u8FDE\u63A5\u5C31\u7EEA")
4470
+ h2("li", null, repairing ? "\u6838\u5BF9\u73B0\u6709\u5E94\u7528\u540D\u79F0\uFF0C\u5E76\u786E\u8BA4\u53EA\u65B0\u589E\u5361\u7247\u56DE\u8C03" : grantingGroupMessages ? "\u6838\u5BF9\u73B0\u6709\u5E94\u7528\uFF0C\u5E76\u786E\u8BA4\u201C\u83B7\u53D6\u7FA4\u7EC4\u4E2D\u6240\u6709\u6D88\u606F\u201D\u6743\u9650" : "\u6838\u5BF9\u5E94\u7528\u540D\u79F0\u4E0E\u6743\u9650\u8303\u56F4\uFF0C\u5E76\u786E\u8BA4\u521B\u5EFA"),
4471
+ h2("li", null, repairing ? "\u4FDD\u6301\u672C\u9875\u6253\u5F00\uFF0C\u7B49\u5F85\u5361\u7247\u6309\u94AE\u4FEE\u590D\u5B8C\u6210" : grantingGroupMessages ? "\u4FDD\u6301\u672C\u9875\u6253\u5F00\uFF0C\u7B49\u5F85\u6743\u9650\u751F\u6548\u5E76\u81EA\u52A8\u5207\u6362\u54CD\u5E94\u65B9\u5F0F" : "\u4FDD\u6301\u672C\u9875\u6253\u5F00\uFF0C\u7B49\u5F85\u65B0\u673A\u5668\u4EBA\u7684\u957F\u8FDE\u63A5\u5C31\u7EEA")
4161
4472
  ),
4162
4473
  h2(
4163
4474
  "div",
@@ -4174,7 +4485,7 @@ function QrPane({ provision, now, onRefresh, onCancel, busy }) {
4174
4485
  rel: "noopener noreferrer"
4175
4486
  }, h2("span", null, "\u5728\u98DE\u4E66\u4E2D\u6253\u5F00")) : null,
4176
4487
  !expired ? h2(Button5, { onClick: onRefresh, disabled: busy }, "\u6362\u4E00\u4E2A\u4E8C\u7EF4\u7801") : null,
4177
- h2(Button5, { onClick: onCancel, disabled: busy }, repairing ? "\u53D6\u6D88\u4FEE\u590D" : "\u53D6\u6D88\u6DFB\u52A0")
4488
+ h2(Button5, { onClick: onCancel, disabled: busy }, repairing ? "\u53D6\u6D88\u4FEE\u590D" : grantingGroupMessages ? "\u53D6\u6D88\u6388\u6743" : "\u53D6\u6D88\u6DFB\u52A0")
4178
4489
  )
4179
4490
  )
4180
4491
  )
@@ -4183,6 +4494,7 @@ function QrPane({ provision, now, onRefresh, onCancel, busy }) {
4183
4494
  function ProvisionProgress({ phase, provision, onCancel, busy }) {
4184
4495
  const connecting = phase === "connecting";
4185
4496
  const repairing = isCallbackRepair(provision);
4497
+ const grantingGroupMessages = isGroupMessagePermission(provision);
4186
4498
  return h2(
4187
4499
  "div",
4188
4500
  {
@@ -4190,17 +4502,18 @@ function ProvisionProgress({ phase, provision, onCancel, busy }) {
4190
4502
  "aria-busy": "true"
4191
4503
  },
4192
4504
  h2("div", { className: "dim-spinner", "aria-hidden": "true" }),
4193
- h2("h3", null, connecting ? repairing ? "\u5DF2\u786E\u8BA4\uFF0C\u6B63\u5728\u5B8C\u6210\u5361\u7247\u6309\u94AE\u4FEE\u590D" : "\u5DF2\u786E\u8BA4\uFF0C\u6B63\u5728\u8FDE\u63A5\u65B0\u673A\u5668\u4EBA" : repairing ? "\u6B63\u5728\u51C6\u5907\u4FEE\u590D\u4E8C\u7EF4\u7801" : "\u6B63\u5728\u51C6\u5907\u6388\u6743\u4E8C\u7EF4\u7801"),
4194
- h2("p", null, connecting ? repairing ? "\u914D\u7F6E\u5DF2\u63D0\u4EA4\uFF0C\u6B63\u5728\u9A8C\u8BC1\u5361\u7247\u6309\u94AE\u56DE\u8C03\u5E76\u91CD\u8FDE\u6B64\u673A\u5668\u4EBA\uFF1B\u6B64\u9636\u6BB5\u65E0\u6CD5\u53D6\u6D88\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u4F1A\u4E2D\u65AD\u3002" : "\u6B63\u5728\u5B89\u5168\u4FDD\u5B58\u51ED\u636E\u5E76\u68C0\u67E5\u65B0\u673A\u5668\u4EBA\u7684\u6D88\u606F\u901A\u9053\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u4F1A\u4E2D\u65AD\u3002" : repairing ? "\u6B63\u5728\u4E3A\u73B0\u6709\u98DE\u4E66\u5E94\u7528\u7533\u8BF7\u4E00\u6B21\u6027\u66F4\u65B0\u4E8C\u7EF4\u7801\uFF0C\u8BF7\u7A0D\u5019\u3002" : "\u6B63\u5728\u5411\u98DE\u4E66\u7533\u8BF7\u4E00\u6B21\u6027\u6388\u6743\u4E8C\u7EF4\u7801\uFF0C\u8BF7\u7A0D\u5019\u3002"),
4505
+ h2("h3", null, connecting ? repairing ? "\u5DF2\u786E\u8BA4\uFF0C\u6B63\u5728\u5B8C\u6210\u5361\u7247\u6309\u94AE\u4FEE\u590D" : grantingGroupMessages ? "\u5DF2\u786E\u8BA4\uFF0C\u6B63\u5728\u542F\u7528\u5168\u90E8\u6D88\u606F\u6A21\u5F0F" : "\u5DF2\u786E\u8BA4\uFF0C\u6B63\u5728\u8FDE\u63A5\u65B0\u673A\u5668\u4EBA" : repairing ? "\u6B63\u5728\u51C6\u5907\u4FEE\u590D\u4E8C\u7EF4\u7801" : grantingGroupMessages ? "\u6B63\u5728\u51C6\u5907\u6743\u9650\u6388\u6743\u4E8C\u7EF4\u7801" : "\u6B63\u5728\u51C6\u5907\u6388\u6743\u4E8C\u7EF4\u7801"),
4506
+ h2("p", null, connecting ? repairing ? "\u914D\u7F6E\u5DF2\u63D0\u4EA4\uFF0C\u6B63\u5728\u9A8C\u8BC1\u5361\u7247\u6309\u94AE\u56DE\u8C03\u5E76\u91CD\u8FDE\u6B64\u673A\u5668\u4EBA\uFF1B\u6B64\u9636\u6BB5\u65E0\u6CD5\u53D6\u6D88\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u4F1A\u4E2D\u65AD\u3002" : grantingGroupMessages ? "\u6743\u9650\u914D\u7F6E\u5DF2\u63D0\u4EA4\uFF0C\u6B63\u5728\u4FDD\u5B58\u8BBE\u7F6E\u5E76\u91CD\u8FDE\u6B64\u673A\u5668\u4EBA\uFF1B\u6B64\u9636\u6BB5\u65E0\u6CD5\u53D6\u6D88\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u4F1A\u4E2D\u65AD\u3002" : "\u6B63\u5728\u5B89\u5168\u4FDD\u5B58\u51ED\u636E\u5E76\u68C0\u67E5\u65B0\u673A\u5668\u4EBA\u7684\u6D88\u606F\u901A\u9053\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u4F1A\u4E2D\u65AD\u3002" : repairing ? "\u6B63\u5728\u4E3A\u73B0\u6709\u98DE\u4E66\u5E94\u7528\u7533\u8BF7\u4E00\u6B21\u6027\u66F4\u65B0\u4E8C\u7EF4\u7801\uFF0C\u8BF7\u7A0D\u5019\u3002" : grantingGroupMessages ? "\u6B63\u5728\u4E3A\u73B0\u6709\u98DE\u4E66\u5E94\u7528\u7533\u8BF7\u7FA4\u6D88\u606F\u6743\u9650\u4E8C\u7EF4\u7801\uFF0C\u8BF7\u7A0D\u5019\u3002" : "\u6B63\u5728\u5411\u98DE\u4E66\u7533\u8BF7\u4E00\u6B21\u6027\u6388\u6743\u4E8C\u7EF4\u7801\uFF0C\u8BF7\u7A0D\u5019\u3002"),
4195
4507
  connecting && onCancel ? h2(
4196
4508
  "div",
4197
4509
  { className: "bxf-actions dim-viewActions", style: { justifyContent: "center" } },
4198
- h2(Button5, { onClick: onCancel, disabled: busy }, repairing ? "\u53D6\u6D88\u4FEE\u590D" : "\u53D6\u6D88\u6DFB\u52A0")
4510
+ h2(Button5, { onClick: onCancel, disabled: busy }, repairing ? "\u53D6\u6D88\u4FEE\u590D" : grantingGroupMessages ? "\u53D6\u6D88\u6388\u6743" : "\u53D6\u6D88\u6DFB\u52A0")
4199
4511
  ) : null
4200
4512
  );
4201
4513
  }
4202
4514
  function ProvisionError2({ error, provision, onRetry, onCancel, busy }) {
4203
4515
  const repairing = isCallbackRepair(provision);
4516
+ const grantingGroupMessages = isGroupMessagePermission(provision);
4204
4517
  return h2(
4205
4518
  "div",
4206
4519
  { className: "bxf-card bxf-provisionCard dim-surfaceCard" },
@@ -4210,7 +4523,7 @@ function ProvisionError2({ error, provision, onRetry, onCancel, busy }) {
4210
4523
  h2(
4211
4524
  "div",
4212
4525
  null,
4213
- h2("h3", null, repairing ? "\u5361\u7247\u6309\u94AE\u6CA1\u6709\u4FEE\u590D\u5B8C\u6210" : "\u65B0\u673A\u5668\u4EBA\u6CA1\u6709\u6DFB\u52A0\u5B8C\u6210"),
4526
+ h2("h3", null, repairing ? "\u5361\u7247\u6309\u94AE\u6CA1\u6709\u4FEE\u590D\u5B8C\u6210" : grantingGroupMessages ? "\u7FA4\u6D88\u606F\u6743\u9650\u6CA1\u6709\u5F00\u901A\u5B8C\u6210" : "\u65B0\u673A\u5668\u4EBA\u6CA1\u6709\u6DFB\u52A0\u5B8C\u6210"),
4214
4527
  h2("p", null, error.message),
4215
4528
  error.code ? h2("span", { className: "bxf-errorCode" }, error.code) : null,
4216
4529
  h2(
@@ -4255,11 +4568,11 @@ function connectionTestNotice2(value) {
4255
4568
  return value?.testMessage ? "\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u5931\u8D25\u3002" : null;
4256
4569
  }
4257
4570
  function RemoveConfirmation2({ bot, busy, onConfirm, onCancel }) {
4258
- const cancelRef = React10.useRef(null);
4571
+ const cancelRef = React11.useRef(null);
4259
4572
  const idPart = bot.botId.replace(/[^a-zA-Z0-9_-]/g, "-");
4260
4573
  const titleId = `bxf-remove-title-${idPart}`;
4261
4574
  const descriptionId = `bxf-remove-description-${idPart}`;
4262
- React10.useEffect(() => cancelRef.current?.focus(), []);
4575
+ React11.useEffect(() => cancelRef.current?.focus(), []);
4263
4576
  return h2(
4264
4577
  "div",
4265
4578
  {
@@ -4292,16 +4605,110 @@ function RemoveConfirmation2({ bot, busy, onConfirm, onCancel }) {
4292
4605
  )
4293
4606
  );
4294
4607
  }
4608
+ function GroupResponseModeEditor({
4609
+ value,
4610
+ permissionGranted = false,
4611
+ disabled = false,
4612
+ authorizationDisabled = false,
4613
+ onSave,
4614
+ onAuthorize
4615
+ }) {
4616
+ const current = normalizeGroupResponseMode(value);
4617
+ const [saving, setSaving] = React11.useState(false);
4618
+ const [authorizing, setAuthorizing] = React11.useState(false);
4619
+ const [error, setError] = React11.useState(null);
4620
+ const change = async (event) => {
4621
+ const next = normalizeGroupResponseMode(event.target.value);
4622
+ if (next === current || saving || disabled) return;
4623
+ setSaving(true);
4624
+ setError(null);
4625
+ try {
4626
+ await onSave?.(next);
4627
+ } catch (cause) {
4628
+ setError(cause?.message ?? "\u7FA4\u804A\u54CD\u5E94\u65B9\u5F0F\u4FEE\u6539\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002");
4629
+ } finally {
4630
+ setSaving(false);
4631
+ }
4632
+ };
4633
+ const authorize = async () => {
4634
+ if (current !== "all" || saving || authorizing || disabled || authorizationDisabled) return;
4635
+ setAuthorizing(true);
4636
+ setError(null);
4637
+ try {
4638
+ await onAuthorize?.();
4639
+ } catch (cause) {
4640
+ setError(cause?.message ?? "\u7FA4\u6D88\u606F\u6743\u9650\u6388\u6743\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002");
4641
+ } finally {
4642
+ setAuthorizing(false);
4643
+ }
4644
+ };
4645
+ return h2(
4646
+ "div",
4647
+ { className: "bxf-responseMode dim-responseMode" },
4648
+ h2(
4649
+ "div",
4650
+ { className: "bxf-responseModeHeader dim-responseModeHeader" },
4651
+ h2("span", null, "\u7FA4\u804A\u54CD\u5E94\u65B9\u5F0F"),
4652
+ saving || authorizing ? h2(
4653
+ "span",
4654
+ { className: "bxf-responseModeStatus dim-responseModeStatus" },
4655
+ saving ? "\u4FDD\u5B58\u4E2D\u2026" : "\u6B63\u5728\u51C6\u5907\u6388\u6743\u2026"
4656
+ ) : null
4657
+ ),
4658
+ h2(
4659
+ "select",
4660
+ {
4661
+ className: "bxf-responseModeSelect dim-responseModeSelect",
4662
+ value: current,
4663
+ disabled: disabled || saving,
4664
+ "aria-label": "\u7FA4\u804A\u54CD\u5E94\u65B9\u5F0F",
4665
+ onChange: (event) => {
4666
+ void change(event);
4667
+ }
4668
+ },
4669
+ h2("option", { value: "mention" }, "\u4EC5\u5728 @\u673A\u5668\u4EBA\u65F6\u54CD\u5E94\uFF08\u63A8\u8350\uFF09"),
4670
+ h2("option", { value: "all" }, "\u54CD\u5E94\u6240\u6709\u7FA4\u6D88\u606F")
4671
+ ),
4672
+ h2(
4673
+ "small",
4674
+ { className: "bxf-responseModeHelp dim-responseModeHelp" },
4675
+ current === "mention" ? permissionGranted ? "\u79C1\u804A\u59CB\u7EC8\u54CD\u5E94\uFF1B\u7FA4\u804A\u4EC5\u5904\u7406\u660E\u786E @\u5F53\u524D\u673A\u5668\u4EBA\u7684\u6D88\u606F\u3002\u7FA4\u6D88\u606F\u6743\u9650\u5DF2\u5F00\u901A\uFF0C\u518D\u6B21\u5207\u6362\u65E0\u9700\u6388\u6743\u3002" : "\u79C1\u804A\u59CB\u7EC8\u54CD\u5E94\uFF1B\u7FA4\u804A\u4EC5\u5904\u7406\u660E\u786E @\u5F53\u524D\u673A\u5668\u4EBA\u7684\u6D88\u606F\u3002\u9009\u62E9\u5168\u90E8\u6D88\u606F\u540E\u4F1A\u6253\u5F00\u98DE\u4E66\u5B98\u65B9\u6388\u6743\u6D41\u7A0B\u3002" : permissionGranted ? "\u5DF2\u5F00\u901A\u201C\u83B7\u53D6\u7FA4\u7EC4\u4E2D\u6240\u6709\u6D88\u606F\u201D\u6743\u9650\uFF08im:message.group_msg\uFF09\uFF1B\u673A\u5668\u4EBA\u4F1A\u5904\u7406\u7FA4\u804A\u4E2D\u7684\u6240\u6709\u53EF\u89C1\u6D88\u606F\u3002" : "\u5C1A\u672A\u786E\u8BA4\u201C\u83B7\u53D6\u7FA4\u7EC4\u4E2D\u6240\u6709\u6D88\u606F\u201D\u6743\u9650\uFF0C\u8BF7\u5B8C\u6210\u98DE\u4E66\u6388\u6743\u3002"
4676
+ ),
4677
+ current === "all" ? h2(
4678
+ "div",
4679
+ { className: "bxf-responseModePermissionAction dim-responseModePermissionAction" },
4680
+ h2(Button5, {
4681
+ className: "bxf-responseModePermissionButton",
4682
+ size: "small",
4683
+ disabled: disabled || authorizationDisabled || saving || authorizing,
4684
+ "aria-busy": authorizing ? "true" : void 0,
4685
+ "aria-label": permissionGranted ? "\u91CD\u65B0\u6388\u6743\u7FA4\u6D88\u606F\u6743\u9650" : "\u6388\u6743\u7FA4\u6D88\u606F\u6743\u9650",
4686
+ onClick: () => {
4687
+ void authorize();
4688
+ }
4689
+ }, authorizing ? "\u6B63\u5728\u51C6\u5907\u2026" : permissionGranted ? "\u91CD\u65B0\u6388\u6743" : "\u53BB\u6388\u6743")
4690
+ ) : null,
4691
+ error ? h2("p", {
4692
+ className: "bxf-responseModeError dim-responseModeError",
4693
+ role: "alert"
4694
+ }, error) : null
4695
+ );
4696
+ }
4295
4697
  function BotCard({
4296
4698
  connection,
4297
4699
  busy,
4298
4700
  repairDisabled,
4701
+ provisionContent,
4702
+ provisionRef,
4299
4703
  actionError,
4300
4704
  testNotice,
4301
4705
  removing,
4302
4706
  onReconnect,
4303
4707
  onRepairCallback,
4304
4708
  onWorkspaceSave,
4709
+ onAgentPresetSave,
4710
+ onGroupResponseModeSave,
4711
+ onGroupMessagePermissionAuthorize,
4305
4712
  onRequestRemove,
4306
4713
  onConfirmRemove,
4307
4714
  onCancelRemove,
@@ -4371,6 +4778,26 @@ function BotCard({
4371
4778
  disabled: Boolean(busy),
4372
4779
  onSave: onWorkspaceSave
4373
4780
  }),
4781
+ h2(AgentPresetEditor, {
4782
+ agentPreset: connection.agentPreset,
4783
+ disabled: Boolean(busy),
4784
+ onSave: onAgentPresetSave
4785
+ }),
4786
+ h2(GroupResponseModeEditor, {
4787
+ value: connection.groupResponseMode,
4788
+ permissionGranted: connection.groupMessagePermissionGranted,
4789
+ disabled: Boolean(busy),
4790
+ authorizationDisabled: repairDisabled,
4791
+ onSave: onGroupResponseModeSave,
4792
+ onAuthorize: onGroupMessagePermissionAuthorize
4793
+ }),
4794
+ provisionContent ? h2("section", {
4795
+ className: "bxf-botProvision dim-botProvision",
4796
+ "aria-label": `${bot.name}\u7684\u98DE\u4E66\u6388\u6743\u6D41\u7A0B`,
4797
+ "data-provision-for": connection.botId,
4798
+ ref: provisionRef,
4799
+ tabIndex: -1
4800
+ }, provisionContent) : null,
4374
4801
  h2(
4375
4802
  "div",
4376
4803
  { className: "bxf-connectedFooter dim-cardFooter" },
@@ -4436,14 +4863,19 @@ function BotList(props) {
4436
4863
  { key: bot.botId },
4437
4864
  h2(BotCard, {
4438
4865
  connection: bot,
4439
- busy: props.busyByBot[bot.botId] ?? (isCallbackRepair(props.provisioning) && props.provisioning.botId === bot.botId ? "callback-repair" : void 0),
4866
+ busy: props.busyByBot[bot.botId] ?? (isTargetedAppUpdate2(props.provisioning) && props.provisioning.botId === bot.botId ? props.provisioning.operation : void 0),
4440
4867
  repairDisabled: Boolean(props.provisioning),
4868
+ provisionContent: isTargetedAppUpdate2(props.provisioning) && props.provisioning.botId === bot.botId ? props.provisionContent : null,
4869
+ provisionRef: props.provisionRef,
4441
4870
  actionError: props.errorsByBot[bot.botId],
4442
4871
  testNotice: props.testNoticesByBot[bot.botId],
4443
4872
  removing: props.removeTargetId === bot.botId,
4444
4873
  onReconnect: () => props.onReconnect(bot),
4445
4874
  onRepairCallback: () => props.onRepairCallback(bot),
4446
4875
  onWorkspaceSave: (workspace) => props.onWorkspaceSave(bot, workspace),
4876
+ onAgentPresetSave: (agentPreset) => props.onAgentPresetSave(bot, agentPreset),
4877
+ onGroupResponseModeSave: (groupResponseMode) => props.onGroupResponseModeSave(bot, groupResponseMode),
4878
+ onGroupMessagePermissionAuthorize: () => props.onGroupMessagePermissionAuthorize(bot),
4447
4879
  onRequestRemove: () => props.onRequestRemove(bot),
4448
4880
  onConfirmRemove: () => props.onConfirmRemove(bot),
4449
4881
  onCancelRemove: props.onCancelRemove,
@@ -4501,53 +4933,56 @@ function mergeFeishuSnapshotState(current, snapshot, { restoreProvisioning = fal
4501
4933
  totals: snapshot.totals,
4502
4934
  provisioning,
4503
4935
  pageError: null,
4504
- statusError: null
4936
+ statusError: null,
4937
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? current.agentPresetCatalog
4505
4938
  };
4506
4939
  }
4507
4940
  function FeishuSettingsTab({ rpcCall }) {
4508
- const [model, setModel] = React10.useState({
4941
+ const [model, setModel] = React11.useState({
4509
4942
  phase: "loading",
4510
4943
  revision: 0,
4511
4944
  bots: [],
4512
4945
  totals: EMPTY_TOTALS2,
4513
4946
  provisioning: null,
4514
4947
  pageError: null,
4515
- statusError: null
4948
+ statusError: null,
4949
+ agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
4516
4950
  });
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);
4951
+ const [pageBusy, setPageBusy] = React11.useState(false);
4952
+ const [provisionBusy, setProvisionBusy] = React11.useState(false);
4953
+ const [credentialOpen, setCredentialOpen] = React11.useState(false);
4954
+ const [credentialBusy, setCredentialBusy] = React11.useState(false);
4955
+ const [credentialError, setCredentialError] = React11.useState(null);
4956
+ const [busyByBot, setBusyByBot] = React11.useState({});
4957
+ const [errorsByBot, setErrorsByBot] = React11.useState({});
4958
+ const [testNoticesByBot, setTestNoticesByBot] = React11.useState({});
4959
+ const [removeTargetId, setRemoveTargetId] = React11.useState(null);
4960
+ const [announcement, setAnnouncement] = React11.useState("");
4961
+ const [now, setNow] = React11.useState(() => Date.now());
4962
+ const [focusBotId, setFocusBotId] = React11.useState(null);
4963
+ const cardRefs = React11.useRef(/* @__PURE__ */ new Map());
4964
+ const removeButtonRefs = React11.useRef(/* @__PURE__ */ new Map());
4965
+ const targetedProvisionRef = React11.useRef(null);
4966
+ const addButtonRef = React11.useRef(null);
4967
+ const mountedRef = React11.useRef(true);
4533
4968
  const workspaceFence = useWorkspaceSnapshotFence();
4534
4969
  const scheduleAnimationFrame = useAnimationFrameScheduler();
4535
- React10.useEffect(() => {
4970
+ React11.useEffect(() => {
4536
4971
  mountedRef.current = true;
4537
4972
  return () => {
4538
4973
  mountedRef.current = false;
4539
4974
  };
4540
4975
  }, []);
4541
- const announce = React10.useCallback((message) => {
4976
+ const announce = React11.useCallback((message) => {
4542
4977
  setAnnouncement("");
4543
4978
  scheduleAnimationFrame(() => {
4544
4979
  if (message) setAnnouncement(message);
4545
4980
  }, "announcement");
4546
4981
  }, [scheduleAnimationFrame]);
4547
- const invoke = React10.useCallback(async (endpoint, payload = {}, signal) => {
4982
+ const invoke = React11.useCallback(async (endpoint, payload = {}, signal) => {
4548
4983
  return unwrapRpcResult3(await rpcCall(endpoint, payload, signal));
4549
4984
  }, [rpcCall]);
4550
- const mergeSnapshot = React10.useCallback((snapshot, { restoreProvisioning = false } = {}) => {
4985
+ const mergeSnapshot = React11.useCallback((snapshot, { restoreProvisioning = false } = {}) => {
4551
4986
  const now2 = Date.now();
4552
4987
  setModel((current) => mergeFeishuSnapshotState(
4553
4988
  current,
@@ -4555,7 +4990,7 @@ function FeishuSettingsTab({ rpcCall }) {
4555
4990
  { restoreProvisioning, now: now2 }
4556
4991
  ));
4557
4992
  }, []);
4558
- const loadStatus = React10.useCallback(async ({ signal, silent = false, restoreProvisioning = false } = {}) => {
4993
+ const loadStatus = React11.useCallback(async ({ signal, silent = false, restoreProvisioning = false } = {}) => {
4559
4994
  const workspaceVersion = workspaceFence.beginStatus();
4560
4995
  if (workspaceVersion === null || !mountedRef.current) return void 0;
4561
4996
  if (!silent) setPageBusy(true);
@@ -4573,12 +5008,12 @@ function FeishuSettingsTab({ rpcCall }) {
4573
5008
  if (!silent && !signal?.aborted && mountedRef.current) setPageBusy(false);
4574
5009
  }
4575
5010
  }, [invoke, mergeSnapshot, workspaceFence]);
4576
- React10.useEffect(() => {
5011
+ React11.useEffect(() => {
4577
5012
  const controller = new AbortController();
4578
5013
  void loadStatus({ signal: controller.signal, restoreProvisioning: true });
4579
5014
  return () => controller.abort();
4580
5015
  }, [loadStatus]);
4581
- React10.useEffect(() => {
5016
+ React11.useEffect(() => {
4582
5017
  if (model.phase !== "ready") return void 0;
4583
5018
  const controller = new AbortController();
4584
5019
  let inFlight = false;
@@ -4597,7 +5032,7 @@ function FeishuSettingsTab({ rpcCall }) {
4597
5032
  window.clearInterval(timer);
4598
5033
  };
4599
5034
  }, [loadStatus, model.phase]);
4600
- React10.useEffect(() => {
5035
+ React11.useEffect(() => {
4601
5036
  if (!focusBotId) return;
4602
5037
  const node = cardRefs.current.get(focusBotId);
4603
5038
  if (!node) return;
@@ -4605,15 +5040,27 @@ function FeishuSettingsTab({ rpcCall }) {
4605
5040
  node.focus({ preventScroll: true });
4606
5041
  setFocusBotId(null);
4607
5042
  }, [focusBotId, model.bots]);
4608
- const startProvisioning = React10.useCallback(async ({
5043
+ const targetedProvisionFocusKey = isTargetedAppUpdate2(model.provisioning) ? `${model.provisioning.botId}:${model.provisioning.attemptId ?? "preparing"}:${model.provisioning.phase}` : null;
5044
+ React11.useEffect(() => {
5045
+ if (!targetedProvisionFocusKey) return;
5046
+ scheduleAnimationFrame(() => {
5047
+ const node = targetedProvisionRef.current;
5048
+ if (!node) return;
5049
+ node.scrollIntoView?.({ block: "nearest", behavior: "smooth" });
5050
+ node.focus?.({ preventScroll: true });
5051
+ }, "targeted-provision-focus");
5052
+ }, [scheduleAnimationFrame, targetedProvisionFocusKey]);
5053
+ const startProvisioning = React11.useCallback(async ({
4609
5054
  replace = false,
4610
5055
  operation = FEISHU_REGISTRATION_OPERATIONS.PROVISION,
4611
5056
  bot
4612
5057
  } = {}) => {
4613
5058
  const repairing = operation === CALLBACK_REPAIR_OPERATION;
4614
- const botId = repairing ? bot?.botId ?? model.provisioning?.botId : void 0;
4615
- const botName = repairing ? bot?.bot?.name ?? model.provisioning?.botName : void 0;
4616
- if (repairing && !botId) return;
5059
+ const grantingGroupMessages = operation === GROUP_MESSAGE_PERMISSION_OPERATION;
5060
+ const targetedUpdate = repairing || grantingGroupMessages;
5061
+ const botId = targetedUpdate ? bot?.botId ?? model.provisioning?.botId : void 0;
5062
+ const botName = targetedUpdate ? bot?.bot?.name ?? model.provisioning?.botName : void 0;
5063
+ if (targetedUpdate && !botId) return;
4617
5064
  setCredentialOpen(false);
4618
5065
  setCredentialError(null);
4619
5066
  setProvisionBusy(true);
@@ -4636,12 +5083,13 @@ function FeishuSettingsTab({ rpcCall }) {
4636
5083
  } catch {
4637
5084
  }
4638
5085
  }
5086
+ const endpoint = repairing ? FEISHU_ENDPOINTS.beginCallbackRepair : grantingGroupMessages ? FEISHU_ENDPOINTS.beginGroupMessagePermission : FEISHU_ENDPOINTS.beginProvisioning;
4639
5087
  const provision2 = normalizeProvisioning2(await invoke(
4640
- repairing ? FEISHU_ENDPOINTS.beginCallbackRepair : FEISHU_ENDPOINTS.beginProvisioning,
4641
- repairing ? { botId } : { locale: "zh-CN" }
5088
+ endpoint,
5089
+ targetedUpdate ? { botId } : { locale: "zh-CN" }
4642
5090
  ));
4643
- if (repairing && (provision2.operation !== CALLBACK_REPAIR_OPERATION || provision2.botId !== botId)) {
4644
- throw new Error("\u98DE\u4E66\u670D\u52A1\u8FD4\u56DE\u4E86\u4E0D\u5339\u914D\u7684\u5361\u7247\u4FEE\u590D\u4E8C\u7EF4\u7801");
5091
+ if (targetedUpdate && (provision2.operation !== operation || provision2.botId !== botId)) {
5092
+ throw new Error(grantingGroupMessages ? "\u98DE\u4E66\u670D\u52A1\u8FD4\u56DE\u4E86\u4E0D\u5339\u914D\u7684\u7FA4\u6D88\u606F\u6743\u9650\u4E8C\u7EF4\u7801" : "\u98DE\u4E66\u670D\u52A1\u8FD4\u56DE\u4E86\u4E0D\u5339\u914D\u7684\u5361\u7247\u4FEE\u590D\u4E8C\u7EF4\u7801");
4645
5093
  }
4646
5094
  const timestamp7 = Date.now();
4647
5095
  setNow(timestamp7);
@@ -4655,7 +5103,7 @@ function FeishuSettingsTab({ rpcCall }) {
4655
5103
  expired: false
4656
5104
  }
4657
5105
  }));
4658
- announce(repairing ? `${botName ?? "\u673A\u5668\u4EBA"}\u7684\u4FEE\u590D\u4E8C\u7EF4\u7801\u5DF2\u751F\u6210\uFF0C\u8BF7\u4F7F\u7528\u98DE\u4E66\u626B\u7801\u3002` : "\u6388\u6743\u4E8C\u7EF4\u7801\u5DF2\u751F\u6210\uFF0C\u8BF7\u4F7F\u7528\u98DE\u4E66\u626B\u7801\u3002");
5106
+ announce(repairing ? `${botName ?? "\u673A\u5668\u4EBA"}\u7684\u4FEE\u590D\u4E8C\u7EF4\u7801\u5DF2\u751F\u6210\uFF0C\u8BF7\u4F7F\u7528\u98DE\u4E66\u626B\u7801\u3002` : grantingGroupMessages ? `${botName ?? "\u673A\u5668\u4EBA"}\u7684\u7FA4\u6D88\u606F\u6743\u9650\u4E8C\u7EF4\u7801\u5DF2\u751F\u6210\uFF0C\u8BF7\u4F7F\u7528\u98DE\u4E66\u786E\u8BA4\u3002` : "\u6388\u6743\u4E8C\u7EF4\u7801\u5DF2\u751F\u6210\uFF0C\u8BF7\u4F7F\u7528\u98DE\u4E66\u626B\u7801\u3002");
4659
5107
  } catch (error) {
4660
5108
  setModel((current) => ({
4661
5109
  ...current,
@@ -4678,7 +5126,7 @@ function FeishuSettingsTab({ rpcCall }) {
4678
5126
  model.provisioning?.botId,
4679
5127
  model.provisioning?.botName
4680
5128
  ]);
4681
- const bindCredentials = React10.useCallback(async ({ identity, secret }) => {
5129
+ const bindCredentials = React11.useCallback(async ({ identity, secret }) => {
4682
5130
  const snapshotVersion = workspaceFence.beginMutation();
4683
5131
  setCredentialBusy(true);
4684
5132
  setCredentialError(null);
@@ -4700,16 +5148,18 @@ function FeishuSettingsTab({ rpcCall }) {
4700
5148
  setCredentialBusy(false);
4701
5149
  }
4702
5150
  }, [announce, invoke, loadStatus, mergeSnapshot, workspaceFence]);
4703
- const cancelProvisioning = React10.useCallback(async () => {
5151
+ const cancelProvisioning = React11.useCallback(async () => {
4704
5152
  const activeProvision = model.provisioning;
4705
5153
  const attemptId = activeProvision?.attemptId;
4706
5154
  const repairing = isCallbackRepair(activeProvision);
4707
- const targetBot = repairing ? model.bots.find((bot) => bot.botId === activeProvision?.botId) : void 0;
5155
+ const grantingGroupMessages = isGroupMessagePermission(activeProvision);
5156
+ const targetedUpdate = isTargetedAppUpdate2(activeProvision);
5157
+ const targetBot = targetedUpdate ? model.bots.find((bot) => bot.botId === activeProvision?.botId) : void 0;
4708
5158
  setProvisionBusy(true);
4709
5159
  try {
4710
5160
  const result = attemptId ? normalizePollResult(await invoke(FEISHU_ENDPOINTS.cancelProvisioning, { attemptId })) : null;
4711
- if (repairing && result) {
4712
- if (result.operation !== CALLBACK_REPAIR_OPERATION || result.botId !== activeProvision.botId) {
5161
+ if (targetedUpdate && result) {
5162
+ if (result.operation !== activeProvision.operation || result.botId !== activeProvision.botId) {
4713
5163
  throw new Error("\u98DE\u4E66\u670D\u52A1\u8FD4\u56DE\u4E86\u4E0D\u5339\u914D\u7684\u6CE8\u518C\u8FDB\u5EA6");
4714
5164
  }
4715
5165
  if (result.status === "connecting") {
@@ -4723,23 +5173,23 @@ function FeishuSettingsTab({ rpcCall }) {
4723
5173
  expired: false
4724
5174
  }
4725
5175
  } : current);
4726
- announce("\u914D\u7F6E\u5DF2\u63D0\u4EA4\uFF0C\u6B63\u5728\u9A8C\u8BC1\u5361\u7247\u6309\u94AE\u56DE\u8C03\u5E76\u91CD\u8FDE\u6B64\u673A\u5668\u4EBA\uFF1B\u6B64\u9636\u6BB5\u65E0\u6CD5\u53D6\u6D88\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u4F1A\u4E2D\u65AD\u3002");
5176
+ announce(grantingGroupMessages ? "\u6743\u9650\u914D\u7F6E\u5DF2\u63D0\u4EA4\uFF0C\u6B63\u5728\u542F\u7528\u5168\u90E8\u6D88\u606F\u6A21\u5F0F\u5E76\u91CD\u8FDE\u6B64\u673A\u5668\u4EBA\uFF1B\u6B64\u9636\u6BB5\u65E0\u6CD5\u53D6\u6D88\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u4F1A\u4E2D\u65AD\u3002" : "\u914D\u7F6E\u5DF2\u63D0\u4EA4\uFF0C\u6B63\u5728\u9A8C\u8BC1\u5361\u7247\u6309\u94AE\u56DE\u8C03\u5E76\u91CD\u8FDE\u6B64\u673A\u5668\u4EBA\uFF1B\u6B64\u9636\u6BB5\u65E0\u6CD5\u53D6\u6D88\uFF0C\u5176\u4ED6\u673A\u5668\u4EBA\u4E0D\u4F1A\u4E2D\u65AD\u3002");
4727
5177
  return;
4728
5178
  }
4729
5179
  if (result.status === "connected") {
4730
5180
  const targetBotName = targetBot?.bot.name ?? activeProvision.botName ?? "\u673A\u5668\u4EBA";
4731
5181
  setModel((current) => ({ ...current, provisioning: null }));
4732
- announce(`${targetBotName}\u7684\u5361\u7247\u6309\u94AE\u5DF2\u4FEE\u590D\u3002`);
5182
+ announce(grantingGroupMessages ? `${targetBotName}\u5DF2\u5F00\u901A\u7FA4\u6D88\u606F\u6743\u9650\uFF0C\u5E76\u542F\u7528\u201C\u54CD\u5E94\u6240\u6709\u7FA4\u6D88\u606F\u201D\u3002` : `${targetBotName}\u7684\u5361\u7247\u6309\u94AE\u5DF2\u4FEE\u590D\u3002`);
4733
5183
  if (activeProvision.botId) setFocusBotId(activeProvision.botId);
4734
5184
  await loadStatus({ silent: true, restoreProvisioning: false });
4735
5185
  return;
4736
5186
  }
4737
5187
  }
4738
5188
  setModel((current) => ({ ...current, provisioning: null }));
4739
- announce(repairing ? "\u5DF2\u53D6\u6D88\u5361\u7247\u6309\u94AE\u4FEE\u590D\u3002" : "\u5DF2\u53D6\u6D88\u6DFB\u52A0\u673A\u5668\u4EBA\u3002");
5189
+ announce(repairing ? "\u5DF2\u53D6\u6D88\u5361\u7247\u6309\u94AE\u4FEE\u590D\u3002" : grantingGroupMessages ? "\u5DF2\u53D6\u6D88\u7FA4\u6D88\u606F\u6743\u9650\u6388\u6743\u3002" : "\u5DF2\u53D6\u6D88\u6DFB\u52A0\u673A\u5668\u4EBA\u3002");
4740
5190
  await loadStatus({ silent: true, restoreProvisioning: false });
4741
5191
  scheduleAnimationFrame(() => {
4742
- if (repairing && activeProvision.botId) {
5192
+ if (targetedUpdate && activeProvision.botId) {
4743
5193
  cardRefs.current.get(activeProvision.botId)?.focus();
4744
5194
  } else {
4745
5195
  addButtonRef.current?.focus();
@@ -4763,7 +5213,7 @@ function FeishuSettingsTab({ rpcCall }) {
4763
5213
  const countdownPhase = model.provisioning?.phase;
4764
5214
  const countdownExpiresAt = model.provisioning?.expiresAt;
4765
5215
  const countdownExpired = model.provisioning?.expired;
4766
- React10.useEffect(() => {
5216
+ React11.useEffect(() => {
4767
5217
  if (!countdownAttemptId || countdownPhase !== "qr" || countdownExpired) return void 0;
4768
5218
  const tick = () => {
4769
5219
  const timestamp7 = Date.now();
@@ -4776,7 +5226,7 @@ function FeishuSettingsTab({ rpcCall }) {
4776
5226
  const timer = window.setInterval(tick, 1e3);
4777
5227
  return () => window.clearInterval(timer);
4778
5228
  }, [countdownAttemptId, countdownPhase, countdownExpiresAt, countdownExpired]);
4779
- React10.useEffect(() => {
5229
+ React11.useEffect(() => {
4780
5230
  const provision2 = model.provisioning;
4781
5231
  if (!provision2 || !["qr", "connecting"].includes(provision2.phase) || !provision2.attemptId || provision2.expired) return void 0;
4782
5232
  const controller = new AbortController();
@@ -4787,26 +5237,26 @@ function FeishuSettingsTab({ rpcCall }) {
4787
5237
  { attemptId: provision2.attemptId },
4788
5238
  controller.signal
4789
5239
  ));
4790
- if (result.operation !== provision2.operation || isCallbackRepair(provision2) && result.botId !== provision2.botId) {
5240
+ if (result.operation !== provision2.operation || isTargetedAppUpdate2(provision2) && result.botId !== provision2.botId) {
4791
5241
  throw new Error("\u98DE\u4E66\u670D\u52A1\u8FD4\u56DE\u4E86\u4E0D\u5339\u914D\u7684\u6CE8\u518C\u8FDB\u5EA6");
4792
5242
  }
4793
5243
  if (result.status === "connected") {
4794
5244
  const snapshot = await loadStatus({ signal: controller.signal, silent: true, restoreProvisioning: false });
4795
5245
  const targetBot = snapshot?.bots.find((bot) => bot.botId === result.botId);
4796
5246
  if (!snapshot) {
4797
- throw new Error(isCallbackRepair(provision2) ? "\u5361\u7247\u6309\u94AE\u5DF2\u66F4\u65B0\uFF0C\u4F46\u6682\u65F6\u65E0\u6CD5\u786E\u8BA4\u673A\u5668\u4EBA\u8FDE\u63A5\u72B6\u6001" : "\u673A\u5668\u4EBA\u5DF2\u7ECF\u521B\u5EFA\uFF0C\u4F46\u6682\u65F6\u65E0\u6CD5\u786E\u8BA4\u8FDE\u63A5\u72B6\u6001");
5247
+ throw new Error(isCallbackRepair(provision2) ? "\u5361\u7247\u6309\u94AE\u5DF2\u66F4\u65B0\uFF0C\u4F46\u6682\u65F6\u65E0\u6CD5\u786E\u8BA4\u673A\u5668\u4EBA\u8FDE\u63A5\u72B6\u6001" : isGroupMessagePermission(provision2) ? "\u7FA4\u6D88\u606F\u6743\u9650\u5DF2\u66F4\u65B0\uFF0C\u4F46\u6682\u65F6\u65E0\u6CD5\u786E\u8BA4\u673A\u5668\u4EBA\u8FDE\u63A5\u72B6\u6001" : "\u673A\u5668\u4EBA\u5DF2\u7ECF\u521B\u5EFA\uFF0C\u4F46\u6682\u65F6\u65E0\u6CD5\u786E\u8BA4\u8FDE\u63A5\u72B6\u6001");
4798
5248
  }
4799
5249
  if (!targetBot?.connected) {
4800
5250
  setModel((current) => current.provisioning?.attemptId === provision2.attemptId ? { ...current, provisioning: { ...current.provisioning, phase: "connecting" } } : current);
4801
5251
  return;
4802
5252
  }
4803
5253
  setModel((current) => ({ ...current, provisioning: null }));
4804
- announce(isCallbackRepair(provision2) ? `${targetBot.bot.name}\u7684\u5361\u7247\u6309\u94AE\u5DF2\u4FEE\u590D\u3002` : targetBot ? `${targetBot.bot.name}\u5DF2\u8FDE\u63A5\uFF0C\u53EF\u4EE5\u5728\u98DE\u4E66\u4E2D\u5F00\u59CB\u804A\u5929\u3002` : "\u65B0\u98DE\u4E66\u673A\u5668\u4EBA\u5DF2\u8FDE\u63A5\uFF0C\u53EF\u4EE5\u5F00\u59CB\u804A\u5929\u3002");
5254
+ announce(isCallbackRepair(provision2) ? `${targetBot.bot.name}\u7684\u5361\u7247\u6309\u94AE\u5DF2\u4FEE\u590D\u3002` : isGroupMessagePermission(provision2) ? `${targetBot.bot.name}\u5DF2\u5F00\u901A\u7FA4\u6D88\u606F\u6743\u9650\uFF0C\u5E76\u542F\u7528\u201C\u54CD\u5E94\u6240\u6709\u7FA4\u6D88\u606F\u201D\u3002` : targetBot ? `${targetBot.bot.name}\u5DF2\u8FDE\u63A5\uFF0C\u53EF\u4EE5\u5728\u98DE\u4E66\u4E2D\u5F00\u59CB\u804A\u5929\u3002` : "\u65B0\u98DE\u4E66\u673A\u5668\u4EBA\u5DF2\u8FDE\u63A5\uFF0C\u53EF\u4EE5\u5F00\u59CB\u804A\u5929\u3002");
4805
5255
  if (result.botId) setFocusBotId(result.botId);
4806
5256
  return;
4807
5257
  }
4808
5258
  if (result.status === "failed") {
4809
- const error = new Error(result.message ?? (isCallbackRepair(provision2) ? "\u98DE\u4E66\u5361\u7247\u6309\u94AE\u4FEE\u590D\u5931\u8D25" : "\u98DE\u4E66\u5E94\u7528\u521B\u5EFA\u5931\u8D25"));
5259
+ const error = new Error(result.message ?? (isCallbackRepair(provision2) ? "\u98DE\u4E66\u5361\u7247\u6309\u94AE\u4FEE\u590D\u5931\u8D25" : isGroupMessagePermission(provision2) ? "\u98DE\u4E66\u7FA4\u6D88\u606F\u6743\u9650\u5F00\u901A\u5931\u8D25" : "\u98DE\u4E66\u5E94\u7528\u521B\u5EFA\u5931\u8D25"));
4810
5260
  error.code = "FEISHU_PROVISION_FAILED";
4811
5261
  throw error;
4812
5262
  }
@@ -4844,7 +5294,7 @@ function FeishuSettingsTab({ rpcCall }) {
4844
5294
  window.clearTimeout(timer);
4845
5295
  };
4846
5296
  }, [announce, invoke, loadStatus, model.provisioning]);
4847
- const setBotBusy = React10.useCallback((botId, value) => {
5297
+ const setBotBusy = React11.useCallback((botId, value) => {
4848
5298
  setBusyByBot((current) => {
4849
5299
  const next = { ...current };
4850
5300
  if (value) next[botId] = value;
@@ -4852,7 +5302,7 @@ function FeishuSettingsTab({ rpcCall }) {
4852
5302
  return next;
4853
5303
  });
4854
5304
  }, []);
4855
- const setBotError = React10.useCallback((botId, error) => {
5305
+ const setBotError = React11.useCallback((botId, error) => {
4856
5306
  setErrorsByBot((current) => {
4857
5307
  const next = { ...current };
4858
5308
  if (error) next[botId] = presentError3(error);
@@ -4860,7 +5310,7 @@ function FeishuSettingsTab({ rpcCall }) {
4860
5310
  return next;
4861
5311
  });
4862
5312
  }, []);
4863
- const repairCallback = React10.useCallback((connection) => {
5313
+ const repairCallback = React11.useCallback((connection) => {
4864
5314
  if (model.provisioning) return;
4865
5315
  setRemoveTargetId(null);
4866
5316
  setBotError(connection.botId, null);
@@ -4874,7 +5324,7 @@ function FeishuSettingsTab({ rpcCall }) {
4874
5324
  bot: connection
4875
5325
  });
4876
5326
  }, [model.provisioning, setBotError, startProvisioning]);
4877
- const reconnectOneBot = React10.useCallback(async (connection) => {
5327
+ const reconnectOneBot = React11.useCallback(async (connection) => {
4878
5328
  const { botId, bot } = connection;
4879
5329
  const snapshotVersion = workspaceFence.beginMutation();
4880
5330
  setBotBusy(botId, "reconnect");
@@ -4914,7 +5364,7 @@ function FeishuSettingsTab({ rpcCall }) {
4914
5364
  setBotBusy(botId, null);
4915
5365
  }
4916
5366
  }, [announce, invoke, loadStatus, mergeSnapshot, setBotBusy, setBotError, workspaceFence]);
4917
- const saveWorkspace = React10.useCallback(async (connection, workspace) => {
5367
+ const saveWorkspace = React11.useCallback(async (connection, workspace) => {
4918
5368
  const { botId } = connection;
4919
5369
  const workspaceVersion = workspaceFence.beginMutation();
4920
5370
  setBotBusy(botId, "workspace");
@@ -4933,15 +5383,82 @@ function FeishuSettingsTab({ rpcCall }) {
4933
5383
  if (mountedRef.current) setBotBusy(botId, null);
4934
5384
  }
4935
5385
  }, [invoke, loadStatus, mergeSnapshot, setBotBusy, setBotError, workspaceFence]);
4936
- const requestRemove = React10.useCallback((connection) => {
5386
+ const saveAgentPreset = React11.useCallback(async (connection, agentPreset) => {
5387
+ const { botId } = connection;
5388
+ const snapshotVersion = workspaceFence.beginMutation();
5389
+ setBotBusy(botId, "preset");
5390
+ setBotError(botId, null);
5391
+ try {
5392
+ const snapshot = normalizeBotsSnapshot(await invoke(
5393
+ FEISHU_ENDPOINTS.setAgentPreset,
5394
+ { botId, agentPreset }
5395
+ ));
5396
+ if (mountedRef.current && workspaceFence.canCommitMutation(snapshotVersion)) {
5397
+ mergeSnapshot(snapshot);
5398
+ }
5399
+ } finally {
5400
+ const shouldRefresh = workspaceFence.endMutation();
5401
+ if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true });
5402
+ if (mountedRef.current) setBotBusy(botId, null);
5403
+ }
5404
+ }, [invoke, loadStatus, mergeSnapshot, setBotBusy, setBotError, workspaceFence]);
5405
+ const authorizeGroupMessages = React11.useCallback(async (connection) => {
5406
+ const { botId } = connection;
5407
+ if (model.provisioning) {
5408
+ throw new Error("\u8BF7\u5148\u5B8C\u6210\u5F53\u524D\u98DE\u4E66\u6388\u6743\u64CD\u4F5C\uFF0C\u518D\u5F00\u901A\u7FA4\u6D88\u606F\u6743\u9650\u3002");
5409
+ }
5410
+ setRemoveTargetId(null);
5411
+ setBotError(botId, null);
5412
+ setTestNoticesByBot((current) => {
5413
+ const next = { ...current };
5414
+ delete next[botId];
5415
+ return next;
5416
+ });
5417
+ await startProvisioning({
5418
+ operation: GROUP_MESSAGE_PERMISSION_OPERATION,
5419
+ bot: connection
5420
+ });
5421
+ }, [model.provisioning, setBotError, startProvisioning]);
5422
+ const saveGroupResponseMode = React11.useCallback(async (connection, groupResponseMode) => {
5423
+ const { botId } = connection;
5424
+ if (groupResponseMode === "all" && connection.groupMessagePermissionGranted !== true) {
5425
+ await authorizeGroupMessages(connection);
5426
+ return;
5427
+ }
5428
+ const snapshotVersion = workspaceFence.beginMutation();
5429
+ setBotBusy(botId, "group-response-mode");
5430
+ setBotError(botId, null);
5431
+ try {
5432
+ const snapshot = normalizeBotsSnapshot(await invoke(
5433
+ FEISHU_ENDPOINTS.setGroupResponseMode,
5434
+ { botId, groupResponseMode }
5435
+ ));
5436
+ if (mountedRef.current && workspaceFence.canCommitMutation(snapshotVersion)) {
5437
+ mergeSnapshot(snapshot);
5438
+ }
5439
+ } finally {
5440
+ const shouldRefresh = workspaceFence.endMutation();
5441
+ if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true });
5442
+ if (mountedRef.current) setBotBusy(botId, null);
5443
+ }
5444
+ }, [
5445
+ invoke,
5446
+ authorizeGroupMessages,
5447
+ loadStatus,
5448
+ mergeSnapshot,
5449
+ setBotBusy,
5450
+ setBotError,
5451
+ workspaceFence
5452
+ ]);
5453
+ const requestRemove = React11.useCallback((connection) => {
4937
5454
  setRemoveTargetId(connection.botId);
4938
5455
  }, []);
4939
- const cancelRemove = React10.useCallback(() => {
5456
+ const cancelRemove = React11.useCallback(() => {
4940
5457
  const botId = removeTargetId;
4941
5458
  setRemoveTargetId(null);
4942
5459
  scheduleAnimationFrame(() => removeButtonRefs.current.get(botId)?.focus(), "focus");
4943
5460
  }, [removeTargetId, scheduleAnimationFrame]);
4944
- const confirmRemove = React10.useCallback(async (connection) => {
5461
+ const confirmRemove = React11.useCallback(async (connection) => {
4945
5462
  const { botId, bot } = connection;
4946
5463
  const snapshotVersion = workspaceFence.beginMutation();
4947
5464
  setBotBusy(botId, "delete");
@@ -4967,6 +5484,7 @@ function FeishuSettingsTab({ rpcCall }) {
4967
5484
  }
4968
5485
  }, [announce, invoke, loadStatus, mergeSnapshot, scheduleAnimationFrame, setBotBusy, setBotError, workspaceFence]);
4969
5486
  const provision = model.provisioning;
5487
+ const targetedProvisioning = isTargetedAppUpdate2(provision);
4970
5488
  const provisionBot = provision?.botId ? model.bots.find((bot) => bot.botId === provision.botId) ?? { botId: provision.botId, bot: { name: provision.botName ?? "\u6B64\u673A\u5668\u4EBA" } } : void 0;
4971
5489
  const restartProvisioning = ({ replace = false } = {}) => startProvisioning({
4972
5490
  replace,
@@ -4992,7 +5510,7 @@ function FeishuSettingsTab({ rpcCall }) {
4992
5510
  provisionContent = h2(ProvisionProgress, {
4993
5511
  phase: "connecting",
4994
5512
  provision,
4995
- onCancel: isCallbackRepair(provision) ? void 0 : () => void cancelProvisioning(),
5513
+ onCancel: isTargetedAppUpdate2(provision) ? void 0 : () => void cancelProvisioning(),
4996
5514
  busy: provisionBusy
4997
5515
  });
4998
5516
  } else if (provision?.phase === "error") {
@@ -5026,15 +5544,17 @@ function FeishuSettingsTab({ rpcCall }) {
5026
5544
  setCredentialError(null);
5027
5545
  }
5028
5546
  }) : null;
5029
- const setCardRef = React10.useCallback((botId, node) => {
5547
+ const setCardRef = React11.useCallback((botId, node) => {
5030
5548
  if (node) cardRefs.current.set(botId, node);
5031
5549
  else cardRefs.current.delete(botId);
5032
5550
  }, []);
5033
- const setRemoveButtonRef = React10.useCallback((botId, node) => {
5551
+ const setRemoveButtonRef = React11.useCallback((botId, node) => {
5034
5552
  if (node) removeButtonRefs.current.set(botId, node);
5035
5553
  else removeButtonRefs.current.delete(botId);
5036
5554
  }, []);
5037
- return h2(
5555
+ return h2(AgentPresetCatalogContext.Provider, {
5556
+ value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
5557
+ }, h2(
5038
5558
  "section",
5039
5559
  { className: "bxf-page dim-channelPage", "aria-label": "\u98DE\u4E66\u673A\u5668\u4EBA\u8BBE\u7F6E" },
5040
5560
  h2(Heading2, {
@@ -5067,10 +5587,10 @@ function FeishuSettingsTab({ rpcCall }) {
5067
5587
  onRetry: () => void loadStatus(),
5068
5588
  busy: pageBusy
5069
5589
  }) : h2(
5070
- React10.Fragment,
5590
+ React11.Fragment,
5071
5591
  null,
5072
5592
  credentialContent,
5073
- provisionContent,
5593
+ targetedProvisioning ? null : provisionContent,
5074
5594
  model.bots.length === 0 && !provision && !credentialOpen ? h2(EmptyView2, { onStart: () => void startProvisioning(), busy: provisionBusy }) : null,
5075
5595
  model.bots.length > 0 ? h2(BotList, {
5076
5596
  bots: model.bots,
@@ -5079,9 +5599,14 @@ function FeishuSettingsTab({ rpcCall }) {
5079
5599
  testNoticesByBot,
5080
5600
  removeTargetId,
5081
5601
  provisioning: provision,
5602
+ provisionContent,
5603
+ provisionRef: targetedProvisionRef,
5082
5604
  onReconnect: (bot) => void reconnectOneBot(bot),
5083
5605
  onRepairCallback: repairCallback,
5084
5606
  onWorkspaceSave: saveWorkspace,
5607
+ onAgentPresetSave: saveAgentPreset,
5608
+ onGroupResponseModeSave: saveGroupResponseMode,
5609
+ onGroupMessagePermissionAuthorize: authorizeGroupMessages,
5085
5610
  onRequestRemove: requestRemove,
5086
5611
  onConfirmRemove: (bot) => void confirmRemove(bot),
5087
5612
  onCancelRemove: cancelRemove,
@@ -5089,7 +5614,7 @@ function FeishuSettingsTab({ rpcCall }) {
5089
5614
  setRemoveButtonRef
5090
5615
  }) : null
5091
5616
  )
5092
- );
5617
+ ));
5093
5618
  }
5094
5619
 
5095
5620
  // plugin-src/client/channels/qq/api.js
@@ -5102,7 +5627,8 @@ var QQ_ENDPOINTS = Object.freeze({
5102
5627
  bindCredentials: "bot.bind-credentials",
5103
5628
  reconnectBot: "bot.reconnect",
5104
5629
  deleteBot: "bot.delete",
5105
- setWorkspace: "bot.workspace.set"
5630
+ setWorkspace: "bot.workspace.set",
5631
+ setAgentPreset: SET_AGENT_PRESET_ENDPOINT
5106
5632
  });
5107
5633
  var PROVISION_STATES2 = /* @__PURE__ */ new Set(["starting", "pending", "refreshing", "connecting", "connected", "failed", "cancelled"]);
5108
5634
  var ACCOUNT_STATES3 = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]);
@@ -5166,6 +5692,7 @@ function normalizeBot3(value) {
5166
5692
  connected,
5167
5693
  state: connected ? "connected" : state,
5168
5694
  workspace: text2(value.workspace, "", 4096),
5695
+ agentPreset: normalizeAgentPresetId(value.agentPreset),
5169
5696
  bot: {
5170
5697
  name: text2(value.bot?.name, "QQ\u673A\u5668\u4EBA", 100),
5171
5698
  appIdMasked: text2(value.bot?.appIdMasked, "\u5E94\u7528\u6807\u8BC6\u5DF2\u5B89\u5168\u4FDD\u5B58", 140)
@@ -5199,6 +5726,7 @@ function normalizeSnapshot3(value) {
5199
5726
  bots,
5200
5727
  totals: { configured: bots.length, connected: bots.filter((bot) => bot.connected).length },
5201
5728
  provisioning: source.provisioning ? normalizeProvisioning3(source.provisioning) : null,
5729
+ agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog),
5202
5730
  ...testMessage ? { testMessage } : {}
5203
5731
  };
5204
5732
  }
@@ -5221,7 +5749,7 @@ function formatRemaining3(milliseconds) {
5221
5749
  }
5222
5750
 
5223
5751
  // plugin-src/client/channels/qq/index.js
5224
- var React11 = __toESM(require("react"), 1);
5752
+ var React12 = __toESM(require("react"), 1);
5225
5753
 
5226
5754
  // plugin-src/client/channels/qq/styles.js
5227
5755
  var QQ_STYLE_ID = "xmanrui-dsh-im-qq-settings";
@@ -5246,7 +5774,7 @@ function installQqStyles() {
5246
5774
 
5247
5775
  // plugin-src/client/channels/qq/index.js
5248
5776
  var ACTIVE_STATES = /* @__PURE__ */ new Set(["pending", "refreshing", "connecting"]);
5249
- var Button7 = React11.forwardRef(function Button8({ children, kind = "secondary", className = "", ...props }, ref) {
5777
+ var Button7 = React12.forwardRef(function Button8({ children, kind = "secondary", className = "", ...props }, ref) {
5250
5778
  return h2("button", {
5251
5779
  ...props,
5252
5780
  ref,
@@ -5460,6 +5988,7 @@ function AccountCard2({
5460
5988
  removing,
5461
5989
  onReconnect,
5462
5990
  onWorkspaceSave,
5991
+ onAgentPresetSave,
5463
5992
  onRequestRemove,
5464
5993
  onConfirmRemove,
5465
5994
  onCancelRemove
@@ -5505,6 +6034,11 @@ function AccountCard2({
5505
6034
  disabled: Boolean(busy),
5506
6035
  onSave: onWorkspaceSave
5507
6036
  }),
6037
+ h2(AgentPresetEditor, {
6038
+ agentPreset: account.agentPreset,
6039
+ disabled: Boolean(busy),
6040
+ onSave: onAgentPresetSave
6041
+ }),
5508
6042
  h2(
5509
6043
  "div",
5510
6044
  { className: "ddt-accountFooter dim-cardFooter" },
@@ -5531,19 +6065,25 @@ function AccountCard2({
5531
6065
  );
5532
6066
  }
5533
6067
  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);
6068
+ const [model, setModel] = React12.useState({
6069
+ phase: "loading",
6070
+ bots: [],
6071
+ totals: { configured: 0, connected: 0 },
6072
+ error: null,
6073
+ agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
6074
+ });
6075
+ const [provision, setProvision] = React12.useState(null);
6076
+ const [busy, setBusy] = React12.useState(false);
6077
+ const [busyByBot, setBusyByBot] = React12.useState({});
6078
+ const [feedbackByBot, setFeedbackByBot] = React12.useState({});
6079
+ const [removeTarget, setRemoveTarget] = React12.useState(null);
6080
+ const [credentialOpen, setCredentialOpen] = React12.useState(false);
6081
+ const [credentialError, setCredentialError] = React12.useState(null);
6082
+ const [now, setNow] = React12.useState(Date.now());
6083
+ const mounted = React12.useRef(true);
5544
6084
  const workspaceFence = useWorkspaceSnapshotFence();
5545
- const addButtonRef = React11.useRef(null);
5546
- React11.useEffect(() => {
6085
+ const addButtonRef = React12.useRef(null);
6086
+ React12.useEffect(() => {
5547
6087
  const disposeDingtalk = installDingtalkStyles();
5548
6088
  const disposeQq = installQqStyles();
5549
6089
  mounted.current = true;
@@ -5553,18 +6093,24 @@ function QqSettingsTab({ rpcCall }) {
5553
6093
  disposeDingtalk();
5554
6094
  };
5555
6095
  }, []);
5556
- const invoke = React11.useCallback(async (endpoint, payload = {}, signal) => {
6096
+ const invoke = React12.useCallback(async (endpoint, payload = {}, signal) => {
5557
6097
  if (typeof rpcCall !== "function") throw new TypeError("QQ \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
5558
6098
  return unwrapRpcResult4(await rpcCall(endpoint, payload, signal));
5559
6099
  }, [rpcCall]);
5560
- const loadStatus = React11.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
6100
+ const loadStatus = React12.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
5561
6101
  const workspaceVersion = workspaceFence.beginStatus();
5562
6102
  if (workspaceVersion === null) return void 0;
5563
6103
  if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
5564
6104
  try {
5565
6105
  const snapshot = normalizeSnapshot3(await invoke(QQ_ENDPOINTS.status, {}, signal));
5566
6106
  if (!mounted.current || signal?.aborted || !workspaceFence.canCommitStatus(workspaceVersion)) return void 0;
5567
- setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
6107
+ setModel({
6108
+ phase: "ready",
6109
+ bots: snapshot.bots,
6110
+ totals: snapshot.totals,
6111
+ error: null,
6112
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
6113
+ });
5568
6114
  if (restore && snapshot.provisioning) setProvision({
5569
6115
  ...snapshot.provisioning,
5570
6116
  durationMs: Math.max(1, snapshot.provisioning.expiresAt - Date.now())
@@ -5577,12 +6123,12 @@ function QqSettingsTab({ rpcCall }) {
5577
6123
  return void 0;
5578
6124
  }
5579
6125
  }, [invoke, workspaceFence]);
5580
- React11.useEffect(() => {
6126
+ React12.useEffect(() => {
5581
6127
  const controller = new AbortController();
5582
6128
  void loadStatus({ signal: controller.signal, restore: true });
5583
6129
  return () => controller.abort();
5584
6130
  }, [loadStatus]);
5585
- React11.useEffect(() => {
6131
+ React12.useEffect(() => {
5586
6132
  if (model.phase !== "ready") return void 0;
5587
6133
  const controller = new AbortController();
5588
6134
  const timer = window.setInterval(() => void loadStatus({ signal: controller.signal, silent: true }), 15e3);
@@ -5591,12 +6137,12 @@ function QqSettingsTab({ rpcCall }) {
5591
6137
  window.clearInterval(timer);
5592
6138
  };
5593
6139
  }, [loadStatus, model.phase]);
5594
- React11.useEffect(() => {
6140
+ React12.useEffect(() => {
5595
6141
  if (!provision || !ACTIVE_STATES.has(provision.status)) return void 0;
5596
6142
  const timer = window.setInterval(() => mounted.current && setNow(Date.now()), 1e3);
5597
6143
  return () => window.clearInterval(timer);
5598
6144
  }, [provision?.attemptId, provision?.status]);
5599
- const startProvisioning = React11.useCallback(async (replace = false) => {
6145
+ const startProvisioning = React12.useCallback(async (replace = false) => {
5600
6146
  setCredentialOpen(false);
5601
6147
  setCredentialError(null);
5602
6148
  setBusy(true);
@@ -5614,7 +6160,7 @@ function QqSettingsTab({ rpcCall }) {
5614
6160
  if (mounted.current) setBusy(false);
5615
6161
  }
5616
6162
  }, [invoke, provision?.attemptId]);
5617
- const bindCredentials = React11.useCallback(async ({ identity, secret }) => {
6163
+ const bindCredentials = React12.useCallback(async ({ identity, secret }) => {
5618
6164
  const snapshotVersion = workspaceFence.beginMutation();
5619
6165
  setBusy(true);
5620
6166
  setCredentialError(null);
@@ -5625,7 +6171,13 @@ function QqSettingsTab({ rpcCall }) {
5625
6171
  ));
5626
6172
  if (!mounted.current) return;
5627
6173
  if (workspaceFence.canCommitMutation(snapshotVersion)) {
5628
- setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
6174
+ setModel({
6175
+ phase: "ready",
6176
+ bots: snapshot.bots,
6177
+ totals: snapshot.totals,
6178
+ error: null,
6179
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
6180
+ });
5629
6181
  }
5630
6182
  setCredentialOpen(false);
5631
6183
  } catch (error) {
@@ -5636,7 +6188,7 @@ function QqSettingsTab({ rpcCall }) {
5636
6188
  if (mounted.current) setBusy(false);
5637
6189
  }
5638
6190
  }, [invoke, loadStatus, workspaceFence]);
5639
- const closeProvision = React11.useCallback(async () => {
6191
+ const closeProvision = React12.useCallback(async () => {
5640
6192
  setBusy(true);
5641
6193
  try {
5642
6194
  if (provision?.attemptId && ACTIVE_STATES.has(provision.status)) {
@@ -5647,7 +6199,7 @@ function QqSettingsTab({ rpcCall }) {
5647
6199
  if (mounted.current) setBusy(false);
5648
6200
  }
5649
6201
  }, [invoke, provision?.attemptId, provision?.status]);
5650
- React11.useEffect(() => {
6202
+ React12.useEffect(() => {
5651
6203
  const attemptId = provision?.attemptId;
5652
6204
  if (!attemptId || !ACTIVE_STATES.has(provision.status)) return void 0;
5653
6205
  const controller = new AbortController();
@@ -5677,13 +6229,19 @@ function QqSettingsTab({ rpcCall }) {
5677
6229
  window.clearTimeout(timer);
5678
6230
  };
5679
6231
  }, [invoke, loadStatus, provision?.attemptId, provision?.pollIntervalMs, provision?.status]);
5680
- const botAction = React11.useCallback(async (account, operation, endpoint, payload) => {
6232
+ const botAction = React12.useCallback(async (account, operation, endpoint, payload) => {
5681
6233
  const snapshotVersion = workspaceFence.beginMutation();
5682
6234
  setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
5683
6235
  try {
5684
6236
  const snapshot = normalizeSnapshot3(await invoke(endpoint, payload));
5685
6237
  if (mounted.current && workspaceFence.canCommitMutation(snapshotVersion)) {
5686
- setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
6238
+ setModel({
6239
+ phase: "ready",
6240
+ bots: snapshot.bots,
6241
+ totals: snapshot.totals,
6242
+ error: null,
6243
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
6244
+ });
5687
6245
  }
5688
6246
  return snapshot;
5689
6247
  } finally {
@@ -5696,7 +6254,7 @@ function QqSettingsTab({ rpcCall }) {
5696
6254
  });
5697
6255
  }
5698
6256
  }, [invoke, loadStatus, workspaceFence]);
5699
- const reconnect = React11.useCallback(async (account) => {
6257
+ const reconnect = React12.useCallback(async (account) => {
5700
6258
  setFeedbackByBot((current) => {
5701
6259
  const next = { ...current };
5702
6260
  delete next[account.botId];
@@ -5759,6 +6317,12 @@ function QqSettingsTab({ rpcCall }) {
5759
6317
  QQ_ENDPOINTS.setWorkspace,
5760
6318
  { botId: account.botId, workspace }
5761
6319
  ),
6320
+ onAgentPresetSave: (agentPreset) => botAction(
6321
+ account,
6322
+ "preset",
6323
+ QQ_ENDPOINTS.setAgentPreset,
6324
+ { botId: account.botId, agentPreset }
6325
+ ),
5762
6326
  onRequestRemove: () => setRemoveTarget(account.botId),
5763
6327
  onCancelRemove: () => setRemoveTarget(null),
5764
6328
  onConfirmRemove: async () => {
@@ -5781,7 +6345,9 @@ function QqSettingsTab({ rpcCall }) {
5781
6345
  setCredentialError(null);
5782
6346
  }
5783
6347
  }) : null;
5784
- return h2(
6348
+ return h2(AgentPresetCatalogContext.Provider, {
6349
+ value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
6350
+ }, h2(
5785
6351
  "section",
5786
6352
  { className: "ddt-page dqq-page dim-channelPage", "aria-label": "QQ \u8BBE\u7F6E" },
5787
6353
  h2(Heading3, {
@@ -5797,14 +6363,14 @@ function QqSettingsTab({ rpcCall }) {
5797
6363
  addButtonRef
5798
6364
  }),
5799
6365
  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,
6366
+ React12.Fragment,
5801
6367
  null,
5802
6368
  credentialView,
5803
6369
  provisionView,
5804
6370
  model.bots.length === 0 && !provision && !credentialOpen ? h2(EmptyView3, { busy, onStart: () => void startProvisioning() }) : null,
5805
6371
  botList
5806
6372
  )
5807
- );
6373
+ ));
5808
6374
  }
5809
6375
 
5810
6376
  // src/channels/office/protocol.mjs
@@ -5886,7 +6452,7 @@ function normalizeOfficeStatus(value) {
5886
6452
  }
5887
6453
 
5888
6454
  // plugin-src/client/channels/office/index.js
5889
- var React12 = __toESM(require("react"), 1);
6455
+ var React13 = __toESM(require("react"), 1);
5890
6456
  function Button9({ children, kind = "secondary", ...props }) {
5891
6457
  return h2("button", { ...props, type: "button", className: "ddt-button", "data-kind": kind }, children);
5892
6458
  }
@@ -5915,12 +6481,12 @@ function stateLabel(model) {
5915
6481
  return "\u5DF2\u914D\u7F6E";
5916
6482
  }
5917
6483
  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({
6484
+ const [model, setModel] = React13.useState(normalizeOfficeStatus(initialStatus));
6485
+ const [phase, setPhase] = React13.useState(initialStatus === void 0 ? "loading" : "ready");
6486
+ const [busy, setBusy] = React13.useState("");
6487
+ const [error, setError] = React13.useState("");
6488
+ const [notice, setNotice] = React13.useState("");
6489
+ const [form, setForm] = React13.useState({
5924
6490
  baseUrl: "",
5925
6491
  deviceId: "local-harness",
5926
6492
  deviceToken: "",
@@ -5929,11 +6495,11 @@ function OfficeSettingsTab({ rpcCall, initialStatus }) {
5929
6495
  workspaces: "",
5930
6496
  instructionPresets: ""
5931
6497
  });
5932
- const invoke = React12.useCallback(async (endpoint, payload = {}) => {
6498
+ const invoke = React13.useCallback(async (endpoint, payload = {}) => {
5933
6499
  if (typeof rpcCall !== "function") throw new Error("AI Office \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
5934
6500
  return unwrapOfficeRpc(await rpcCall(endpoint, payload));
5935
6501
  }, [rpcCall]);
5936
- const adopt = React12.useCallback((value) => {
6502
+ const adopt = React13.useCallback((value) => {
5937
6503
  const next = normalizeOfficeStatus(value?.snapshot ?? value);
5938
6504
  setModel(next);
5939
6505
  if (next.config) setForm((current) => ({
@@ -5948,7 +6514,7 @@ function OfficeSettingsTab({ rpcCall, initialStatus }) {
5948
6514
  }));
5949
6515
  return next;
5950
6516
  }, []);
5951
- const load = React12.useCallback(async () => {
6517
+ const load = React13.useCallback(async () => {
5952
6518
  try {
5953
6519
  adopt(await invoke(OFFICE_RPC_ENDPOINTS.status));
5954
6520
  setPhase("ready");
@@ -5958,7 +6524,7 @@ function OfficeSettingsTab({ rpcCall, initialStatus }) {
5958
6524
  setError(caught.message);
5959
6525
  }
5960
6526
  }, [adopt, invoke]);
5961
- React12.useEffect(() => {
6527
+ React13.useEffect(() => {
5962
6528
  void load();
5963
6529
  }, [load]);
5964
6530
  const run = async (name2, operation) => {
@@ -5975,7 +6541,7 @@ function OfficeSettingsTab({ rpcCall, initialStatus }) {
5975
6541
  setBusy("");
5976
6542
  }
5977
6543
  };
5978
- const hooks = React12.useMemo(() => {
6544
+ const hooks = React13.useMemo(() => {
5979
6545
  try {
5980
6546
  return officeHookUrls(form.baseUrl);
5981
6547
  } catch {
@@ -6160,7 +6726,7 @@ var normalizeSnapshot4 = api2.normalizeSnapshot;
6160
6726
  var presentError5 = api2.presentError;
6161
6727
 
6162
6728
  // plugin-src/client/channels/slack/index.js
6163
- var React13 = __toESM(require("react"), 1);
6729
+ var React14 = __toESM(require("react"), 1);
6164
6730
 
6165
6731
  // src/channels/slack/manifest.mjs
6166
6732
  var SLACK_APP_MANIFEST_YAML = `_metadata:
@@ -6233,10 +6799,10 @@ function installSlackStyles() {
6233
6799
 
6234
6800
  // plugin-src/client/channels/slack/index.js
6235
6801
  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();
6802
+ const [botToken, setBotToken] = React14.useState("");
6803
+ const [appToken, setAppToken] = React14.useState("");
6804
+ const [copied, setCopied] = React14.useState(false);
6805
+ const headingId = React14.useId();
6240
6806
  const copyManifest = async () => {
6241
6807
  try {
6242
6808
  await navigator.clipboard.writeText(SLACK_APP_MANIFEST_YAML);
@@ -6392,7 +6958,7 @@ var normalizeSnapshot5 = api3.normalizeSnapshot;
6392
6958
  var presentError6 = api3.presentError;
6393
6959
 
6394
6960
  // plugin-src/client/channels/telegram/index.js
6395
- var React14 = __toESM(require("react"), 1);
6961
+ var React15 = __toESM(require("react"), 1);
6396
6962
 
6397
6963
  // plugin-src/client/channels/telegram/styles.js
6398
6964
  var TELEGRAM_STYLE_ID = "xmanrui-dsh-im-telegram-settings";
@@ -6458,11 +7024,11 @@ function allowedUsersFromText(value) {
6458
7024
  function TelegramAccessSettings({ account, busy = false, onSave }) {
6459
7025
  const policy = policyFor(account);
6460
7026
  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(() => {
7027
+ const accessHelpId = React15.useId();
7028
+ const [accessMode, setAccessMode] = React15.useState(policy.accessMode);
7029
+ const [allowedUsers, setAllowedUsers] = React15.useState(sourceUsers);
7030
+ const [error, setError] = React15.useState(null);
7031
+ React15.useEffect(() => {
6466
7032
  setAccessMode(policy.accessMode);
6467
7033
  setAllowedUsers(sourceUsers);
6468
7034
  setError(null);
@@ -6611,7 +7177,8 @@ var WECOM_ENDPOINTS = Object.freeze({
6611
7177
  bindCredentials: "bot.bind-credentials",
6612
7178
  reconnectBot: "bot.reconnect",
6613
7179
  deleteBot: "bot.delete",
6614
- setWorkspace: "bot.workspace.set"
7180
+ setWorkspace: "bot.workspace.set",
7181
+ setAgentPreset: SET_AGENT_PRESET_ENDPOINT
6615
7182
  });
6616
7183
  var PROVISION_STATES3 = /* @__PURE__ */ new Set(["starting", "pending", "refreshing", "connecting", "connected", "failed", "cancelled"]);
6617
7184
  var ACCOUNT_STATES4 = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]);
@@ -6681,6 +7248,7 @@ function normalizeBot4(value) {
6681
7248
  connected,
6682
7249
  state: connected ? "connected" : state,
6683
7250
  workspace: text3(value.workspace, "", 4096),
7251
+ agentPreset: normalizeAgentPresetId(value.agentPreset),
6684
7252
  bot: {
6685
7253
  name: text3(value.bot?.name, "\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA", 100),
6686
7254
  appIdMasked: text3(value.bot?.appIdMasked, "\u5E94\u7528\u6807\u8BC6\u5DF2\u5B89\u5168\u4FDD\u5B58", 140)
@@ -6704,7 +7272,8 @@ function normalizeSnapshot6(value) {
6704
7272
  bots,
6705
7273
  totals: { configured: bots.length, connected: bots.filter((bot) => bot.connected).length },
6706
7274
  provisioning: source.provisioning ? normalizeProvisioning4(source.provisioning) : null,
6707
- testMessage: normalizeTestMessage3(source.testMessage)
7275
+ testMessage: normalizeTestMessage3(source.testMessage),
7276
+ agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog)
6708
7277
  };
6709
7278
  }
6710
7279
  function presentError7(error) {
@@ -6719,7 +7288,7 @@ function formatRemaining4(milliseconds) {
6719
7288
  }
6720
7289
 
6721
7290
  // plugin-src/client/channels/wecom/index.js
6722
- var React15 = __toESM(require("react"), 1);
7291
+ var React16 = __toESM(require("react"), 1);
6723
7292
 
6724
7293
  // plugin-src/client/channels/wecom/styles.js
6725
7294
  var WECOM_STYLE_ID = "xmanrui-dsh-im-wecom-settings";
@@ -6744,7 +7313,7 @@ function installWecomStyles() {
6744
7313
 
6745
7314
  // plugin-src/client/channels/wecom/index.js
6746
7315
  var ACTIVE_STATES2 = /* @__PURE__ */ new Set(["pending", "refreshing", "connecting"]);
6747
- var Button10 = React15.forwardRef(function Button11({ children, kind = "secondary", className = "", ...props }, ref) {
7316
+ var Button10 = React16.forwardRef(function Button11({ children, kind = "secondary", className = "", ...props }, ref) {
6748
7317
  return h2("button", {
6749
7318
  ...props,
6750
7319
  ref,
@@ -6958,6 +7527,7 @@ function AccountCard3({
6958
7527
  removing,
6959
7528
  onReconnect,
6960
7529
  onWorkspaceSave,
7530
+ onAgentPresetSave,
6961
7531
  onRequestRemove,
6962
7532
  onConfirmRemove,
6963
7533
  onCancelRemove
@@ -7003,6 +7573,11 @@ function AccountCard3({
7003
7573
  disabled: Boolean(busy),
7004
7574
  onSave: onWorkspaceSave
7005
7575
  }),
7576
+ h2(AgentPresetEditor, {
7577
+ agentPreset: account.agentPreset,
7578
+ disabled: Boolean(busy),
7579
+ onSave: onAgentPresetSave
7580
+ }),
7006
7581
  h2(
7007
7582
  "div",
7008
7583
  { className: "ddt-accountFooter dim-cardFooter" },
@@ -7029,21 +7604,27 @@ function AccountCard3({
7029
7604
  );
7030
7605
  }
7031
7606
  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);
7607
+ const [model, setModel] = React16.useState({
7608
+ phase: "loading",
7609
+ bots: [],
7610
+ totals: { configured: 0, connected: 0 },
7611
+ error: null,
7612
+ agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
7613
+ });
7614
+ const [provision, setProvision] = React16.useState(null);
7615
+ const [busy, setBusy] = React16.useState(false);
7616
+ const [busyByBot, setBusyByBot] = React16.useState({});
7617
+ const [feedbackByBot, setFeedbackByBot] = React16.useState({});
7618
+ const [removeTarget, setRemoveTarget] = React16.useState(null);
7619
+ const [credentialOpen, setCredentialOpen] = React16.useState(false);
7620
+ const [credentialError, setCredentialError] = React16.useState(null);
7621
+ const [notice, setNotice] = React16.useState("");
7622
+ const [now, setNow] = React16.useState(Date.now());
7623
+ const mounted = React16.useRef(true);
7043
7624
  const workspaceFence = useWorkspaceSnapshotFence();
7044
- const addButtonRef = React15.useRef(null);
7045
- const noticeFrameRef = React15.useRef(null);
7046
- const announce = React15.useCallback((message) => {
7625
+ const addButtonRef = React16.useRef(null);
7626
+ const noticeFrameRef = React16.useRef(null);
7627
+ const announce = React16.useCallback((message) => {
7047
7628
  if (!mounted.current) return;
7048
7629
  if (noticeFrameRef.current !== null) {
7049
7630
  window.cancelAnimationFrame(noticeFrameRef.current);
@@ -7057,7 +7638,7 @@ function WecomSettingsTab({ rpcCall }) {
7057
7638
  });
7058
7639
  }
7059
7640
  }, []);
7060
- React15.useEffect(() => {
7641
+ React16.useEffect(() => {
7061
7642
  const disposeDingtalk = installDingtalkStyles();
7062
7643
  const disposeWecom = installWecomStyles();
7063
7644
  mounted.current = true;
@@ -7071,18 +7652,24 @@ function WecomSettingsTab({ rpcCall }) {
7071
7652
  disposeDingtalk();
7072
7653
  };
7073
7654
  }, []);
7074
- const invoke = React15.useCallback(async (endpoint, payload = {}, signal) => {
7655
+ const invoke = React16.useCallback(async (endpoint, payload = {}, signal) => {
7075
7656
  if (typeof rpcCall !== "function") throw new TypeError("\u4F01\u4E1A\u5FAE\u4FE1\u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
7076
7657
  return unwrapRpcResult7(await rpcCall(endpoint, payload, signal));
7077
7658
  }, [rpcCall]);
7078
- const loadStatus = React15.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
7659
+ const loadStatus = React16.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
7079
7660
  const workspaceVersion = workspaceFence.beginStatus();
7080
7661
  if (workspaceVersion === null) return void 0;
7081
7662
  if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
7082
7663
  try {
7083
7664
  const snapshot = normalizeSnapshot6(await invoke(WECOM_ENDPOINTS.status, {}, signal));
7084
7665
  if (!mounted.current || signal?.aborted || !workspaceFence.canCommitStatus(workspaceVersion)) return void 0;
7085
- setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
7666
+ setModel({
7667
+ phase: "ready",
7668
+ bots: snapshot.bots,
7669
+ totals: snapshot.totals,
7670
+ error: null,
7671
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
7672
+ });
7086
7673
  if (restore && snapshot.provisioning) setProvision({
7087
7674
  ...snapshot.provisioning,
7088
7675
  durationMs: Math.max(1, snapshot.provisioning.expiresAt - Date.now())
@@ -7095,12 +7682,12 @@ function WecomSettingsTab({ rpcCall }) {
7095
7682
  return void 0;
7096
7683
  }
7097
7684
  }, [invoke, workspaceFence]);
7098
- React15.useEffect(() => {
7685
+ React16.useEffect(() => {
7099
7686
  const controller = new AbortController();
7100
7687
  void loadStatus({ signal: controller.signal, restore: true });
7101
7688
  return () => controller.abort();
7102
7689
  }, [loadStatus]);
7103
- React15.useEffect(() => {
7690
+ React16.useEffect(() => {
7104
7691
  if (model.phase !== "ready") return void 0;
7105
7692
  const controller = new AbortController();
7106
7693
  const timer = window.setInterval(() => void loadStatus({ signal: controller.signal, silent: true }), 15e3);
@@ -7109,12 +7696,12 @@ function WecomSettingsTab({ rpcCall }) {
7109
7696
  window.clearInterval(timer);
7110
7697
  };
7111
7698
  }, [loadStatus, model.phase]);
7112
- React15.useEffect(() => {
7699
+ React16.useEffect(() => {
7113
7700
  if (!provision || !ACTIVE_STATES2.has(provision.status)) return void 0;
7114
7701
  const timer = window.setInterval(() => mounted.current && setNow(Date.now()), 1e3);
7115
7702
  return () => window.clearInterval(timer);
7116
7703
  }, [provision?.attemptId, provision?.status]);
7117
- const startProvisioning = React15.useCallback(async (replace = false) => {
7704
+ const startProvisioning = React16.useCallback(async (replace = false) => {
7118
7705
  setCredentialOpen(false);
7119
7706
  setCredentialError(null);
7120
7707
  setBusy(true);
@@ -7132,7 +7719,7 @@ function WecomSettingsTab({ rpcCall }) {
7132
7719
  if (mounted.current) setBusy(false);
7133
7720
  }
7134
7721
  }, [invoke, provision?.attemptId]);
7135
- const bindCredentials = React15.useCallback(async ({ identity, secret }) => {
7722
+ const bindCredentials = React16.useCallback(async ({ identity, secret }) => {
7136
7723
  const snapshotVersion = workspaceFence.beginMutation();
7137
7724
  setBusy(true);
7138
7725
  setCredentialError(null);
@@ -7143,7 +7730,13 @@ function WecomSettingsTab({ rpcCall }) {
7143
7730
  ));
7144
7731
  if (!mounted.current) return;
7145
7732
  if (workspaceFence.canCommitMutation(snapshotVersion)) {
7146
- setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
7733
+ setModel({
7734
+ phase: "ready",
7735
+ bots: snapshot.bots,
7736
+ totals: snapshot.totals,
7737
+ error: null,
7738
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
7739
+ });
7147
7740
  }
7148
7741
  setCredentialOpen(false);
7149
7742
  } catch (error) {
@@ -7154,7 +7747,7 @@ function WecomSettingsTab({ rpcCall }) {
7154
7747
  if (mounted.current) setBusy(false);
7155
7748
  }
7156
7749
  }, [invoke, loadStatus, workspaceFence]);
7157
- const closeProvision = React15.useCallback(async () => {
7750
+ const closeProvision = React16.useCallback(async () => {
7158
7751
  setBusy(true);
7159
7752
  try {
7160
7753
  if (provision?.attemptId && ACTIVE_STATES2.has(provision.status)) {
@@ -7165,7 +7758,7 @@ function WecomSettingsTab({ rpcCall }) {
7165
7758
  if (mounted.current) setBusy(false);
7166
7759
  }
7167
7760
  }, [invoke, provision?.attemptId, provision?.status]);
7168
- React15.useEffect(() => {
7761
+ React16.useEffect(() => {
7169
7762
  const attemptId = provision?.attemptId;
7170
7763
  if (!attemptId || !ACTIVE_STATES2.has(provision.status)) return void 0;
7171
7764
  const controller = new AbortController();
@@ -7195,13 +7788,19 @@ function WecomSettingsTab({ rpcCall }) {
7195
7788
  window.clearTimeout(timer);
7196
7789
  };
7197
7790
  }, [invoke, loadStatus, provision?.attemptId, provision?.pollIntervalMs, provision?.status]);
7198
- const botAction = React15.useCallback(async (account, operation, endpoint, payload) => {
7791
+ const botAction = React16.useCallback(async (account, operation, endpoint, payload) => {
7199
7792
  const snapshotVersion = workspaceFence.beginMutation();
7200
7793
  setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
7201
7794
  try {
7202
7795
  const snapshot = normalizeSnapshot6(await invoke(endpoint, payload));
7203
7796
  if (mounted.current && workspaceFence.canCommitMutation(snapshotVersion)) {
7204
- setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
7797
+ setModel({
7798
+ phase: "ready",
7799
+ bots: snapshot.bots,
7800
+ totals: snapshot.totals,
7801
+ error: null,
7802
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
7803
+ });
7205
7804
  }
7206
7805
  return snapshot;
7207
7806
  } finally {
@@ -7214,7 +7813,7 @@ function WecomSettingsTab({ rpcCall }) {
7214
7813
  });
7215
7814
  }
7216
7815
  }, [invoke, loadStatus, workspaceFence]);
7217
- const reconnect = React15.useCallback(async (account) => {
7816
+ const reconnect = React16.useCallback(async (account) => {
7218
7817
  setFeedbackByBot((current) => {
7219
7818
  const next = { ...current };
7220
7819
  delete next[account.botId];
@@ -7288,6 +7887,12 @@ function WecomSettingsTab({ rpcCall }) {
7288
7887
  WECOM_ENDPOINTS.setWorkspace,
7289
7888
  { botId: account.botId, workspace }
7290
7889
  ),
7890
+ onAgentPresetSave: (agentPreset) => botAction(
7891
+ account,
7892
+ "preset",
7893
+ WECOM_ENDPOINTS.setAgentPreset,
7894
+ { botId: account.botId, agentPreset }
7895
+ ),
7291
7896
  onRequestRemove: () => setRemoveTarget(account.botId),
7292
7897
  onCancelRemove: () => setRemoveTarget(null),
7293
7898
  onConfirmRemove: async () => {
@@ -7310,7 +7915,9 @@ function WecomSettingsTab({ rpcCall }) {
7310
7915
  setCredentialError(null);
7311
7916
  }
7312
7917
  }) : null;
7313
- return h2(
7918
+ return h2(AgentPresetCatalogContext.Provider, {
7919
+ value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
7920
+ }, h2(
7314
7921
  "section",
7315
7922
  { className: "ddt-page dwecom-page dim-channelPage", "aria-label": "\u4F01\u4E1A\u5FAE\u4FE1\u8BBE\u7F6E" },
7316
7923
  h2(Heading4, {
@@ -7327,18 +7934,18 @@ function WecomSettingsTab({ rpcCall }) {
7327
7934
  }),
7328
7935
  h2("div", { className: "ddt-visuallyHidden", role: "status", "aria-live": "polite" }, notice),
7329
7936
  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,
7937
+ React16.Fragment,
7331
7938
  null,
7332
7939
  credentialView,
7333
7940
  provisionView,
7334
7941
  model.bots.length === 0 && !provision && !credentialOpen ? h2(EmptyView4, { busy, onStart: () => void startProvisioning() }) : null,
7335
7942
  botList
7336
7943
  )
7337
- );
7944
+ ));
7338
7945
  }
7339
7946
 
7340
7947
  // plugin-src/client/channels/weixin/index.js
7341
- var React16 = __toESM(require("react"), 1);
7948
+ var React17 = __toESM(require("react"), 1);
7342
7949
 
7343
7950
  // plugin-src/client/channels/weixin/api.js
7344
7951
  var WEIXIN_RPC_CHANNEL = "/weixin";
@@ -7350,7 +7957,8 @@ var WEIXIN_ENDPOINTS = Object.freeze({
7350
7957
  cancelProvisioning: "provision.cancel",
7351
7958
  reconnectBot: "bot.reconnect",
7352
7959
  deleteBot: "bot.delete",
7353
- setWorkspace: "bot.workspace.set"
7960
+ setWorkspace: "bot.workspace.set",
7961
+ setAgentPreset: SET_AGENT_PRESET_ENDPOINT
7354
7962
  });
7355
7963
  var ACCOUNT_STATES5 = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]);
7356
7964
  var PROVISION_STATES4 = /* @__PURE__ */ new Set([
@@ -7448,6 +8056,7 @@ function normalizeBot5(value) {
7448
8056
  connected,
7449
8057
  configured: value.configured === true,
7450
8058
  workspace: string(value.workspace).slice(0, 4096),
8059
+ agentPreset: normalizeAgentPresetId(value.agentPreset),
7451
8060
  bot: {
7452
8061
  name: string(value.bot.name, "\u5FAE\u4FE1\u673A\u5668\u4EBA"),
7453
8062
  accountIdMasked: string(value.bot.accountIdMasked, "\u5DF2\u5B89\u5168\u4FDD\u5B58")
@@ -7483,7 +8092,8 @@ function normalizeSnapshot7(value) {
7483
8092
  connected: bots.filter((bot) => bot.connected).length
7484
8093
  },
7485
8094
  provisioning: value.provisioning ? normalizeProvisioning5(value.provisioning) : null,
7486
- testMessage: normalizeTestMessage4(value.testMessage)
8095
+ testMessage: normalizeTestMessage4(value.testMessage),
8096
+ agentPresetCatalog: normalizeAgentPresetCatalog(value.agentPresetCatalog)
7487
8097
  };
7488
8098
  }
7489
8099
  function presentError8(error) {
@@ -7620,7 +8230,7 @@ function installWeixinStyles() {
7620
8230
  }
7621
8231
 
7622
8232
  // plugin-src/client/channels/weixin/index.js
7623
- var Button12 = React16.forwardRef(function Button13({ children, kind = "secondary", className = "", ...props }, ref) {
8233
+ var Button12 = React17.forwardRef(function Button13({ children, kind = "secondary", className = "", ...props }, ref) {
7624
8234
  return h2("button", {
7625
8235
  ...props,
7626
8236
  ref,
@@ -7693,14 +8303,14 @@ function EmptyView5({ onStart, busy }) {
7693
8303
  );
7694
8304
  }
7695
8305
  function QrPanel4({ provision, now, busy, onRefresh, onCancel }) {
7696
- const [imageFailed, setImageFailed] = React16.useState(false);
8306
+ const [imageFailed, setImageFailed] = React17.useState(false);
7697
8307
  const source = safeQrSource5(provision.qrCodeDataUrl);
7698
8308
  const href = safeVerificationUrl(provision.verificationUrl);
7699
8309
  const remaining = Math.max(0, provision.expiresAt - now);
7700
8310
  const expired = remaining === 0 || provision.status === "expired";
7701
8311
  const duration = Math.max(1, provision.durationMs ?? 5 * 6e4);
7702
8312
  const progress = Math.round(Math.min(1, remaining / duration) * 100);
7703
- React16.useEffect(() => setImageFailed(false), [source]);
8313
+ React17.useEffect(() => setImageFailed(false), [source]);
7704
8314
  return h2(
7705
8315
  "div",
7706
8316
  { className: "dxw-card dim-surfaceCard" },
@@ -7767,9 +8377,9 @@ function QrPanel4({ provision, now, busy, onRefresh, onCancel }) {
7767
8377
  );
7768
8378
  }
7769
8379
  function VerificationPanel({ provision, busy, onSubmit, onCancel }) {
7770
- const [code, setCode] = React16.useState("");
8380
+ const [code, setCode] = React17.useState("");
7771
8381
  const valid = /^\d{4,8}$/.test(code);
7772
- React16.useEffect(() => setCode(""), [provision.attemptId]);
8382
+ React17.useEffect(() => setCode(""), [provision.attemptId]);
7773
8383
  return h2(
7774
8384
  "div",
7775
8385
  { className: "dxw-card dim-surfaceCard" },
@@ -7867,6 +8477,7 @@ function AccountCard4({
7867
8477
  removing,
7868
8478
  onReconnect,
7869
8479
  onWorkspaceSave,
8480
+ onAgentPresetSave,
7870
8481
  onRequestRemove,
7871
8482
  onConfirmRemove,
7872
8483
  onCancelRemove
@@ -7917,6 +8528,11 @@ function AccountCard4({
7917
8528
  disabled: Boolean(busy),
7918
8529
  onSave: onWorkspaceSave
7919
8530
  }),
8531
+ h2(AgentPresetEditor, {
8532
+ agentPreset: account.agentPreset,
8533
+ disabled: Boolean(busy),
8534
+ onSave: onAgentPresetSave
8535
+ }),
7920
8536
  h2(
7921
8537
  "div",
7922
8538
  { className: "dxw-accountFooter dim-cardFooter" },
@@ -7975,6 +8591,7 @@ function AccountList2(props) {
7975
8591
  removing: props.removeTarget === account.botId,
7976
8592
  onReconnect: () => props.onReconnect(account),
7977
8593
  onWorkspaceSave: (workspace) => props.onWorkspaceSave(account, workspace),
8594
+ onAgentPresetSave: (agentPreset) => props.onAgentPresetSave(account, agentPreset),
7978
8595
  onRequestRemove: () => props.onRequestRemove(account),
7979
8596
  onConfirmRemove: () => props.onConfirmRemove(account),
7980
8597
  onCancelRemove: props.onCancelRemove
@@ -7993,40 +8610,41 @@ function mergeWeixinProvisioningSnapshot(current, incoming, { restoreProvisionin
7993
8610
  };
7994
8611
  }
7995
8612
  function WeixinSettingsTab({ rpcCall }) {
7996
- const [model, setModel] = React16.useState({
8613
+ const [model, setModel] = React17.useState({
7997
8614
  phase: "loading",
7998
8615
  bots: [],
7999
8616
  totals: EMPTY_TOTALS3,
8000
8617
  revision: 0,
8001
- error: null
8618
+ error: null,
8619
+ agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
8002
8620
  });
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);
8621
+ const [provision, setProvision] = React17.useState(null);
8622
+ const [busy, setBusy] = React17.useState(false);
8623
+ const [busyByBot, setBusyByBot] = React17.useState({});
8624
+ const [feedbackByBot, setFeedbackByBot] = React17.useState({});
8625
+ const [removeTarget, setRemoveTarget] = React17.useState(null);
8626
+ const [notice, setNotice] = React17.useState("");
8627
+ const [now, setNow] = React17.useState(() => Date.now());
8628
+ const addButtonRef = React17.useRef(null);
8629
+ const mountedRef = React17.useRef(true);
8012
8630
  const workspaceFence = useWorkspaceSnapshotFence();
8013
8631
  const scheduleAnimationFrame = useAnimationFrameScheduler();
8014
- React16.useEffect(() => {
8632
+ React17.useEffect(() => {
8015
8633
  mountedRef.current = true;
8016
8634
  return () => {
8017
8635
  mountedRef.current = false;
8018
8636
  };
8019
8637
  }, []);
8020
- const announce = React16.useCallback((value) => {
8638
+ const announce = React17.useCallback((value) => {
8021
8639
  setNotice("");
8022
8640
  scheduleAnimationFrame(() => {
8023
8641
  if (value) setNotice(value);
8024
8642
  }, "announcement");
8025
8643
  }, [scheduleAnimationFrame]);
8026
- const invoke = React16.useCallback(async (endpoint, payload = {}, signal) => {
8644
+ const invoke = React17.useCallback(async (endpoint, payload = {}, signal) => {
8027
8645
  return unwrapRpcResult8(await rpcCall(endpoint, payload, signal));
8028
8646
  }, [rpcCall]);
8029
- const loadStatus = React16.useCallback(async ({
8647
+ const loadStatus = React17.useCallback(async ({
8030
8648
  signal,
8031
8649
  silent = false,
8032
8650
  restoreProvisioning = false
@@ -8042,7 +8660,8 @@ function WeixinSettingsTab({ rpcCall }) {
8042
8660
  bots: snapshot.bots,
8043
8661
  totals: snapshot.totals,
8044
8662
  revision: snapshot.revision,
8045
- error: null
8663
+ error: null,
8664
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
8046
8665
  });
8047
8666
  if (snapshot.provisioning) {
8048
8667
  setProvision((current) => mergeWeixinProvisioningSnapshot(
@@ -8062,12 +8681,12 @@ function WeixinSettingsTab({ rpcCall }) {
8062
8681
  return void 0;
8063
8682
  }
8064
8683
  }, [invoke, workspaceFence]);
8065
- React16.useEffect(() => {
8684
+ React17.useEffect(() => {
8066
8685
  const controller = new AbortController();
8067
8686
  void loadStatus({ signal: controller.signal, restoreProvisioning: true });
8068
8687
  return () => controller.abort();
8069
8688
  }, [loadStatus]);
8070
- React16.useEffect(() => {
8689
+ React17.useEffect(() => {
8071
8690
  if (model.phase !== "ready") return void 0;
8072
8691
  const controller = new AbortController();
8073
8692
  let running = false;
@@ -8086,12 +8705,12 @@ function WeixinSettingsTab({ rpcCall }) {
8086
8705
  window.clearInterval(timer);
8087
8706
  };
8088
8707
  }, [loadStatus, model.phase]);
8089
- React16.useEffect(() => {
8708
+ React17.useEffect(() => {
8090
8709
  if (!provision || !["pending", "scanned"].includes(provision.status)) return void 0;
8091
8710
  const timer = window.setInterval(() => setNow(Date.now()), 1e3);
8092
8711
  return () => window.clearInterval(timer);
8093
8712
  }, [provision?.attemptId, provision?.status]);
8094
- const startProvisioning = React16.useCallback(async ({ replace = false } = {}) => {
8713
+ const startProvisioning = React17.useCallback(async ({ replace = false } = {}) => {
8095
8714
  setBusy(true);
8096
8715
  try {
8097
8716
  if (replace && provision?.attemptId) {
@@ -8112,7 +8731,7 @@ function WeixinSettingsTab({ rpcCall }) {
8112
8731
  setBusy(false);
8113
8732
  }
8114
8733
  }, [announce, invoke, provision?.attemptId]);
8115
- const cancelProvisioning = React16.useCallback(async () => {
8734
+ const cancelProvisioning = React17.useCallback(async () => {
8116
8735
  setBusy(true);
8117
8736
  try {
8118
8737
  if (provision?.attemptId && !["failed", "expired", "cancelled"].includes(provision.status)) {
@@ -8127,7 +8746,7 @@ function WeixinSettingsTab({ rpcCall }) {
8127
8746
  setBusy(false);
8128
8747
  }
8129
8748
  }, [announce, invoke, provision?.attemptId, provision?.status, scheduleAnimationFrame]);
8130
- const submitVerification = React16.useCallback(async (verifyCode) => {
8749
+ const submitVerification = React17.useCallback(async (verifyCode) => {
8131
8750
  if (!provision?.attemptId) return;
8132
8751
  setBusy(true);
8133
8752
  try {
@@ -8143,7 +8762,7 @@ function WeixinSettingsTab({ rpcCall }) {
8143
8762
  setBusy(false);
8144
8763
  }
8145
8764
  }, [announce, invoke, provision?.attemptId]);
8146
- React16.useEffect(() => {
8765
+ React17.useEffect(() => {
8147
8766
  const attemptId = provision?.attemptId;
8148
8767
  if (!attemptId || !["pending", "scanned", "connecting"].includes(provision.status)) return void 0;
8149
8768
  const controller = new AbortController();
@@ -8191,7 +8810,7 @@ function WeixinSettingsTab({ rpcCall }) {
8191
8810
  controller.abort();
8192
8811
  };
8193
8812
  }, [announce, invoke, loadStatus, provision?.attemptId, provision?.status, provision?.pollIntervalMs]);
8194
- const setBotBusy = React16.useCallback((botId, value) => {
8813
+ const setBotBusy = React17.useCallback((botId, value) => {
8195
8814
  setBusyByBot((current) => {
8196
8815
  const next = { ...current };
8197
8816
  if (value) next[botId] = value;
@@ -8199,7 +8818,7 @@ function WeixinSettingsTab({ rpcCall }) {
8199
8818
  return next;
8200
8819
  });
8201
8820
  }, []);
8202
- const reconnect = React16.useCallback(async (account) => {
8821
+ const reconnect = React17.useCallback(async (account) => {
8203
8822
  const snapshotVersion = workspaceFence.beginMutation();
8204
8823
  setBotBusy(account.botId, "reconnect");
8205
8824
  setFeedbackByBot((current) => {
@@ -8213,7 +8832,13 @@ function WeixinSettingsTab({ rpcCall }) {
8213
8832
  { botId: account.botId, sendTest: true }
8214
8833
  ));
8215
8834
  if (mountedRef.current && workspaceFence.canCommitMutation(snapshotVersion)) {
8216
- setModel((current) => ({ ...current, bots: snapshot.bots, totals: snapshot.totals, revision: snapshot.revision }));
8835
+ setModel((current) => ({
8836
+ ...current,
8837
+ bots: snapshot.bots,
8838
+ totals: snapshot.totals,
8839
+ revision: snapshot.revision,
8840
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? current.agentPresetCatalog
8841
+ }));
8217
8842
  }
8218
8843
  const refreshed = snapshot.bots.find((bot) => bot.botId === account.botId);
8219
8844
  let feedback;
@@ -8244,7 +8869,7 @@ function WeixinSettingsTab({ rpcCall }) {
8244
8869
  setBotBusy(account.botId, null);
8245
8870
  }
8246
8871
  }, [announce, invoke, loadStatus, setBotBusy, workspaceFence]);
8247
- const saveWorkspace = React16.useCallback(async (account, workspace) => {
8872
+ const saveWorkspace = React17.useCallback(async (account, workspace) => {
8248
8873
  const workspaceVersion = workspaceFence.beginMutation();
8249
8874
  setBotBusy(account.botId, "workspace");
8250
8875
  try {
@@ -8258,7 +8883,32 @@ function WeixinSettingsTab({ rpcCall }) {
8258
8883
  bots: snapshot.bots,
8259
8884
  totals: snapshot.totals,
8260
8885
  revision: snapshot.revision,
8261
- error: null
8886
+ error: null,
8887
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
8888
+ });
8889
+ }
8890
+ } finally {
8891
+ const shouldRefresh = workspaceFence.endMutation();
8892
+ if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true });
8893
+ if (mountedRef.current) setBotBusy(account.botId, null);
8894
+ }
8895
+ }, [invoke, loadStatus, setBotBusy, workspaceFence]);
8896
+ const saveAgentPreset = React17.useCallback(async (account, agentPreset) => {
8897
+ const snapshotVersion = workspaceFence.beginMutation();
8898
+ setBotBusy(account.botId, "preset");
8899
+ try {
8900
+ const snapshot = normalizeSnapshot7(await invoke(
8901
+ WEIXIN_ENDPOINTS.setAgentPreset,
8902
+ { botId: account.botId, agentPreset }
8903
+ ));
8904
+ if (mountedRef.current && workspaceFence.canCommitMutation(snapshotVersion)) {
8905
+ setModel({
8906
+ phase: "ready",
8907
+ bots: snapshot.bots,
8908
+ totals: snapshot.totals,
8909
+ revision: snapshot.revision,
8910
+ error: null,
8911
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
8262
8912
  });
8263
8913
  }
8264
8914
  } finally {
@@ -8267,7 +8917,7 @@ function WeixinSettingsTab({ rpcCall }) {
8267
8917
  if (mountedRef.current) setBotBusy(account.botId, null);
8268
8918
  }
8269
8919
  }, [invoke, loadStatus, setBotBusy, workspaceFence]);
8270
- const remove = React16.useCallback(async (account) => {
8920
+ const remove = React17.useCallback(async (account) => {
8271
8921
  const snapshotVersion = workspaceFence.beginMutation();
8272
8922
  setBotBusy(account.botId, "delete");
8273
8923
  try {
@@ -8276,7 +8926,13 @@ function WeixinSettingsTab({ rpcCall }) {
8276
8926
  confirm: true
8277
8927
  }));
8278
8928
  if (mountedRef.current && workspaceFence.canCommitMutation(snapshotVersion)) {
8279
- setModel((current) => ({ ...current, bots: snapshot.bots, totals: snapshot.totals, revision: snapshot.revision }));
8929
+ setModel((current) => ({
8930
+ ...current,
8931
+ bots: snapshot.bots,
8932
+ totals: snapshot.totals,
8933
+ revision: snapshot.revision,
8934
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? current.agentPresetCatalog
8935
+ }));
8280
8936
  }
8281
8937
  setRemoveTarget(null);
8282
8938
  announce("\u5FAE\u4FE1\u8D26\u53F7\u53CA\u672C\u673A\u51ED\u636E\u5DF2\u79FB\u9664\u3002");
@@ -8320,7 +8976,9 @@ function WeixinSettingsTab({ rpcCall }) {
8320
8976
  onClose: () => void cancelProvisioning()
8321
8977
  });
8322
8978
  }
8323
- return h2(
8979
+ return h2(AgentPresetCatalogContext.Provider, {
8980
+ value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
8981
+ }, h2(
8324
8982
  "section",
8325
8983
  { className: "dxw-page dim-channelPage", "aria-label": "\u5FAE\u4FE1\u8BBE\u7F6E" },
8326
8984
  h2(Heading5, {
@@ -8343,7 +9001,7 @@ function WeixinSettingsTab({ rpcCall }) {
8343
9001
  h2(Button12, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
8344
9002
  )
8345
9003
  ) : h2(
8346
- React16.Fragment,
9004
+ React17.Fragment,
8347
9005
  null,
8348
9006
  provisionView,
8349
9007
  model.bots.length === 0 && !provision ? h2(EmptyView5, { onStart: () => void startProvisioning(), busy }) : null,
@@ -8354,12 +9012,13 @@ function WeixinSettingsTab({ rpcCall }) {
8354
9012
  removeTarget,
8355
9013
  onReconnect: (account) => void reconnect(account),
8356
9014
  onWorkspaceSave: saveWorkspace,
9015
+ onAgentPresetSave: saveAgentPreset,
8357
9016
  onRequestRemove: (account) => setRemoveTarget(account.botId),
8358
9017
  onConfirmRemove: (account) => void remove(account),
8359
9018
  onCancelRemove: () => setRemoveTarget(null)
8360
9019
  }) : null
8361
9020
  )
8362
- );
9021
+ ));
8363
9022
  }
8364
9023
 
8365
9024
  // plugin-src/client/channels/whatsapp/api.js
@@ -8371,7 +9030,8 @@ var WHATSAPP_ENDPOINTS = Object.freeze({
8371
9030
  cancelProvisioning: "provision.cancel",
8372
9031
  reconnectBot: "bot.reconnect",
8373
9032
  deleteBot: "bot.delete",
8374
- setWorkspace: "bot.workspace.set"
9033
+ setWorkspace: "bot.workspace.set",
9034
+ setAgentPreset: SET_AGENT_PRESET_ENDPOINT
8375
9035
  });
8376
9036
  var PROVISION_STATES5 = /* @__PURE__ */ new Set(["starting", "pending", "connecting", "connected", "failed", "cancelled"]);
8377
9037
  var BOT_STATES = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]);
@@ -8436,6 +9096,7 @@ function normalizeBot6(value) {
8436
9096
  connected,
8437
9097
  state: connected ? "connected" : state,
8438
9098
  workspace: text4(value.workspace, "", 4096),
9099
+ agentPreset: normalizeAgentPresetId(value.agentPreset),
8439
9100
  bot: {
8440
9101
  name: text4(value.bot?.name, "WhatsApp\u673A\u5668\u4EBA", 100),
8441
9102
  idMasked: text4(value.bot?.idMasked, "WhatsApp\u8D26\u53F7", 140)
@@ -8460,7 +9121,8 @@ function normalizeSnapshot8(value) {
8460
9121
  revision: Number.isSafeInteger(source.revision) ? source.revision : 0,
8461
9122
  bots,
8462
9123
  totals: { configured: bots.length, connected: bots.filter((bot) => bot.connected).length },
8463
- provisioning: source.provisioning ? normalizeProvisioning6(source.provisioning) : null
9124
+ provisioning: source.provisioning ? normalizeProvisioning6(source.provisioning) : null,
9125
+ agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog)
8464
9126
  };
8465
9127
  }
8466
9128
  function presentError9(error) {
@@ -8475,7 +9137,7 @@ function formatRemaining6(milliseconds) {
8475
9137
  }
8476
9138
 
8477
9139
  // plugin-src/client/channels/whatsapp/index.js
8478
- var React17 = __toESM(require("react"), 1);
9140
+ var React18 = __toESM(require("react"), 1);
8479
9141
 
8480
9142
  // plugin-src/client/channels/whatsapp/styles.js
8481
9143
  var WHATSAPP_STYLE_ID = "xmanrui-dsh-im-whatsapp-settings";
@@ -8500,7 +9162,7 @@ function installWhatsappStyles() {
8500
9162
 
8501
9163
  // plugin-src/client/channels/whatsapp/index.js
8502
9164
  var ACTIVE_STATES3 = /* @__PURE__ */ new Set(["pending", "connecting"]);
8503
- var Button14 = React17.forwardRef(function Button15({ children, kind = "secondary", className = "", ...props }, ref) {
9165
+ var Button14 = React18.forwardRef(function Button15({ children, kind = "secondary", className = "", ...props }, ref) {
8504
9166
  return h2("button", {
8505
9167
  ...props,
8506
9168
  ref,
@@ -8722,6 +9384,7 @@ function WhatsappAccountCard({
8722
9384
  removing,
8723
9385
  onReconnect,
8724
9386
  onWorkspaceSave,
9387
+ onAgentPresetSave,
8725
9388
  onRequestRemove,
8726
9389
  onConfirmRemove,
8727
9390
  onCancelRemove
@@ -8781,6 +9444,11 @@ function WhatsappAccountCard({
8781
9444
  disabled: Boolean(busy),
8782
9445
  onSave: onWorkspaceSave
8783
9446
  }),
9447
+ h2(AgentPresetEditor, {
9448
+ agentPreset: account.agentPreset,
9449
+ disabled: Boolean(busy),
9450
+ onSave: onAgentPresetSave
9451
+ }),
8784
9452
  h2(
8785
9453
  "div",
8786
9454
  { className: "ddt-accountFooter dim-cardFooter" },
@@ -8815,22 +9483,23 @@ function WhatsappAccountCard({
8815
9483
  );
8816
9484
  }
8817
9485
  function WhatsappSettingsTab({ rpcCall }) {
8818
- const [model, setModel] = React17.useState({
9486
+ const [model, setModel] = React18.useState({
8819
9487
  phase: "loading",
8820
9488
  bots: [],
8821
9489
  totals: { configured: 0, connected: 0 },
8822
- error: null
9490
+ error: null,
9491
+ agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG
8823
9492
  });
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);
9493
+ const [provision, setProvision] = React18.useState(null);
9494
+ const [busy, setBusy] = React18.useState(false);
9495
+ const [busyByBot, setBusyByBot] = React18.useState({});
9496
+ const [testNoticeByBot, setTestNoticeByBot] = React18.useState({});
9497
+ const [removeTarget, setRemoveTarget] = React18.useState(null);
9498
+ const [now, setNow] = React18.useState(Date.now());
9499
+ const mounted = React18.useRef(true);
8831
9500
  const workspaceFence = useWorkspaceSnapshotFence();
8832
- const addButtonRef = React17.useRef(null);
8833
- React17.useEffect(() => {
9501
+ const addButtonRef = React18.useRef(null);
9502
+ React18.useEffect(() => {
8834
9503
  const disposeDingtalk = installDingtalkStyles();
8835
9504
  const disposeWhatsapp = installWhatsappStyles();
8836
9505
  mounted.current = true;
@@ -8840,18 +9509,24 @@ function WhatsappSettingsTab({ rpcCall }) {
8840
9509
  disposeDingtalk();
8841
9510
  };
8842
9511
  }, []);
8843
- const invoke = React17.useCallback(async (endpoint, payload = {}, signal) => {
9512
+ const invoke = React18.useCallback(async (endpoint, payload = {}, signal) => {
8844
9513
  if (typeof rpcCall !== "function") throw new TypeError("WhatsApp \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
8845
9514
  return unwrapRpcResult9(await rpcCall(endpoint, payload, signal));
8846
9515
  }, [rpcCall]);
8847
- const loadStatus = React17.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
9516
+ const loadStatus = React18.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
8848
9517
  const workspaceVersion = workspaceFence.beginStatus();
8849
9518
  if (workspaceVersion === null) return void 0;
8850
9519
  if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
8851
9520
  try {
8852
9521
  const snapshot = normalizeSnapshot8(await invoke(WHATSAPP_ENDPOINTS.status, {}, signal));
8853
9522
  if (!mounted.current || signal?.aborted || !workspaceFence.canCommitStatus(workspaceVersion)) return void 0;
8854
- setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
9523
+ setModel({
9524
+ phase: "ready",
9525
+ bots: snapshot.bots,
9526
+ totals: snapshot.totals,
9527
+ error: null,
9528
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
9529
+ });
8855
9530
  if (restore && snapshot.provisioning) setProvision({
8856
9531
  ...snapshot.provisioning,
8857
9532
  durationMs: Math.max(1, snapshot.provisioning.expiresAt - Date.now())
@@ -8868,12 +9543,12 @@ function WhatsappSettingsTab({ rpcCall }) {
8868
9543
  return void 0;
8869
9544
  }
8870
9545
  }, [invoke, workspaceFence]);
8871
- React17.useEffect(() => {
9546
+ React18.useEffect(() => {
8872
9547
  const controller = new AbortController();
8873
9548
  void loadStatus({ signal: controller.signal, restore: true });
8874
9549
  return () => controller.abort();
8875
9550
  }, [loadStatus]);
8876
- React17.useEffect(() => {
9551
+ React18.useEffect(() => {
8877
9552
  if (model.phase !== "ready") return void 0;
8878
9553
  const controller = new AbortController();
8879
9554
  const timer = window.setInterval(
@@ -8885,12 +9560,12 @@ function WhatsappSettingsTab({ rpcCall }) {
8885
9560
  window.clearInterval(timer);
8886
9561
  };
8887
9562
  }, [loadStatus, model.phase]);
8888
- React17.useEffect(() => {
9563
+ React18.useEffect(() => {
8889
9564
  if (!provision || !ACTIVE_STATES3.has(provision.status)) return void 0;
8890
9565
  const timer = window.setInterval(() => mounted.current && setNow(Date.now()), 1e3);
8891
9566
  return () => window.clearInterval(timer);
8892
9567
  }, [provision?.attemptId, provision?.status]);
8893
- const startProvisioning = React17.useCallback(async (replace = false) => {
9568
+ const startProvisioning = React18.useCallback(async (replace = false) => {
8894
9569
  setBusy(true);
8895
9570
  try {
8896
9571
  if (replace && provision?.attemptId) {
@@ -8908,7 +9583,7 @@ function WhatsappSettingsTab({ rpcCall }) {
8908
9583
  if (mounted.current) setBusy(false);
8909
9584
  }
8910
9585
  }, [invoke, provision?.attemptId]);
8911
- const closeProvision = React17.useCallback(async () => {
9586
+ const closeProvision = React18.useCallback(async () => {
8912
9587
  setBusy(true);
8913
9588
  try {
8914
9589
  if (provision?.attemptId && ACTIVE_STATES3.has(provision.status)) {
@@ -8919,7 +9594,7 @@ function WhatsappSettingsTab({ rpcCall }) {
8919
9594
  if (mounted.current) setBusy(false);
8920
9595
  }
8921
9596
  }, [invoke, provision?.attemptId, provision?.status]);
8922
- React17.useEffect(() => {
9597
+ React18.useEffect(() => {
8923
9598
  const attemptId = provision?.attemptId;
8924
9599
  if (!attemptId || !ACTIVE_STATES3.has(provision.status)) return void 0;
8925
9600
  const controller = new AbortController();
@@ -8960,7 +9635,7 @@ function WhatsappSettingsTab({ rpcCall }) {
8960
9635
  if (timer) window.clearTimeout(timer);
8961
9636
  };
8962
9637
  }, [invoke, loadStatus, provision?.attemptId, provision?.status]);
8963
- const botAction = React17.useCallback(async (account, operation, endpoint, payload) => {
9638
+ const botAction = React18.useCallback(async (account, operation, endpoint, payload) => {
8964
9639
  const snapshotVersion = workspaceFence.beginMutation();
8965
9640
  setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
8966
9641
  if (operation === "reconnect") {
@@ -8974,7 +9649,13 @@ function WhatsappSettingsTab({ rpcCall }) {
8974
9649
  const value = await invoke(endpoint, payload);
8975
9650
  const snapshot = normalizeSnapshot8(value);
8976
9651
  if (mounted.current && workspaceFence.canCommitMutation(snapshotVersion)) {
8977
- setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
9652
+ setModel({
9653
+ phase: "ready",
9654
+ bots: snapshot.bots,
9655
+ totals: snapshot.totals,
9656
+ error: null,
9657
+ agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
9658
+ });
8978
9659
  if (operation === "reconnect") {
8979
9660
  setTestNoticeByBot((current) => ({
8980
9661
  ...current,
@@ -9025,6 +9706,12 @@ function WhatsappSettingsTab({ rpcCall }) {
9025
9706
  WHATSAPP_ENDPOINTS.setWorkspace,
9026
9707
  { botId: account.botId, workspace }
9027
9708
  ),
9709
+ onAgentPresetSave: (agentPreset) => botAction(
9710
+ account,
9711
+ "preset",
9712
+ WHATSAPP_ENDPOINTS.setAgentPreset,
9713
+ { botId: account.botId, agentPreset }
9714
+ ),
9028
9715
  onRequestRemove: () => setRemoveTarget(account.botId),
9029
9716
  onCancelRemove: () => setRemoveTarget(null),
9030
9717
  onConfirmRemove: async () => {
@@ -9036,7 +9723,9 @@ function WhatsappSettingsTab({ rpcCall }) {
9036
9723
  }
9037
9724
  }))))
9038
9725
  ) : null;
9039
- return h2(
9726
+ return h2(AgentPresetCatalogContext.Provider, {
9727
+ value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG
9728
+ }, h2(
9040
9729
  "section",
9041
9730
  {
9042
9731
  className: "ddt-page dwa-page dim-channelPage",
@@ -9059,7 +9748,7 @@ function WhatsappSettingsTab({ rpcCall }) {
9059
9748
  h2(Button14, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
9060
9749
  )
9061
9750
  ) : h2(
9062
- React17.Fragment,
9751
+ React18.Fragment,
9063
9752
  null,
9064
9753
  provision?.status === "pending" ? h2(QrPanel5, {
9065
9754
  provision,
@@ -9075,7 +9764,7 @@ function WhatsappSettingsTab({ rpcCall }) {
9075
9764
  }) : model.bots.length === 0 ? h2(EmptyView6, { busy, onStart: () => void startProvisioning(false) }) : null,
9076
9765
  botList
9077
9766
  )
9078
- );
9767
+ ));
9079
9768
  }
9080
9769
 
9081
9770
  // plugin-src/client/styles.js
@@ -9222,6 +9911,14 @@ var CSS11 = String.raw`
9222
9911
  .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
9912
  .dim-panel .dim-workspaceEdit:disabled { cursor: not-allowed; opacity: .55; }
9224
9913
  .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; }
9914
+ .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); }
9915
+ .dim-panel .dim-presetHeader { display: contents; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: normal; }
9916
+ .dim-panel .dim-presetHeader > span { grid-column: 1; grid-row: 1; white-space: nowrap; }
9917
+ .dim-panel .dim-presetStatus { grid-column: 2; grid-row: 1; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 12px; white-space: nowrap; }
9918
+ .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; }
9919
+ .dim-panel .dim-presetSelect:disabled { cursor: not-allowed; opacity: .55; }
9920
+ .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; }
9921
+ .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
9922
  .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
9923
  .dim-directoryPickerBackdrop, .dim-directoryPickerBackdrop *, .dim-directoryPickerBackdrop *::before, .dim-directoryPickerBackdrop *::after { box-sizing: border-box; }
9227
9924
  .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 +10149,8 @@ function IMSettingsTab({
9452
10149
  officeRpcCall,
9453
10150
  workspaceDirectoryPicker
9454
10151
  }) {
9455
- const [selected, setSelected] = React18.useState("weixin");
9456
- const githubTooltipId = React18.useId();
10152
+ const [selected, setSelected] = React19.useState("weixin");
10153
+ const githubTooltipId = React19.useId();
9457
10154
  const active = CHANNELS.find((channel4) => channel4.id === selected) ?? CHANNELS[0];
9458
10155
  return h2(
9459
10156
  WorkspaceDirectoryPickerContext.Provider,