keepwork-sdk 1.0.2 → 1.0.3

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 (37) hide show
  1. package/dist-npm/keepwork-sdk.js +1759 -389
  2. package/dist-npm/keepwork-sdk.js.map +1 -1
  3. package/dist-npm/types/index.d.ts +3 -1
  4. package/dist-npm/types/index.d.ts.map +1 -1
  5. package/dist-npm/types/src/ai-chat/AIChat.core.d.ts +21 -3
  6. package/dist-npm/types/src/ai-chat/AIChat.core.d.ts.map +1 -1
  7. package/dist-npm/types/src/ai-chat/AIChat.session.d.ts +6 -5
  8. package/dist-npm/types/src/ai-chat/AIChat.session.d.ts.map +1 -1
  9. package/dist-npm/types/src/ai-chat/AIGenerators.base.d.ts +57 -3
  10. package/dist-npm/types/src/ai-chat/AIGenerators.base.d.ts.map +1 -1
  11. package/dist-npm/types/src/ai-chat/AIGenerators.media.d.ts.map +1 -1
  12. package/dist-npm/types/src/core/keepworkSDK.core.d.ts +8 -12
  13. package/dist-npm/types/src/core/keepworkSDK.core.d.ts.map +1 -1
  14. package/dist-npm/types/src/core/keepworkSDK.d.ts +35 -28
  15. package/dist-npm/types/src/core/keepworkSDK.d.ts.map +1 -1
  16. package/dist-npm/types/src/core/keepworkSDK.pages.d.ts.map +1 -1
  17. package/dist-npm/types/src/digital-human/DigitalHuman.d.ts +30 -1
  18. package/dist-npm/types/src/digital-human/DigitalHuman.d.ts.map +1 -1
  19. package/dist-npm/types/src/digital-human/DigitalHumanFrame.d.ts +25 -2
  20. package/dist-npm/types/src/digital-human/DigitalHumanFrame.d.ts.map +1 -1
  21. package/dist-npm/types/src/rtc/AIChatRTC.session.d.ts +2 -1
  22. package/dist-npm/types/src/rtc/AIChatRTC.session.d.ts.map +1 -1
  23. package/dist-npm/types/src/rtc/LocalRTC.d.ts +146 -25
  24. package/dist-npm/types/src/rtc/LocalRTC.d.ts.map +1 -1
  25. package/dist-npm/types/src/rtc/LocalRTCController.d.ts +244 -0
  26. package/dist-npm/types/src/rtc/LocalRTCController.d.ts.map +1 -0
  27. package/dist-npm/types/src/tools/MinigameTools.d.ts +13 -0
  28. package/dist-npm/types/src/tools/MinigameTools.d.ts.map +1 -1
  29. package/dist-npm/types/src/types/api.d.ts +306 -0
  30. package/dist-npm/types/src/types/api.d.ts.map +1 -0
  31. package/dist-npm/types/src/ui/LocalAPIKeySettings.d.ts.map +1 -1
  32. package/dist-npm/types/src/ui/LoginWindow.d.ts.map +1 -1
  33. package/dist-npm/types/src/user/SocialFriends.d.ts +6 -5
  34. package/dist-npm/types/src/user/SocialFriends.d.ts.map +1 -1
  35. package/dist-npm/types/src/user/UserWorks.d.ts +18 -17
  36. package/dist-npm/types/src/user/UserWorks.d.ts.map +1 -1
  37. package/package.json +6 -2
@@ -887,7 +887,7 @@ if (typeof window !== "undefined") {
887
887
  window.LocalStorageUtil = LocalStorageUtil;
888
888
  window.StorageUtil = StorageUtil;
889
889
  }
890
- const console$p = SDKLogger.createModuleConsole("PersonalPageStore");
890
+ const console$q = SDKLogger.createModuleConsole("PersonalPageStore");
891
891
  class PersonalPageStoreBase {
892
892
  constructor(sdk) {
893
893
  // ──────────────────── 字段 ────────────────────
@@ -970,7 +970,7 @@ class PersonalPageStoreBase {
970
970
  try {
971
971
  handler(...args);
972
972
  } catch (e) {
973
- console$p.warn(`PersonalPageStore: listener for '${event}' threw:`, e);
973
+ console$q.warn(`PersonalPageStore: listener for '${event}' threw:`, e);
974
974
  }
975
975
  }
976
976
  }
@@ -1087,7 +1087,7 @@ class PersonalPageStoreBase {
1087
1087
  if (typeof mountedFolder === "string") {
1088
1088
  const parts = mountedFolder.replace(/^\/+|\/+$/g, "").split("/").filter(Boolean);
1089
1089
  if (parts.length < 2) {
1090
- console$p.warn("PersonalPageStore: mountedFolder path requires at least username/workspace");
1090
+ console$q.warn("PersonalPageStore: mountedFolder path requires at least username/workspace");
1091
1091
  return null;
1092
1092
  }
1093
1093
  const username = parts[0];
@@ -1098,7 +1098,7 @@ class PersonalPageStoreBase {
1098
1098
  if (typeof mountedFolder === "object" && mountedFolder !== null) {
1099
1099
  const m = mountedFolder;
1100
1100
  if (!m["username"] || !m["workspace"]) {
1101
- console$p.warn("PersonalPageStore: mountedFolder object requires username and workspace");
1101
+ console$q.warn("PersonalPageStore: mountedFolder object requires username and workspace");
1102
1102
  return null;
1103
1103
  }
1104
1104
  return {
@@ -1107,7 +1107,7 @@ class PersonalPageStoreBase {
1107
1107
  remoteStorePath: m["remoteStorePath"] ?? this.remoteStorePath
1108
1108
  };
1109
1109
  }
1110
- console$p.warn("PersonalPageStore: mountedFolder must be a path string, object, or null");
1110
+ console$q.warn("PersonalPageStore: mountedFolder must be a path string, object, or null");
1111
1111
  return null;
1112
1112
  }
1113
1113
  // ──────────────────── SearchPath ────────────────────
@@ -1127,10 +1127,10 @@ class PersonalPageStoreBase {
1127
1127
  if (existing) {
1128
1128
  existing.baseUrl = normalizedBaseUrl;
1129
1129
  existing.isReadonly = isReadonly;
1130
- console$p.log(`[PersonalPageStore] Updated search path: ${normalizedPrefix} -> ${normalizedBaseUrl} (readonly=${isReadonly})`);
1130
+ console$q.log(`[PersonalPageStore] Updated search path: ${normalizedPrefix} -> ${normalizedBaseUrl} (readonly=${isReadonly})`);
1131
1131
  } else {
1132
1132
  this._searchPaths.push({ prefix: normalizedPrefix, baseUrl: normalizedBaseUrl, isReadonly });
1133
- console$p.log(`[PersonalPageStore] Added search path: ${normalizedPrefix} -> ${normalizedBaseUrl} (readonly=${isReadonly})`);
1133
+ console$q.log(`[PersonalPageStore] Added search path: ${normalizedPrefix} -> ${normalizedBaseUrl} (readonly=${isReadonly})`);
1134
1134
  }
1135
1135
  }
1136
1136
  /**
@@ -1184,7 +1184,7 @@ class PersonalPageStoreBase {
1184
1184
  if (!resp.ok) continue;
1185
1185
  const text = await resp.text();
1186
1186
  if (text != null) {
1187
- console$p.log(`[PersonalPageStore] SearchPath found: ${effectiveName} -> ${fileUrl}`);
1187
+ console$q.log(`[PersonalPageStore] SearchPath found: ${effectiveName} -> ${fileUrl}`);
1188
1188
  const pageKey = this._toInternalPageKey(pageName);
1189
1189
  this.personalPageData[pageKey] = this.personalPageData[pageKey] ?? {};
1190
1190
  this.setValueByPath(this.personalPageData[pageKey], "content", text);
@@ -1193,7 +1193,7 @@ class PersonalPageStoreBase {
1193
1193
  } catch {
1194
1194
  }
1195
1195
  }
1196
- console$p.log(`[PersonalPageStore] SearchPath not found: ${effectiveName}`);
1196
+ console$q.log(`[PersonalPageStore] SearchPath not found: ${effectiveName}`);
1197
1197
  this._searchPathNotFound.add(effectiveName);
1198
1198
  return null;
1199
1199
  }
@@ -1457,7 +1457,7 @@ class PersonalPageStoreBase {
1457
1457
  const newUsername = username || null;
1458
1458
  if (this.overrideUsername !== newUsername) this.reset();
1459
1459
  this.overrideUsername = newUsername;
1460
- console$p.log(`PersonalPageStore: Override username set to: ${this.overrideUsername ?? "current user"}`);
1460
+ console$q.log(`PersonalPageStore: Override username set to: ${this.overrideUsername ?? "current user"}`);
1461
1461
  }
1462
1462
  /**
1463
1463
  * 获取当前使用的用户名(override 或当前登录用户)。
@@ -1503,7 +1503,7 @@ class PersonalPageStoreBase {
1503
1503
  if (this.userProfileLoaded) return;
1504
1504
  try {
1505
1505
  if (!this.sdk.token) {
1506
- console$p.log("personalPageStore: No token set, using anonymous mode");
1506
+ console$q.log("personalPageStore: No token set, using anonymous mode");
1507
1507
  return;
1508
1508
  }
1509
1509
  await ((_b2 = (_a2 = this.sdk).getUserProfile) == null ? void 0 : _b2.call(_a2, { useCache: true }));
@@ -4579,7 +4579,7 @@ class AudioEngine {
4579
4579
  };
4580
4580
  }
4581
4581
  }
4582
- const console$o = SDKLogger.createModuleConsole("Speech");
4582
+ const console$p = SDKLogger.createModuleConsole("Speech");
4583
4583
  let _md5 = null;
4584
4584
  async function getMd5() {
4585
4585
  if (_md5) return _md5;
@@ -4651,7 +4651,7 @@ class Speech {
4651
4651
  if (valid.includes(mode)) {
4652
4652
  this.fallbackMode = mode;
4653
4653
  } else {
4654
- console$o.warn(`Invalid fallbackMode "${mode}", expected one of: ${valid.join(", ")}`);
4654
+ console$p.warn(`Invalid fallbackMode "${mode}", expected one of: ${valid.join(", ")}`);
4655
4655
  }
4656
4656
  }
4657
4657
  /** 获取共享 AudioEngine 实例(优先 sdk.audioEngine,否则 AudioEngine.getShared())。 */
@@ -4684,7 +4684,7 @@ class Speech {
4684
4684
  }
4685
4685
  const OfflineCtx = typeof window !== "undefined" ? window.OfflineAudioContext || window.webkitOfflineAudioContext : null;
4686
4686
  if (!OfflineCtx) {
4687
- console$o.warn("当前浏览器不支持 OfflineAudioContext,返回原始采样率音频");
4687
+ console$p.warn("当前浏览器不支持 OfflineAudioContext,返回原始采样率音频");
4688
4688
  return audioBuffer;
4689
4689
  }
4690
4690
  const frameCount = Math.max(1, Math.ceil(audioBuffer.duration * nextSampleRate));
@@ -5013,7 +5013,7 @@ class Speech {
5013
5013
  }
5014
5014
  });
5015
5015
  } catch (error) {
5016
- console$o.error("停止音频播放时发生错误:", error);
5016
+ console$p.error("停止音频播放时发生错误:", error);
5017
5017
  }
5018
5018
  }
5019
5019
  /** 停止所有音频(stopAllAudio 的别名)。 */
@@ -5038,10 +5038,10 @@ class Speech {
5038
5038
  audio.src = audioUrl;
5039
5039
  void ((_b2 = (_a2 = this.resumeSharedAudioEngine()) == null ? void 0 : _a2.finally) == null ? void 0 : _b2.call(_a2, () => {
5040
5040
  const p = audio.play();
5041
- if (p) p.catch((err) => console$o.error("Error playing YouDao audio:", err));
5041
+ if (p) p.catch((err) => console$p.error("Error playing YouDao audio:", err));
5042
5042
  }));
5043
5043
  } catch (e) {
5044
- console$o.error("Error with YouDao API:", e);
5044
+ console$p.error("Error with YouDao API:", e);
5045
5045
  }
5046
5046
  }
5047
5047
  // ──────────────────── 语音转文字(STT)────────────────────
@@ -5058,10 +5058,10 @@ class Speech {
5058
5058
  maxDuration: 60,
5059
5059
  sampleRate: 16e3,
5060
5060
  format: "wav",
5061
- onStart: () => console$o.log("录音开始"),
5062
- onStop: () => console$o.log("录音停止"),
5063
- onError: (e) => console$o.error("录音错误:", e),
5064
- onResult: (r) => console$o.log("识别结果:", r),
5061
+ onStart: () => console$p.log("录音开始"),
5062
+ onStop: () => console$p.log("录音停止"),
5063
+ onError: (e) => console$p.error("录音错误:", e),
5064
+ onResult: (r) => console$p.log("识别结果:", r),
5065
5065
  ...options
5066
5066
  };
5067
5067
  if (!((_a2 = navigator.mediaDevices) == null ? void 0 : _a2.getUserMedia)) throw new Error("当前浏览器不支持录音功能,请使用现代浏览器并确保在HTTPS环境下访问");
@@ -5295,7 +5295,7 @@ class Speech {
5295
5295
  });
5296
5296
  }
5297
5297
  }
5298
- const console$n = SDKLogger.createModuleConsole("MqttManager");
5298
+ const console$o = SDKLogger.createModuleConsole("MqttManager");
5299
5299
  class MqttManager {
5300
5300
  constructor() {
5301
5301
  __publicField(this, "client", null);
@@ -5367,11 +5367,11 @@ class MqttManager {
5367
5367
  }
5368
5368
  }
5369
5369
  } catch (e) {
5370
- console$n.error("Failed to load MQTT config from storage:", e);
5370
+ console$o.error("Failed to load MQTT config from storage:", e);
5371
5371
  }
5372
5372
  }
5373
5373
  if (!effectiveConfig) {
5374
- console$n.error("MQTT connect: No config provided and no stored config");
5374
+ console$o.error("MQTT connect: No config provided and no stored config");
5375
5375
  this.status = "error";
5376
5376
  this.emit("statusChange", this.status);
5377
5377
  return;
@@ -5379,7 +5379,7 @@ class MqttManager {
5379
5379
  try {
5380
5380
  await this.LoadMQTT();
5381
5381
  } catch (e) {
5382
- console$n.error("Failed to load MQTT library:", e);
5382
+ console$o.error("Failed to load MQTT library:", e);
5383
5383
  this.status = "error";
5384
5384
  this.emit("statusChange", this.status);
5385
5385
  return;
@@ -5403,7 +5403,7 @@ class MqttManager {
5403
5403
  reconnectPeriod: 5e3,
5404
5404
  connectTimeout: 30 * 1e3
5405
5405
  };
5406
- console$n.log("Connecting to MQTT with options:", connectOptions);
5406
+ console$o.log("Connecting to MQTT with options:", connectOptions);
5407
5407
  try {
5408
5408
  this.client = mqtt.connect(connectOptions);
5409
5409
  this.client.on("connect", () => {
@@ -5411,7 +5411,7 @@ class MqttManager {
5411
5411
  topics == null ? void 0 : topics.forEach((topic) => this.subscribe(topic));
5412
5412
  });
5413
5413
  this.client.on("error", (err) => {
5414
- console$n.error("MQTT Error:", err);
5414
+ console$o.error("MQTT Error:", err);
5415
5415
  this.status = "error";
5416
5416
  this.emit("statusChange", this.status);
5417
5417
  this.emit("error", err);
@@ -5425,7 +5425,7 @@ class MqttManager {
5425
5425
  this.client.on("reconnect", () => {
5426
5426
  var _a3;
5427
5427
  this.retryCount++;
5428
- console$n.log(`MQTT Reconnecting... (${this.retryCount}/${this.maxRetries})`);
5428
+ console$o.log(`MQTT Reconnecting... (${this.retryCount}/${this.maxRetries})`);
5429
5429
  if (this.retryCount > this.maxRetries) {
5430
5430
  (_a3 = this.client) == null ? void 0 : _a3.end();
5431
5431
  this.status = "error";
@@ -5434,21 +5434,21 @@ class MqttManager {
5434
5434
  }
5435
5435
  });
5436
5436
  } catch (e) {
5437
- console$n.error("MQTT Connection Exception:", e);
5437
+ console$o.error("MQTT Connection Exception:", e);
5438
5438
  this.status = "error";
5439
5439
  this.emit("statusChange", this.status);
5440
5440
  }
5441
5441
  }
5442
5442
  /** @private 连接成功回调。 */
5443
5443
  OnConnect() {
5444
- console$n.log("MQTT Connected");
5444
+ console$o.log("MQTT Connected");
5445
5445
  this.status = "connected";
5446
5446
  this.retryCount = 0;
5447
5447
  this.emit("statusChange", this.status);
5448
5448
  }
5449
5449
  /** @private 连接关闭回调。 */
5450
5450
  OnClose() {
5451
- console$n.log("MQTT Closed");
5451
+ console$o.log("MQTT Closed");
5452
5452
  if (this.status !== "disconnected") {
5453
5453
  this.status = "disconnected";
5454
5454
  this.emit("statusChange", this.status);
@@ -5459,7 +5459,7 @@ class MqttManager {
5459
5459
  */
5460
5460
  OnMessage(topic, message) {
5461
5461
  const msgStr = String(message);
5462
- console$n.log(`message ${msgStr}
5462
+ console$o.log(`message ${msgStr}
5463
5463
  On topic: ${topic}`);
5464
5464
  localStorage.setItem(`mqtt_msg_${topic}`, msgStr);
5465
5465
  this.emit("message", { topic, message: msgStr });
@@ -5486,7 +5486,7 @@ On topic: ${topic}`);
5486
5486
  var _a2;
5487
5487
  if ((_a2 = this.client) == null ? void 0 : _a2.connected) {
5488
5488
  this.client.subscribe(topic, (err) => {
5489
- if (!err) console$n.log(`Subscribed to ${topic}`);
5489
+ if (!err) console$o.log(`Subscribed to ${topic}`);
5490
5490
  });
5491
5491
  }
5492
5492
  }
@@ -6282,7 +6282,7 @@ class SandboxToolEnv {
6282
6282
  });
6283
6283
  }
6284
6284
  }
6285
- const console$m = SDKLogger.createModuleConsole("AgentTool");
6285
+ const console$n = SDKLogger.createModuleConsole("AgentTool");
6286
6286
  class AgentTool {
6287
6287
  constructor(sdk) {
6288
6288
  __publicField(this, "sdk");
@@ -6322,7 +6322,7 @@ class AgentTool {
6322
6322
  } = args;
6323
6323
  if (!task) return "Failed: 'task' is a required parameter.";
6324
6324
  const resolvedAgentName = agentName || this.currentAgentName(session);
6325
- console$m.log(`[CopilotTools] runAsyncAgentTask session='${session.name ?? "root"}' target='${resolvedAgentName}' callbackMode=${callbackMode ?? "delay"} task=${this.summarizeDebugValue(task)}`);
6325
+ console$n.log(`[CopilotTools] runAsyncAgentTask session='${session.name ?? "root"}' target='${resolvedAgentName}' callbackMode=${callbackMode ?? "delay"} task=${this.summarizeDebugValue(task)}`);
6326
6326
  const expandedTask = await this.expandTemplate(session, task, "task");
6327
6327
  const expandedSystemPrompt = await this.expandTemplate(session, systemPrompt, "systemPrompt");
6328
6328
  const router = (_a2 = this.sdk) == null ? void 0 : _a2.agentRouter;
@@ -6447,11 +6447,11 @@ class AgentTool {
6447
6447
  if (!value.includes("${") && !/^\s*\/\S+/.test(value)) return value;
6448
6448
  try {
6449
6449
  const expanded = await session.sandbox.processTemplate(value);
6450
- console$m.log(`[CopilotTools] runAsyncAgentTask ${label} expanded via sandbox: ${this.summarizeDebugValue(expanded)}`);
6450
+ console$n.log(`[CopilotTools] runAsyncAgentTask ${label} expanded via sandbox: ${this.summarizeDebugValue(expanded)}`);
6451
6451
  return expanded;
6452
6452
  } catch (e) {
6453
6453
  if (e && e["isRestartAgentSignal"]) throw e;
6454
- console$m.warn(`[CopilotTools] runAsyncAgentTask: ${label} template expansion failed. Error: ${e.message}`);
6454
+ console$n.warn(`[CopilotTools] runAsyncAgentTask: ${label} template expansion failed. Error: ${e.message}`);
6455
6455
  return value;
6456
6456
  }
6457
6457
  }
@@ -6493,7 +6493,7 @@ ${resultText}`;
6493
6493
  try {
6494
6494
  await ((_a3 = target == null ? void 0 : target["sendMessage"]) == null ? void 0 : _a3.call(target, payload));
6495
6495
  } catch (e) {
6496
- console$m.error(`[CopilotTools] ${targetLabel}.sendMessage failed:`, e);
6496
+ console$n.error(`[CopilotTools] ${targetLabel}.sendMessage failed:`, e);
6497
6497
  }
6498
6498
  }).catch(async (e) => {
6499
6499
  var _a3;
@@ -6501,7 +6501,7 @@ ${resultText}`;
6501
6501
  try {
6502
6502
  await ((_a3 = target == null ? void 0 : target["sendMessage"]) == null ? void 0 : _a3.call(target, payload));
6503
6503
  } catch (e2) {
6504
- console$m.error(`[CopilotTools] ${targetLabel}.sendMessage failed:`, e2);
6504
+ console$n.error(`[CopilotTools] ${targetLabel}.sendMessage failed:`, e2);
6505
6505
  }
6506
6506
  });
6507
6507
  return `Task submitted to agent '${agentName}'. The agent is working in a brand-new background session. Results will be delivered as a message via ${targetLabel} when complete.`;
@@ -6587,7 +6587,7 @@ __publicField(AgentTool, "definitions", [
6587
6587
  }
6588
6588
  }
6589
6589
  ]);
6590
- const console$l = SDKLogger.createModuleConsole("PersonalPageTool");
6590
+ const console$m = SDKLogger.createModuleConsole("PersonalPageTool");
6591
6591
  class PersonalPageTool {
6592
6592
  constructor(sdk) {
6593
6593
  __publicField(this, "sdk");
@@ -6630,19 +6630,19 @@ class PersonalPageTool {
6630
6630
  }
6631
6631
  if (name === "personal_page_load") {
6632
6632
  try {
6633
- console$l.log(`[PersonalPageTool] Loading data from file '${filePageName}', key '${storageKey}'`);
6633
+ console$m.log(`[PersonalPageTool] Loading data from file '${filePageName}', key '${storageKey}'`);
6634
6634
  if (!this.sdk.personalPageStore) throw new Error("SDK PersonalPageStore not available");
6635
6635
  const data = storageKey ? await this.sdk.personalPageStore.loadPageData(filePageName, storageKey) : await this.sdk.personalPageStore.loadPageData(filePageName);
6636
6636
  return JSON.stringify(data);
6637
6637
  } catch (e) {
6638
- console$l.error("[PersonalPageTool] Load failed:", e);
6638
+ console$m.error("[PersonalPageTool] Load failed:", e);
6639
6639
  return `Failed to load data: ${e.message}`;
6640
6640
  }
6641
6641
  }
6642
6642
  if (name === "personal_page_save") {
6643
6643
  const { data, bForceFlush } = args;
6644
6644
  try {
6645
- console$l.log(`[PersonalPageTool] Saving data to file '${filePageName}', key '${storageKey}'`, data);
6645
+ console$m.log(`[PersonalPageTool] Saving data to file '${filePageName}', key '${storageKey}'`, data);
6646
6646
  if (!this.sdk.personalPageStore) throw new Error("SDK PersonalPageStore not available");
6647
6647
  await this.sdk.personalPageStore.savePageData(
6648
6648
  filePageName,
@@ -6652,7 +6652,7 @@ class PersonalPageTool {
6652
6652
  );
6653
6653
  return "Data saved successfully.";
6654
6654
  } catch (e) {
6655
- console$l.error("[PersonalPageTool] Save failed:", e);
6655
+ console$m.error("[PersonalPageTool] Save failed:", e);
6656
6656
  return `Failed to save data: ${e.message}`;
6657
6657
  }
6658
6658
  }
@@ -6712,7 +6712,7 @@ __publicField(PersonalPageTool, "definitions", [
6712
6712
  }
6713
6713
  }
6714
6714
  ]);
6715
- const console$k = SDKLogger.createModuleConsole("ExecuteTool");
6715
+ const console$l = SDKLogger.createModuleConsole("ExecuteTool");
6716
6716
  const AsyncFunction = Object.getPrototypeOf(async function() {
6717
6717
  }).constructor;
6718
6718
  class ExecuteTool {
@@ -6762,7 +6762,7 @@ class ExecuteTool {
6762
6762
  async runAppCmd(code, config = {}) {
6763
6763
  const logs = [];
6764
6764
  try {
6765
- const originalConsole = console$k;
6765
+ const originalConsole = console$l;
6766
6766
  const wrappedConsole = Object.create(originalConsole);
6767
6767
  const consoleMethods = ["log", "info", "warn", "error", "debug"];
6768
6768
  for (const level of consoleMethods) {
@@ -7901,7 +7901,7 @@ __publicField(AppTools, "definitions", [
7901
7901
  }
7902
7902
  }
7903
7903
  ]);
7904
- const console$j = SDKLogger.createModuleConsole("AIChat");
7904
+ const console$k = SDKLogger.createModuleConsole("AIChat");
7905
7905
  class AIChat {
7906
7906
  constructor(sdk) {
7907
7907
  __publicField(this, "sdk");
@@ -7993,7 +7993,7 @@ class AIChat {
7993
7993
  try {
7994
7994
  token = window.localStorage.getItem("token");
7995
7995
  } catch (e) {
7996
- console$j.warn("Failed to get token from localStorage:", e);
7996
+ console$k.warn("Failed to get token from localStorage:", e);
7997
7997
  }
7998
7998
  }
7999
7999
  if (token) headers["authorization"] = `Bearer ${token}`;
@@ -8107,7 +8107,7 @@ class AIChat {
8107
8107
  const session = options._session;
8108
8108
  for (const toolCall of toolCalls) {
8109
8109
  if (session == null ? void 0 : session._restartSignal) {
8110
- console$j.log("[AIChat] _restartSignal detected, aborting tool loop");
8110
+ console$k.log("[AIChat] _restartSignal detected, aborting tool loop");
8111
8111
  break;
8112
8112
  }
8113
8113
  const fnName = (_d = toolCall.function) == null ? void 0 : _d.name;
@@ -8120,14 +8120,14 @@ class AIChat {
8120
8120
  if (session) session["_restartSignal"] = error;
8121
8121
  throw error;
8122
8122
  }
8123
- console$j.error("Tool call execution failed:", error);
8123
+ console$k.error("Tool call execution failed:", error);
8124
8124
  const errorResult = { error: error.message ?? "Tool execution failed" };
8125
8125
  toolResults.push({ tool_call_id: toolCall.id, result: errorResult });
8126
8126
  (_f = options.onToolResult) == null ? void 0 : _f.call(options, { name: fnName, toolCallId: toolCall.id, result: errorResult });
8127
8127
  }
8128
8128
  }
8129
8129
  if (session == null ? void 0 : session._restartSignal) {
8130
- console$j.log("[AIChat] _restartSignal detected, aborting chat recursion");
8130
+ console$k.log("[AIChat] _restartSignal detected, aborting chat recursion");
8131
8131
  return assistantContent ?? "";
8132
8132
  }
8133
8133
  for (const toolResult of toolResults) {
@@ -8204,7 +8204,7 @@ class AIChat {
8204
8204
  throw new Error("createSession not yet initialized — ensure AIChat.session.ts is imported");
8205
8205
  }
8206
8206
  }
8207
- const console$i = SDKLogger.createModuleConsole("ChildSessionMixin");
8207
+ const console$j = SDKLogger.createModuleConsole("ChildSessionMixin");
8208
8208
  function _generateUUID() {
8209
8209
  if (typeof crypto !== "undefined" && crypto.randomUUID) return crypto.randomUUID();
8210
8210
  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
@@ -8275,7 +8275,7 @@ const childSessionMethods = {
8275
8275
  if (this.sandbox) childSession["sandbox"] = this.sandbox;
8276
8276
  const entry = { session: childSession, queue: [], isRunning: false };
8277
8277
  this._childSessions[name] = entry;
8278
- console$i.log(`[ChildSessionMixin] Child agent '${name}' created`);
8278
+ console$j.log(`[ChildSessionMixin] Child agent '${name}' created`);
8279
8279
  return entry;
8280
8280
  },
8281
8281
  /**
@@ -8302,7 +8302,7 @@ const childSessionMethods = {
8302
8302
  2. ${task}`;
8303
8303
  if (tools) last.tools = [.../* @__PURE__ */ new Set([...last.tools, ...tools])];
8304
8304
  last.maxIterations = Math.max(last.maxIterations, maxIterations);
8305
- console$i.log(`[ChildSessionMixin] Merged task into queue for child '${name}'`);
8305
+ console$j.log(`[ChildSessionMixin] Merged task into queue for child '${name}'`);
8306
8306
  return last.promise;
8307
8307
  }
8308
8308
  let resolve;
@@ -8377,15 +8377,15 @@ const childSessionMethods = {
8377
8377
  const result = typeof response === "string" ? response : (response == null ? void 0 : response["result"]) ?? "";
8378
8378
  const taskSummary = taskObj.description ?? (taskObj.task.length > 80 ? taskObj.task.slice(0, 80) + "..." : taskObj.task);
8379
8379
  this._pendingChildResults.push({ agentName: name, taskId: taskObj.id, taskSummary, result });
8380
- console$i.log(`[ChildSessionMixin] Child '${name}' completed task ${taskObj.id}; pendingChildResults=${this._pendingChildResults.length}; result=${_summarize(result)}`);
8380
+ console$j.log(`[ChildSessionMixin] Child '${name}' completed task ${taskObj.id}; pendingChildResults=${this._pendingChildResults.length}; result=${_summarize(result)}`);
8381
8381
  (_h = self._handleChildCallback) == null ? void 0 : _h.call(self, taskObj.callbackMode, taskObj.debounceSeconds);
8382
8382
  taskObj.resolve(result);
8383
8383
  } catch (e) {
8384
- console$i.error(`[ChildSessionMixin] Child '${name}' task ${taskObj.id} failed:`, e);
8384
+ console$j.error(`[ChildSessionMixin] Child '${name}' task ${taskObj.id} failed:`, e);
8385
8385
  const errorResult = { error: e.message ?? "Child agent task failed" };
8386
8386
  const taskSummary = taskObj.description ?? (taskObj.task.length > 80 ? taskObj.task.slice(0, 80) + "..." : taskObj.task);
8387
8387
  this._pendingChildResults.push({ agentName: name, taskId: taskObj.id, taskSummary, result: errorResult });
8388
- console$i.warn(`[ChildSessionMixin] Child '${name}' failure queued; taskId=${taskObj.id}; pendingChildResults=${this._pendingChildResults.length}; result=${_summarize(errorResult)}`);
8388
+ console$j.warn(`[ChildSessionMixin] Child '${name}' failure queued; taskId=${taskObj.id}; pendingChildResults=${this._pendingChildResults.length}; result=${_summarize(errorResult)}`);
8389
8389
  (_i = self._handleChildCallback) == null ? void 0 : _i.call(self, taskObj.callbackMode, taskObj.debounceSeconds);
8390
8390
  taskObj.reject(e);
8391
8391
  }
@@ -8415,7 +8415,7 @@ const childSessionMethods = {
8415
8415
  try {
8416
8416
  this.onChildStream(event);
8417
8417
  } catch (e) {
8418
- console$i.warn("[ChildSessionMixin] onChildStream callback error:", e);
8418
+ console$j.warn("[ChildSessionMixin] onChildStream callback error:", e);
8419
8419
  }
8420
8420
  }
8421
8421
  },
@@ -8435,7 +8435,7 @@ const childSessionMethods = {
8435
8435
  const results = this._pendingChildResults;
8436
8436
  this._pendingChildResults = [];
8437
8437
  if (results.length > 0) {
8438
- console$i.log(
8438
+ console$j.log(
8439
8439
  `[ChildSessionMixin] _consumePendingChildResults session='${this.name ?? "root"}' consumed ${results.length} result(s): ` + results.map((item) => `${item.agentName}:${item.taskId}:${_summarize(item.result, 80)}`).join(" | ")
8440
8440
  );
8441
8441
  }
@@ -8452,7 +8452,7 @@ const childSessionMethods = {
8452
8452
  */
8453
8453
  _handleChildCallback(mode, debounceSeconds) {
8454
8454
  var _a2, _b2;
8455
- console$i.log(
8455
+ console$j.log(
8456
8456
  `[ChildSessionMixin] _handleChildCallback session='${this.name ?? "root"}' mode=${mode ?? "delay"} debounceSeconds=${debounceSeconds ?? 5} pendingChildResults=${this._pendingChildResults.length}`
8457
8457
  );
8458
8458
  const selfCb = this;
@@ -8468,7 +8468,7 @@ const childSessionMethods = {
8468
8468
  * @private
8469
8469
  */
8470
8470
  async _triggerImmediateCallback() {
8471
- console$i.warn("[ChildSessionMixin] _triggerImmediateCallback not overridden — child results will accumulate until next send.");
8471
+ console$j.warn("[ChildSessionMixin] _triggerImmediateCallback not overridden — child results will accumulate until next send.");
8472
8472
  },
8473
8473
  /**
8474
8474
  * 延迟执行 `_triggerImmediateCallback`(用于 debounce 模式)。
@@ -8481,7 +8481,7 @@ const childSessionMethods = {
8481
8481
  await this._triggerImmediateCallback();
8482
8482
  }, seconds * 1e3);
8483
8483
  this._debounceTimers.push(timerId);
8484
- console$i.log(`[ChildSessionMixin] Debounce callback set: ${seconds}s pendingChildResults=${this._pendingChildResults.length}`);
8484
+ console$j.log(`[ChildSessionMixin] Debounce callback set: ${seconds}s pendingChildResults=${this._pendingChildResults.length}`);
8485
8485
  },
8486
8486
  /**
8487
8487
  * 取消所有待处理的 debounce 定时器。
@@ -8535,7 +8535,137 @@ const childSessionMethods = {
8535
8535
  this._pendingChildResults = [];
8536
8536
  }
8537
8537
  };
8538
- const console$h = SDKLogger.createModuleConsole("ImageUtils");
8538
+ function detectFormat(url) {
8539
+ if (!url || typeof url !== "string") return null;
8540
+ const path = url.split(/[?#]/)[0].toLowerCase();
8541
+ if (path.endsWith(".json")) return "json";
8542
+ if (path.endsWith(".yml") || path.endsWith(".yaml")) return "yml";
8543
+ if (path.endsWith(".md")) return "md";
8544
+ return null;
8545
+ }
8546
+ function normalize(config) {
8547
+ if (!config || typeof config !== "object") return config;
8548
+ if (config.content && !config.system_prompt) {
8549
+ const body = String(config.content).trim();
8550
+ if (body) config.system_prompt = body;
8551
+ }
8552
+ delete config.content;
8553
+ if (config.workspace) {
8554
+ if (!config.tools) config.tools = {};
8555
+ if (!config.tools.fileOps) config.tools.fileOps = { enabled: false };
8556
+ if (!config.tools.fileOps.workspace) {
8557
+ config.tools.fileOps.workspace = config.workspace;
8558
+ }
8559
+ }
8560
+ if (config.mountFolder) {
8561
+ if (!config.tools) config.tools = {};
8562
+ if (!config.tools.fileOps) config.tools.fileOps = { enabled: false };
8563
+ if (!config.tools.fileOps.mountFolder) {
8564
+ config.tools.fileOps.mountFolder = config.mountFolder;
8565
+ }
8566
+ }
8567
+ if (config.history_length !== void 0 && config.historyLength === void 0) {
8568
+ config.historyLength = config.history_length;
8569
+ }
8570
+ delete config.history_length;
8571
+ return config;
8572
+ }
8573
+ function parseTable(tableText) {
8574
+ if (!tableText || typeof tableText !== "string") return {};
8575
+ const lines = tableText.split("\n").map((l) => l.trim()).filter((l) => l.startsWith("|"));
8576
+ if (lines.length < 2) return {};
8577
+ const headerCells = lines[0].split("|").map((c) => c.trim()).filter(Boolean);
8578
+ const dataLines = lines.slice(2);
8579
+ const rows = dataLines.map((line) => {
8580
+ const cells = line.split("|").map((c) => c.trim()).filter(Boolean);
8581
+ const row = {};
8582
+ headerCells.forEach((h, i) => {
8583
+ row[h] = cells[i] !== void 0 ? cells[i] : "";
8584
+ });
8585
+ return row;
8586
+ });
8587
+ if (headerCells.length === 2) {
8588
+ const [keyCol, valCol] = headerCells;
8589
+ const isKV = keyCol.toLowerCase() === "key" && valCol.toLowerCase() === "value";
8590
+ if (isKV) {
8591
+ const obj = {};
8592
+ for (const row of rows) {
8593
+ const k = row[keyCol];
8594
+ if (k) obj[k] = _coerceValue(row[valCol]);
8595
+ }
8596
+ return obj;
8597
+ }
8598
+ }
8599
+ return rows;
8600
+ }
8601
+ function _coerceValue(v) {
8602
+ if (v === "true") return true;
8603
+ if (v === "false") return false;
8604
+ if (v === "null" || v === "") return null;
8605
+ const n = Number(v);
8606
+ if (!isNaN(n) && v.trim() !== "") return n;
8607
+ return v;
8608
+ }
8609
+ function parse(text, formatHint) {
8610
+ if (!text || typeof text !== "string") return {};
8611
+ const trimmed = text.trim();
8612
+ if (formatHint === "json" || trimmed.startsWith("{") || trimmed.startsWith("[")) {
8613
+ return normalize(JSON.parse(trimmed));
8614
+ }
8615
+ if (trimmed.startsWith("---\n") || trimmed.startsWith("---\r\n")) {
8616
+ const YML = _getYMLParser();
8617
+ if (YML) return normalize(YML.yamlToObject(trimmed, true));
8618
+ }
8619
+ const firstLine = trimmed.split("\n").find((l) => l.trim());
8620
+ if (firstLine && firstLine.trim().startsWith("|")) {
8621
+ return normalize(parseTable(trimmed));
8622
+ }
8623
+ try {
8624
+ return normalize(JSON.parse(trimmed));
8625
+ } catch {
8626
+ const YML = _getYMLParser();
8627
+ if (YML) return normalize(YML.yamlToObject(trimmed, true));
8628
+ }
8629
+ return {};
8630
+ }
8631
+ async function fetchConfig(source) {
8632
+ if (source && typeof source === "object") {
8633
+ return normalize({ ...source });
8634
+ }
8635
+ if (typeof source !== "string" || !source.trim()) {
8636
+ throw new Error("AgentConfig.fetch: source must be a non-empty string or object");
8637
+ }
8638
+ const trimmed = source.trim();
8639
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
8640
+ return normalize(JSON.parse(trimmed));
8641
+ }
8642
+ const resp = await fetch(trimmed);
8643
+ if (!resp.ok) {
8644
+ throw new Error(`AgentConfig.fetch: fetch failed (${resp.status}) for ${trimmed}`);
8645
+ }
8646
+ let text = await resp.text();
8647
+ text = text.replace(
8648
+ /<script[^>]*\/___vscode_livepreview_injected_script[^>]*><\/script>\s*/gi,
8649
+ ""
8650
+ );
8651
+ const format = detectFormat(trimmed);
8652
+ return parse(text, format);
8653
+ }
8654
+ function _getYMLParser() {
8655
+ if (typeof YMLParser !== "undefined") return YMLParser;
8656
+ if (typeof window !== "undefined" && window.YMLParser) {
8657
+ return window.YMLParser;
8658
+ }
8659
+ return null;
8660
+ }
8661
+ const AgentConfig = {
8662
+ detectFormat,
8663
+ normalize,
8664
+ parse,
8665
+ parseTable,
8666
+ fetch: fetchConfig
8667
+ };
8668
+ const console$i = SDKLogger.createModuleConsole("ImageUtils");
8539
8669
  const DEFAULT_MAX_BYTES = 32 * 1024;
8540
8670
  function base64ByteSize(base64) {
8541
8671
  const len = base64.length;
@@ -8578,7 +8708,7 @@ async function compressBase64Image(inputBase64, options = {}) {
8578
8708
  for (const quality of QUALITY_STEPS) {
8579
8709
  const result = canvasToBase64(img, width, height, quality);
8580
8710
  if (result.byteSize <= maxBytes) {
8581
- console$h.log(
8711
+ console$i.log(
8582
8712
  `[ImageUtils] Compressed image: ${width}x${height} q=${quality} → ${(result.byteSize / 1024).toFixed(1)} KB`
8583
8713
  );
8584
8714
  return result.base64;
@@ -8588,7 +8718,7 @@ async function compressBase64Image(inputBase64, options = {}) {
8588
8718
  height = Math.max(1, Math.floor(height / 2));
8589
8719
  }
8590
8720
  const final = canvasToBase64(img, width, height, 0.2);
8591
- console$h.warn(
8721
+ console$i.warn(
8592
8722
  `[ImageUtils] Could not compress below ${maxBytes} bytes; final size ${final.byteSize}`
8593
8723
  );
8594
8724
  return final.base64;
@@ -8658,7 +8788,7 @@ async function uploadTempVisionImage(base64OrDataURI, options = {}) {
8658
8788
  try {
8659
8789
  const headResp = await fetch(cdnUrl, { method: "HEAD" });
8660
8790
  if (headResp.ok) {
8661
- console$h.log(`[ImageUtils] File already exists at ${cdnUrl}, skipping upload`);
8791
+ console$i.log(`[ImageUtils] File already exists at ${cdnUrl}, skipping upload`);
8662
8792
  return cdnUrl;
8663
8793
  }
8664
8794
  } catch {
@@ -8686,7 +8816,7 @@ async function uploadTempVisionImage(base64OrDataURI, options = {}) {
8686
8816
  if (!uploadResp.ok) {
8687
8817
  throw new Error(`[ImageUtils] Qiniu upload failed: HTTP ${uploadResp.status}`);
8688
8818
  }
8689
- console$h.log(`[ImageUtils] Uploaded to ${cdnUrl} (${(blob.size / 1024).toFixed(1)} KB)`);
8819
+ console$i.log(`[ImageUtils] Uploaded to ${cdnUrl} (${(blob.size / 1024).toFixed(1)} KB)`);
8690
8820
  return cdnUrl;
8691
8821
  }
8692
8822
  let _cachedMd5 = null;
@@ -8706,7 +8836,7 @@ async function _getMd5() {
8706
8836
  }
8707
8837
  return _cachedMd5;
8708
8838
  }
8709
- const console$g = SDKLogger.createModuleConsole("AIChat");
8839
+ const console$h = SDKLogger.createModuleConsole("AIChat");
8710
8840
  class ChatSession {
8711
8841
  constructor(aiChat, options = {}) {
8712
8842
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -8795,7 +8925,7 @@ class ChatSession {
8795
8925
  if (router) {
8796
8926
  this._registeredAsAgent = ((_b2 = router.register) == null ? void 0 : _b2.call(router, this.name, this)) ?? false;
8797
8927
  if (!this._registeredAsAgent) {
8798
- console$g.warn(`[ChatSession] Failed to register agent '${this.name}' — name may already be taken.`);
8928
+ console$h.warn(`[ChatSession] Failed to register agent '${this.name}' — name may already be taken.`);
8799
8929
  }
8800
8930
  }
8801
8931
  }
@@ -8844,7 +8974,7 @@ class ChatSession {
8844
8974
  this._workspaceExplicitlySet = true;
8845
8975
  this.workspace = nextWorkspace;
8846
8976
  if (this.sandbox) this.sandbox.workspace = nextSandboxWorkspace;
8847
- console$g.log(`[ChatSession] Workspace set to: ${this.workspace ?? "(none)"}`);
8977
+ console$h.log(`[ChatSession] Workspace set to: ${this.workspace ?? "(none)"}`);
8848
8978
  }
8849
8979
  /**
8850
8980
  * 设置只读回退层的 mountFolder 配置。
@@ -8853,7 +8983,7 @@ class ChatSession {
8853
8983
  setMountFolder(mountFolderConfig) {
8854
8984
  this.mountFolder = mountFolderConfig ?? null;
8855
8985
  if (this.sandbox) this.sandbox.mountFolder = this.mountFolder;
8856
- console$g.log(`[ChatSession] MountFolder set to: ${this.mountFolder ?? "(none)"}`);
8986
+ console$h.log(`[ChatSession] MountFolder set to: ${this.mountFolder ?? "(none)"}`);
8857
8987
  }
8858
8988
  /** 设置 API 缓存 Key(传递给 aiGenerators.chat)。 */
8859
8989
  setAPICacheKey(key) {
@@ -8922,7 +9052,7 @@ class ChatSession {
8922
9052
  const s = typeof v === "string" ? v : JSON.stringify(v);
8923
9053
  return s.length > 180 ? s.slice(0, 180) + "..." : s;
8924
9054
  };
8925
- console$g.log(`[ChatSession.send] start session='${this.name ?? "root"}' userMessage=${summarizeDebugValue2(userMessage)}`);
9055
+ console$h.log(`[ChatSession.send] start session='${this.name ?? "root"}' userMessage=${summarizeDebugValue2(userMessage)}`);
8926
9056
  try {
8927
9057
  let resolvedUserMessage = userMessage;
8928
9058
  const effectiveOptions = {
@@ -8934,11 +9064,11 @@ class ChatSession {
8934
9064
  const shouldProcessTemplate = effectiveOptions.runCode === true && typeof userMessage === "string";
8935
9065
  if (shouldProcessTemplate) {
8936
9066
  resolvedUserMessage = await this.sandbox.processTemplate(userMessage);
8937
- console$g.log(`[ChatSession.send] runCode resolved userMessage=${summarizeDebugValue2(resolvedUserMessage)}`);
9067
+ console$h.log(`[ChatSession.send] runCode resolved userMessage=${summarizeDebugValue2(resolvedUserMessage)}`);
8938
9068
  }
8939
9069
  const childResults = ((_b2 = this._consumePendingChildResults) == null ? void 0 : _b2.call(this)) ?? [];
8940
9070
  if (childResults.length > 0) {
8941
- console$g.log(`[ChatSession.send] injecting ${childResults.length} pending child result(s) into session='${this.name ?? "root"}'`);
9071
+ console$h.log(`[ChatSession.send] injecting ${childResults.length} pending child result(s) into session='${this.name ?? "root"}'`);
8942
9072
  const toolCalls = childResults.map((cr) => ({
8943
9073
  id: cr.taskId,
8944
9074
  type: "function",
@@ -8960,7 +9090,7 @@ class ChatSession {
8960
9090
  else if (Array.isArray(resolvedUserMessage)) resolvedUserMessage = [{ type: "text", text: contextPrefix }, ...resolvedUserMessage];
8961
9091
  }
8962
9092
  if (effectiveOptions.skipIfMessageTextEmpty === true && shouldProcessTemplate && typeof resolvedUserMessage === "string" && resolvedUserMessage.trim() === "" && childResults.length === 0) {
8963
- console$g.log(`[ChatSession.send] skipped empty runCode result in session='${this.name ?? "root"}'`);
9093
+ console$h.log(`[ChatSession.send] skipped empty runCode result in session='${this.name ?? "root"}'`);
8964
9094
  return "";
8965
9095
  }
8966
9096
  if (Array.isArray(resolvedUserMessage)) {
@@ -8978,7 +9108,7 @@ class ChatSession {
8978
9108
  try {
8979
9109
  effectiveOptions.onUserMessage(userMessageEntry);
8980
9110
  } catch (e) {
8981
- console$g.warn("[ChatSession.send] onUserMessage callback failed:", e);
9111
+ console$h.warn("[ChatSession.send] onUserMessage callback failed:", e);
8982
9112
  }
8983
9113
  }
8984
9114
  }
@@ -8988,7 +9118,7 @@ class ChatSession {
8988
9118
  this._evictExcessImages(effectiveOptions.imageConfig.maxImageHistoryCount);
8989
9119
  }
8990
9120
  this._lastSendOptions = effectiveOptions;
8991
- console$g.log("[ChatSession.send] options:", effectiveOptions);
9121
+ console$h.log("[ChatSession.send] options:", effectiveOptions);
8992
9122
  let currentGptChatId;
8993
9123
  const response = await this.aiChat.chat({
8994
9124
  ...effectiveOptions,
@@ -9035,11 +9165,11 @@ class ChatSession {
9035
9165
  if (history) this.historyId = history.id;
9036
9166
  }
9037
9167
  } catch (e) {
9038
- console$g.warn("Failed to fetch chat history", e);
9168
+ console$h.warn("Failed to fetch chat history", e);
9039
9169
  }
9040
9170
  }
9041
9171
  } catch (e) {
9042
- console$g.error("Failed to save chat history", e);
9172
+ console$h.error("Failed to save chat history", e);
9043
9173
  }
9044
9174
  return response;
9045
9175
  } finally {
@@ -9062,10 +9192,10 @@ class ChatSession {
9062
9192
  if (!url || !url.startsWith("data:")) return item;
9063
9193
  try {
9064
9194
  const cdnUrl = await uploadTempVisionImage(url, { sdk: this.aiChat.sdk });
9065
- console$g.log("[ChatSession] Uploaded inline image to CDN:", cdnUrl);
9195
+ console$h.log("[ChatSession] Uploaded inline image to CDN:", cdnUrl);
9066
9196
  return { ...item, image_url: { ...item.image_url, url: cdnUrl } };
9067
9197
  } catch (e) {
9068
- console$g.warn("[ChatSession] Failed to upload inline image, keeping data-URI:", e);
9198
+ console$h.warn("[ChatSession] Failed to upload inline image, keeping data-URI:", e);
9069
9199
  return item;
9070
9200
  }
9071
9201
  })
@@ -9090,7 +9220,7 @@ class ChatSession {
9090
9220
  const keepFrom = roundStarts[roundStarts.length - limit];
9091
9221
  const evicted = keepFrom - systemEnd;
9092
9222
  this.messages = [...this.messages.slice(0, systemEnd), ...this.messages.slice(keepFrom)];
9093
- console$g.log(`[ChatSession] Evicted ${evicted} message(s), kept ${limit} round(s)`);
9223
+ console$h.log(`[ChatSession] Evicted ${evicted} message(s), kept ${limit} round(s)`);
9094
9224
  }
9095
9225
  /**
9096
9226
  * 从历史中移除超出 maxCount 限制的旧图片,
@@ -9118,7 +9248,7 @@ class ChatSession {
9118
9248
  msg.content[partIdx] = { type: "text", text: "(user_image)" };
9119
9249
  removedCount++;
9120
9250
  }
9121
- if (removedCount > 0) console$g.log(`[ChatSession] Evicted ${removedCount} image(s) from history, kept ${maxCount}`);
9251
+ if (removedCount > 0) console$h.log(`[ChatSession] Evicted ${removedCount} image(s) from history, kept ${maxCount}`);
9122
9252
  }
9123
9253
  // ──────────────────── 历史管理 ────────────────────
9124
9254
  /** 清空对话历史(仅清空 messages 数组)。 */
@@ -9145,7 +9275,7 @@ class ChatSession {
9145
9275
  */
9146
9276
  async restartAgent(promptFile, tools, _options) {
9147
9277
  var _a2, _b2, _c, _d, _e, _f;
9148
- console$g.log(`[ChatSession] restartAgent session='${this.name ?? "root"}' promptFile=${promptFile ?? "(none)"}`);
9278
+ console$h.log(`[ChatSession] restartAgent session='${this.name ?? "root"}' promptFile=${promptFile ?? "(none)"}`);
9149
9279
  const normalizedTools = Array.isArray(tools) ? tools.filter((t) => typeof t === "string" && t.trim()) : [];
9150
9280
  const cleanupFn = this["_cleanupChildSessions"];
9151
9281
  cleanupFn == null ? void 0 : cleanupFn.call(this);
@@ -9161,18 +9291,17 @@ class ChatSession {
9161
9291
  const hasPromptFile = !!(promptFile && typeof promptFile === "string" && promptFile.trim());
9162
9292
  if (hasPromptFile) {
9163
9293
  try {
9164
- const AgentConfig2 = (await Promise.resolve().then(() => AgentConfig$1)).default;
9165
- loadedConfig = await AgentConfig2.fetch(promptFile.trim());
9294
+ loadedConfig = await AgentConfig.fetch(promptFile.trim());
9166
9295
  } catch (e) {
9167
- console$g.warn(`[ChatSession] restartAgent failed to load '${promptFile}': ${e.message}`);
9296
+ console$h.warn(`[ChatSession] restartAgent failed to load '${promptFile}': ${e.message}`);
9168
9297
  }
9169
9298
  }
9170
9299
  const systemPrompt = loadedConfig ? loadedConfig["system_prompt"] ?? loadedConfig["systemPrompt"] ?? defaults.systemPrompt : defaults.systemPrompt;
9171
9300
  if (hasPromptFile && loadedConfig) {
9172
9301
  this._savedMessages = this.messages.filter((m) => m.role !== "system");
9173
- console$g.log(`[ChatSession] restartAgent: saved ${this._savedMessages.length} history messages`);
9302
+ console$h.log(`[ChatSession] restartAgent: saved ${this._savedMessages.length} history messages`);
9174
9303
  } else if (!hasPromptFile && this._savedMessages) {
9175
- console$g.log(`[ChatSession] restartAgent: keeping ${this._savedMessages.length} saved history messages for summarizer`);
9304
+ console$h.log(`[ChatSession] restartAgent: keeping ${this._savedMessages.length} saved history messages for summarizer`);
9176
9305
  }
9177
9306
  this.messages = [];
9178
9307
  if (systemPrompt) this.messages.push({ role: "system", content: systemPrompt });
@@ -9213,7 +9342,7 @@ function installAIChatSessionFactory() {
9213
9342
  Object.assign(ChatSession.prototype, childSessionMethods);
9214
9343
  Object.assign(ChatSession.prototype, {
9215
9344
  async _triggerImmediateCallback() {
9216
- console$g.log(
9345
+ console$h.log(
9217
9346
  `[ChatSession] _triggerImmediateCallback session='${this.name ?? "root"}' pendingChildResults=${this._pendingChildResults.length} isSending=${this._isSending}`
9218
9347
  );
9219
9348
  if (this._isSending) {
@@ -9226,11 +9355,11 @@ function installAIChatSessionFactory() {
9226
9355
  });
9227
9356
  }
9228
9357
  if (this._pendingChildResults.length === 0) return;
9229
- console$g.log("[ChatSession] Immediate callback: sending child results to parent");
9358
+ console$h.log("[ChatSession] Immediate callback: sending child results to parent");
9230
9359
  try {
9231
9360
  await this.send(null, this._lastSendOptions);
9232
9361
  } catch (e) {
9233
- console$g.error("[ChatSession] Immediate callback send failed:", e);
9362
+ console$h.error("[ChatSession] Immediate callback send failed:", e);
9234
9363
  }
9235
9364
  }
9236
9365
  });
@@ -9242,7 +9371,7 @@ function installAIChatSessionFactory() {
9242
9371
  }
9243
9372
  installAIChatSessionFactory();
9244
9373
  installAIChatSessionFactory();
9245
- const console$f = SDKLogger.createModuleConsole("SummarizeTool");
9374
+ const console$g = SDKLogger.createModuleConsole("SummarizeTool");
9246
9375
  const _SummarizeTool = class _SummarizeTool {
9247
9376
  constructor() {
9248
9377
  __publicField(this, "config", {});
@@ -9318,7 +9447,7 @@ const _SummarizeTool = class _SummarizeTool {
9318
9447
  };
9319
9448
  const filtered = this.filterSessionMessages(session, filters);
9320
9449
  const result = filtered.slice(-count);
9321
- console$f.log(`[SummarizeTool] get_history_messages: ${result.length}/${filtered.length} messages (count=${count})`);
9450
+ console$g.log(`[SummarizeTool] get_history_messages: ${result.length}/${filtered.length} messages (count=${count})`);
9322
9451
  return JSON.stringify(
9323
9452
  result.map((msg) => ({
9324
9453
  role: msg.role,
@@ -9338,7 +9467,7 @@ const _SummarizeTool = class _SummarizeTool {
9338
9467
  const filters = args["filters"] ?? null;
9339
9468
  const filtered = this.filterSessionMessages(session, filters);
9340
9469
  const total = Array.isArray(session.messages) ? session.messages.length : Array.isArray(session._history) ? session._history.length : 0;
9341
- console$f.log(`[SummarizeTool] get_history_messages_count: ${filtered.length}/${total}`);
9470
+ console$g.log(`[SummarizeTool] get_history_messages_count: ${filtered.length}/${total}`);
9342
9471
  return JSON.stringify({ count: filtered.length, total });
9343
9472
  }
9344
9473
  /**
@@ -9388,7 +9517,7 @@ ${result.summary}`;
9388
9517
  const messageBased = _SummarizeTool.isMessageBased(session);
9389
9518
  if (isAsync) {
9390
9519
  if (session._isSummarizing) {
9391
- console$f.log("[SummarizeTool] Async summarization already running, merging");
9520
+ console$g.log("[SummarizeTool] Async summarization already running, merging");
9392
9521
  return { summary: "", removedCount: 0, remainingCount: messages.length, status: "merged" };
9393
9522
  }
9394
9523
  session._isSummarizing = true;
@@ -9399,7 +9528,7 @@ ${result.summary}`;
9399
9528
  }
9400
9529
  const sdk = session.sdk ?? ((_a2 = session.aiChat) == null ? void 0 : _a2.sdk);
9401
9530
  if (!(sdk == null ? void 0 : sdk.aiChat)) {
9402
- console$f.error("[SummarizeTool] No SDK instance available on session");
9531
+ console$g.error("[SummarizeTool] No SDK instance available on session");
9403
9532
  return { summary: "", removedCount: 0, remainingCount: messages.length };
9404
9533
  }
9405
9534
  const keepRecentRounds = Number(options.keepRecentRounds) || 3;
@@ -9445,7 +9574,7 @@ ${result.summary}`;
9445
9574
  }
9446
9575
  return result;
9447
9576
  } catch (e) {
9448
- console$f.error("[SummarizeTool] Summarization failed:", e);
9577
+ console$g.error("[SummarizeTool] Summarization failed:", e);
9449
9578
  return { summary: "", removedCount: 0, remainingCount: messages.length };
9450
9579
  } finally {
9451
9580
  agent.destroy();
@@ -9464,10 +9593,10 @@ ${result.summary}`;
9464
9593
  taskSummary: "Conversation summarization",
9465
9594
  result: result.summary
9466
9595
  });
9467
- console$f.log(`[SummarizeTool] Async summary queued as pending result (taskId=${taskId})`);
9596
+ console$g.log(`[SummarizeTool] Async summary queued as pending result (taskId=${taskId})`);
9468
9597
  }
9469
9598
  } catch (e) {
9470
- console$f.error("[SummarizeTool] Async summarization failed:", e);
9599
+ console$g.error("[SummarizeTool] Async summarization failed:", e);
9471
9600
  } finally {
9472
9601
  session._isSummarizing = false;
9473
9602
  }
@@ -9628,7 +9757,7 @@ class SummarizeAgent {
9628
9757
  */
9629
9758
  async loadConfig(config, { skipCopilotToolsOverride = false } = {}) {
9630
9759
  this._config = config;
9631
- console$f.log("[SummarizeAgent] Config loaded:", Object.keys(config).join(", "));
9760
+ console$g.log("[SummarizeAgent] Config loaded:", Object.keys(config).join(", "));
9632
9761
  if (this.session) await this._createAgentSession();
9633
9762
  if (!skipCopilotToolsOverride) this._overrideCopilotToolsExecutor();
9634
9763
  return config;
@@ -9646,7 +9775,7 @@ class SummarizeAgent {
9646
9775
  this._timer = setInterval(() => {
9647
9776
  void this._onTick();
9648
9777
  }, interval * 1e3);
9649
- console$f.log(`[SummarizeAgent] Silent-tick timer started (${interval}s)`);
9778
+ console$g.log(`[SummarizeAgent] Silent-tick timer started (${interval}s)`);
9650
9779
  }
9651
9780
  /** 停止静默计时器。 */
9652
9781
  stopTimer() {
@@ -9688,7 +9817,7 @@ class SummarizeAgent {
9688
9817
  }
9689
9818
  this._checkAndTriggerRunning = true;
9690
9819
  this._autoTriggerSignature = signature;
9691
- console$f.log(`[SummarizeAgent] Auto-summarization triggered: ${roundCount} rounds, ${totalTextLength} chars`);
9820
+ console$g.log(`[SummarizeAgent] Auto-summarization triggered: ${roundCount} rounds, ${totalTextLength} chars`);
9692
9821
  try {
9693
9822
  if (this._config) {
9694
9823
  try {
@@ -9697,7 +9826,7 @@ class SummarizeAgent {
9697
9826
  this._onEvent("summarized", { summary: result.summary, removedCount: result.removedCount, remainingCount: result.remainingCount, trigger: "auto", mode: result.mode, summarizeBeginTime: result.summarizeBeginTime });
9698
9827
  }
9699
9828
  } catch (e) {
9700
- console$f.error("[SummarizeAgent] Auto-summarization (agent) failed:", e);
9829
+ console$g.error("[SummarizeAgent] Auto-summarization (agent) failed:", e);
9701
9830
  }
9702
9831
  return;
9703
9832
  }
@@ -9711,7 +9840,7 @@ class SummarizeAgent {
9711
9840
  this._onEvent("summarized", { summary: result.summary, removedCount: result.removedCount, remainingCount: result.remainingCount, trigger: "auto", mode: result.mode, summarizeBeginTime: result.summarizeBeginTime });
9712
9841
  }
9713
9842
  } catch (e) {
9714
- console$f.error("[SummarizeAgent] Auto-summarization fallback failed:", e);
9843
+ console$g.error("[SummarizeAgent] Auto-summarization fallback failed:", e);
9715
9844
  }
9716
9845
  } finally {
9717
9846
  this._checkAndTriggerRunning = false;
@@ -9727,7 +9856,7 @@ class SummarizeAgent {
9727
9856
  async run(options = {}) {
9728
9857
  var _a2, _b2, _c, _d, _e, _f;
9729
9858
  if (this._running) {
9730
- console$f.log("[SummarizeAgent] Already running, skipping");
9859
+ console$g.log("[SummarizeAgent] Already running, skipping");
9731
9860
  return { summary: "", removedCount: 0, remainingCount: ((_b2 = (_a2 = this.session) == null ? void 0 : _a2.messages) == null ? void 0 : _b2.length) ?? 0 };
9732
9861
  }
9733
9862
  const session = this.session;
@@ -9735,7 +9864,7 @@ class SummarizeAgent {
9735
9864
  if (!this._config) return { summary: "", removedCount: 0, remainingCount: session.messages.length };
9736
9865
  if (!this._agentSession) await this._createAgentSession();
9737
9866
  if (!this._agentSession) {
9738
- console$f.error("[SummarizeAgent] Failed to create agent session");
9867
+ console$g.error("[SummarizeAgent] Failed to create agent session");
9739
9868
  return { summary: "", removedCount: 0, remainingCount: session.messages.length };
9740
9869
  }
9741
9870
  const opts = this._getOptions();
@@ -9744,11 +9873,11 @@ class SummarizeAgent {
9744
9873
  const summarizeBeginTime = Date.now();
9745
9874
  const conversationMessages = session.messages.filter((m) => m.role !== "system");
9746
9875
  if (conversationMessages.length < 2) {
9747
- console$f.log("[SummarizeAgent] Not enough messages to summarize");
9876
+ console$g.log("[SummarizeAgent] Not enough messages to summarize");
9748
9877
  return { summary: "", removedCount: 0, remainingCount: session.messages.length };
9749
9878
  }
9750
9879
  this._running = true;
9751
- console$f.log(`[SummarizeAgent] Running: ${conversationMessages.length} messages, mode=${mode}`);
9880
+ console$g.log(`[SummarizeAgent] Running: ${conversationMessages.length} messages, mode=${mode}`);
9752
9881
  try {
9753
9882
  const agentSession = this._agentSession;
9754
9883
  const agentConfig = this._config;
@@ -9757,7 +9886,7 @@ class SummarizeAgent {
9757
9886
  try {
9758
9887
  systemPrompt = ((_c = agentSession.sandbox) == null ? void 0 : _c.processTemplate) ? await agentSession.sandbox.processTemplate(rawPrompt) : rawPrompt;
9759
9888
  } catch (e) {
9760
- console$f.warn("[SummarizeAgent] Prompt template failed, using raw:", e);
9889
+ console$g.warn("[SummarizeAgent] Prompt template failed, using raw:", e);
9761
9890
  systemPrompt = rawPrompt;
9762
9891
  }
9763
9892
  const serialized = SummarizeTool.serializeMessages(conversationMessages);
@@ -9786,7 +9915,7 @@ class SummarizeAgent {
9786
9915
  );
9787
9916
  const summaryText = (lastAssistant == null ? void 0 : lastAssistant.content) ?? "";
9788
9917
  if (!summaryText) {
9789
- console$f.warn("[SummarizeAgent] Empty summary returned");
9918
+ console$g.warn("[SummarizeAgent] Empty summary returned");
9790
9919
  return { summary: "", removedCount: 0, remainingCount: session.messages.length };
9791
9920
  }
9792
9921
  const cleanedMessages = session.messages.filter(
@@ -9800,17 +9929,17 @@ class SummarizeAgent {
9800
9929
  await ((_f = aiChat == null ? void 0 : aiChat.updateChatHistory) == null ? void 0 : _f.call(aiChat, session.historyId, { id: session.historyId, chatId: session.chatId, modId: session.modId, model: session.model, messages: cleanedMessages }));
9801
9930
  }
9802
9931
  } catch (e) {
9803
- console$f.warn("[SummarizeAgent] Failed to persist summarized history:", e);
9932
+ console$g.warn("[SummarizeAgent] Failed to persist summarized history:", e);
9804
9933
  }
9805
9934
  if (mode === "replace") {
9806
9935
  session.messages = cleanedMessages;
9807
9936
  this.syncHistoryToRTC();
9808
9937
  }
9809
9938
  const remainingCount = session.messages.length;
9810
- console$f.log(`[SummarizeAgent] ${mode} mode: removed ${removedCount} from history, session ${mode === "replace" ? "updated" : "unchanged"}`);
9939
+ console$g.log(`[SummarizeAgent] ${mode} mode: removed ${removedCount} from history, session ${mode === "replace" ? "updated" : "unchanged"}`);
9811
9940
  return { summary: summaryText, removedCount, remainingCount, mode, summarizeBeginTime };
9812
9941
  } catch (e) {
9813
- console$f.error("[SummarizeAgent] Failed:", e);
9942
+ console$g.error("[SummarizeAgent] Failed:", e);
9814
9943
  return { summary: "", removedCount: 0, remainingCount: session.messages.length };
9815
9944
  } finally {
9816
9945
  this._running = false;
@@ -9832,7 +9961,7 @@ class SummarizeAgent {
9832
9961
  newHistory.push({ role: msg.role, text, roundId: null });
9833
9962
  }
9834
9963
  vcSession._history = newHistory;
9835
- console$f.log(`[SummarizeAgent] Synced history to RTC (${newHistory.length} entries)`);
9964
+ console$g.log(`[SummarizeAgent] Synced history to RTC (${newHistory.length} entries)`);
9836
9965
  }
9837
9966
  /** 完全销毁 agent:停止计时器,清除所有状态。 */
9838
9967
  destroy() {
@@ -9896,7 +10025,7 @@ class SummarizeAgent {
9896
10025
  }
9897
10026
  this._tickSummarizing = true;
9898
10027
  this._autoTriggerSignature = signature;
9899
- console$f.log(`[SummarizeAgent] Silent-tick triggered: ${roundCount} rounds, ${totalTextLength} chars`);
10028
+ console$g.log(`[SummarizeAgent] Silent-tick triggered: ${roundCount} rounds, ${totalTextLength} chars`);
9900
10029
  try {
9901
10030
  let result;
9902
10031
  if (this._config) {
@@ -9909,7 +10038,7 @@ class SummarizeAgent {
9909
10038
  this._onEvent("summarized", { summary: result.summary, removedCount: result.removedCount, remainingCount: result.remainingCount, trigger: "silentTick", mode: result.mode, summarizeBeginTime: result.summarizeBeginTime });
9910
10039
  }
9911
10040
  } catch (e) {
9912
- console$f.error("[SummarizeAgent] Silent-tick failed:", e);
10041
+ console$g.error("[SummarizeAgent] Silent-tick failed:", e);
9913
10042
  } finally {
9914
10043
  this._tickSummarizing = false;
9915
10044
  if (this._pendingAutoCheck) this._drainPendingAutoCheck();
@@ -9934,7 +10063,7 @@ class SummarizeAgent {
9934
10063
  this._agentSession = sdk.aiChat.createSession({ model, workspace, mountFolder, enableTools, skipHistory: true });
9935
10064
  if ((_e = this.session) == null ? void 0 : _e.sandbox) this._agentSession["sandbox"] = this.session.sandbox;
9936
10065
  this._registerHistoryAPIsOnSandbox(this._agentSession["sandbox"]);
9937
- console$f.log(`[SummarizeAgent] Agent session created (model=${model}, tools=${enableTools.join(",")})`);
10066
+ console$g.log(`[SummarizeAgent] Agent session created (model=${model}, tools=${enableTools.join(",")})`);
9938
10067
  return this._agentSession;
9939
10068
  }
9940
10069
  /** @private 覆盖 CopilotTools 的 summarize 分类 executor,使其通过 agent 执行。 */
@@ -9963,7 +10092,7 @@ class SummarizeAgent {
9963
10092
  definitions: mergedDefs,
9964
10093
  executor: async (fnName, fnArgs, config) => {
9965
10094
  if (fnName === "summarize_conversation") {
9966
- console$f.log(`[SummarizeAgent] summarize_conversation tool called: ${(fnArgs == null ? void 0 : fnArgs["reason"]) ?? "(no reason)"}`);
10095
+ console$g.log(`[SummarizeAgent] summarize_conversation tool called: ${(fnArgs == null ? void 0 : fnArgs["reason"]) ?? "(no reason)"}`);
9967
10096
  try {
9968
10097
  const result = await agent.run();
9969
10098
  if (result.summary) {
@@ -9975,7 +10104,7 @@ ${result.summary}`;
9975
10104
  }
9976
10105
  return "No summarization needed — conversation is short enough.";
9977
10106
  } catch (e) {
9978
- console$f.error("[SummarizeAgent] summarize_conversation tool failed:", e);
10107
+ console$g.error("[SummarizeAgent] summarize_conversation tool failed:", e);
9979
10108
  return `Summarization failed: ${e.message}`;
9980
10109
  }
9981
10110
  }
@@ -9984,7 +10113,7 @@ ${result.summary}`;
9984
10113
  }
9985
10114
  });
9986
10115
  this._registerHistoryAPIsOnSandbox((_f = this.session) == null ? void 0 : _f.sandbox);
9987
- console$f.log("[SummarizeAgent] Overrode CopilotTools summarize executor");
10116
+ console$g.log("[SummarizeAgent] Overrode CopilotTools summarize executor");
9988
10117
  }
9989
10118
  /**
9990
10119
  * @private 在 sandbox 上注册 get_history_messages / get_history_messages_count 自定义 API,
@@ -10004,7 +10133,7 @@ ${result.summary}`;
10004
10133
  });
10005
10134
  }
10006
10135
  }
10007
- const console$e = SDKLogger.createModuleConsole("CopilotTools");
10136
+ const console$f = SDKLogger.createModuleConsole("CopilotTools");
10008
10137
  class CopilotTools {
10009
10138
  constructor(sdk) {
10010
10139
  /** 工具分类注册表 */
@@ -10163,7 +10292,7 @@ class CopilotTools {
10163
10292
  const sessionName = (sessionObj == null ? void 0 : sessionObj["name"]) || (sessionObj == null ? void 0 : sessionObj["workspace"]) || "direct";
10164
10293
  const source = (config == null ? void 0 : config["_proxied"]) ? "proxied" : "local";
10165
10294
  const routerId = ((_c = (_b2 = (_a2 = this.sdk) == null ? void 0 : _a2.agentRouter) == null ? void 0 : _b2.instanceId) == null ? void 0 : _c.slice(0, 8)) || (typeof window !== "undefined" && window.parent === window ? "parent" : "iframe");
10166
- console$e.log(`[CopilotTools|${routerId}] ${source} execute: ${fnName} [category: ${categoryName}, session: ${sessionName}]`);
10295
+ console$f.log(`[CopilotTools|${routerId}] ${source} execute: ${fnName} [category: ${categoryName}, session: ${sessionName}]`);
10167
10296
  return this._registry[categoryName].executor(fnName, fnArgs, config);
10168
10297
  }
10169
10298
  /**
@@ -10296,7 +10425,7 @@ class CopilotTools {
10296
10425
  /** AppTools 类引用(供外部访问) */
10297
10426
  __publicField(CopilotTools, "AppTools");
10298
10427
  CopilotTools.AppTools = AppTools;
10299
- const console$d = SDKLogger.createModuleConsole("WxLaunchApp");
10428
+ const console$e = SDKLogger.createModuleConsole("WxLaunchApp");
10300
10429
  class WxLaunchApp {
10301
10430
  constructor(sdk) {
10302
10431
  __publicField(this, "sdk");
@@ -10395,27 +10524,27 @@ class WxLaunchApp {
10395
10524
  /** 在容器中创建唤起按钮,返回按钮元素 id(失败返回 null)。 */
10396
10525
  createLaunchButton(container, options = {}) {
10397
10526
  if (!container) {
10398
- console$d.error("Container element is required");
10527
+ console$e.error("Container element is required");
10399
10528
  return null;
10400
10529
  }
10401
10530
  if (!this.isConfigured) {
10402
- console$d.warn("WeChat SDK not configured. Please call initialize() first, or the button may not work properly.");
10403
- console$d.warn('Example: await wxLaunchApp.initialize({ appId: "your-appid" })');
10531
+ console$e.warn("WeChat SDK not configured. Please call initialize() first, or the button may not work properly.");
10532
+ console$e.warn('Example: await wxLaunchApp.initialize({ appId: "your-appid" })');
10404
10533
  if (options.autoInit && options.appId) {
10405
- console$d.log("Attempting auto-initialization...");
10534
+ console$e.log("Attempting auto-initialization...");
10406
10535
  this.initialize({
10407
10536
  appId: options.appId,
10408
10537
  url: options.url,
10409
10538
  debug: options.debug
10410
10539
  }).then((result) => {
10411
10540
  if (result.success) {
10412
- console$d.log("Auto-initialization successful, recreating button...");
10541
+ console$e.log("Auto-initialization successful, recreating button...");
10413
10542
  this.createLaunchButton(container, { ...options, autoInit: false });
10414
10543
  } else {
10415
- console$d.error("Auto-initialization failed:", result.message);
10544
+ console$e.error("Auto-initialization failed:", result.message);
10416
10545
  }
10417
10546
  }).catch((err) => {
10418
- console$d.error("Auto-initialization error:", err);
10547
+ console$e.error("Auto-initialization error:", err);
10419
10548
  });
10420
10549
  return null;
10421
10550
  }
@@ -10497,7 +10626,7 @@ class WxLaunchApp {
10497
10626
  this.downloadUrl = url;
10498
10627
  }
10499
10628
  }
10500
- const console$c = SDKLogger.createModuleConsole("WxAuth");
10629
+ const console$d = SDKLogger.createModuleConsole("WxAuth");
10501
10630
  const DEFAULT_CONFIG = {
10502
10631
  appId: "wx7935c49369d421c1",
10503
10632
  // 默认微信 App ID
@@ -10558,7 +10687,7 @@ class WxAuth {
10558
10687
  */
10559
10688
  authorize(options = {}) {
10560
10689
  if (typeof window === "undefined") {
10561
- console$c.warn("WxAuth.authorize: Not in browser environment");
10690
+ console$d.warn("WxAuth.authorize: Not in browser environment");
10562
10691
  return;
10563
10692
  }
10564
10693
  const appId = options.appId || this.appId;
@@ -10612,7 +10741,7 @@ class WxAuth {
10612
10741
  }
10613
10742
  const res = await response.json();
10614
10743
  if (appId === this.maisiAppId) {
10615
- console$c.log("Using Maisi App ID for wxCodeToToken");
10744
+ console$d.log("Using Maisi App ID for wxCodeToToken");
10616
10745
  onSuccess(res);
10617
10746
  return { success: true, data: res, isMaisi: true };
10618
10747
  }
@@ -14248,6 +14377,7 @@ const SERVICE_PRESETS = {
14248
14377
  { name: "openrouter-gpt-5.5", modelId: "openai/gpt-5.5", category: "Chat" },
14249
14378
  { name: "openrouter-claude-opus-4-8", modelId: "anthropic/claude-4.8-opus", category: "Chat" },
14250
14379
  { name: "openrouter-claude", modelId: "anthropic/claude-4.8-opus", category: "Chat" },
14380
+ { name: "openrouter-claude-fable-5", modelId: "anthropic/claude-fable-5", category: "Chat" },
14251
14381
  { name: "openrouter-claude-sonnet", modelId: "anthropic/claude-4.6-sonnet", category: "Chat" },
14252
14382
  { name: "openrouter-gemini-3.1-flash-image-preview", modelId: "google/gemini-3.1-flash-image-preview", category: "Image" },
14253
14383
  { name: "openrouter-gemini-3-pro-image-preview", modelId: "google/gemini-3-pro-image-preview", category: "Image" },
@@ -14726,6 +14856,7 @@ class LocalAPIKeySettings {
14726
14856
  }
14727
14857
  }
14728
14858
  async save() {
14859
+ var _a2;
14729
14860
  const data = this._serializeData();
14730
14861
  try {
14731
14862
  if (this.storageMode === "personalPageStore") {
@@ -14736,6 +14867,14 @@ class LocalAPIKeySettings {
14736
14867
  } else {
14737
14868
  localStorage.setItem(STORAGE_KEY, JSON.stringify(data, null, 2));
14738
14869
  }
14870
+ const onSave = (_a2 = this._showOptions) == null ? void 0 : _a2.onSave;
14871
+ if (typeof onSave === "function") {
14872
+ try {
14873
+ onSave();
14874
+ } catch (e) {
14875
+ console.warn("[LocalAPIKeySettings] onSave callback failed:", e);
14876
+ }
14877
+ }
14739
14878
  } catch (e) {
14740
14879
  console.warn("[LocalAPIKeySettings] Failed to save settings:", e);
14741
14880
  }
@@ -15872,7 +16011,7 @@ class LocalAPIKeySettings {
15872
16011
  return `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"/><line x1="1" y1="1" x2="23" y2="23"/></svg>`;
15873
16012
  }
15874
16013
  }
15875
- const console$b = SDKLogger.createModuleConsole("AIGenerators");
16014
+ const console$c = SDKLogger.createModuleConsole("AIGenerators");
15876
16015
  const DEFAULT_CHAT_MODEL = "keepwork-flash";
15877
16016
  const DEFAULT_IMAGE_MODEL = "keepwork-image";
15878
16017
  const DEFAULT_VIDEO_MODEL = "keepwork-video";
@@ -15962,6 +16101,41 @@ class AIGeneratorsBase {
15962
16101
  var _a2;
15963
16102
  return ((_a2 = this.sdk.localAPIKeySettings) == null ? void 0 : _a2.resolveModelSettings(model ?? "", { provider })) ?? { model: model ?? "" };
15964
16103
  }
16104
+ /**
16105
+ * 判断某个模型当前是否「可能可用」(potentially usable):
16106
+ * - 本地 API key 功能已启用(localAPIKeySettings.enabled):模型必须能解析出 apiKey,
16107
+ * 跳过没有配置 key 的模型。
16108
+ * - 未启用:请求走 Keepwork proxy,跳过必须依赖本地 key 直连的模型
16109
+ * (如 OpenRouter `provider/model` 格式),其余模型视为可用。
16110
+ *
16111
+ * @param model - 模型名字符串,或带 name/modelId/enabled 字段的配置对象
16112
+ * (如 `localAPIKeySettings.listModels()` 的条目)。enabled === false 的条目直接视为不可用。
16113
+ * 传入空字符串 / null / undefined 时按默认聊天模型(DEFAULT_CHAT_MODEL)判断,
16114
+ * 与 chat() 缺省 model 的行为一致。
16115
+ */
16116
+ isModelUsable(model) {
16117
+ if (model == null || model === "") return this.isModelUsable(DEFAULT_CHAT_MODEL);
16118
+ const isObj = typeof model === "object";
16119
+ if (isObj && model.enabled === false) return false;
16120
+ const name = isObj ? model.name || model.modelId || "" : String(model).trim();
16121
+ if (!name) return false;
16122
+ const localSettings = this.sdk.localAPIKeySettings;
16123
+ const resolved = this._resolveLocalModelSettings(name);
16124
+ if (localSettings == null ? void 0 : localSettings.enabled) {
16125
+ return !!resolved.apiKey;
16126
+ }
16127
+ const modelId = isObj && model.modelId || resolved.model || name;
16128
+ const provider = this._detectProvider(String(modelId));
16129
+ return provider !== "openrouter" && !String(modelId).includes("/");
16130
+ }
16131
+ /**
16132
+ * 过滤出「可能可用」的模型列表(规则见 isModelUsable)。
16133
+ * 支持字符串数组或 `localAPIKeySettings.listModels()` 返回的配置对象数组。
16134
+ */
16135
+ filterUsableModels(models) {
16136
+ if (!Array.isArray(models)) return [];
16137
+ return models.filter((m) => this.isModelUsable(m));
16138
+ }
15965
16139
  /** 判断是否应优先使用直连路由。 */
15966
16140
  _shouldPreferDirect(localModelSettings, opts = {}) {
15967
16141
  if (opts["directOnly"]) return true;
@@ -16135,6 +16309,16 @@ class AIGeneratorsBase {
16135
16309
  if (this._shouldDisableReasoning(model)) return false;
16136
16310
  return reasoning;
16137
16311
  }
16312
+ /**
16313
+ * 解析 maxTokens 选项:同时接受 camelCase `maxTokens` 与下划线 `max_tokens`,
16314
+ * 返回正整数或 undefined(不传)。
16315
+ */
16316
+ _resolveMaxTokens(options) {
16317
+ const raw = (options == null ? void 0 : options.maxTokens) ?? (options == null ? void 0 : options.max_tokens);
16318
+ const value = Number(raw);
16319
+ if (!Number.isFinite(value) || value < 1) return void 0;
16320
+ return Math.floor(value);
16321
+ }
16138
16322
  _buildReasoningBodyField(provider, reasoning) {
16139
16323
  if (reasoning === void 0) return null;
16140
16324
  if (provider === "openrouter") {
@@ -16198,7 +16382,7 @@ class AIGeneratorsBase {
16198
16382
  * 支持流式和非流式两种模式,统一发出 AIChat 兼容的回调。
16199
16383
  */
16200
16384
  async chatViaProxy(options = {}) {
16201
- const { messages = [], stream = true, reasoning, knowledgeBaseCodes = [], apiCacheKey, abortController, extraHeaders, onMessage, onReasoning, onToolCall, onComplete, onError } = options;
16385
+ const { messages = [], stream = true, reasoning, knowledgeBaseCodes = [], apiCacheKey, abortController, extraHeaders, onMessage, onReasoning, onToolCall, onToolCallChunk, onComplete, onError } = options;
16202
16386
  const requestedModel = options["model"] ?? "keepwork-pro";
16203
16387
  const tools = options["tools"] ?? [];
16204
16388
  if (!messages || messages.length === 0) throw new Error("Messages array is required and cannot be empty");
@@ -16211,6 +16395,8 @@ class AIGeneratorsBase {
16211
16395
  if (extraHeaders) Object.assign(headers, extraHeaders);
16212
16396
  const requestBody = { messages, model, stream };
16213
16397
  if (localModelSettings.apiKey) requestBody["apiKey"] = localModelSettings.apiKey;
16398
+ const maxTokens = this._resolveMaxTokens(options);
16399
+ if (maxTokens !== void 0) requestBody["max_tokens"] = maxTokens;
16214
16400
  const reasoningOpt = this._normalizeReasoningOption(model, reasoning);
16215
16401
  if (reasoningOpt !== void 0) requestBody["reasoning"] = reasoningOpt;
16216
16402
  if ((knowledgeBaseCodes == null ? void 0 : knowledgeBaseCodes.length) > 0) requestBody["knowledgeBaseCodes"] = knowledgeBaseCodes;
@@ -16227,7 +16413,7 @@ class AIGeneratorsBase {
16227
16413
  const msg = response.status === 429 ? "请求频率过高,请稍后重试" : `请求失败: ${response.status} ${response.statusText}`;
16228
16414
  throw new Error(msg);
16229
16415
  }
16230
- if (stream) return await this._readKeepworkProxySSE(response, { onMessage, onReasoning, onToolCall, onComplete });
16416
+ if (stream) return await this._readKeepworkProxySSE(response, { onMessage, onReasoning, onToolCall, onToolCallChunk, onComplete });
16231
16417
  const result = await response.json();
16232
16418
  const fullResultText = typeof result["result"] === "string" ? result["result"] : "";
16233
16419
  const reasoningText = typeof result["reasoning_content"] === "string" ? result["reasoning_content"] : "";
@@ -16236,21 +16422,21 @@ class AIGeneratorsBase {
16236
16422
  try {
16237
16423
  onReasoning(reasoningText, reasoningText, result);
16238
16424
  } catch (e) {
16239
- console$b.warn("onReasoning failed:", e);
16425
+ console$c.warn("onReasoning failed:", e);
16240
16426
  }
16241
16427
  }
16242
16428
  if (onToolCall) for (const tc of toolCalls) {
16243
16429
  try {
16244
16430
  onToolCall(tc);
16245
16431
  } catch (e) {
16246
- console$b.warn("onToolCall failed:", e);
16432
+ console$c.warn("onToolCall failed:", e);
16247
16433
  }
16248
16434
  }
16249
16435
  if (onMessage && (fullResultText || reasoningText)) {
16250
16436
  try {
16251
16437
  onMessage(fullResultText, { result: fullResultText, reasoning_content: reasoningText, tool_calls: toolCalls });
16252
16438
  } catch (e) {
16253
- console$b.warn("onMessage failed:", e);
16439
+ console$c.warn("onMessage failed:", e);
16254
16440
  }
16255
16441
  }
16256
16442
  if (onComplete) onComplete(fullResultText, { result: fullResultText, reasoning_content: reasoningText, tool_calls: toolCalls, raw: result, headers: Object.fromEntries(response.headers.entries()) });
@@ -16260,56 +16446,174 @@ class AIGeneratorsBase {
16260
16446
  throw error;
16261
16447
  }
16262
16448
  }
16263
- /** @private 解析 Keepwork proxy SSE 流,返回累积文本。 */
16264
- async _readKeepworkProxySSE(response, { onMessage, onReasoning, onToolCall, onComplete } = {}) {
16449
+ /**
16450
+ * @private 解析 Keepwork proxy SSE 流,返回累积文本。
16451
+ *
16452
+ * 增量式设计(性能关键):每条 `data:` 行只解析一次,缓冲区只保留未收完的尾行。
16453
+ * 此前的实现把全部已收数据留在缓冲区、每次网络读取都重新 split + JSON.parse 所有行——
16454
+ * create_file 把整页 HTML 拆成数千条小 delta 行传输时,这会变成 O(n²) 的主线程占用,
16455
+ * 浏览器完全没机会绘制(UI 卡死、CSS 动画停帧、工具调用 chip 直到流结束才出现)。
16456
+ */
16457
+ async _readKeepworkProxySSE(response, { onMessage, onReasoning, onToolCall, onToolCallChunk, onComplete } = {}) {
16265
16458
  var _a2;
16266
16459
  const reader = (_a2 = response.body) == null ? void 0 : _a2.getReader();
16267
16460
  const decoder = new TextDecoder("utf-8");
16268
- let result = "", fullResult = "", fullReasoningContent = "";
16269
- let emittedReasoningLen = 0;
16461
+ let buffer = "", fullResult = "", fullReasoningContent = "";
16270
16462
  const toolCalls = [];
16463
+ const deltaToolCallsByIndex = /* @__PURE__ */ new Map();
16464
+ const announcedDelta = /* @__PURE__ */ new Set();
16465
+ const announceDeltaToolCalls = () => {
16466
+ var _a3;
16467
+ for (const entry of deltaToolCallsByIndex.values()) {
16468
+ if (!((_a3 = entry.function) == null ? void 0 : _a3.name) || announcedDelta.has(entry)) continue;
16469
+ announcedDelta.add(entry);
16470
+ if (onToolCall) {
16471
+ try {
16472
+ onToolCall(entry);
16473
+ } catch (e) {
16474
+ console$c.warn("onToolCall failed:", e);
16475
+ }
16476
+ }
16477
+ }
16478
+ };
16271
16479
  if (!reader) {
16272
16480
  if (onComplete) onComplete("", { result: "", reasoning_content: "", tool_calls: [] });
16273
16481
  return "";
16274
16482
  }
16275
- while (true) {
16276
- const { done, value } = await reader.read();
16277
- if (done) {
16278
- if (onComplete) onComplete(fullResult, { result: fullResult, reasoning_content: fullReasoningContent, tool_calls: toolCalls, headers: Object.fromEntries(response.headers.entries()) });
16279
- break;
16483
+ const consumePayload = (json) => {
16484
+ var _a3, _b2, _c, _d;
16485
+ let textChanged = false;
16486
+ if (typeof json["result"] === "string" && json["result"]) {
16487
+ fullResult += json["result"];
16488
+ textChanged = true;
16489
+ }
16490
+ if (typeof json["reasoning_content"] === "string" && json["reasoning_content"]) {
16491
+ fullReasoningContent += json["reasoning_content"];
16492
+ textChanged = true;
16493
+ if (onReasoning) {
16494
+ try {
16495
+ onReasoning(fullReasoningContent, json["reasoning_content"]);
16496
+ } catch (e) {
16497
+ console$c.warn("onReasoning failed:", e);
16498
+ }
16499
+ }
16280
16500
  }
16281
- result += decoder.decode(value, { stream: true });
16282
- const datas = result.split("\n");
16283
- try {
16284
- const responsesArray = datas.filter((d) => d.startsWith("data: {")).map((d) => JSON.parse(d.substring("data: ".length)));
16285
- const responseResult = responsesArray.map((r) => r["result"]).filter((d) => d !== void 0).reduce((a, b) => a + b, "");
16286
- const responseReasoning = responsesArray.map((r) => r["reasoning_content"]).filter((d) => d !== void 0).reduce((a, b) => a + b, "");
16287
- responsesArray.forEach((r) => {
16288
- if (r["tool_calls"] && Array.isArray(r["tool_calls"])) {
16289
- r["tool_calls"].forEach((toolCall) => {
16290
- if (!toolCalls.find((tc) => tc.id === toolCall.id)) {
16291
- toolCalls.push(toolCall);
16292
- if (onToolCall) onToolCall(toolCall);
16501
+ if (Array.isArray(json["tool_calls"])) {
16502
+ for (const toolCall of json["tool_calls"]) {
16503
+ const existing = toolCalls.find((tc) => tc.id === toolCall.id);
16504
+ if (!existing) {
16505
+ toolCalls.push(toolCall);
16506
+ if (onToolCallChunk) {
16507
+ try {
16508
+ onToolCallChunk(toolCall);
16509
+ } catch (e) {
16510
+ console$c.warn("onToolCallChunk failed:", e);
16293
16511
  }
16294
- });
16512
+ }
16513
+ if (onToolCall) {
16514
+ try {
16515
+ onToolCall(toolCall);
16516
+ } catch (e) {
16517
+ console$c.warn("onToolCall failed:", e);
16518
+ }
16519
+ }
16520
+ } else {
16521
+ const nextArgs = (_a3 = toolCall.function) == null ? void 0 : _a3.arguments;
16522
+ if (typeof nextArgs === "string" && nextArgs.length > (((_c = (_b2 = existing.function) == null ? void 0 : _b2.arguments) == null ? void 0 : _c.length) ?? 0)) {
16523
+ if (!existing.function) existing.function = { name: ((_d = toolCall.function) == null ? void 0 : _d.name) ?? "", arguments: "" };
16524
+ existing.function.arguments = nextArgs;
16525
+ if (onToolCallChunk) {
16526
+ try {
16527
+ onToolCallChunk(existing);
16528
+ } catch (e) {
16529
+ console$c.warn("onToolCallChunk failed:", e);
16530
+ }
16531
+ }
16532
+ }
16295
16533
  }
16296
- });
16297
- if (responseResult.length > 0) fullResult = responseResult;
16298
- if (responseReasoning.length > 0) fullReasoningContent = responseReasoning;
16299
- if (onReasoning && fullReasoningContent.length > emittedReasoningLen) {
16300
- const reasoningDelta = fullReasoningContent.slice(emittedReasoningLen);
16301
- emittedReasoningLen = fullReasoningContent.length;
16534
+ }
16535
+ }
16536
+ if (Array.isArray(json["tool_calls_delta"])) {
16537
+ for (const frag of json["tool_calls_delta"]) {
16538
+ const idx = frag["index"] ?? 0;
16539
+ let entry = deltaToolCallsByIndex.get(idx);
16540
+ if (!entry) {
16541
+ entry = { id: "", type: "function", function: { name: "", arguments: "" }, index: idx };
16542
+ deltaToolCallsByIndex.set(idx, entry);
16543
+ toolCalls.push(entry);
16544
+ }
16545
+ if (typeof frag["id"] === "string" && frag["id"]) entry.id = frag["id"];
16546
+ if (typeof frag["type"] === "string" && frag["type"]) entry.type = frag["type"];
16547
+ const fn = frag["function"];
16548
+ let tcChanged = false;
16549
+ if (typeof (fn == null ? void 0 : fn["name"]) === "string" && fn["name"]) {
16550
+ entry.function.name += fn["name"];
16551
+ tcChanged = true;
16552
+ }
16553
+ if (typeof (fn == null ? void 0 : fn["arguments"]) === "string" && fn["arguments"]) {
16554
+ entry.function.arguments += fn["arguments"];
16555
+ tcChanged = true;
16556
+ }
16557
+ if (tcChanged && entry.function.name && onToolCallChunk) {
16558
+ try {
16559
+ onToolCallChunk(entry);
16560
+ } catch (e) {
16561
+ console$c.warn("onToolCallChunk failed:", e);
16562
+ }
16563
+ }
16564
+ }
16565
+ }
16566
+ return textChanged;
16567
+ };
16568
+ const consumeTail = () => {
16569
+ const line = buffer.trim();
16570
+ buffer = "";
16571
+ if (!line.startsWith("data: {")) return false;
16572
+ try {
16573
+ return consumePayload(JSON.parse(line.substring("data: ".length)));
16574
+ } catch (_) {
16575
+ return false;
16576
+ }
16577
+ };
16578
+ while (true) {
16579
+ const { done, value } = await reader.read();
16580
+ if (done) {
16581
+ const tailChanged = consumeTail();
16582
+ if (tailChanged && onMessage) {
16302
16583
  try {
16303
- onReasoning(fullReasoningContent, reasoningDelta);
16584
+ onMessage(fullResult, { result: fullResult, reasoning_content: fullReasoningContent, tool_calls: toolCalls });
16304
16585
  } catch (e) {
16305
- console$b.warn("onReasoning failed:", e);
16586
+ console$c.warn("onMessage failed:", e);
16306
16587
  }
16307
16588
  }
16308
- if ((responseResult.length > 0 || responseReasoning.length > 0) && onMessage) {
16589
+ announceDeltaToolCalls();
16590
+ if (onComplete) onComplete(fullResult, { result: fullResult, reasoning_content: fullReasoningContent, tool_calls: toolCalls, headers: Object.fromEntries(response.headers.entries()) });
16591
+ break;
16592
+ }
16593
+ buffer += decoder.decode(value, { stream: true });
16594
+ const lines = buffer.split("\n");
16595
+ buffer = lines.pop() ?? "";
16596
+ let textChanged = false;
16597
+ for (const line of lines) {
16598
+ if (!line.startsWith("data: {")) continue;
16599
+ let json;
16600
+ try {
16601
+ json = JSON.parse(line.substring("data: ".length));
16602
+ } catch (_) {
16603
+ continue;
16604
+ }
16605
+ try {
16606
+ if (consumePayload(json)) textChanged = true;
16607
+ } catch (e) {
16608
+ console$c.warn("Error consuming stream payload:", e);
16609
+ }
16610
+ }
16611
+ if (textChanged && onMessage) {
16612
+ try {
16309
16613
  onMessage(fullResult, { result: fullResult, reasoning_content: fullReasoningContent, tool_calls: toolCalls });
16614
+ } catch (e) {
16615
+ console$c.warn("onMessage failed:", e);
16310
16616
  }
16311
- } catch (e) {
16312
- console$b.warn("Error parsing stream response:", e);
16313
16617
  }
16314
16618
  }
16315
16619
  return fullResult;
@@ -16320,7 +16624,7 @@ class AIGeneratorsBase {
16320
16624
  */
16321
16625
  async chatCompletion(options = {}) {
16322
16626
  var _a2, _b2, _c;
16323
- const { messages, prompt, stream = false, temperature, responseFormat, abortController, extraHeaders, onMessage, onReasoning, onToolCall, onComplete, onError } = options;
16627
+ const { messages, prompt, stream = false, temperature, responseFormat, abortController, extraHeaders, onMessage, onReasoning, onToolCall, onToolCallChunk, onComplete, onError } = options;
16324
16628
  const requestedModel = options["model"] ?? DEFAULT_CHAT_MODEL;
16325
16629
  const tools = options["tools"];
16326
16630
  try {
@@ -16333,11 +16637,12 @@ class AIGeneratorsBase {
16333
16637
  }
16334
16638
  if (!apiKey) throw new Error(`Direct chat requires API key for provider: ${provider ?? "unknown"}`);
16335
16639
  const normalizedMessages = this._normalizeMessages(messages, prompt);
16640
+ const maxTokens = this._resolveMaxTokens(options);
16336
16641
  const isGemini = provider === "gemini" || provider === "google";
16337
16642
  if (isGemini && (tools == null ? void 0 : tools.length)) throw new Error("Direct chat with tools is not supported for provider: gemini");
16338
16643
  if (isGemini) {
16339
16644
  const wrappedOnMessage = stream && onMessage ? (fullText, _delta, raw) => onMessage(fullText, { result: fullText, reasoning_content: "", tool_calls: [] }, raw) : void 0;
16340
- const result = await this._callGoogleLLM({ model, apiKey, messages: normalizedMessages, stream, temperature, responseFormat, onMessage: wrappedOnMessage, abortController, extraHeaders });
16645
+ const result = await this._callGoogleLLM({ model, apiKey, messages: normalizedMessages, stream, temperature, maxTokens, responseFormat, onMessage: wrappedOnMessage, abortController, extraHeaders });
16341
16646
  const res = result;
16342
16647
  if (stream) {
16343
16648
  if (onComplete) onComplete(res.text, { result: res.text, reasoning_content: "", tool_calls: [] });
@@ -16358,6 +16663,7 @@ class AIGeneratorsBase {
16358
16663
  if (extraHeaders) Object.assign(headers, extraHeaders);
16359
16664
  const body = { model, messages: this._normalizeOpenAIChatMessages(normalizedMessages), stream };
16360
16665
  if (temperature !== void 0) body["temperature"] = temperature;
16666
+ if (maxTokens !== void 0) body["max_tokens"] = maxTokens;
16361
16667
  if (responseFormat) body["response_format"] = typeof responseFormat === "string" ? { type: responseFormat } : responseFormat;
16362
16668
  if (tools == null ? void 0 : tools.length) body["tools"] = tools;
16363
16669
  const reasoningOpt = this._normalizeReasoningOption(model, options["reasoning"]);
@@ -16383,35 +16689,35 @@ class AIGeneratorsBase {
16383
16689
  try {
16384
16690
  onReasoning(reasoningText, reasoningText, data);
16385
16691
  } catch (e) {
16386
- console$b.warn("onReasoning failed:", e);
16692
+ console$c.warn("onReasoning failed:", e);
16387
16693
  }
16388
16694
  }
16389
16695
  if (onToolCall) for (const tc of tCalls) {
16390
16696
  try {
16391
16697
  onToolCall(tc);
16392
16698
  } catch (e) {
16393
- console$b.warn("onToolCall failed:", e);
16699
+ console$c.warn("onToolCall failed:", e);
16394
16700
  }
16395
16701
  }
16396
16702
  if (onMessage && (text || reasoningText)) {
16397
16703
  try {
16398
16704
  onMessage(text, { result: text, reasoning_content: reasoningText, tool_calls: tCalls });
16399
16705
  } catch (e) {
16400
- console$b.warn("onMessage failed:", e);
16706
+ console$c.warn("onMessage failed:", e);
16401
16707
  }
16402
16708
  }
16403
16709
  if (onComplete) onComplete(text, { result: text, reasoning_content: reasoningText, tool_calls: tCalls, raw: data, headers: Object.fromEntries(response.headers.entries()) });
16404
16710
  return text;
16405
16711
  }
16406
- return await this._readChatCompletionSSE(response, { onMessage, onReasoning, onToolCall, onComplete });
16712
+ return await this._readChatCompletionSSE(response, { onMessage, onReasoning, onToolCall, onToolCallChunk, onComplete });
16407
16713
  } catch (error) {
16408
16714
  if (onError) onError(error);
16409
16715
  throw error;
16410
16716
  }
16411
16717
  }
16412
16718
  /** @private 解析 OpenAI/OpenRouter chat-completion SSE,返回累积文本。 */
16413
- async _readChatCompletionSSE(response, { onMessage, onReasoning, onToolCall, onComplete } = {}) {
16414
- var _a2, _b2, _c;
16719
+ async _readChatCompletionSSE(response, { onMessage, onReasoning, onToolCall, onToolCallChunk, onComplete } = {}) {
16720
+ var _a2, _b2, _c, _d;
16415
16721
  const reader = (_a2 = response.body) == null ? void 0 : _a2.getReader();
16416
16722
  if (!reader) {
16417
16723
  if (onComplete) onComplete("", { result: "", reasoning_content: "", tool_calls: [] });
@@ -16473,7 +16779,7 @@ class AIGeneratorsBase {
16473
16779
  try {
16474
16780
  onReasoning(fullReasoningContent, rPiece, json);
16475
16781
  } catch (e) {
16476
- console$b.warn("onReasoning failed:", e);
16782
+ console$c.warn("onReasoning failed:", e);
16477
16783
  }
16478
16784
  }
16479
16785
  }
@@ -16488,8 +16794,22 @@ class AIGeneratorsBase {
16488
16794
  if (tc["id"]) entry.id = tc["id"];
16489
16795
  if (tc["type"]) entry.type = tc["type"];
16490
16796
  const fn = tc["function"];
16491
- if (fn == null ? void 0 : fn["name"]) entry.function.name += fn["name"];
16492
- if (fn == null ? void 0 : fn["arguments"]) entry.function.arguments += fn["arguments"];
16797
+ let tcChanged = false;
16798
+ if (fn == null ? void 0 : fn["name"]) {
16799
+ entry.function.name += fn["name"];
16800
+ tcChanged = true;
16801
+ }
16802
+ if (fn == null ? void 0 : fn["arguments"]) {
16803
+ entry.function.arguments += fn["arguments"];
16804
+ tcChanged = true;
16805
+ }
16806
+ if (tcChanged && onToolCallChunk && ((_d = entry.function) == null ? void 0 : _d.name)) {
16807
+ try {
16808
+ onToolCallChunk(entry);
16809
+ } catch (e) {
16810
+ console$c.warn("onToolCallChunk failed:", e);
16811
+ }
16812
+ }
16493
16813
  }
16494
16814
  }
16495
16815
  if (choice["finish_reason"] === "tool_calls") announce();
@@ -16511,22 +16831,24 @@ class AIGeneratorsBase {
16511
16831
  const provider = this._directProvider(localModelSettings, { ...options, model });
16512
16832
  if (!apiKey) throw new Error("Direct provider API key is required");
16513
16833
  const normalizedMessages = this._normalizeMessages(messages, prompt);
16834
+ const maxTokens = this._resolveMaxTokens(options);
16514
16835
  let result;
16515
16836
  if (provider === "gemini" || provider === "google") {
16516
- result = await this._callGoogleLLM({ model, apiKey, messages: normalizedMessages, stream, temperature, responseFormat, onMessage, onReasoning, abortController, extraHeaders });
16837
+ result = await this._callGoogleLLM({ model, apiKey, messages: normalizedMessages, stream, temperature, maxTokens, responseFormat, onMessage, onReasoning, abortController, extraHeaders });
16517
16838
  } else if (provider === "openai") {
16518
- result = await this._callOpenAILLM({ model, apiKey, messages: normalizedMessages, stream, temperature, responseFormat, onMessage, onReasoning, abortController, extraHeaders });
16839
+ result = await this._callOpenAILLM({ model, apiKey, messages: normalizedMessages, stream, temperature, maxTokens, responseFormat, onMessage, onReasoning, abortController, extraHeaders });
16519
16840
  } else if (provider === "openrouter") {
16520
- result = await this._callOpenRouterLLM({ model, apiKey, messages: normalizedMessages, stream, temperature, responseFormat, onMessage, onReasoning, abortController, extraHeaders });
16841
+ result = await this._callOpenRouterLLM({ model, apiKey, messages: normalizedMessages, stream, temperature, maxTokens, responseFormat, onMessage, onReasoning, abortController, extraHeaders });
16521
16842
  } else {
16522
16843
  throw new Error(`Direct provider is not supported: ${provider ?? "unknown"}`);
16523
16844
  }
16524
16845
  return returnRaw ? result : result == null ? void 0 : result.text;
16525
16846
  }
16526
16847
  async _callOpenAILLM(opts) {
16527
- const { model, apiKey, messages, stream = false, temperature, responseFormat, onMessage, onReasoning, abortController, extraHeaders } = opts;
16848
+ const { model, apiKey, messages, stream = false, temperature, maxTokens, responseFormat, onMessage, onReasoning, abortController, extraHeaders } = opts;
16528
16849
  const body = { model, messages, stream };
16529
16850
  if (temperature !== void 0) body["temperature"] = temperature;
16851
+ if (maxTokens !== void 0) body["max_tokens"] = maxTokens;
16530
16852
  if (responseFormat) body["response_format"] = typeof responseFormat === "string" ? { type: responseFormat } : responseFormat;
16531
16853
  const resp = await fetch("https://api.openai.com/v1/chat/completions", {
16532
16854
  method: "POST",
@@ -16540,9 +16862,10 @@ class AIGeneratorsBase {
16540
16862
  return { text: this._extractOpenAIText(data), data };
16541
16863
  }
16542
16864
  async _callOpenRouterLLM(opts) {
16543
- const { model, apiKey, messages, stream = false, temperature, responseFormat, onMessage, onReasoning, onImage, abortController, extraHeaders, extraBody } = opts;
16865
+ const { model, apiKey, messages, stream = false, temperature, maxTokens, responseFormat, onMessage, onReasoning, onImage, abortController, extraHeaders, extraBody } = opts;
16544
16866
  const body = { model, messages, stream };
16545
16867
  if (temperature !== void 0) body["temperature"] = temperature;
16868
+ if (maxTokens !== void 0) body["max_tokens"] = maxTokens;
16546
16869
  if (responseFormat) body["response_format"] = typeof responseFormat === "string" ? { type: responseFormat } : responseFormat;
16547
16870
  if (extraBody) Object.assign(body, extraBody);
16548
16871
  const resp = await fetch("https://openrouter.ai/api/v1/chat/completions", {
@@ -16563,9 +16886,10 @@ class AIGeneratorsBase {
16563
16886
  return { text: this._extractOpenAIText(data), data };
16564
16887
  }
16565
16888
  async _callGoogleLLM(opts) {
16566
- const { model, apiKey, messages, stream = false, temperature, responseFormat, onMessage, onReasoning, abortController, extraHeaders } = opts;
16889
+ const { model, apiKey, messages, stream = false, temperature, maxTokens, responseFormat, onMessage, onReasoning, abortController, extraHeaders } = opts;
16567
16890
  const generationConfig = {};
16568
16891
  if (temperature !== void 0) generationConfig["temperature"] = temperature;
16892
+ if (maxTokens !== void 0) generationConfig["maxOutputTokens"] = maxTokens;
16569
16893
  if (responseFormat === "json_object" || (responseFormat == null ? void 0 : responseFormat["type"]) === "json_object") generationConfig["responseMimeType"] = "application/json";
16570
16894
  const body = { contents: this._messagesToGeminiContents(messages) };
16571
16895
  if (Object.keys(generationConfig).length > 0) body["generationConfig"] = generationConfig;
@@ -16599,7 +16923,7 @@ class AIGeneratorsBase {
16599
16923
  try {
16600
16924
  json = JSON.parse(payload);
16601
16925
  } catch (err) {
16602
- console$b.warn("[AIGenerators] Ignoring malformed SSE payload:", payload, err);
16926
+ console$c.warn("[AIGenerators] Ignoring malformed SSE payload:", payload, err);
16603
16927
  return;
16604
16928
  }
16605
16929
  sawChunk = true;
@@ -16622,7 +16946,7 @@ class AIGeneratorsBase {
16622
16946
  result = await reader.read();
16623
16947
  } catch (err) {
16624
16948
  if (fullText || reasoningText || sawChunk) {
16625
- console$b.warn("[AIGenerators] SSE stream ended with read error after content:", err);
16949
+ console$c.warn("[AIGenerators] SSE stream ended with read error after content:", err);
16626
16950
  return fullText || reasoningText;
16627
16951
  }
16628
16952
  throw err;
@@ -16654,7 +16978,7 @@ class AIGeneratorsBase {
16654
16978
  try {
16655
16979
  json = JSON.parse(payload);
16656
16980
  } catch (err) {
16657
- console$b.warn("[AIGenerators] Ignoring malformed image SSE payload:", payload, err);
16981
+ console$c.warn("[AIGenerators] Ignoring malformed image SSE payload:", payload, err);
16658
16982
  return;
16659
16983
  }
16660
16984
  lastData = json;
@@ -18966,7 +19290,7 @@ function applyPagesMixin(TargetClass) {
18966
19290
  return this.get(`/sites/${siteId}`);
18967
19291
  };
18968
19292
  }
18969
- const console$a = SDKLogger.createModuleConsole("AgentRouter");
19293
+ const console$b = SDKLogger.createModuleConsole("AgentRouter");
18970
19294
  const MSG$1 = {
18971
19295
  REGISTER: "agent_register",
18972
19296
  REGISTER_ACK: "agent_register_ack",
@@ -19069,11 +19393,11 @@ const _AgentRouter = class _AgentRouter {
19069
19393
  register(agentName, session) {
19070
19394
  if (!agentName) return false;
19071
19395
  if (this.localAgents.has(agentName) || this.routeTable.has(agentName)) {
19072
- console$a.warn(`[AgentRouter] Agent '${agentName}' already registered.`);
19396
+ console$b.warn(`[AgentRouter] Agent '${agentName}' already registered.`);
19073
19397
  return false;
19074
19398
  }
19075
19399
  this.localAgents.set(agentName, { session });
19076
- console$a.log(`[AgentRouter] Registered local agent '${agentName}'`);
19400
+ console$b.log(`[AgentRouter] Registered local agent '${agentName}'`);
19077
19401
  this._broadcast({ is_agent_router: true, type: MSG$1.REGISTER, agentName, instanceId: this.instanceId }, null);
19078
19402
  return true;
19079
19403
  }
@@ -19084,7 +19408,7 @@ const _AgentRouter = class _AgentRouter {
19084
19408
  unregister(agentName) {
19085
19409
  if (!this.localAgents.has(agentName)) return;
19086
19410
  this.localAgents.delete(agentName);
19087
- console$a.log(`[AgentRouter] Unregistered local agent '${agentName}'`);
19411
+ console$b.log(`[AgentRouter] Unregistered local agent '${agentName}'`);
19088
19412
  this._broadcast({ is_agent_router: true, type: MSG$1.UNREGISTER, agentName, instanceId: this.instanceId }, null);
19089
19413
  }
19090
19414
  /**
@@ -19133,7 +19457,7 @@ const _AgentRouter = class _AgentRouter {
19133
19457
  submitTask(agentName, payload, callbacks = {}) {
19134
19458
  const route = this.routeTable.get(agentName);
19135
19459
  if (!route) {
19136
- console$a.warn(`[AgentRouter] submitTask failed: no route to '${agentName}'`);
19460
+ console$b.warn(`[AgentRouter] submitTask failed: no route to '${agentName}'`);
19137
19461
  return Promise.reject(new Error(`No route to remote agent '${agentName}'`));
19138
19462
  }
19139
19463
  const taskId = generateUUID();
@@ -19216,7 +19540,7 @@ const _AgentRouter = class _AgentRouter {
19216
19540
  */
19217
19541
  attachNPLJS(npljs, options = {}) {
19218
19542
  if (!npljs) {
19219
- console$a.warn("[AgentRouter] attachNPLJS: npljs instance is required");
19543
+ console$b.warn("[AgentRouter] attachNPLJS: npljs instance is required");
19220
19544
  return this;
19221
19545
  }
19222
19546
  const sendEventName = options.sendEventName ?? "@webparacraft_backgroundAgent";
@@ -19236,7 +19560,7 @@ const _AgentRouter = class _AgentRouter {
19236
19560
  this._onMessage({ data: msg, source: proxy });
19237
19561
  });
19238
19562
  this.addChildWindow(proxy);
19239
- console$a.log(`[AgentRouter] attachNPLJS: send=${sendEventName} recv=${recvEventName}`);
19563
+ console$b.log(`[AgentRouter] attachNPLJS: send=${sendEventName} recv=${recvEventName}`);
19240
19564
  return this;
19241
19565
  }
19242
19566
  /**
@@ -19250,7 +19574,7 @@ const _AgentRouter = class _AgentRouter {
19250
19574
  if (npljs && this._nplRecvEventName) npljs.OffMsg(this._nplRecvEventName);
19251
19575
  this._nplWindowProxy = null;
19252
19576
  this._nplRecvEventName = null;
19253
- console$a.log("[AgentRouter] detachNPLJS: bridge removed");
19577
+ console$b.log("[AgentRouter] detachNPLJS: bridge removed");
19254
19578
  }
19255
19579
  // ─────────────────────── 传输帮助 ───────────────────────
19256
19580
  /**
@@ -19266,7 +19590,7 @@ const _AgentRouter = class _AgentRouter {
19266
19590
  }
19267
19591
  windowRef.postMessage(msg, "*");
19268
19592
  } catch (e) {
19269
- console$a.warn("[AgentRouter] _sendTo failed:", e);
19593
+ console$b.warn("[AgentRouter] _sendTo failed:", e);
19270
19594
  }
19271
19595
  }
19272
19596
  /**
@@ -19350,7 +19674,7 @@ const _AgentRouter = class _AgentRouter {
19350
19674
  }
19351
19675
  this.routeTable.set(agentName, { windowRef: sourceWindow, sourceInstanceId: instanceId, learnedFrom: sourceWindow, updatedAt: Date.now() });
19352
19676
  this._indexWindowAgent(sourceWindow, agentName);
19353
- console$a.log(`[AgentRouter] Discovered remote agent '${agentName}' via instance ${instanceId}`);
19677
+ console$b.log(`[AgentRouter] Discovered remote agent '${agentName}' via instance ${instanceId}`);
19354
19678
  if (sourceWindow !== window.parent) this.connectedWindows.add(sourceWindow);
19355
19679
  if (!syncBackfill) {
19356
19680
  this._sendTo(sourceWindow, { is_agent_router: true, type: MSG$1.REGISTER_ACK, agentName, instanceId: this.instanceId });
@@ -19360,14 +19684,14 @@ const _AgentRouter = class _AgentRouter {
19360
19684
  }
19361
19685
  /** @private 处理 agent_register_ack(当前仅记录日志)。 */
19362
19686
  _handleRegisterAck(msg) {
19363
- console$a.log(`[AgentRouter] Registration of '${msg.agentName}' acknowledged by instance ${msg.instanceId}`);
19687
+ console$b.log(`[AgentRouter] Registration of '${msg.agentName}' acknowledged by instance ${msg.instanceId}`);
19364
19688
  }
19365
19689
  /** @private 处理 agent_register_reject(回滚本地乐观注册)。 */
19366
19690
  _handleRegisterReject(msg) {
19367
- console$a.warn(`[AgentRouter] Registration of '${msg.agentName}' rejected: ${msg.reason}`);
19691
+ console$b.warn(`[AgentRouter] Registration of '${msg.agentName}' rejected: ${msg.reason}`);
19368
19692
  if (this.localAgents.has(msg.agentName ?? "")) {
19369
19693
  this.localAgents.delete(msg.agentName ?? "");
19370
- console$a.warn(`[AgentRouter] Rolled back local registration of '${msg.agentName}'`);
19694
+ console$b.warn(`[AgentRouter] Rolled back local registration of '${msg.agentName}'`);
19371
19695
  }
19372
19696
  }
19373
19697
  /** @private 处理 agent_unregister(验证来源后删除路由,防止劫持)。 */
@@ -19377,7 +19701,7 @@ const _AgentRouter = class _AgentRouter {
19377
19701
  const existing = this.routeTable.get(agentName);
19378
19702
  if (existing) {
19379
19703
  if (existing.windowRef !== sourceWindow || existing.sourceInstanceId !== instanceId) {
19380
- console$a.warn(`[AgentRouter] _handleUnregister ignored stale unregister for '${agentName}'`);
19704
+ console$b.warn(`[AgentRouter] _handleUnregister ignored stale unregister for '${agentName}'`);
19381
19705
  return;
19382
19706
  }
19383
19707
  this.routeTable.delete(agentName);
@@ -19407,7 +19731,7 @@ const _AgentRouter = class _AgentRouter {
19407
19731
  this._createRelayTask(taskId, agentName, route.windowRef, sourceWindow);
19408
19732
  return;
19409
19733
  }
19410
- console$a.warn(`[AgentRouter] _handleTask no route found for agent='${agentName}' taskId=${taskId}`);
19734
+ console$b.warn(`[AgentRouter] _handleTask no route found for agent='${agentName}' taskId=${taskId}`);
19411
19735
  this._sendTo(sourceWindow, {
19412
19736
  is_agent_router: true,
19413
19737
  type: MSG$1.TASK_RESULT,
@@ -19461,7 +19785,7 @@ const _AgentRouter = class _AgentRouter {
19461
19785
  const result = typeof response === "string" ? response : (response == null ? void 0 : response["result"]) ?? "";
19462
19786
  this._sendTo(replyWindow, { is_agent_router: true, type: MSG$1.TASK_RESULT, taskId, result, error: null, sourceInstanceId: this.instanceId });
19463
19787
  } catch (e) {
19464
- console$a.error("[AgentRouter] Local task execution failed:", e);
19788
+ console$b.error("[AgentRouter] Local task execution failed:", e);
19465
19789
  this._sendTo(replyWindow, {
19466
19790
  is_agent_router: true,
19467
19791
  type: MSG$1.TASK_RESULT,
@@ -19528,11 +19852,11 @@ const _AgentRouter = class _AgentRouter {
19528
19852
  const { taskId = "", result, error } = msg;
19529
19853
  const record = this.taskRegistry.get(taskId);
19530
19854
  if (!record) {
19531
- console$a.warn(`[AgentRouter] _handleTaskResult dropped taskId=${taskId}: no task record`);
19855
+ console$b.warn(`[AgentRouter] _handleTaskResult dropped taskId=${taskId}: no task record`);
19532
19856
  return;
19533
19857
  }
19534
19858
  if (!this._isExpectedTaskSource(record, sourceWindow)) {
19535
- console$a.warn(`[AgentRouter] _handleTaskResult dropped taskId=${taskId}: unexpected source`);
19859
+ console$b.warn(`[AgentRouter] _handleTaskResult dropped taskId=${taskId}: unexpected source`);
19536
19860
  return;
19537
19861
  }
19538
19862
  if (record.kind === "origin") {
@@ -19558,7 +19882,7 @@ const _AgentRouter = class _AgentRouter {
19558
19882
  const { taskId = "" } = msg;
19559
19883
  const record = this.taskRegistry.get(taskId);
19560
19884
  if (!record) {
19561
- console$a.warn(`[AgentRouter] _handleStream dropped taskId=${taskId}: no task record`);
19885
+ console$b.warn(`[AgentRouter] _handleStream dropped taskId=${taskId}: no task record`);
19562
19886
  return;
19563
19887
  }
19564
19888
  if (!this._isExpectedTaskSource(record, sourceWindow)) return;
@@ -20048,6 +20372,10 @@ const _KeepworkSDK = class _KeepworkSDK {
20048
20372
  * 因此 npm 包中 `API_KEYS.maisi` 为 `''`;如需鉴权请通过 `setUserApiKey()` 自行提供。
20049
20373
  */
20050
20374
  __publicField(_KeepworkSDK, "API_KEYS", {
20375
+ // 正常情况下 `__MAISI_API_KEY__` 由 Vite `define` 在构建/转译期替换为字符串字面量。
20376
+ // 但当源码 `.ts` 被裸服务(如 maisi dev server 直接以 ESM 提供未做 define 替换的文件)时,
20377
+ // 该全局并不存在,直接引用会抛 ReferenceError 导致整个 SDK 加载失败。
20378
+ // 用 typeof 守卫做安全回退:缺失时退化为空串(与 npm 构建一致),不再抛错。
20051
20379
  maisi: ""
20052
20380
  });
20053
20381
  /** bundle 自身 URL(由 index.ts 在加载时写入,供 DigitalHumanFrame 等推导 CDN 路径)。 */
@@ -20561,7 +20889,7 @@ function normalizeRTCMessageContent(content) {
20561
20889
  if (content === null || content === void 0) return null;
20562
20890
  return typeof content === "string" ? content : JSON.stringify(content);
20563
20891
  }
20564
- const console$9 = SDKLogger.createModuleConsole("AIChatRTC");
20892
+ const console$a = SDKLogger.createModuleConsole("AIChatRTC");
20565
20893
  class RTCChatSession {
20566
20894
  /**
20567
20895
  * @param {AIChatRTC} aiChatRTC - Parent AIChatRTC instance
@@ -20679,7 +21007,7 @@ class RTCChatSession {
20679
21007
  try {
20680
21008
  fn(data);
20681
21009
  } catch (e) {
20682
- console$9.warn(`[RTCChatSession] Event '${event}' listener error:`, e);
21010
+ console$a.warn(`[RTCChatSession] Event '${event}' listener error:`, e);
20683
21011
  }
20684
21012
  }
20685
21013
  }
@@ -20778,7 +21106,7 @@ class RTCChatSession {
20778
21106
  taskId: this.taskId
20779
21107
  });
20780
21108
  } catch (e) {
20781
- console$9.warn("[RTCChatSession] stop voiceChat warning:", e == null ? void 0 : e.message);
21109
+ console$a.warn("[RTCChatSession] stop voiceChat warning:", e == null ? void 0 : e.message);
20782
21110
  }
20783
21111
  }
20784
21112
  this._cleanup();
@@ -20842,7 +21170,7 @@ class RTCChatSession {
20842
21170
  this.rtcEngine.leaveRoom();
20843
21171
  VERTC.destroyEngine(this.rtcEngine);
20844
21172
  } catch (e) {
20845
- console$9.warn("[RTCChatSession] cleanup warning:", e == null ? void 0 : e.message);
21173
+ console$a.warn("[RTCChatSession] cleanup warning:", e == null ? void 0 : e.message);
20846
21174
  }
20847
21175
  this.rtcEngine = null;
20848
21176
  }
@@ -21194,7 +21522,7 @@ class RTCChatSession {
21194
21522
  */
21195
21523
  async restartAgent(promptFile, tools) {
21196
21524
  var _a2, _b2;
21197
- console$9.log(`[RTCChatSession] restartAgent promptFile=${promptFile || "(none)"}`);
21525
+ console$a.log(`[RTCChatSession] restartAgent promptFile=${promptFile || "(none)"}`);
21198
21526
  const normalizedTools = Array.isArray(tools) ? tools.filter((t) => typeof t === "string" && t.trim()) : [];
21199
21527
  if (typeof this._cleanupChildSessions === "function") {
21200
21528
  this._cleanupChildSessions();
@@ -21207,10 +21535,9 @@ class RTCChatSession {
21207
21535
  let loadedConfig = null;
21208
21536
  if (promptFile && typeof promptFile === "string" && promptFile.trim()) {
21209
21537
  try {
21210
- const AgentConfig2 = (await Promise.resolve().then(() => AgentConfig$1)).default;
21211
- loadedConfig = await AgentConfig2.fetch(promptFile.trim());
21538
+ loadedConfig = await AgentConfig.fetch(promptFile.trim());
21212
21539
  } catch (e) {
21213
- console$9.warn(`[RTCChatSession] restartAgent failed to load '${promptFile}': ${e == null ? void 0 : e.message}`);
21540
+ console$a.warn(`[RTCChatSession] restartAgent failed to load '${promptFile}': ${e == null ? void 0 : e.message}`);
21214
21541
  }
21215
21542
  }
21216
21543
  if (loadedConfig) {
@@ -21558,7 +21885,7 @@ class RTCChatSession {
21558
21885
  for (const toolCall of toolCalls) {
21559
21886
  const funcName = (_a2 = toolCall.function) == null ? void 0 : _a2.name;
21560
21887
  if (!funcName) {
21561
- console$9.warn("[AIChatRTC] Skipping tool call with empty function name", toolCall == null ? void 0 : toolCall.id);
21888
+ console$a.warn("[AIChatRTC] Skipping tool call with empty function name", toolCall == null ? void 0 : toolCall.id);
21562
21889
  if (this.rtcEngine && toolCall.id) {
21563
21890
  await this._sendFuncResultChunked(toolCall.id, "OK", "unknown");
21564
21891
  }
@@ -21579,7 +21906,7 @@ class RTCChatSession {
21579
21906
  if (e && e.isRestartAgentSignal) {
21580
21907
  const dh = this._digitalHuman;
21581
21908
  if (dh && typeof dh.restartAgent === "function") {
21582
- console$9.log(`[AIChatRTC] RestartAgentSignal caught in _handleFunctionCall, restarting with '${e.promptFile}'`);
21909
+ console$a.log(`[AIChatRTC] RestartAgentSignal caught in _handleFunctionCall, restarting with '${e.promptFile}'`);
21583
21910
  if (this.rtcEngine && toolCall.id) {
21584
21911
  await this._sendFuncResultChunked(toolCall.id, "Agent restarting...", funcName);
21585
21912
  }
@@ -21589,7 +21916,7 @@ class RTCChatSession {
21589
21916
  try {
21590
21917
  await dh.restartAgent(promptFile, tools);
21591
21918
  } catch (err) {
21592
- console$9.error("[AIChatRTC] deferred restartAgent failed:", err == null ? void 0 : err.message);
21919
+ console$a.error("[AIChatRTC] deferred restartAgent failed:", err == null ? void 0 : err.message);
21593
21920
  dh.emit("error", { error: err, stage: "restartAgent" });
21594
21921
  }
21595
21922
  }, 0);
@@ -21845,7 +22172,7 @@ function installRTCChatSessionMixin() {
21845
22172
  try {
21846
22173
  await this.send(null, this._lastSendOptions);
21847
22174
  } catch (e) {
21848
- console$9.error("[RTCChatSession] Immediate callback send failed:", e);
22175
+ console$a.error("[RTCChatSession] Immediate callback send failed:", e);
21849
22176
  }
21850
22177
  };
21851
22178
  }
@@ -21909,7 +22236,7 @@ let AIChatRTC$1 = (_a = class {
21909
22236
  return new RTCChatSession(this, config);
21910
22237
  }
21911
22238
  }, __publicField(_a, "AGENT_STATE", AGENT_STATE), __publicField(_a, "AGENT_STATE_LABELS", AGENT_STATE_LABELS), __publicField(_a, "COMMAND", COMMAND), _a);
21912
- const console$8 = SDKLogger.createModuleConsole("AIChatRTCLocal");
22239
+ const console$9 = SDKLogger.createModuleConsole("AIChatRTCLocal");
21913
22240
  const _win = typeof window !== "undefined" ? window : {};
21914
22241
  const DEFAULT_VAD_CDN = {
21915
22242
  ortUrl: "https://cdn.keepwork.com/keepwork/cdn/voice/ort.min.js",
@@ -23116,7 +23443,7 @@ class WebSpeechASRBackend extends BaseASRBackend {
23116
23443
  };
23117
23444
  this._recognition.onerror = (event) => {
23118
23445
  if (event.error === "no-speech" || event.error === "aborted") return;
23119
- console$8.warn("[LocalRTCSession] SpeechRecognition error:", event.error);
23446
+ console$9.warn("[LocalRTCSession] SpeechRecognition error:", event.error);
23120
23447
  const message = event.error === "not-allowed" ? "Microphone permission denied for speech recognition" : `SpeechRecognition error: ${event.error}`;
23121
23448
  this.session._handleASRError(message, { fatal: false });
23122
23449
  };
@@ -24086,7 +24413,7 @@ class LocalNeuralTTSBackend extends BaseTTSBackend {
24086
24413
  if (Number.isFinite(this._numSpeakers) && this._numSpeakers > 0) {
24087
24414
  if (speakerId < 0 || speakerId >= this._numSpeakers) {
24088
24415
  const fallbackSpeakerId = ((_b2 = this._resolvedConfig) == null ? void 0 : _b2.defaultSpeakerId) || 0;
24089
- console$8.warn(`[LocalNeuralTTS] Speaker ${speakerId} is out of range for preset ${((_d = (_c = this._resolvedConfig) == null ? void 0 : _c.activePreset) == null ? void 0 : _d.id) || "unknown"} (numSpeakers=${this._numSpeakers}); using ${fallbackSpeakerId} instead.`);
24416
+ console$9.warn(`[LocalNeuralTTS] Speaker ${speakerId} is out of range for preset ${((_d = (_c = this._resolvedConfig) == null ? void 0 : _c.activePreset) == null ? void 0 : _d.id) || "unknown"} (numSpeakers=${this._numSpeakers}); using ${fallbackSpeakerId} instead.`);
24090
24417
  speakerId = fallbackSpeakerId >= 0 && fallbackSpeakerId < this._numSpeakers ? fallbackSpeakerId : 0;
24091
24418
  }
24092
24419
  }
@@ -24143,7 +24470,7 @@ class LocalNeuralTTSBackend extends BaseTTSBackend {
24143
24470
  }
24144
24471
  }
24145
24472
  const SENTENCE_END_RE = /[。!?!?\n]+/;
24146
- const console$7 = SDKLogger.createModuleConsole("AIChatRTCLocal");
24473
+ const console$8 = SDKLogger.createModuleConsole("AIChatRTCLocal");
24147
24474
  class LocalRTCSession {
24148
24475
  /**
24149
24476
  * @param {AIChatRTCLocal} parent - Parent AIChatRTCLocal instance
@@ -24217,7 +24544,7 @@ class LocalRTCSession {
24217
24544
  fn(data);
24218
24545
  } catch (eRaw) {
24219
24546
  const e = eRaw;
24220
- console$7.warn(`[LocalRTCSession] Event '${event}' listener error:`, e);
24547
+ console$8.warn(`[LocalRTCSession] Event '${event}' listener error:`, e);
24221
24548
  }
24222
24549
  }
24223
24550
  }
@@ -24518,10 +24845,10 @@ class LocalRTCSession {
24518
24845
  }
24519
24846
  });
24520
24847
  this._micVAD.start();
24521
- console$7.log("[LocalRTCSession] Silero VAD started (single-thread)");
24848
+ console$8.log("[LocalRTCSession] Silero VAD started (single-thread)");
24522
24849
  } catch (eRaw) {
24523
24850
  const e = eRaw;
24524
- console$7.warn("[LocalRTCSession] Silero VAD init failed, falling back to energy VAD:", e.message);
24851
+ console$8.warn("[LocalRTCSession] Silero VAD init failed, falling back to energy VAD:", e.message);
24525
24852
  this.parent._vadAvailable = false;
24526
24853
  await this._initEnergyVAD();
24527
24854
  }
@@ -24577,7 +24904,7 @@ class LocalRTCSession {
24577
24904
  this._silenceStart = 0;
24578
24905
  }
24579
24906
  }, ENERGY_VAD.SAMPLE_INTERVAL);
24580
- console$7.log("[LocalRTCSession] Energy VAD started (fallback)");
24907
+ console$8.log("[LocalRTCSession] Energy VAD started (fallback)");
24581
24908
  }
24582
24909
  _onSpeechStart() {
24583
24910
  if (this._isTTSSpeaking || this._isLLMStreaming) {
@@ -24734,7 +25061,7 @@ class LocalRTCSession {
24734
25061
  _notifyMediaStreamChanged(stream) {
24735
25062
  if (!this._asrBackend || typeof this._asrBackend.attachMediaStream !== "function") return;
24736
25063
  this._asrBackend.attachMediaStream(stream).catch((error) => {
24737
- console$7.warn("[LocalRTCSession] Failed to rebind ASR media stream:", (error == null ? void 0 : error.message) || error);
25064
+ console$8.warn("[LocalRTCSession] Failed to rebind ASR media stream:", (error == null ? void 0 : error.message) || error);
24738
25065
  });
24739
25066
  }
24740
25067
  _beginRecognitionRound() {
@@ -24850,7 +25177,7 @@ class LocalRTCSession {
24850
25177
  var _a2, _b2;
24851
25178
  const MAX_ITERATIONS = 10;
24852
25179
  if (iteration >= MAX_ITERATIONS) {
24853
- console$7.warn("[LocalRTCSession] Max tool-call iterations reached");
25180
+ console$8.warn("[LocalRTCSession] Max tool-call iterations reached");
24854
25181
  if (this.isActive && !this.isMuted) this._setState(AGENT_STATE.LISTENING);
24855
25182
  return;
24856
25183
  }
@@ -24894,7 +25221,7 @@ class LocalRTCSession {
24894
25221
  },
24895
25222
  onError: (error) => {
24896
25223
  if (error.name === "AbortError") return;
24897
- console$7.error("[LocalRTCSession] LLM error:", error.message);
25224
+ console$8.error("[LocalRTCSession] LLM error:", error.message);
24898
25225
  this.emit("error", { error: error.message || "LLM request failed" });
24899
25226
  }
24900
25227
  });
@@ -24902,7 +25229,7 @@ class LocalRTCSession {
24902
25229
  const error = errorRaw;
24903
25230
  this._isLLMStreaming = false;
24904
25231
  if (error.name === "AbortError") return;
24905
- console$7.error("[LocalRTCSession] LLM error:", error.message);
25232
+ console$8.error("[LocalRTCSession] LLM error:", error.message);
24906
25233
  this.emit("error", { error: error.message || "LLM request failed" });
24907
25234
  if (this.isActive && !this.isMuted) this._setState(AGENT_STATE.LISTENING);
24908
25235
  return;
@@ -25038,7 +25365,7 @@ class LocalRTCSession {
25038
25365
  if ((error == null ? void 0 : error.name) === "AbortError" || this._ttsToken !== drainToken) {
25039
25366
  break;
25040
25367
  }
25041
- console$7.warn("[LocalRTCSession] TTS error:", (error == null ? void 0 : error.message) || error);
25368
+ console$8.warn("[LocalRTCSession] TTS error:", (error == null ? void 0 : error.message) || error);
25042
25369
  this._emitTTSStatus("error", {
25043
25370
  provider: this.ttsBackendName,
25044
25371
  message: (error == null ? void 0 : error.message) || String(error)
@@ -25145,7 +25472,7 @@ Result: ${resultText}`;
25145
25472
  };
25146
25473
  }
25147
25474
  installLocalRTCSessionMixin();
25148
- const console$6 = SDKLogger.createModuleConsole("AIChatRTCLocal");
25475
+ const console$7 = SDKLogger.createModuleConsole("AIChatRTCLocal");
25149
25476
  installLocalRTCSessionMixin();
25150
25477
  let AIChatRTCLocal$1 = (_b = class {
25151
25478
  /**
@@ -25193,12 +25520,12 @@ let AIChatRTCLocal$1 = (_b = class {
25193
25520
  this._loadPromise = this._loadScripts().then(() => {
25194
25521
  this._vadAvailable = !!(typeof vad !== "undefined" && vad.MicVAD);
25195
25522
  if (this._vadAvailable) {
25196
- console$6.log("[AIChatRTCLocal] Silero VAD loaded");
25523
+ console$7.log("[AIChatRTCLocal] Silero VAD loaded");
25197
25524
  } else {
25198
- console$6.warn("[AIChatRTCLocal] VAD bundle loaded but vad.MicVAD not found — using energy fallback");
25525
+ console$7.warn("[AIChatRTCLocal] VAD bundle loaded but vad.MicVAD not found — using energy fallback");
25199
25526
  }
25200
25527
  }).catch(() => {
25201
- console$6.warn("[AIChatRTCLocal] Failed to load Silero VAD — using energy fallback");
25528
+ console$7.warn("[AIChatRTCLocal] Failed to load Silero VAD — using energy fallback");
25202
25529
  this._vadAvailable = false;
25203
25530
  this._loadPromise = null;
25204
25531
  });
@@ -25269,7 +25596,7 @@ let AIChatRTCLocal$1 = (_b = class {
25269
25596
  return this._rememberBlobURL(cacheKey, cached);
25270
25597
  }
25271
25598
  } catch (error) {
25272
- console$6.warn(`[AIChatRTCLocal] IndexedDB cache read failed for ${fileName}:`, (error == null ? void 0 : error.message) || error);
25599
+ console$7.warn(`[AIChatRTCLocal] IndexedDB cache read failed for ${fileName}:`, (error == null ? void 0 : error.message) || error);
25273
25600
  }
25274
25601
  try {
25275
25602
  const response = await fetch(remoteUrl);
@@ -25284,7 +25611,7 @@ let AIChatRTCLocal$1 = (_b = class {
25284
25611
  });
25285
25612
  return this._rememberBlobURL(cacheKey, blob);
25286
25613
  } catch (error) {
25287
- console$6.warn(`[AIChatRTCLocal] Failed to cache ${fileName}:`, (error == null ? void 0 : error.message) || error);
25614
+ console$7.warn(`[AIChatRTCLocal] Failed to cache ${fileName}:`, (error == null ? void 0 : error.message) || error);
25288
25615
  return null;
25289
25616
  }
25290
25617
  }
@@ -25361,6 +25688,886 @@ let AIChatRTCLocal$1 = (_b = class {
25361
25688
  AIChatRTCLocal$1.DEFAULT_MODEL_CDN = DEFAULT_KEEPWORK_MODEL_CDN;
25362
25689
  AIChatRTCLocal$1.DEFAULT_MODEL_BASE = getDefaultModelAssetBase();
25363
25690
  const AIChatRTCLocal = AIChatRTCLocal$1;
25691
+ const LocalRTC = typeof window !== "undefined" && window.SherpaOnnxLocalRTC || null;
25692
+ const console$6 = SDKLogger.createModuleConsole("LocalRTCController");
25693
+ const SHERPA_DEFAULT_BASE = "https://cdn.keepwork.com/npm/sherpaonnx-full-js/";
25694
+ function normalizeText(text) {
25695
+ return String(text || "").replace(/[\s\r\n\t.,,。!!??、]+/g, "");
25696
+ }
25697
+ function waitForSherpaGlobal(timeoutMs = 15e3) {
25698
+ if (typeof window === "undefined") return Promise.resolve(false);
25699
+ const w = window;
25700
+ if (w.SherpaOnnxLocalRTC) return Promise.resolve(true);
25701
+ return new Promise((resolve) => {
25702
+ const start = Date.now();
25703
+ const tick = () => {
25704
+ if (w.SherpaOnnxLocalRTC) return resolve(true);
25705
+ if (Date.now() - start >= timeoutMs) return resolve(false);
25706
+ setTimeout(tick, 50);
25707
+ };
25708
+ tick();
25709
+ });
25710
+ }
25711
+ function loadScript$1(src) {
25712
+ return new Promise((resolve, reject) => {
25713
+ if (typeof document === "undefined") {
25714
+ resolve();
25715
+ return;
25716
+ }
25717
+ if (document.querySelector(`script[src^="${src.split("?")[0]}"]`)) {
25718
+ resolve();
25719
+ return;
25720
+ }
25721
+ const s = document.createElement("script");
25722
+ s.src = src;
25723
+ s.async = false;
25724
+ s.onload = () => resolve();
25725
+ s.onerror = () => reject(new Error(`Failed to load ${src}`));
25726
+ document.head.appendChild(s);
25727
+ });
25728
+ }
25729
+ class LocalRTCController {
25730
+ constructor(options) {
25731
+ // ── 运行时引用 ──
25732
+ __publicField(this, "host");
25733
+ __publicField(this, "config");
25734
+ __publicField(this, "hooks");
25735
+ /** SherpaOnnxLocalRTC 实例 */
25736
+ __publicField(this, "_rtc", null);
25737
+ /** LocalRTC 类 */
25738
+ __publicField(this, "_LocalRTC", null);
25739
+ /** 预加载是否已完成 */
25740
+ __publicField(this, "_preloaded", false);
25741
+ /** 预加载 Promise(防止并发重复预加载) */
25742
+ __publicField(this, "_preloadPromise", null);
25743
+ __publicField(this, "_sherpaBase", "");
25744
+ __publicField(this, "_sherpaVersion", "");
25745
+ // ── 加载进度 ──
25746
+ __publicField(this, "_loadProgress", {
25747
+ phase: "未开始",
25748
+ wasm: 0,
25749
+ kws: 0,
25750
+ asr: 0,
25751
+ source: "",
25752
+ done: false,
25753
+ error: ""
25754
+ });
25755
+ // ── 本地语音会话状态 ──
25756
+ __publicField(this, "_localVcActive", false);
25757
+ __publicField(this, "_localVcProcessing", false);
25758
+ __publicField(this, "_localTtsPlaying", false);
25759
+ __publicField(this, "_localTtsEndTime", 0);
25760
+ __publicField(this, "_localAsrEndTime", 0);
25761
+ __publicField(this, "_wakeViaKwsLocalAsr", false);
25762
+ __publicField(this, "_currentTtsText", "");
25763
+ __publicField(this, "_localAsrPausedForTts", false);
25764
+ __publicField(this, "_savedBargeInThreshold", null);
25765
+ __publicField(this, "_ttsVersion", 0);
25766
+ __publicField(this, "_lastSentAsrText", "");
25767
+ // ── textSpeech 事件订阅句柄(destroy 时解绑)──
25768
+ __publicField(this, "_onTtsStartBound", null);
25769
+ __publicField(this, "_onTtsEndBound", null);
25770
+ __publicField(this, "_onTtsCanceledBound", null);
25771
+ if (!(options == null ? void 0 : options.host)) throw new Error("LocalRTCController requires a host");
25772
+ this.host = options.host;
25773
+ this.config = options.config || {};
25774
+ this.hooks = options.hooks || {};
25775
+ this._LocalRTC = options.localRTCClass || null;
25776
+ this._onTtsStartBound = () => {
25777
+ void this.onTtsStart();
25778
+ };
25779
+ this._onTtsEndBound = () => {
25780
+ void this.onTtsEnd();
25781
+ };
25782
+ this._onTtsCanceledBound = () => {
25783
+ void this.onTtsCanceled();
25784
+ };
25785
+ this.host.on("textSpeechStart", this._onTtsStartBound);
25786
+ this.host.on("textSpeechEnd", this._onTtsEndBound);
25787
+ this.host.on("textSpeechCanceled", this._onTtsCanceledBound);
25788
+ }
25789
+ // =================== 公共状态查询 ===================
25790
+ /** 当前是否处于本地 ASR 活跃会话 */
25791
+ isLocalVcActive() {
25792
+ return this._localVcActive;
25793
+ }
25794
+ /** 当前 RTC 实例(供调试 / 高级用法) */
25795
+ getRtc() {
25796
+ return this._rtc;
25797
+ }
25798
+ /** 读取当前加载进度快照 */
25799
+ getLoadProgress() {
25800
+ return { ...this._loadProgress };
25801
+ }
25802
+ /** 内部:合并更新进度并通知钩子 */
25803
+ _setProgress(patch) {
25804
+ var _a2, _b2;
25805
+ this._loadProgress = { ...this._loadProgress, ...patch };
25806
+ const snap = this.getLoadProgress();
25807
+ try {
25808
+ (_b2 = (_a2 = this.hooks).onLoadProgress) == null ? void 0 : _b2.call(_a2, snap);
25809
+ } catch {
25810
+ }
25811
+ }
25812
+ _emitStatus(text) {
25813
+ var _a2, _b2;
25814
+ try {
25815
+ (_b2 = (_a2 = this.hooks).onStatus) == null ? void 0 : _b2.call(_a2, text);
25816
+ } catch {
25817
+ }
25818
+ }
25819
+ _isLocalAsrEnabled() {
25820
+ var _a2, _b2;
25821
+ try {
25822
+ return !!((_b2 = (_a2 = this.hooks).isLocalAsrEnabled) == null ? void 0 : _b2.call(_a2));
25823
+ } catch {
25824
+ return false;
25825
+ }
25826
+ }
25827
+ /** 获取打断(barge-in)配置:是否允许 TTS 播放时被打断 */
25828
+ _getInterruptionEnabled() {
25829
+ var _a2, _b2;
25830
+ return ((_b2 = (_a2 = this.config) == null ? void 0 : _a2.interruption) == null ? void 0 : _b2.enabled) !== false;
25831
+ }
25832
+ /** 是否为 android 原生 ASR 运行时(决定是否走「伪 KWS」方案) */
25833
+ _isAndroidRuntime() {
25834
+ const cfg = this.config || {};
25835
+ const asrRuntime = cfg.asrRuntime || (cfg.runtime === "android" ? "android" : "wasm");
25836
+ return asrRuntime === "android";
25837
+ }
25838
+ // =================== 解析 LocalRTC 类 ===================
25839
+ _resolveLocalRTCClass() {
25840
+ if (this._LocalRTC) return this._LocalRTC;
25841
+ if (LocalRTC) return LocalRTC;
25842
+ const w = typeof window !== "undefined" ? window : null;
25843
+ return w && w.SherpaOnnxLocalRTC || null;
25844
+ }
25845
+ // =================== 预加载 ===================
25846
+ /**
25847
+ * 预加载本地 RTC 资源并构造 SherpaOnnxLocalRTC 实例。
25848
+ * 幂等:重复调用返回同一个 Promise。
25849
+ * @returns rtc 实例或 null(不可用)
25850
+ */
25851
+ async preload() {
25852
+ if (this.config.enabled === false) return null;
25853
+ if (this._preloaded) return this._rtc;
25854
+ if (this._preloadPromise) return this._preloadPromise;
25855
+ this._setProgress({ phase: "开始预加载...", done: false, error: "" });
25856
+ this._preloadPromise = this._doPreload().then((rtc) => {
25857
+ this._preloaded = true;
25858
+ this._preloadPromise = null;
25859
+ return rtc;
25860
+ }).catch((e) => {
25861
+ this._preloadPromise = null;
25862
+ this._setProgress({ phase: "加载失败", error: String((e == null ? void 0 : e.message) || e) });
25863
+ console$6.warn("[LocalRTCController] 预加载失败:", e);
25864
+ this._emitStatus("本地语音不可用");
25865
+ return null;
25866
+ });
25867
+ return this._preloadPromise;
25868
+ }
25869
+ async _doPreload() {
25870
+ var _a2, _b2, _c, _d, _e, _f, _g;
25871
+ const cfg = this.config;
25872
+ const w = typeof window !== "undefined" ? window : {};
25873
+ const base = cfg.baseUrl || w.sherpaonnxBase || SHERPA_DEFAULT_BASE;
25874
+ w.sherpaonnxBase = base;
25875
+ this._sherpaBase = base;
25876
+ this._sherpaVersion = cfg.version || "";
25877
+ const echo = cfg.echoGuard || {};
25878
+ if (echo.prefixRatio != null) w._echoPrefixRatio = echo.prefixRatio;
25879
+ if (echo.bigramRatio != null) w._echoBigramRatio = echo.bigramRatio;
25880
+ if (!w.SherpaOnnxLocalRTC) {
25881
+ this._setProgress({ phase: "加载 sherpa-onnx-local-rtc.js..." });
25882
+ await loadScript$1(base + "sherpa-onnx-local-rtc.js").catch(
25883
+ (e) => console$6.warn("[LocalRTCController] local-rtc 脚本加载失败", e)
25884
+ );
25885
+ }
25886
+ const ready = await waitForSherpaGlobal();
25887
+ if (!ready) {
25888
+ throw new Error("window.SherpaOnnxLocalRTC 未就绪(sherpa-onnx-local-rtc.js 加载失败?)");
25889
+ }
25890
+ await Promise.all([
25891
+ loadScript$1(base + "sherpa-onnx-kws.js").catch((e) => console$6.warn("[LocalRTCController] kws 脚本加载失败", e))
25892
+ ]);
25893
+ const LocalRTC2 = this._resolveLocalRTCClass();
25894
+ if (!LocalRTC2) {
25895
+ throw new Error("LocalRTC 类不可用");
25896
+ }
25897
+ this._LocalRTC = LocalRTC2;
25898
+ const asrRuntime = cfg.asrRuntime || (cfg.runtime === "android" ? "android" : "wasm");
25899
+ const rtc = new LocalRTC2({
25900
+ baseUrl: base,
25901
+ asrRuntime,
25902
+ minAudioRms: cfg.minAudioRms ?? 0.015,
25903
+ audioSource: ((_a2 = cfg.audio) == null ? void 0 : _a2.source) || "voice_communication",
25904
+ aecEnabled: ((_b2 = cfg.audio) == null ? void 0 : _b2.aecEnabled) !== false,
25905
+ vadConfig: ((_c = cfg.vad) == null ? void 0 : _c.enabled) !== false ? cfg.vad || void 0 : void 0,
25906
+ kwsKeywords: ((_d = cfg.kws) == null ? void 0 : _d.keywords) || [],
25907
+ kwsThreshold: ((_e = cfg.kws) == null ? void 0 : _e.threshold) ?? 0.25,
25908
+ bargeInThreshold: ((_f = cfg.bargeIn) == null ? void 0 : _f.threshold) ?? 0.04,
25909
+ bargeInMinFrames: ((_g = cfg.bargeIn) == null ? void 0 : _g.minFrames) ?? 2,
25910
+ onLog: (msg) => console$6.log(msg),
25911
+ onError: (msg) => console$6.warn("ERROR " + msg),
25912
+ onStateChange: (state, old) => console$6.log(`State: ${old} -> ${state}`),
25913
+ onKwsDetected: (keyword, action) => this._handleKwsDetected(keyword, action),
25914
+ onAsrPartial: (text) => this._handleAsrPartial(text),
25915
+ onAsrResult: (text, all) => this._handleAsrResult(text, all),
25916
+ onProgress: (label, pct, source) => {
25917
+ const key = String(label || "").toLowerCase().includes("asr") ? "asr" : "kws";
25918
+ this._setProgress({ [key]: Math.round(pct), source: source || this._loadProgress.source, phase: `加载${label} ${Math.round(pct)}%` });
25919
+ }
25920
+ });
25921
+ this._rtc = rtc;
25922
+ w.localRTC = rtc;
25923
+ this._setProgress({ phase: "加载 WASM 运行时...", wasm: 5 });
25924
+ console$6.log("Calling init()...");
25925
+ await rtc.init();
25926
+ this._setProgress({ phase: "WASM 就绪", wasm: 100 });
25927
+ console$6.log("WASM runtime ready");
25928
+ const tasks = [
25929
+ rtc.preloadKws().then(() => {
25930
+ this._setProgress({ kws: 100 });
25931
+ console$6.log("KWS preloaded");
25932
+ }).catch((e) => {
25933
+ this._setProgress({ error: "KWS:" + ((e == null ? void 0 : e.message) || e) });
25934
+ console$6.warn("KWS preload failed: " + ((e == null ? void 0 : e.message) || e));
25935
+ })
25936
+ ];
25937
+ if (cfg.preloadAsr) {
25938
+ tasks.push(
25939
+ rtc.preloadAsr().then(() => {
25940
+ this._setProgress({ asr: 100 });
25941
+ console$6.log("ASR preloaded");
25942
+ }).catch((e) => {
25943
+ this._setProgress({ error: "ASR:" + ((e == null ? void 0 : e.message) || e) });
25944
+ console$6.warn("ASR preload failed: " + ((e == null ? void 0 : e.message) || e));
25945
+ })
25946
+ );
25947
+ } else {
25948
+ this._setProgress({ asr: -1 });
25949
+ }
25950
+ await Promise.all(tasks);
25951
+ this._setProgress({ phase: "就绪", done: true });
25952
+ await this._startKwsWithRetry();
25953
+ return rtc;
25954
+ }
25955
+ /**
25956
+ * 启动 WASM KWS,失败时重试若干次。
25957
+ * "Could not start audio source" 多为麦克风被上一次会话/其它进程残留占用。
25958
+ */
25959
+ async _startKwsWithRetry(maxAttempts = 3, delayMs = 500) {
25960
+ var _a2;
25961
+ const rtc = this._rtc;
25962
+ if (!rtc) return false;
25963
+ for (let i = 1; i <= maxAttempts; i++) {
25964
+ try {
25965
+ await rtc.start();
25966
+ console$6.log(`WASM KWS listening started${i > 1 ? `(第 ${i} 次尝试成功)` : ""}`);
25967
+ return true;
25968
+ } catch (e) {
25969
+ const msg = (e == null ? void 0 : e.message) || e;
25970
+ console$6.warn(`start() 第 ${i}/${maxAttempts} 次失败: ${msg}`);
25971
+ if (i < maxAttempts) {
25972
+ try {
25973
+ (_a2 = rtc.stop) == null ? void 0 : _a2.call(rtc);
25974
+ } catch {
25975
+ }
25976
+ await new Promise((r) => setTimeout(r, delayMs));
25977
+ } else {
25978
+ this._emitStatus("麦克风被占用,唤醒启动失败");
25979
+ console$6.warn("WASM KWS 启动最终失败:麦克风可能被其它应用/会话占用");
25980
+ }
25981
+ }
25982
+ }
25983
+ return false;
25984
+ }
25985
+ // =================== 唤醒待机 / 麦克风让渡 ===================
25986
+ /**
25987
+ * 对话结束后重启 WASM KWS 唤醒监听(web/android 统一),回到「待唤醒」状态。
25988
+ */
25989
+ async resumeKws() {
25990
+ const rtc = this._rtc;
25991
+ if (!rtc) return false;
25992
+ if (this._localVcActive) return false;
25993
+ const ok = await this._startKwsWithRetry();
25994
+ if (ok) {
25995
+ this._emitStatus("待唤醒...");
25996
+ console$6.log("对话已结束,WASM KWS 已重启(待唤醒)");
25997
+ }
25998
+ return ok;
25999
+ }
26000
+ /**
26001
+ * 启动云端 RTC 前,停掉 WASM KWS 释放麦克风(+短延迟确保设备真正释放)。
26002
+ */
26003
+ async releaseMicForCloud() {
26004
+ const rtc = this._rtc;
26005
+ if (!(rtc == null ? void 0 : rtc.stop)) return;
26006
+ try {
26007
+ rtc.stop();
26008
+ console$6.log("云端启动前:已停止 WASM KWS 并释放麦克风");
26009
+ await new Promise((r) => setTimeout(r, 150));
26010
+ } catch (e) {
26011
+ console$6.warn("releaseMicForCloud error: " + ((e == null ? void 0 : e.message) || e));
26012
+ }
26013
+ }
26014
+ // =================== 热切换:本地 ASR ⇄ 云端 ===================
26015
+ /**
26016
+ * 开启本地 ASR(从云端切换到本地,或直接启动本地语音会话)。
26017
+ * @returns 是否成功
26018
+ */
26019
+ async enableLocalAsr(opts = {}) {
26020
+ const skipSwitchToAsr = opts.skipSwitchToAsr === true || this._wakeViaKwsLocalAsr === true;
26021
+ this._wakeViaKwsLocalAsr = false;
26022
+ const rtc = this._rtc;
26023
+ if (!rtc) {
26024
+ console$6.warn("LocalRTC not available");
26025
+ this._emitStatus("本地语音不可用");
26026
+ return false;
26027
+ }
26028
+ if (this._localVcActive) {
26029
+ console$6.log("enableLocalAsr 忽略:已处于本地对话中");
26030
+ return true;
26031
+ }
26032
+ console$6.log(`Starting local voice chat (ASR -> LLM -> TTS)...${skipSwitchToAsr ? "(switchToAsr 交给 SDK 自动)" : ""}`);
26033
+ this._localVcActive = true;
26034
+ let asrOk = false;
26035
+ if (skipSwitchToAsr) {
26036
+ asrOk = true;
26037
+ } else {
26038
+ if (this._localAsrEndTime) {
26039
+ const sinceEnd = Date.now() - this._localAsrEndTime;
26040
+ const minGap = 600;
26041
+ if (sinceEnd >= 0 && sinceEnd < minGap) {
26042
+ const wait = minGap - sinceEnd;
26043
+ console$6.log(`距上次本地 ASR 结束 ${sinceEnd}ms,等 ${wait}ms 供原生 AudioRecord 重初始化`);
26044
+ await new Promise((r) => setTimeout(r, wait));
26045
+ }
26046
+ }
26047
+ try {
26048
+ asrOk = await rtc.switchToAsr();
26049
+ } catch (e) {
26050
+ console$6.warn("switchToAsr error: " + ((e == null ? void 0 : e.message) || e));
26051
+ }
26052
+ }
26053
+ if (!asrOk) {
26054
+ console$6.log("ASR not available (no native bridge?). Cannot start local voice chat.");
26055
+ this._localVcActive = false;
26056
+ this._emitStatus("本地 ASR 不可用");
26057
+ return false;
26058
+ }
26059
+ this._localTtsEndTime = Date.now();
26060
+ this._localTtsPlaying = false;
26061
+ this._currentTtsText = "";
26062
+ this._emitStatus("本地:聆听中...");
26063
+ console$6.log("Now using local ASR");
26064
+ return true;
26065
+ }
26066
+ /**
26067
+ * 关闭本地 ASR。
26068
+ * @param opts.resumeKws 结束后是否回到 WASM KWS 待唤醒(true=结束对话;false=热切换到云端)
26069
+ */
26070
+ async disableLocalAsr(opts = {}) {
26071
+ var _a2, _b2, _c, _d;
26072
+ const resumeKws = opts.resumeKws !== false;
26073
+ this._localVcActive = false;
26074
+ this._lastSentAsrText = "";
26075
+ this._localAsrEndTime = Date.now();
26076
+ this._localTtsPlaying = false;
26077
+ this._localAsrPausedForTts = false;
26078
+ this._restoreBargeIn();
26079
+ if (!resumeKws) {
26080
+ try {
26081
+ if ((_a2 = this._rtc) == null ? void 0 : _a2.stopAsr) {
26082
+ this._rtc.stopAsr();
26083
+ await new Promise((r) => setTimeout(r, 500));
26084
+ }
26085
+ (_c = (_b2 = this._rtc) == null ? void 0 : _b2.stop) == null ? void 0 : _c.call(_b2);
26086
+ await new Promise((r) => setTimeout(r, 500));
26087
+ console$6.log("本地 ASR 已结束(热切换到云端:原生 ASR 已停、麦克风已释放,不重启 KWS)");
26088
+ } catch (e) {
26089
+ console$6.warn("disableLocalAsr(stop for cloud) error: " + ((e == null ? void 0 : e.message) || e));
26090
+ }
26091
+ return;
26092
+ }
26093
+ try {
26094
+ if ((_d = this._rtc) == null ? void 0 : _d.stopAsr) {
26095
+ this._rtc.stopAsr();
26096
+ this._emitStatus("待唤醒...");
26097
+ console$6.log("本地 ASR 已结束(stopAsr → 自动回到 KWS 待唤醒)");
26098
+ return;
26099
+ }
26100
+ } catch (e) {
26101
+ console$6.warn("disableLocalAsr stopAsr error: " + ((e == null ? void 0 : e.message) || e));
26102
+ }
26103
+ void this.resumeKws();
26104
+ }
26105
+ // =================== TTS 生命周期联动(内置订阅 host 事件) ===================
26106
+ /** host textSpeechStart:TTS 开始播放 */
26107
+ async onTtsStart() {
26108
+ if (!this._localVcActive || !this._rtc || !this._isLocalAsrEnabled()) return;
26109
+ this._localTtsPlaying = true;
26110
+ this._currentTtsText = "";
26111
+ if (this._getInterruptionEnabled()) {
26112
+ this._localAsrPausedForTts = false;
26113
+ console$6.log("TTS 开始 — 打断模式,ASR 保持活跃(AEC + echo guard)");
26114
+ } else {
26115
+ try {
26116
+ const alreadyPaused = this._rtc.isAsrPaused && this._rtc.isAsrPaused();
26117
+ if (alreadyPaused) {
26118
+ this._localAsrPausedForTts = true;
26119
+ this._suppressBargeIn();
26120
+ console$6.log("TTS 开始 — ASR 已处于暂停状态,保持暂停");
26121
+ } else if (this._rtc.getState && this._rtc.getState() === "ASR_LISTENING") {
26122
+ this._suppressBargeIn();
26123
+ await this._rtc.pauseAsr();
26124
+ this._localAsrPausedForTts = true;
26125
+ console$6.log("TTS 开始 — ASR 已暂停(pauseAsr,能量打断已禁用)");
26126
+ } else {
26127
+ this._localAsrPausedForTts = false;
26128
+ }
26129
+ } catch (e) {
26130
+ console$6.warn("onTtsStart error: " + ((e == null ? void 0 : e.message) || e));
26131
+ }
26132
+ }
26133
+ }
26134
+ /** host textSpeechEnd:TTS 播放结束 */
26135
+ async onTtsEnd() {
26136
+ if (!this._localVcActive || !this._isLocalAsrEnabled()) return;
26137
+ this._localTtsPlaying = false;
26138
+ this._localTtsEndTime = Date.now();
26139
+ if (!this._localAsrPausedForTts) {
26140
+ console$6.log("TTS 结束 — ASR 未曾暂停");
26141
+ this._emitStatus("本地:聆听中...");
26142
+ return;
26143
+ }
26144
+ this._localAsrPausedForTts = false;
26145
+ this._restoreBargeIn();
26146
+ if (this._localVcActive && !this._localVcProcessing && this._rtc) {
26147
+ try {
26148
+ await this._rtc.resumeAsr();
26149
+ console$6.log("TTS 结束 — ASR 已恢复(resumeAsr)");
26150
+ this._emitStatus("本地:聆听中...");
26151
+ } catch (e) {
26152
+ console$6.warn("resumeAsr after TTS error: " + ((e == null ? void 0 : e.message) || e));
26153
+ }
26154
+ }
26155
+ }
26156
+ /** host textSpeechCanceled:TTS 被打断/取消 */
26157
+ async onTtsCanceled() {
26158
+ if (!this._localVcActive || !this._isLocalAsrEnabled()) return;
26159
+ this._localTtsPlaying = false;
26160
+ this._localTtsEndTime = Date.now();
26161
+ if (!this._localAsrPausedForTts) {
26162
+ console$6.log("TTS 取消 — ASR 未曾暂停");
26163
+ this._emitStatus("本地:聆听中...");
26164
+ return;
26165
+ }
26166
+ this._localAsrPausedForTts = false;
26167
+ if (this._localVcProcessing) {
26168
+ console$6.log("TTS 取消(新 LLM 调用进行中,ASR 保持暂停)");
26169
+ return;
26170
+ }
26171
+ this._restoreBargeIn();
26172
+ if (this._localVcActive && !this._localVcProcessing && this._rtc) {
26173
+ try {
26174
+ await this._rtc.resumeAsr();
26175
+ console$6.log("TTS 取消 — ASR 已恢复(resumeAsr)");
26176
+ this._emitStatus("本地:聆听中...");
26177
+ } catch (e) {
26178
+ console$6.warn("resumeAsr after cancel error: " + ((e == null ? void 0 : e.message) || e));
26179
+ }
26180
+ }
26181
+ }
26182
+ /** 非打断模式:禁用 barge-in 能量检测(阈值推到 Infinity)。幂等。 */
26183
+ _suppressBargeIn() {
26184
+ const rtc = this._rtc;
26185
+ if (!rtc || this._savedBargeInThreshold != null) return;
26186
+ if (typeof rtc._bargeInThreshold === "number") {
26187
+ this._savedBargeInThreshold = rtc._bargeInThreshold;
26188
+ rtc._bargeInThreshold = Infinity;
26189
+ }
26190
+ }
26191
+ /** 恢复被 _suppressBargeIn 暂存的 barge-in 能量阈值。 */
26192
+ _restoreBargeIn() {
26193
+ const rtc = this._rtc;
26194
+ if (!rtc || this._savedBargeInThreshold == null) return;
26195
+ rtc._bargeInThreshold = this._savedBargeInThreshold;
26196
+ this._savedBargeInThreshold = null;
26197
+ }
26198
+ // =================== LocalRTC 回调 ===================
26199
+ _handleKwsDetected(keyword, action) {
26200
+ var _a2, _b2;
26201
+ console$6.log(`KWS "${keyword}" (${action})`);
26202
+ if (action === "start") {
26203
+ void this._wakeAndStartChat();
26204
+ } else if (action === "stop") {
26205
+ (_b2 = (_a2 = this.hooks).onToggleVoiceChat) == null ? void 0 : _b2.call(_a2, "stop");
26206
+ }
26207
+ }
26208
+ /**
26209
+ * 唤醒命中后启动对话。按模式区分麦克风处理。
26210
+ */
26211
+ async _wakeAndStartChat() {
26212
+ var _a2, _b2, _c, _d;
26213
+ const localAsr = this._isLocalAsrEnabled();
26214
+ if (!localAsr) {
26215
+ try {
26216
+ (_b2 = (_a2 = this._rtc) == null ? void 0 : _a2.stop) == null ? void 0 : _b2.call(_a2);
26217
+ console$6.log("唤醒命中(云端):已同步停止 KWS(阻止 SDK 自动 switchToAsr)并释放麦克风");
26218
+ } catch (e) {
26219
+ console$6.warn("唤醒命中云端 stop 失败: " + ((e == null ? void 0 : e.message) || e));
26220
+ }
26221
+ } else {
26222
+ this._wakeViaKwsLocalAsr = true;
26223
+ console$6.log("唤醒命中(localASR):由 SDK 自动 switchToAsr,回调不重复切换");
26224
+ }
26225
+ (_d = (_c = this.hooks).onToggleVoiceChat) == null ? void 0 : _d.call(_c, "start");
26226
+ }
26227
+ _isLikelyEcho(text) {
26228
+ const w = typeof window !== "undefined" ? window : {};
26229
+ const Cls = this._LocalRTC || w.SherpaOnnxLocalRTC;
26230
+ if (!(Cls == null ? void 0 : Cls.isLikelyEcho)) return false;
26231
+ return Cls.isLikelyEcho(text, this._currentTtsText, {
26232
+ prefixRatio: w._echoPrefixRatio,
26233
+ bigramRatio: w._echoBigramRatio
26234
+ });
26235
+ }
26236
+ _handleAsrPartial(text) {
26237
+ console$6.log(`ASR partial: ${text}`);
26238
+ if (!this._localVcActive || !String(text || "").trim()) return;
26239
+ this._emitStatus(`本地:${String(text).slice(0, 30)}...`);
26240
+ if (this._localTtsPlaying && this._getInterruptionEnabled()) {
26241
+ if (!this._isLikelyEcho(text)) {
26242
+ console$6.log(`[Barge-in partial] 取消 TTS+LLM:"${text}"`);
26243
+ this._localTtsPlaying = false;
26244
+ try {
26245
+ void this.host.cancelSend("barge-in");
26246
+ } catch (e) {
26247
+ console$6.warn("cancelSend error: " + ((e == null ? void 0 : e.message) || e));
26248
+ }
26249
+ }
26250
+ }
26251
+ }
26252
+ async _handleAsrResult(text, _all) {
26253
+ var _a2, _b2, _c, _d, _e, _f;
26254
+ const trimmed = normalizeText(text);
26255
+ const keywords = ((_b2 = (_a2 = this.config) == null ? void 0 : _a2.kws) == null ? void 0 : _b2.keywords) || [];
26256
+ const stopKeywords = keywords.filter((k) => k.type === "stop").map((k) => k.keyword);
26257
+ const startKeywords = keywords.filter((k) => k.type === "start").map((k) => k.keyword);
26258
+ const matches = (list) => list.some(
26259
+ (kw) => trimmed === kw || trimmed.endsWith(kw) || trimmed.includes(kw) && trimmed.length <= kw.length + 2
26260
+ );
26261
+ if (matches(stopKeywords)) {
26262
+ console$6.log("Stop keyword in ASR — hanging up");
26263
+ (_d = (_c = this.hooks).onToggleVoiceChat) == null ? void 0 : _d.call(_c, "stop");
26264
+ return;
26265
+ }
26266
+ if (!this._localVcActive) {
26267
+ console$6.log(`未在对话中,忽略 ASR:"${trimmed}"`);
26268
+ return;
26269
+ }
26270
+ if (matches(startKeywords)) {
26271
+ console$6.log(`Start keyword in ASR — ignoring: "${trimmed}"`);
26272
+ return;
26273
+ }
26274
+ console$6.log(`ASR result: ${text}`);
26275
+ if (this._localTtsPlaying) {
26276
+ if (this._isLikelyEcho(text)) {
26277
+ console$6.log(`[Echo Guard] 忽略 TTS 期间回声: ${text}`);
26278
+ return;
26279
+ }
26280
+ if (this._getInterruptionEnabled()) {
26281
+ console$6.log(`[Barge-in] 用户打断 TTS(final): ${text}`);
26282
+ this._localTtsPlaying = false;
26283
+ } else {
26284
+ console$6.log(`[非打断] 忽略 TTS 期间 ASR 结果: ${text}`);
26285
+ return;
26286
+ }
26287
+ }
26288
+ if (this._localTtsEndTime && Date.now() - this._localTtsEndTime < 1500) {
26289
+ if (this._isLikelyEcho(text)) {
26290
+ console$6.log(`[Echo Guard] 忽略 TTS 后回声 (${Date.now() - this._localTtsEndTime}ms): ${text}`);
26291
+ return;
26292
+ }
26293
+ }
26294
+ if (this._lastSentAsrText && trimmed === normalizeText(this._lastSentAsrText)) {
26295
+ console$6.log(`忽略重复 ASR: ${text}`);
26296
+ this._lastSentAsrText = "";
26297
+ return;
26298
+ }
26299
+ if (this._localVcActive && String(text).trim()) {
26300
+ this._ttsVersion++;
26301
+ const myVersion = this._ttsVersion;
26302
+ this._localVcProcessing = true;
26303
+ this._lastSentAsrText = text;
26304
+ try {
26305
+ (_f = (_e = this.hooks).onUserSpeech) == null ? void 0 : _f.call(_e, String(text).trim());
26306
+ } catch (e) {
26307
+ console$6.warn("onUserSpeech error: " + ((e == null ? void 0 : e.message) || e));
26308
+ }
26309
+ this._emitStatus("本地:思考中...");
26310
+ try {
26311
+ await this.host.cancelSend("barge-in");
26312
+ const result = await this.host.sendMessage(text, { textToSpeech: true });
26313
+ const reply = (result == null ? void 0 : result.finalText) || "";
26314
+ console$6.log(
26315
+ `LLM reply v${myVersion} (${reply.length} chars)` + (myVersion !== this._ttsVersion ? " [superseded]" : "")
26316
+ );
26317
+ if (reply && this._localVcActive) {
26318
+ this._emitStatus("本地:等待 TTS...");
26319
+ }
26320
+ } catch (e) {
26321
+ console$6.warn("Local VC error: " + ((e == null ? void 0 : e.message) || e));
26322
+ if (this._localVcActive && myVersion === this._ttsVersion) {
26323
+ this._emitStatus("本地:聆听中...");
26324
+ }
26325
+ } finally {
26326
+ this._localVcProcessing = false;
26327
+ }
26328
+ }
26329
+ }
26330
+ // =================== 调试 API(下沉自 silvermind 调试面板) ===================
26331
+ /** 读取当前 LocalRTC 相关属性快照(供调试面板展示)。 */
26332
+ getDebugConfig() {
26333
+ var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q;
26334
+ const cfg = this.config || {};
26335
+ const rtc = this._rtc;
26336
+ const w = typeof window !== "undefined" ? window : {};
26337
+ return {
26338
+ runtime: cfg.runtime || "-",
26339
+ asrRuntime: cfg.asrRuntime || (cfg.runtime === "android" ? "android" : "wasm"),
26340
+ state: (rtc == null ? void 0 : rtc.getState) ? rtc.getState() : "-",
26341
+ localVcActive: this._localVcActive,
26342
+ localAsrEnabled: this._isLocalAsrEnabled(),
26343
+ isAndroid: this._isAndroidRuntime(),
26344
+ loadProgress: this.getLoadProgress(),
26345
+ sherpa: (() => {
26346
+ const base = this._sherpaBase || cfg.baseUrl || w.sherpaonnxBase || SHERPA_DEFAULT_BASE;
26347
+ const version = this._sherpaVersion || cfg.version || (rtc == null ? void 0 : rtc._version) || "";
26348
+ return {
26349
+ base,
26350
+ version: version || "latest",
26351
+ isDefaultCdn: base === SHERPA_DEFAULT_BASE,
26352
+ coreUrl: base + "sherpa-onnx-local-rtc.js",
26353
+ wasmUrl: base + "sherpa-onnx-wasm-full.wasm",
26354
+ kwsUrl: base + "sherpa-onnx-kws.js"
26355
+ };
26356
+ })(),
26357
+ interruptionEnabled: ((_a2 = cfg.interruption) == null ? void 0 : _a2.enabled) !== false,
26358
+ minAudioRms: (rtc == null ? void 0 : rtc._minAudioRms) != null ? rtc._minAudioRms : cfg.minAudioRms ?? 0.015,
26359
+ bargeInThreshold: this._savedBargeInThreshold != null ? this._savedBargeInThreshold : (rtc == null ? void 0 : rtc._bargeInThreshold) != null && Number.isFinite(rtc._bargeInThreshold) ? rtc._bargeInThreshold : ((_b2 = cfg.bargeIn) == null ? void 0 : _b2.threshold) ?? 0.04,
26360
+ bargeInMinFrames: (rtc == null ? void 0 : rtc._bargeInMinFrames) != null ? rtc._bargeInMinFrames : ((_c = cfg.bargeIn) == null ? void 0 : _c.minFrames) ?? 2,
26361
+ kwsThreshold: (rtc == null ? void 0 : rtc._kwsThreshold) != null ? rtc._kwsThreshold : ((_d = cfg.kws) == null ? void 0 : _d.threshold) ?? 0.25,
26362
+ kwsKeywords: (Array.isArray(rtc == null ? void 0 : rtc._kwsKeywords) && rtc._kwsKeywords.length ? rtc._kwsKeywords : (_e = cfg.kws) == null ? void 0 : _e.keywords) || [],
26363
+ echoPrefixRatio: w._echoPrefixRatio ?? ((_f = cfg.echoGuard) == null ? void 0 : _f.prefixRatio) ?? 0.8,
26364
+ echoBigramRatio: w._echoBigramRatio ?? ((_g = cfg.echoGuard) == null ? void 0 : _g.bigramRatio) ?? 0.5,
26365
+ audioSource: (rtc == null ? void 0 : rtc._audioSource) ?? ((_h = cfg.audio) == null ? void 0 : _h.source) ?? "voice_communication",
26366
+ aecEnabled: (rtc == null ? void 0 : rtc.getAecEnabled) ? rtc.getAecEnabled() : ((_i = cfg.audio) == null ? void 0 : _i.aecEnabled) !== false,
26367
+ vadEnabled: ((_j = rtc == null ? void 0 : rtc._vadConfig) == null ? void 0 : _j.enabled) ?? ((_k = cfg.vad) == null ? void 0 : _k.enabled) !== false,
26368
+ vadThreshold: ((_l = rtc == null ? void 0 : rtc._vadConfig) == null ? void 0 : _l.threshold) ?? ((_m = cfg.vad) == null ? void 0 : _m.threshold) ?? 0.3,
26369
+ vadMinSilence: ((_n = rtc == null ? void 0 : rtc._vadConfig) == null ? void 0 : _n.minSilenceDuration) ?? ((_o = cfg.vad) == null ? void 0 : _o.minSilenceDuration) ?? 0.4,
26370
+ vadMinSpeech: ((_p = rtc == null ? void 0 : rtc._vadConfig) == null ? void 0 : _p.minSpeechDuration) ?? ((_q = cfg.vad) == null ? void 0 : _q.minSpeechDuration) ?? 0.1
26371
+ };
26372
+ }
26373
+ /** 组装当前 VAD 配置对象。 */
26374
+ _buildVadConfig() {
26375
+ const cfg = this.config || {};
26376
+ const rtc = this._rtc;
26377
+ const cur = (rtc == null ? void 0 : rtc._vadConfig) || cfg.vad || {};
26378
+ return {
26379
+ enabled: cur.enabled !== false,
26380
+ threshold: cur.threshold ?? 0.3,
26381
+ minSilenceDuration: cur.minSilenceDuration ?? 0.4,
26382
+ minSpeechDuration: cur.minSpeechDuration ?? 0.1
26383
+ };
26384
+ }
26385
+ /** 把 VAD 配置热更新到 rtc 实例 + 原生桥接(live 生效)。 */
26386
+ _applyVadConfig(vad2) {
26387
+ const cfg = this.config || (this.config = {});
26388
+ cfg.vad = { ...cfg.vad || {}, ...vad2 };
26389
+ const rtc = this._rtc;
26390
+ if (rtc) rtc._vadConfig = { ...vad2 };
26391
+ try {
26392
+ const w = typeof window !== "undefined" ? window : {};
26393
+ const bridge = w.SherpaOnnxAsrBridge || w.AndroidAsrBridge || null;
26394
+ if (bridge && typeof bridge.configureVad === "function") {
26395
+ bridge.configureVad(JSON.stringify(vad2));
26396
+ }
26397
+ } catch (e) {
26398
+ console$6.warn("configureVad error: " + ((e == null ? void 0 : e.message) || e));
26399
+ }
26400
+ }
26401
+ /** 热更新单个 LocalRTC 属性(仅内存,不持久化)。 */
26402
+ applyDebugSetting(key, value) {
26403
+ var _a2, _b2, _c, _d;
26404
+ const cfg = this.config || (this.config = {});
26405
+ const rtc = this._rtc;
26406
+ const w = typeof window !== "undefined" ? window : {};
26407
+ switch (key) {
26408
+ case "interruptionEnabled": {
26409
+ if (!cfg.interruption) cfg.interruption = {};
26410
+ cfg.interruption.enabled = !!value;
26411
+ if (value && this._savedBargeInThreshold != null) this._restoreBargeIn();
26412
+ console$6.log(`[Debug] interruption.enabled = ${!!value}`);
26413
+ break;
26414
+ }
26415
+ case "minAudioRms": {
26416
+ cfg.minAudioRms = value;
26417
+ try {
26418
+ (_a2 = rtc == null ? void 0 : rtc.setMinAudioRms) == null ? void 0 : _a2.call(rtc, value);
26419
+ } catch (e) {
26420
+ console$6.warn("setMinAudioRms error: " + ((e == null ? void 0 : e.message) || e));
26421
+ }
26422
+ console$6.log(`[Debug] minAudioRms = ${value}`);
26423
+ break;
26424
+ }
26425
+ case "bargeInThreshold": {
26426
+ if (!cfg.bargeIn) cfg.bargeIn = {};
26427
+ cfg.bargeIn.threshold = value;
26428
+ if (this._savedBargeInThreshold != null) this._savedBargeInThreshold = value;
26429
+ else if (rtc) rtc._bargeInThreshold = value;
26430
+ console$6.log(`[Debug] bargeIn.threshold = ${value}`);
26431
+ break;
26432
+ }
26433
+ case "bargeInMinFrames": {
26434
+ if (!cfg.bargeIn) cfg.bargeIn = {};
26435
+ cfg.bargeIn.minFrames = value;
26436
+ if (rtc) rtc._bargeInMinFrames = value;
26437
+ console$6.log(`[Debug] bargeIn.minFrames = ${value}`);
26438
+ break;
26439
+ }
26440
+ case "kwsThreshold": {
26441
+ if (!cfg.kws) cfg.kws = {};
26442
+ cfg.kws.threshold = value;
26443
+ try {
26444
+ (_b2 = rtc == null ? void 0 : rtc.setKwsThreshold) == null ? void 0 : _b2.call(rtc, value);
26445
+ } catch (e) {
26446
+ console$6.warn("setKwsThreshold error: " + ((e == null ? void 0 : e.message) || e));
26447
+ }
26448
+ console$6.log(`[Debug] kws.threshold = ${value}`);
26449
+ break;
26450
+ }
26451
+ case "echoPrefixRatio": {
26452
+ if (!cfg.echoGuard) cfg.echoGuard = {};
26453
+ cfg.echoGuard.prefixRatio = value;
26454
+ w._echoPrefixRatio = value;
26455
+ console$6.log(`[Debug] echoGuard.prefixRatio = ${value}`);
26456
+ break;
26457
+ }
26458
+ case "echoBigramRatio": {
26459
+ if (!cfg.echoGuard) cfg.echoGuard = {};
26460
+ cfg.echoGuard.bigramRatio = value;
26461
+ w._echoBigramRatio = value;
26462
+ console$6.log(`[Debug] echoGuard.bigramRatio = ${value}`);
26463
+ break;
26464
+ }
26465
+ case "audioSource": {
26466
+ if (!cfg.audio) cfg.audio = {};
26467
+ cfg.audio.source = value;
26468
+ if (rtc) rtc._audioSource = value;
26469
+ try {
26470
+ const bridge = w.SherpaOnnxAsrBridge || w.AndroidAsrBridge || null;
26471
+ (_c = bridge == null ? void 0 : bridge.configureAudioSource) == null ? void 0 : _c.call(bridge, JSON.stringify({ source: value }));
26472
+ } catch (e) {
26473
+ console$6.warn("configureAudioSource error: " + ((e == null ? void 0 : e.message) || e));
26474
+ }
26475
+ console$6.log(`[Debug] audio.source = ${value}(下次录音生效)`);
26476
+ break;
26477
+ }
26478
+ case "aecEnabled": {
26479
+ if (!cfg.audio) cfg.audio = {};
26480
+ cfg.audio.aecEnabled = !!value;
26481
+ try {
26482
+ (_d = rtc == null ? void 0 : rtc.setAecEnabled) == null ? void 0 : _d.call(rtc, !!value);
26483
+ } catch (e) {
26484
+ console$6.warn("setAecEnabled error: " + ((e == null ? void 0 : e.message) || e));
26485
+ }
26486
+ console$6.log(`[Debug] audio.aecEnabled = ${!!value}`);
26487
+ break;
26488
+ }
26489
+ case "vadEnabled": {
26490
+ const vad2 = this._buildVadConfig();
26491
+ vad2.enabled = !!value;
26492
+ this._applyVadConfig(vad2);
26493
+ console$6.log(`[Debug] vad.enabled = ${!!value}`);
26494
+ break;
26495
+ }
26496
+ case "vadThreshold": {
26497
+ const vad2 = this._buildVadConfig();
26498
+ vad2.threshold = value;
26499
+ this._applyVadConfig(vad2);
26500
+ console$6.log(`[Debug] vad.threshold = ${value}`);
26501
+ break;
26502
+ }
26503
+ case "vadMinSilence": {
26504
+ const vad2 = this._buildVadConfig();
26505
+ vad2.minSilenceDuration = value;
26506
+ this._applyVadConfig(vad2);
26507
+ console$6.log(`[Debug] vad.minSilenceDuration = ${value}`);
26508
+ break;
26509
+ }
26510
+ case "vadMinSpeech": {
26511
+ const vad2 = this._buildVadConfig();
26512
+ vad2.minSpeechDuration = value;
26513
+ this._applyVadConfig(vad2);
26514
+ console$6.log(`[Debug] vad.minSpeechDuration = ${value}`);
26515
+ break;
26516
+ }
26517
+ default:
26518
+ console$6.warn(`[Debug] 未知属性: ${key}`);
26519
+ }
26520
+ }
26521
+ /** 查询 Android 音频效果状态(AEC/NS/AGC),需原生桥接支持。 */
26522
+ async queryAudioEffects() {
26523
+ const rtc = this._rtc;
26524
+ if (!rtc || typeof rtc.getAudioEffectsInfo !== "function") {
26525
+ return { error: "getAudioEffectsInfo 不可用(需 Android 原生桥接)" };
26526
+ }
26527
+ try {
26528
+ return await rtc.getAudioEffectsInfo();
26529
+ } catch (e) {
26530
+ return { error: String((e == null ? void 0 : e.message) || e) };
26531
+ }
26532
+ }
26533
+ // =================== 销毁 ===================
26534
+ /** 销毁控制器(页面卸载/auth 切换/host destroy 时)。 */
26535
+ async destroy() {
26536
+ var _a2, _b2, _c, _d;
26537
+ try {
26538
+ if (this._onTtsStartBound) this.host.off("textSpeechStart", this._onTtsStartBound);
26539
+ if (this._onTtsEndBound) this.host.off("textSpeechEnd", this._onTtsEndBound);
26540
+ if (this._onTtsCanceledBound) this.host.off("textSpeechCanceled", this._onTtsCanceledBound);
26541
+ } catch {
26542
+ }
26543
+ this._onTtsStartBound = null;
26544
+ this._onTtsEndBound = null;
26545
+ this._onTtsCanceledBound = null;
26546
+ const wasLocalVc = this._localVcActive;
26547
+ this._localVcActive = false;
26548
+ this._wakeViaKwsLocalAsr = false;
26549
+ if (this._rtc) {
26550
+ try {
26551
+ if (wasLocalVc && this._rtc.stopAsr) {
26552
+ try {
26553
+ this._rtc.stopAsr();
26554
+ } catch {
26555
+ }
26556
+ await new Promise((r) => setTimeout(r, 300));
26557
+ }
26558
+ (_b2 = (_a2 = this._rtc).stop) == null ? void 0 : _b2.call(_a2);
26559
+ (_d = (_c = this._rtc).destroy) == null ? void 0 : _d.call(_c);
26560
+ } catch (e) {
26561
+ console$6.warn("destroy error: " + ((e == null ? void 0 : e.message) || e));
26562
+ }
26563
+ }
26564
+ this._rtc = null;
26565
+ this._preloaded = false;
26566
+ this._preloadPromise = null;
26567
+ this._localAsrEndTime = 0;
26568
+ if (typeof window !== "undefined") window.localRTC = null;
26569
+ }
26570
+ }
25364
26571
  SDKLogger.createModuleConsole("SpeechRTC");
25365
26572
  const DEFAULT_WS_URL = "wss://openspeech.bytedance.com/api/v3/tts/bidirection";
25366
26573
  const DEFAULT_PROXY_URL = "wss://speechrtc.keepwork.com/tts";
@@ -26913,145 +28120,6 @@ class SpeechRTCSession {
26913
28120
  this._audioTime = 0;
26914
28121
  }
26915
28122
  }
26916
- function detectFormat(url) {
26917
- if (!url || typeof url !== "string") return null;
26918
- const path = url.split(/[?#]/)[0].toLowerCase();
26919
- if (path.endsWith(".json")) return "json";
26920
- if (path.endsWith(".yml") || path.endsWith(".yaml")) return "yml";
26921
- if (path.endsWith(".md")) return "md";
26922
- return null;
26923
- }
26924
- function normalize(config) {
26925
- if (!config || typeof config !== "object") return config;
26926
- if (config.content && !config.system_prompt) {
26927
- const body = String(config.content).trim();
26928
- if (body) config.system_prompt = body;
26929
- }
26930
- delete config.content;
26931
- if (config.workspace) {
26932
- if (!config.tools) config.tools = {};
26933
- if (!config.tools.fileOps) config.tools.fileOps = { enabled: false };
26934
- if (!config.tools.fileOps.workspace) {
26935
- config.tools.fileOps.workspace = config.workspace;
26936
- }
26937
- }
26938
- if (config.mountFolder) {
26939
- if (!config.tools) config.tools = {};
26940
- if (!config.tools.fileOps) config.tools.fileOps = { enabled: false };
26941
- if (!config.tools.fileOps.mountFolder) {
26942
- config.tools.fileOps.mountFolder = config.mountFolder;
26943
- }
26944
- }
26945
- if (config.history_length !== void 0 && config.historyLength === void 0) {
26946
- config.historyLength = config.history_length;
26947
- }
26948
- delete config.history_length;
26949
- return config;
26950
- }
26951
- function parseTable(tableText) {
26952
- if (!tableText || typeof tableText !== "string") return {};
26953
- const lines = tableText.split("\n").map((l) => l.trim()).filter((l) => l.startsWith("|"));
26954
- if (lines.length < 2) return {};
26955
- const headerCells = lines[0].split("|").map((c) => c.trim()).filter(Boolean);
26956
- const dataLines = lines.slice(2);
26957
- const rows = dataLines.map((line) => {
26958
- const cells = line.split("|").map((c) => c.trim()).filter(Boolean);
26959
- const row = {};
26960
- headerCells.forEach((h, i) => {
26961
- row[h] = cells[i] !== void 0 ? cells[i] : "";
26962
- });
26963
- return row;
26964
- });
26965
- if (headerCells.length === 2) {
26966
- const [keyCol, valCol] = headerCells;
26967
- const isKV = keyCol.toLowerCase() === "key" && valCol.toLowerCase() === "value";
26968
- if (isKV) {
26969
- const obj = {};
26970
- for (const row of rows) {
26971
- const k = row[keyCol];
26972
- if (k) obj[k] = _coerceValue(row[valCol]);
26973
- }
26974
- return obj;
26975
- }
26976
- }
26977
- return rows;
26978
- }
26979
- function _coerceValue(v) {
26980
- if (v === "true") return true;
26981
- if (v === "false") return false;
26982
- if (v === "null" || v === "") return null;
26983
- const n = Number(v);
26984
- if (!isNaN(n) && v.trim() !== "") return n;
26985
- return v;
26986
- }
26987
- function parse(text, formatHint) {
26988
- if (!text || typeof text !== "string") return {};
26989
- const trimmed = text.trim();
26990
- if (formatHint === "json" || trimmed.startsWith("{") || trimmed.startsWith("[")) {
26991
- return normalize(JSON.parse(trimmed));
26992
- }
26993
- if (trimmed.startsWith("---\n") || trimmed.startsWith("---\r\n")) {
26994
- const YML = _getYMLParser();
26995
- if (YML) return normalize(YML.yamlToObject(trimmed, true));
26996
- }
26997
- const firstLine = trimmed.split("\n").find((l) => l.trim());
26998
- if (firstLine && firstLine.trim().startsWith("|")) {
26999
- return normalize(parseTable(trimmed));
27000
- }
27001
- try {
27002
- return normalize(JSON.parse(trimmed));
27003
- } catch {
27004
- const YML = _getYMLParser();
27005
- if (YML) return normalize(YML.yamlToObject(trimmed, true));
27006
- }
27007
- return {};
27008
- }
27009
- async function fetchConfig(source) {
27010
- if (source && typeof source === "object") {
27011
- return normalize({ ...source });
27012
- }
27013
- if (typeof source !== "string" || !source.trim()) {
27014
- throw new Error("AgentConfig.fetch: source must be a non-empty string or object");
27015
- }
27016
- const trimmed = source.trim();
27017
- if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
27018
- return normalize(JSON.parse(trimmed));
27019
- }
27020
- const resp = await fetch(trimmed);
27021
- if (!resp.ok) {
27022
- throw new Error(`AgentConfig.fetch: fetch failed (${resp.status}) for ${trimmed}`);
27023
- }
27024
- let text = await resp.text();
27025
- text = text.replace(
27026
- /<script[^>]*\/___vscode_livepreview_injected_script[^>]*><\/script>\s*/gi,
27027
- ""
27028
- );
27029
- const format = detectFormat(trimmed);
27030
- return parse(text, format);
27031
- }
27032
- function _getYMLParser() {
27033
- if (typeof YMLParser !== "undefined") return YMLParser;
27034
- if (typeof window !== "undefined" && window.YMLParser) {
27035
- return window.YMLParser;
27036
- }
27037
- return null;
27038
- }
27039
- const AgentConfig = {
27040
- detectFormat,
27041
- normalize,
27042
- parse,
27043
- parseTable,
27044
- fetch: fetchConfig
27045
- };
27046
- const AgentConfig$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
27047
- __proto__: null,
27048
- default: AgentConfig,
27049
- detectFormat,
27050
- fetchConfig,
27051
- normalize,
27052
- parse,
27053
- parseTable
27054
- }, Symbol.toStringTag, { value: "Module" }));
27055
28123
  function clearExternalContextDebounce(target) {
27056
28124
  if (target._externalContextDebounceTimer) {
27057
28125
  clearTimeout(target._externalContextDebounceTimer);
@@ -27347,11 +28415,14 @@ const _MinigameTools = class _MinigameTools {
27347
28415
  const name = slotName ?? options["slot"] ?? this._activeSlot ?? "default";
27348
28416
  const slot = this._getSlot(name);
27349
28417
  if (!(slot == null ? void 0 : slot.container)) return false;
28418
+ this._cancelPendingClose(slot);
27350
28419
  const frameOptions = { ...slot.defaultFrameOptions ?? {}, ...options["frameOptions"] ?? {}, ...options };
27351
28420
  delete frameOptions["frameOptions"];
27352
28421
  this._applyFrameOptions(slot, frameOptions);
27353
28422
  this.setSlotForegroundElements(name, options["foregroundElements"], options["foregroundOptions"]);
28423
+ const wasHidden = slot.container.style.display === "none";
27354
28424
  slot.container.style.display = "flex";
28425
+ if (wasHidden) this._animateSlotIn(slot);
27355
28426
  this.setActiveSlot(name);
27356
28427
  const session = this.getLaunchSession(name);
27357
28428
  if (session) {
@@ -27367,6 +28438,7 @@ const _MinigameTools = class _MinigameTools {
27367
28438
  const visible = options.visible !== false;
27368
28439
  const slotName = this._resolveSlot(fnArgs["slot"]);
27369
28440
  const slot = this._getOrCreateSlot(slotName);
28441
+ this._cancelPendingClose(slot);
27370
28442
  const session = this.getLaunchSession(slotName);
27371
28443
  const shouldShow = visible && !((session == null ? void 0 : session.preload) && session.revealed === false);
27372
28444
  const pKey = fnArgs["persistKey"] ?? null;
@@ -27385,6 +28457,7 @@ const _MinigameTools = class _MinigameTools {
27385
28457
  const url = store ? store.getAbsUrl(relativePath) : relativePath;
27386
28458
  (_e = this._log) == null ? void 0 : _e.call(this, `[load_minigame] slot=${slotName}, ${relativePath} → ${url}`);
27387
28459
  const c = slot.container;
28460
+ const wasHidden = c.style.display === "none";
27388
28461
  c.style.left = "50%";
27389
28462
  c.style.top = "50%";
27390
28463
  c.style.right = "";
@@ -27394,6 +28467,7 @@ const _MinigameTools = class _MinigameTools {
27394
28467
  slot.title.textContent = "🎮 " + relativePath.split("/").pop();
27395
28468
  if (slot.defaultFrameOptions) this._applyFrameOptions(slot, shouldShow ? slot.defaultFrameOptions : this._withoutBackdrop(slot.defaultFrameOptions));
27396
28469
  this._applyFrameOptions(slot, shouldShow ? fnArgs : this._withoutBackdrop(fnArgs));
28470
+ if (shouldShow && wasHidden) this._animateSlotIn(slot);
27397
28471
  this.setActiveSlot(slotName);
27398
28472
  const iframe = slot.iframe;
27399
28473
  const loaded = await new Promise((resolve) => {
@@ -27441,11 +28515,19 @@ iframe已设置但未收到gameLoaded确认,URL: ${url}` : `[小游戏预加
27441
28515
  if (!slot) return;
27442
28516
  const reason = opts.reason ?? "user";
27443
28517
  const wasVisible = ((_a2 = slot.container) == null ? void 0 : _a2.style.display) !== "none";
27444
- if (slot.container) slot.container.style.display = "none";
27445
- if (slot.iframe) slot.iframe.src = "";
27446
28518
  slot.persistKey = null;
27447
28519
  this._hideBackdrop(slot);
27448
28520
  this._restoreForegroundElements(slot);
28521
+ const finalize = () => {
28522
+ if (slot.container) {
28523
+ slot.container.style.display = "none";
28524
+ slot.container.style.opacity = "1";
28525
+ slot.container.style.transition = "";
28526
+ }
28527
+ if (slot.iframe) slot.iframe.src = "";
28528
+ };
28529
+ if (wasVisible && reason !== "destroy" && this._slotTransitionEnabled(slot)) this._animateSlotOut(slot, finalize);
28530
+ else finalize();
27449
28531
  if (slotName === this._activeSlot) {
27450
28532
  let fallback = null;
27451
28533
  for (const [name, s] of this._slots) {
@@ -27609,6 +28691,10 @@ iframe已设置但未收到gameLoaded确认,URL: ${url}` : `[小游戏预加
27609
28691
  document.body.appendChild(el);
27610
28692
  slot.backdropEl = el;
27611
28693
  }
28694
+ if (slot.backdropHideTimer) {
28695
+ clearTimeout(slot.backdropHideTimer);
28696
+ slot.backdropHideTimer = null;
28697
+ }
27612
28698
  const zIndex = parseInt(((_a2 = slot.container) == null ? void 0 : _a2.style.zIndex) || String(this.zIndex), 10) - 1;
27613
28699
  slot.backdropEl.style.zIndex = String(zIndex);
27614
28700
  slot.backdropEl.style.background = bg;
@@ -27617,10 +28703,79 @@ iframe已设置但未收到gameLoaded确认,URL: ${url}` : `[小游戏预加
27617
28703
  slot.backdropEl.style.opacity = "1";
27618
28704
  }
27619
28705
  _hideBackdrop(slot) {
27620
- if (slot.backdropEl) {
27621
- slot.backdropEl.style.opacity = "0";
27622
- slot.backdropEl.style.display = "none";
28706
+ const el = slot.backdropEl;
28707
+ if (!el) return;
28708
+ if (slot.backdropHideTimer) {
28709
+ clearTimeout(slot.backdropHideTimer);
28710
+ slot.backdropHideTimer = null;
28711
+ }
28712
+ el.style.opacity = "0";
28713
+ slot.backdropHideTimer = setTimeout(() => {
28714
+ slot.backdropHideTimer = null;
28715
+ if (el.style.opacity === "0") el.style.display = "none";
28716
+ }, 220);
28717
+ }
28718
+ /** slot 是否启用显隐过渡动画(frameOptions.transition,默认启用)。 */
28719
+ _slotTransitionEnabled(slot) {
28720
+ var _a2;
28721
+ return ((_a2 = slot.defaultFrameOptions) == null ? void 0 : _a2.transition) !== false;
28722
+ }
28723
+ /** 取消尚未完成的关闭淡出收尾(重新打开 slot 时调用)。 */
28724
+ _cancelPendingClose(slot) {
28725
+ if (slot.closeTimer) {
28726
+ clearTimeout(slot.closeTimer);
28727
+ slot.closeTimer = null;
28728
+ }
28729
+ if (slot.container) {
28730
+ slot.container.style.transition = "";
28731
+ slot.container.style.opacity = "1";
28732
+ }
28733
+ }
28734
+ /**
28735
+ * 弹窗淡入:opacity 0→1 + 轻微 scale 放大。动画结束后移除 transition,
28736
+ * 避免影响后续拖拽(拖拽直接改 left/top/transform)。
28737
+ */
28738
+ _animateSlotIn(slot) {
28739
+ const c = slot.container;
28740
+ if (!c) return;
28741
+ this._cancelPendingClose(slot);
28742
+ if (!this._slotTransitionEnabled(slot)) {
28743
+ c.style.opacity = "1";
28744
+ return;
28745
+ }
28746
+ const baseTransform = c.style.transform || "none";
28747
+ const enterTransform = baseTransform === "none" ? "scale(0.96)" : `${baseTransform} scale(0.96)`;
28748
+ c.style.transition = "none";
28749
+ c.style.opacity = "0";
28750
+ c.style.transform = enterTransform;
28751
+ void c.offsetWidth;
28752
+ c.style.transition = "opacity 0.28s ease, transform 0.28s ease";
28753
+ c.style.opacity = "1";
28754
+ c.style.transform = baseTransform;
28755
+ setTimeout(() => {
28756
+ if (c.style.opacity === "1") {
28757
+ c.style.transition = "";
28758
+ }
28759
+ }, 320);
28760
+ }
28761
+ /** 弹窗淡出;done 在动画结束后调用(负责 display:none 与清理)。 */
28762
+ _animateSlotOut(slot, done) {
28763
+ const c = slot.container;
28764
+ if (!c) {
28765
+ done();
28766
+ return;
27623
28767
  }
28768
+ const baseTransform = c.style.transform || "none";
28769
+ const exitTransform = baseTransform === "none" ? "scale(0.96)" : `${baseTransform} scale(0.96)`;
28770
+ c.style.transition = "opacity 0.24s ease, transform 0.24s ease";
28771
+ void c.offsetWidth;
28772
+ c.style.opacity = "0";
28773
+ c.style.transform = exitTransform;
28774
+ slot.closeTimer = setTimeout(() => {
28775
+ slot.closeTimer = null;
28776
+ c.style.transform = baseTransform;
28777
+ done();
28778
+ }, 260);
27624
28779
  }
27625
28780
  _normalizeForegroundElements(elements) {
27626
28781
  const source = Array.isArray(elements) ? elements : elements ? [elements] : [];
@@ -28641,13 +29796,16 @@ const _DigitalHuman = class _DigitalHuman {
28641
29796
  * @param {HTMLElement} [options.container] - Container element for avatar rendering
28642
29797
  * @param {Object} [options.config] - Initial configuration
28643
29798
  */
28644
- constructor({ sdk, container, config, subtitle } = {}) {
29799
+ constructor({ sdk, container, config, subtitle, localRTC } = {}) {
28645
29800
  var _a2, _b2;
28646
29801
  if (!sdk) throw new Error("DigitalHuman requires a KeepworkSDK instance");
28647
29802
  this.sdk = sdk;
28648
29803
  this.container = container || null;
28649
29804
  this.config = config || {};
28650
29805
  this._subtitleOverlay = new DigitalHumanSubtitleOverlay(this, subtitle ?? this.config.subtitle);
29806
+ this._localRTCConfig = localRTC ?? this.config.localRTC ?? void 0;
29807
+ this._localRTCController = null;
29808
+ this._localRTCHooks = null;
28651
29809
  EventEmitterMixin$1._initEvents.call(this);
28652
29810
  this._avatarRoot = null;
28653
29811
  this._videoIdle = null;
@@ -28916,6 +30074,93 @@ const _DigitalHuman = class _DigitalHuman {
28916
30074
  showSubtitle(text, options = {}) {
28917
30075
  this._subtitleOverlay.updateAssistant(text, { definite: true, paragraph: true, ...options });
28918
30076
  }
30077
+ // ========================================================================
30078
+ // Local RTC (sherpa-onnx KWS + local ASR + cloud TTS) — host-level API
30079
+ // ========================================================================
30080
+ /**
30081
+ * 内部:确保 LocalRTCController 已构造(host 用本 DigitalHuman 实例的注入接口)。
30082
+ * @returns {LocalRTCController|null}
30083
+ */
30084
+ _ensureLocalRTCController() {
30085
+ if (this._localRTCController) return this._localRTCController;
30086
+ if (!this._localRTCConfig) return null;
30087
+ const self = this;
30088
+ this._localRTCController = new LocalRTCController({
30089
+ host: {
30090
+ send: (text, opts) => self.send(text, opts),
30091
+ sendMessage: (text, opts) => self.sendMessage(text, opts),
30092
+ cancelSend: (reason) => self.cancelSend(reason),
30093
+ updateVoiceChat: (command, opts) => {
30094
+ var _a2;
30095
+ return (_a2 = self.updateVoiceChat) == null ? void 0 : _a2.call(self, command, opts);
30096
+ },
30097
+ startVoiceChat: (preset) => {
30098
+ var _a2;
30099
+ return (_a2 = self.startVoiceChat) == null ? void 0 : _a2.call(self, preset);
30100
+ },
30101
+ stopVoiceChat: () => {
30102
+ var _a2;
30103
+ return (_a2 = self.stopVoiceChat) == null ? void 0 : _a2.call(self);
30104
+ },
30105
+ on: (event, handler) => self.on(event, handler),
30106
+ off: (event, handler) => self.off(event, handler)
30107
+ },
30108
+ config: this._localRTCConfig,
30109
+ hooks: this._localRTCHooks || {}
30110
+ });
30111
+ return this._localRTCController;
30112
+ }
30113
+ /** 获取 LocalRTCController 实例(高级用法 / 调试)。 */
30114
+ getLocalRTCController() {
30115
+ return this._localRTCController;
30116
+ }
30117
+ /** 设置/更新本地 RTC 配置;若已有控制器则同步配置。 */
30118
+ setLocalRTCConfig(config) {
30119
+ this._localRTCConfig = config;
30120
+ if (this._localRTCController) this._localRTCController.config = config || {};
30121
+ }
30122
+ /** 读取当前本地 RTC 配置。 */
30123
+ getLocalRTCConfig() {
30124
+ return this._localRTCConfig;
30125
+ }
30126
+ /**
30127
+ * 注入业务钩子(onUserSpeech 字幕、onStatus、isLocalAsrEnabled、onToggleVoiceChat、onLoadProgress)。
30128
+ * 控制器已构造时同步合并,未构造时缓存待构造时使用。
30129
+ */
30130
+ setLocalRTCHooks(hooks) {
30131
+ this._localRTCHooks = { ...this._localRTCHooks || {}, ...hooks || {} };
30132
+ if (this._localRTCController) this._localRTCController.hooks = this._localRTCHooks;
30133
+ }
30134
+ /** 便捷:注册本地 ASR 识别到用户说话的回调。 */
30135
+ onLocalUserSpeech(cb) {
30136
+ this.setLocalRTCHooks({ onUserSpeech: cb });
30137
+ }
30138
+ /** 本地 ASR 语音会话是否活跃。 */
30139
+ isLocalVoiceActive() {
30140
+ var _a2;
30141
+ return !!((_a2 = this._localRTCController) == null ? void 0 : _a2.isLocalVcActive());
30142
+ }
30143
+ /** 启动本地 ASR 语音会话(ASR → LLM → 云端 TTS)。 */
30144
+ async enableLocalVoice(opts) {
30145
+ const ctrl = this._ensureLocalRTCController();
30146
+ if (!ctrl) return false;
30147
+ return ctrl.enableLocalAsr(opts);
30148
+ }
30149
+ /** 结束本地 ASR 语音会话。 */
30150
+ async disableLocalVoice(opts) {
30151
+ if (!this._localRTCController) return;
30152
+ return this._localRTCController.disableLocalAsr(opts);
30153
+ }
30154
+ /** 重启 WASM KWS 回到待唤醒。 */
30155
+ async resumeLocalKws() {
30156
+ if (!this._localRTCController) return false;
30157
+ return this._localRTCController.resumeKws();
30158
+ }
30159
+ /** 云端启动前释放麦克风(停 WASM KWS)。 */
30160
+ async releaseLocalMicForCloud() {
30161
+ if (!this._localRTCController) return;
30162
+ return this._localRTCController.releaseMicForCloud();
30163
+ }
28919
30164
  _getAudioEngine() {
28920
30165
  var _a2;
28921
30166
  return ((_a2 = this.sdk) == null ? void 0 : _a2.audioEngine) || AudioEngine.getShared();
@@ -32477,6 +33722,7 @@ You only need to play idle animation if you are looping in indefinite loop of an
32477
33722
  * @returns {Promise<Object>} { session, config, bootResponse? }
32478
33723
  */
32479
33724
  async initFromConfig(config = {}) {
33725
+ var _a2;
32480
33726
  this.characterConfig = config;
32481
33727
  if (Object.prototype.hasOwnProperty.call(config, "subtitle")) {
32482
33728
  this.setSubtitleConfig(config.subtitle);
@@ -32507,6 +33753,19 @@ You only need to play idle animation if you are looping in indefinite loop of an
32507
33753
  if (config.pageRouters) {
32508
33754
  this._pageRouters = config.pageRouters;
32509
33755
  }
33756
+ if (Object.prototype.hasOwnProperty.call(config, "localRTC")) {
33757
+ this._localRTCConfig = config.localRTC;
33758
+ }
33759
+ if (this._localRTCConfig && this._localRTCConfig.enabled !== false) {
33760
+ try {
33761
+ this._ensureLocalRTCController();
33762
+ void ((_a2 = this._localRTCController) == null ? void 0 : _a2.preload().catch((e) => {
33763
+ console$4.warn("[DigitalHuman] LocalRTC preload failed:", e);
33764
+ }));
33765
+ } catch (e) {
33766
+ console$4.warn("[DigitalHuman] LocalRTC init failed:", e);
33767
+ }
33768
+ }
32510
33769
  return { session, config };
32511
33770
  }
32512
33771
  // ========================================================================
@@ -34769,6 +36028,13 @@ ${turn.Content}`;
34769
36028
  this._clearVoiceIdleTimer();
34770
36029
  this._clearVoiceLifecycleHandlers();
34771
36030
  this._subtitleOverlay.destroy();
36031
+ if (this._localRTCController) {
36032
+ try {
36033
+ await this._localRTCController.destroy();
36034
+ } catch (e) {
36035
+ }
36036
+ this._localRTCController = null;
36037
+ }
34772
36038
  if ((_a2 = this._vcSession) == null ? void 0 : _a2.isActive) {
34773
36039
  try {
34774
36040
  await this._vcSession.stop();
@@ -35086,7 +36352,7 @@ class DigitalHumanFrame {
35086
36352
  * @param {string} [options.sdkSrc] - URL to keepworkSDK.iife.js
35087
36353
  * @param {boolean} [options.useLocalSdk] - Set to true if sdkSrc is a local ES module instead of IIFE bundle
35088
36354
  */
35089
- constructor({ sdk, container, sdkSrc, useLocalSdk, useExternalHtml, workspace, proxyCategories, subtitle } = {}) {
36355
+ constructor({ sdk, container, sdkSrc, useLocalSdk, useExternalHtml, workspace, proxyCategories, subtitle, localRTC } = {}) {
35090
36356
  __publicField(this, "sdk");
35091
36357
  __publicField(this, "container");
35092
36358
  __publicField(this, "workspace");
@@ -35104,6 +36370,9 @@ class DigitalHumanFrame {
35104
36370
  this.sdk = sdk;
35105
36371
  this.container = container;
35106
36372
  this.workspace = workspace;
36373
+ this.localRTCConfig = localRTC;
36374
+ this._localRTCController = null;
36375
+ this._localRTCHooks = null;
35107
36376
  this._useLocalSdk = useLocalSdk !== void 0 ? !!useLocalSdk : !!sdk.constructor.sourceIsModule;
35108
36377
  this._useExternalHtml = !!useExternalHtml;
35109
36378
  this._proxyCategories = proxyCategories || null;
@@ -35891,9 +37160,15 @@ class DigitalHumanFrame {
35891
37160
  }
35892
37161
  }
35893
37162
  async initFromConfig(config = {}) {
37163
+ var _a2;
35894
37164
  if (this.subtitleConfig !== void 0 && !Object.prototype.hasOwnProperty.call(config, "subtitle")) {
35895
37165
  config = { ...config, subtitle: this.subtitleConfig };
35896
37166
  }
37167
+ if (Object.prototype.hasOwnProperty.call(config, "localRTC")) {
37168
+ if (this.localRTCConfig === void 0) this.localRTCConfig = config.localRTC;
37169
+ const { localRTC: _stripped, ...rest } = config;
37170
+ config = rest;
37171
+ }
35897
37172
  await this._prepareConfigForIframe(config);
35898
37173
  this.characterConfig = config;
35899
37174
  const frameState = extractDigitalHumanFrameState(config);
@@ -35906,6 +37181,16 @@ class DigitalHumanFrame {
35906
37181
  this.subtitleConfig = frameState.subtitleConfig;
35907
37182
  if (frameState.avatarOnlyMode) this.avatarOnlyMode = true;
35908
37183
  const res = await this._rpc(MSG.INIT_FROM_CONFIG, { config });
37184
+ if (this.localRTCConfig && this.localRTCConfig.enabled !== false) {
37185
+ try {
37186
+ this._ensureLocalRTCController();
37187
+ void ((_a2 = this._localRTCController) == null ? void 0 : _a2.preload().catch((e) => {
37188
+ console$3.warn("[DigitalHumanFrame] LocalRTC preload failed:", e);
37189
+ }));
37190
+ } catch (e) {
37191
+ console$3.warn("[DigitalHumanFrame] LocalRTC init failed:", e);
37192
+ }
37193
+ }
35909
37194
  return {
35910
37195
  session: this._proxySession,
35911
37196
  config: res.config,
@@ -36106,6 +37391,80 @@ class DigitalHumanFrame {
36106
37391
  const result = await this._rpc(MSG.GET_SUBTITLE_CONFIG);
36107
37392
  return (result == null ? void 0 : result.config) || null;
36108
37393
  }
37394
+ // ========================================================================
37395
+ // Local RTC (sherpa-onnx KWS + local ASR + cloud TTS) — host-level API
37396
+ // 控制器在父窗口,host 用本 DHF 实例(镜像 sendMessage/cancelSend/updateVoiceChat,
37397
+ // 并转发内层 textSpeech 事件)。
37398
+ // ========================================================================
37399
+ /** 内部:确保父窗口 LocalRTCController 已构造。 */
37400
+ _ensureLocalRTCController() {
37401
+ if (this._localRTCController) return this._localRTCController;
37402
+ if (!this.localRTCConfig) return null;
37403
+ const self = this;
37404
+ this._localRTCController = new LocalRTCController({
37405
+ host: {
37406
+ send: (text, opts) => self.send(text, opts),
37407
+ sendMessage: (text, opts) => self.sendMessage(text, opts),
37408
+ cancelSend: (reason) => self.cancelSend(reason),
37409
+ updateVoiceChat: (command, opts) => self.updateVoiceChat(command, opts || {}),
37410
+ startVoiceChat: (preset) => self.startVoiceChat(preset || {}),
37411
+ stopVoiceChat: () => self.stopVoiceChat(),
37412
+ on: (event, handler) => self.on(event, handler),
37413
+ off: (event, handler) => self.off(event, handler)
37414
+ },
37415
+ config: this.localRTCConfig,
37416
+ hooks: this._localRTCHooks || {}
37417
+ });
37418
+ return this._localRTCController;
37419
+ }
37420
+ /** 获取父窗口 LocalRTCController 实例(高级用法 / 调试)。 */
37421
+ getLocalRTCController() {
37422
+ return this._localRTCController;
37423
+ }
37424
+ /** 设置/更新本地 RTC 配置;若已有控制器则同步配置。 */
37425
+ setLocalRTCConfig(config) {
37426
+ this.localRTCConfig = config;
37427
+ if (this._localRTCController) this._localRTCController.config = config || {};
37428
+ }
37429
+ /** 读取当前本地 RTC 配置。 */
37430
+ getLocalRTCConfig() {
37431
+ return this.localRTCConfig;
37432
+ }
37433
+ /** 注入业务钩子(onUserSpeech 字幕、onStatus、isLocalAsrEnabled、onToggleVoiceChat、onLoadProgress)。 */
37434
+ setLocalRTCHooks(hooks) {
37435
+ this._localRTCHooks = { ...this._localRTCHooks || {}, ...hooks || {} };
37436
+ if (this._localRTCController) this._localRTCController.hooks = this._localRTCHooks;
37437
+ }
37438
+ /** 便捷:注册本地 ASR 识别到用户说话的回调。 */
37439
+ onLocalUserSpeech(cb) {
37440
+ this.setLocalRTCHooks({ onUserSpeech: cb });
37441
+ }
37442
+ /** 本地 ASR 语音会话是否活跃。 */
37443
+ isLocalVoiceActive() {
37444
+ var _a2;
37445
+ return !!((_a2 = this._localRTCController) == null ? void 0 : _a2.isLocalVcActive());
37446
+ }
37447
+ /** 启动本地 ASR 语音会话(ASR → LLM → 云端 TTS)。 */
37448
+ async enableLocalVoice(opts) {
37449
+ const ctrl = this._ensureLocalRTCController();
37450
+ if (!ctrl) return false;
37451
+ return ctrl.enableLocalAsr(opts);
37452
+ }
37453
+ /** 结束本地 ASR 语音会话。 */
37454
+ async disableLocalVoice(opts) {
37455
+ if (!this._localRTCController) return;
37456
+ return this._localRTCController.disableLocalAsr(opts);
37457
+ }
37458
+ /** 重启 WASM KWS 回到待唤醒。 */
37459
+ async resumeLocalKws() {
37460
+ if (!this._localRTCController) return false;
37461
+ return this._localRTCController.resumeKws();
37462
+ }
37463
+ /** 云端启动前释放麦克风(停 WASM KWS)。 */
37464
+ async releaseLocalMicForCloud() {
37465
+ if (!this._localRTCController) return;
37466
+ return this._localRTCController.releaseMicForCloud();
37467
+ }
36109
37468
  async clearSubtitle(options = {}) {
36110
37469
  return this._rpc(MSG.CLEAR_SUBTITLE, { options });
36111
37470
  }
@@ -36363,6 +37722,13 @@ class DigitalHumanFrame {
36363
37722
  this._authStateUnsubscribe();
36364
37723
  this._authStateUnsubscribe = null;
36365
37724
  }
37725
+ if (this._localRTCController) {
37726
+ try {
37727
+ await this._localRTCController.destroy();
37728
+ } catch (_) {
37729
+ }
37730
+ this._localRTCController = null;
37731
+ }
36366
37732
  await this._tearDownIframe("DigitalHumanFrame destroyed");
36367
37733
  window.removeEventListener("message", this._onMessage);
36368
37734
  clearExternalContextDebounce(this);
@@ -38158,6 +39524,8 @@ if (typeof window !== "undefined") {
38158
39524
  window.AIChat = AIChat;
38159
39525
  window.AIChatRTC = AIChatRTC$1;
38160
39526
  window.AIChatRTCLocal = AIChatRTCLocal;
39527
+ window.LocalRTC = LocalRTC;
39528
+ window.LocalRTCController = LocalRTCController;
38161
39529
  window.SpeechRTC = SpeechRTC;
38162
39530
  window.DigitalHuman = DigitalHuman;
38163
39531
  window.DigitalHumanFrame = DigitalHumanFrame;
@@ -38186,6 +39554,8 @@ export {
38186
39554
  DigitalHumanFrame,
38187
39555
  KeepworkSDK,
38188
39556
  LocalAPIKeySettings,
39557
+ LocalRTC,
39558
+ LocalRTCController,
38189
39559
  LocalStorageUtil,
38190
39560
  LoginWindow,
38191
39561
  MinigameTools,