openclaw-openagent 1.0.6 → 1.0.7

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.
@@ -29,7 +29,7 @@ import { sendC2CCustomMessage, waitForC2CReply } from '../tim/c2c.js';
29
29
  import type { OasnTransport, TaskHandle, TaskRequest } from '../transport/types.js';
30
30
  import fs from 'node:fs';
31
31
  import nodePath from 'node:path';
32
- import { resolveOasnAccessUrl } from '../util/url-resolver.js';
32
+ import { resolveOasnAccessUrl, resolveOasnWebuiUrl } from '../util/url-resolver.js';
33
33
 
34
34
  // ── Helpers ──
35
35
 
@@ -350,7 +350,8 @@ export function createCallRemoteAgentTool(): ToolDescriptor {
350
350
  const accessBase = rt.config?.accessApiBase || rt.config?.apiBase;
351
351
  if (result.webuiContinuation?.launchUrl) {
352
352
  const rawLink = result.webuiContinuation.launchUrl;
353
- const link = resolveOasnAccessUrl(accessBase, rawLink);
353
+ const claimUrl = rt.config?.claimUrl;
354
+ const link = resolveOasnWebuiUrl(accessBase, claimUrl, rawLink);
354
355
  const label = result.webuiContinuation.displayName || 'Open WebUI';
355
356
  content = content.trim()
356
357
  ? `${content}\n\n${label}: ${link}`
package/src/compat.ts CHANGED
@@ -15,6 +15,9 @@ export const PLUGIN_VERSION = '1.0.0';
15
15
 
16
16
  export const SUPPORTED_HOST_MIN = '2026.3.22';
17
17
 
18
+ /** Plugin has been verified up to this OpenClaw host version */
19
+ export const MAX_TESTED_VERSION = '2026.6.30';
20
+
18
21
  export interface OpenClawVersion {
19
22
  year: number;
20
23
  month: number;
@@ -71,6 +74,15 @@ export function assertHostCompatibility(hostVersion: string | undefined): void {
71
74
  }
72
75
  if (isHostVersionSupported(hostVersion)) {
73
76
  logger.info(`[compat] Host OpenClaw ${hostVersion} >= ${SUPPORTED_HOST_MIN}, OK.`);
77
+ // Warn but don't block for versions beyond tested range
78
+ const host = parseOpenClawVersion(hostVersion);
79
+ const max = parseOpenClawVersion(MAX_TESTED_VERSION);
80
+ if (host && max && compareVersions(host, max) > 0) {
81
+ logger.warn(
82
+ `[compat] Host OpenClaw ${hostVersion} is beyond max tested version ${MAX_TESTED_VERSION}. ` +
83
+ `The plugin may encounter compatibility issues.`,
84
+ );
85
+ }
74
86
  return;
75
87
  }
76
88
  throw new Error(
@@ -2,7 +2,7 @@
2
2
  * OpenAgent UI Override — Auto-generated, do not edit directly!
3
3
  *
4
4
  * Source: src/plugin-ui/modules/
5
- * Built: 2026-06-25T08:37:18.204Z
5
+ * Built: 2026-06-25T09:33:52.490Z
6
6
  *
7
7
  * To modify, edit the source modules and run:
8
8
  * npm run build:override
@@ -1587,6 +1587,97 @@ function AgentCard(agent, opts = {}) {
1587
1587
  return card;
1588
1588
  }
1589
1589
 
1590
+ // ════════════════════════════════════════════════════════════════
1591
+ // MODULE: agent-book/panel/theme-adapter.js
1592
+ // ════════════════════════════════════════════════════════════════
1593
+
1594
+ // ── OpenAgent Theme Adapter ──
1595
+ //
1596
+ // 运行时检测 OpenClaw Control UI 主题系统(v4.x vscode tokens / v5.x glassmorphism),
1597
+ // 注入 --oa-* CSS 变量供 styles.js 中的 var() fallback 消费。
1598
+ //
1599
+ // 必须早于 styles.js 加载(由 build.cjs MANIFEST 顺序保证)。
1600
+
1601
+ (function _clInitThemeAdapter() {
1602
+ if (window.__openagentThemeAdapterInstalled) return;
1603
+ window.__openagentThemeAdapterInstalled = true;
1604
+
1605
+ /**
1606
+ * 检测当前 OC 主题系统类型。
1607
+ * @returns {{ hasGlass: boolean, hasVscode: boolean, isDark: boolean }}
1608
+ */
1609
+ function detectThemeSystem() {
1610
+ var cs = getComputedStyle(document.documentElement);
1611
+ var hasGlass = !!(cs.getPropertyValue('--glass-surface').trim());
1612
+ var hasKn = !!(cs.getPropertyValue('--kn-bg-primary').trim());
1613
+ var hasVscode = !!(cs.getPropertyValue('--vscode-editor-background').trim());
1614
+
1615
+ // 多策略暗色检测:v4 data-theme-mode / v5 data-theme / prefers-color-scheme
1616
+ var themeMode = document.documentElement.getAttribute('data-theme-mode') // v4.x
1617
+ || document.documentElement.getAttribute('data-theme') // v5.x
1618
+ || '';
1619
+ var isDark = themeMode.indexOf('dark') >= 0
1620
+ || window.matchMedia('(prefers-color-scheme: dark)').matches;
1621
+
1622
+ return {
1623
+ hasGlass: hasGlass || hasKn,
1624
+ hasVscode: hasVscode,
1625
+ isDark: isDark
1626
+ };
1627
+ }
1628
+
1629
+ /**
1630
+ * 注入 --oa-* CSS 变量到 :root。
1631
+ * styles.js 中的 var(--oa-xxx, fallback) 会优先取这些值。
1632
+ */
1633
+ function setupThemeVariables() {
1634
+ var system = detectThemeSystem();
1635
+ var root = document.documentElement;
1636
+
1637
+ if (system.hasGlass) {
1638
+ // v5.x Glassmorphism: 使用 --glass-* / --kn-* token 族
1639
+ root.style.setProperty('--oa-panel-bg', 'var(--glass-surface, var(--card))');
1640
+ root.style.setProperty('--oa-panel-text', 'var(--kn-text-primary, var(--text, #e0e0e0))');
1641
+ root.style.setProperty('--oa-card-bg', 'var(--glass-card, var(--card, #f5f5f5))');
1642
+ root.style.setProperty('--oa-card-hover-bg', 'var(--glass-card-hover, var(--card-hover))');
1643
+ root.style.setProperty('--oa-border', 'var(--glass-border, var(--border, #e5e5e5))');
1644
+ root.style.setProperty('--oa-shadow-color', 'rgba(0, 0, 0, 0.08)');
1645
+ root.style.setProperty('--oa-divider', 'var(--glass-divider, var(--border, #eee))');
1646
+ root.style.setProperty('--oa-muted-text', 'var(--kn-text-secondary, var(--text-secondary, #999))');
1647
+ } else if (system.hasVscode) {
1648
+ // v4.x VSCode tokens: 使用 --vscode-* 变量
1649
+ root.style.setProperty('--oa-panel-bg', 'var(--vscode-editor-background, #fff)');
1650
+ root.style.setProperty('--oa-panel-text', 'var(--vscode-editor-foreground, #2F2F33)');
1651
+ root.style.setProperty('--oa-card-bg', 'var(--vscode-sideBar-background, #f5f5f5)');
1652
+ root.style.setProperty('--oa-card-hover-bg', 'var(--vscode-list-hoverBackground, #f0f0f0)');
1653
+ root.style.setProperty('--oa-border', 'var(--vscode-panel-border, #e5e5e5)');
1654
+ root.style.setProperty('--oa-shadow-color', 'rgba(0, 0, 0, 0.10)');
1655
+ root.style.setProperty('--oa-divider', 'var(--vscode-sideBar-border, #eee)');
1656
+ root.style.setProperty('--oa-muted-text', 'var(--vscode-descriptionForeground, #999)');
1657
+ }
1658
+ // 无特征 → 不注入变量,styles.js 中的 var() 会 fallback 到硬编码值
1659
+
1660
+ // 注入暗色 class 标记供 styles.js 选择器使用
1661
+ if (system.isDark) {
1662
+ root.style.setProperty('--oa-is-dark', '1');
1663
+ } else {
1664
+ root.style.setProperty('--oa-is-dark', '0');
1665
+ }
1666
+ }
1667
+
1668
+ // 立即执行 + 监听主题变更(v5.x 可能运行时切换主题)
1669
+ setupThemeVariables();
1670
+
1671
+ // 监听 data-theme / data-theme-mode 属性变更
1672
+ var themeObserver = new MutationObserver(function () {
1673
+ setupThemeVariables();
1674
+ });
1675
+ themeObserver.observe(document.documentElement, {
1676
+ attributes: true,
1677
+ attributeFilter: ['data-theme', 'data-theme-mode', 'class']
1678
+ });
1679
+ })();
1680
+
1590
1681
  // ════════════════════════════════════════════════════════════════
1591
1682
  // MODULE: agent-book/panel/styles.js
1592
1683
  // ════════════════════════════════════════════════════════════════
@@ -1704,10 +1795,10 @@ function injectStyles() {
1704
1795
  .openagent-agent-panel {
1705
1796
  position: fixed;
1706
1797
  max-height: none;
1707
- background: #fff;
1708
- border: 1.2px solid rgba(47, 47, 51, 0.12);
1798
+ background: var(--oa-panel-bg, #fff);
1799
+ border: 1.2px solid var(--oa-border, rgba(47, 47, 51, 0.12));
1709
1800
  border-radius: 18px;
1710
- box-shadow: 0 -4px 24px rgba(0,0,0,0.08), 0 2px 8px rgba(0,0,0,0.04);
1801
+ box-shadow: 0 -4px 24px var(--oa-shadow-color, rgba(0,0,0,0.08)), 0 2px 8px var(--oa-shadow-color, rgba(0,0,0,0.04));
1711
1802
  z-index: 99999;
1712
1803
  overflow: hidden;
1713
1804
  display: none;
@@ -1751,7 +1842,7 @@ function injectStyles() {
1751
1842
  flex-shrink: 0;
1752
1843
  }
1753
1844
  .openagent-panel-close {
1754
- background: rgba(249, 249, 249, 1);
1845
+ background: var(--oa-card-bg, rgba(249, 249, 249, 1));
1755
1846
  border: none;
1756
1847
  color: rgba(47, 47, 51, 0.6);
1757
1848
  cursor: pointer;
@@ -2211,41 +2302,61 @@ function injectStyles() {
2211
2302
  padding: 32px; text-align: center; color: #aaa; font-size: 13px;
2212
2303
  }
2213
2304
 
2214
- /* ── 深色主题(统一) ── */
2215
- [data-theme-mode="dark"] .openagent-agent-panel {
2305
+ /* ── 深色主题(v4: data-theme-mode / v5: data-theme) ── */
2306
+ [data-theme-mode="dark"] .openagent-agent-panel,
2307
+ [data-theme="dark"] .openagent-agent-panel {
2216
2308
  background: #1c1c1c; border-color: rgba(255,255,255,0.12);
2217
2309
  box-shadow: 0 -4px 24px rgba(0,0,0,0.3);
2218
2310
  }
2219
- [data-theme-mode="dark"] .openagent-panel-head { border-color: #2a2a2a; }
2220
- [data-theme-mode="dark"] .openagent-panel-title { color: rgba(224, 224, 224, 0.8); }
2221
- [data-theme-mode="dark"] .openagent-panel-close { background: #2a2a2a; color: #888; }
2222
- [data-theme-mode="dark"] .openagent-panel-close:hover { background: #333; color: #ccc; }
2223
- [data-theme-mode="dark"] .openagent-section-heading { color: rgba(224, 224, 224, 0.45); }
2224
- [data-theme-mode="dark"] .openagent-parent-heading { color: rgba(224, 224, 224, 0.3); }
2225
- [data-theme-mode="dark"] .openagent-panel-divider { border-color: rgba(255,255,255,0.1); }
2226
-
2227
- [data-theme-mode="dark"] .agent-card__name { color: #e0e0e0; }
2228
- [data-theme-mode="dark"] .agent-card__bio { color: rgba(224, 224, 224, 0.7); }
2229
- [data-theme-mode="dark"] .agent-card__stat { color: #64748b; }
2230
-
2231
- [data-theme-mode="dark"] .agent-card--mention { background: transparent; }
2232
- [data-theme-mode="dark"] .agent-card--mention:hover { background: #252525; }
2233
- [data-theme-mode="dark"] .agent-card--mention .agent-card__action {
2311
+ [data-theme-mode="dark"] .openagent-panel-head,
2312
+ [data-theme="dark"] .openagent-panel-head { border-color: #2a2a2a; }
2313
+ [data-theme-mode="dark"] .openagent-panel-title,
2314
+ [data-theme="dark"] .openagent-panel-title { color: rgba(224, 224, 224, 0.8); }
2315
+ [data-theme-mode="dark"] .openagent-panel-close,
2316
+ [data-theme="dark"] .openagent-panel-close { background: #2a2a2a; color: #888; }
2317
+ [data-theme-mode="dark"] .openagent-panel-close:hover,
2318
+ [data-theme="dark"] .openagent-panel-close:hover { background: #333; color: #ccc; }
2319
+ [data-theme-mode="dark"] .openagent-section-heading,
2320
+ [data-theme="dark"] .openagent-section-heading { color: rgba(224, 224, 224, 0.45); }
2321
+ [data-theme-mode="dark"] .openagent-parent-heading,
2322
+ [data-theme="dark"] .openagent-parent-heading { color: rgba(224, 224, 224, 0.3); }
2323
+ [data-theme-mode="dark"] .openagent-panel-divider,
2324
+ [data-theme="dark"] .openagent-panel-divider { border-color: rgba(255,255,255,0.1); }
2325
+
2326
+ [data-theme-mode="dark"] .agent-card__name,
2327
+ [data-theme="dark"] .agent-card__name { color: #e0e0e0; }
2328
+ [data-theme-mode="dark"] .agent-card__bio,
2329
+ [data-theme="dark"] .agent-card__bio { color: rgba(224, 224, 224, 0.7); }
2330
+ [data-theme-mode="dark"] .agent-card__stat,
2331
+ [data-theme="dark"] .agent-card__stat { color: #64748b; }
2332
+
2333
+ [data-theme-mode="dark"] .agent-card--mention,
2334
+ [data-theme="dark"] .agent-card--mention { background: transparent; }
2335
+ [data-theme-mode="dark"] .agent-card--mention:hover,
2336
+ [data-theme="dark"] .agent-card--mention:hover { background: #252525; }
2337
+ [data-theme-mode="dark"] .agent-card--mention .agent-card__action,
2338
+ [data-theme="dark"] .agent-card--mention .agent-card__action {
2234
2339
  background: #333; border-color: #555; color: #ddd;
2235
2340
  }
2236
- [data-theme-mode="dark"] .agent-card--mention .agent-card__action:hover { background: #444; }
2237
- [data-theme-mode="dark"] .agent-card--search:hover { background: #252525; }
2238
- [data-theme-mode="dark"] .agent-card--search.openagent-card-selected {
2341
+ [data-theme-mode="dark"] .agent-card--mention .agent-card__action:hover,
2342
+ [data-theme="dark"] .agent-card--mention .agent-card__action:hover { background: #444; }
2343
+ [data-theme-mode="dark"] .agent-card--search:hover,
2344
+ [data-theme="dark"] .agent-card--search:hover { background: #252525; }
2345
+ [data-theme-mode="dark"] .agent-card--search.openagent-card-selected,
2346
+ [data-theme="dark"] .agent-card--search.openagent-card-selected {
2239
2347
  background: rgba(99, 102, 241, 0.12);
2240
2348
  outline-color: rgba(99, 102, 241, 0.4);
2241
2349
  }
2242
- [data-theme-mode="dark"] .agent-card--search .agent-card__action {
2350
+ [data-theme-mode="dark"] .agent-card--search .agent-card__action,
2351
+ [data-theme="dark"] .agent-card--search .agent-card__action {
2243
2352
  background: #333; border-color: #555; color: #ddd;
2244
2353
  }
2245
- [data-theme-mode="dark"] .agent-card--search .agent-card__action:hover { background: #444; }
2354
+ [data-theme-mode="dark"] .agent-card--search .agent-card__action:hover,
2355
+ [data-theme="dark"] .agent-card--search .agent-card__action:hover { background: #444; }
2246
2356
 
2247
2357
 
2248
- [data-theme-mode="dark"] .agent-card--inline {
2358
+ [data-theme-mode="dark"] .agent-card--inline,
2359
+ [data-theme="dark"] .agent-card--inline {
2249
2360
  background: linear-gradient(135deg, #1e1b3a 0%, #1a1a2e 100%);
2250
2361
  border-color: #2d2b55;
2251
2362
  }
@@ -2255,7 +2366,8 @@ function injectStyles() {
2255
2366
  }
2256
2367
  [data-theme-mode="dark"] .agent-card--inline .agent-card__name { color: #e2e8f0; }
2257
2368
  [data-theme-mode="dark"] .agent-card--inline .agent-card__avatar { border-color: #2d2b55; }
2258
- [data-theme-mode="dark"] .agent-card__loading {
2369
+ [data-theme-mode="dark"] .agent-card__loading,
2370
+ [data-theme="dark"] .agent-card__loading {
2259
2371
  background: #1e1b3a; border-color: #2d2b55; color: #64748b;
2260
2372
  }
2261
2373
  `;
@@ -2890,8 +3002,8 @@ liveTextarea.dispatchEvent(new Event('change', { bubbles: true }));
2890
3002
  //
2891
3003
  // tryInject() 只做 DOM 注入(@ 按钮 + CSS class),不绑定任何事件。
2892
3004
 
2893
- // ── textarea 选择器(全局复用)──
2894
- // 多策略回退:v3 "Message..." / v4 "发消息..." / 结构化选择器
3005
+ // ── textarea 选择器(多版本兼容)──
3006
+ // v3 "Message..." / v4 "发消息..." / v5.x 结构化选择器
2895
3007
  const _CL_TEXTAREA_SELECTOR = [
2896
3008
  'textarea[placeholder*="发消息"]',
2897
3009
  'textarea[placeholder*="Message"]',
@@ -2900,6 +3012,8 @@ const _CL_TEXTAREA_SELECTOR = [
2900
3012
  '.chat-composer textarea',
2901
3013
  '.chat-input textarea',
2902
3014
  '[class*="composer"] textarea:not([placeholder*="Search"])',
3015
+ '[class*="chat-input"] textarea',
3016
+ '[data-component="chat-input"] textarea',
2903
3017
  ].join(', ');
2904
3018
 
2905
3019
  function _getTextarea() {
@@ -3015,37 +3129,59 @@ function _installSendClickInterceptor() {
3015
3129
  }, true);
3016
3130
  }
3017
3131
 
3018
- function tryInject() {
3019
- const textarea = _getTextarea();
3020
-
3021
- if (!textarea) return false;
3022
- _installSendClickInterceptor();
3023
- if (document.querySelector('.openagent-at-btn')) return true; // 已注入
3024
-
3025
- // ── 定位插入点:麦克风图标所在的 toolbar 行 ──
3026
- const textareaParent = textarea.parentElement;
3027
- let toolbarRow = null;
3132
+ /**
3133
+ * 多策略定位 textarea 所在输入区域的工具栏行。
3134
+ * v4.x: 工具栏在 textarea.parentElement.children 中
3135
+ * v5.x: 工具栏可能嵌套在祖父容器或包含 toolbar class
3136
+ * 返回 null 表示未找到则回退到 textarea 父级插入
3137
+ */
3138
+ function _findToolbarRow(textarea) {
3139
+ var parent = textarea.parentElement;
3028
3140
 
3029
- if (textareaParent) {
3030
- for (const child of textareaParent.children) {
3031
- if (child !== textarea && child.querySelector('svg')) {
3032
- toolbarRow = child;
3033
- break;
3034
- }
3141
+ // 策略 1(v4.x): textarea 兄弟元素含 SVG 图标
3142
+ if (parent) {
3143
+ for (var i = 0; i < parent.children.length; i++) {
3144
+ if (parent.children[i] !== textarea && parent.children[i].querySelector('svg')) return parent.children[i];
3035
3145
  }
3036
3146
  }
3037
- if (!toolbarRow && textareaParent) {
3038
- const grandparent = textareaParent.parentElement;
3147
+
3148
+ // 策略 2(v4.x): 祖父级兄弟元素含 SVG 图标
3149
+ if (parent) {
3150
+ var grandparent = parent.parentElement;
3039
3151
  if (grandparent) {
3040
- for (const child of grandparent.children) {
3041
- if (child !== textareaParent && child.querySelector('svg')) {
3042
- toolbarRow = child;
3043
- break;
3044
- }
3152
+ for (var j = 0; j < grandparent.children.length; j++) {
3153
+ if (grandparent.children[j] !== parent && grandparent.children[j].querySelector('svg')) return grandparent.children[j];
3045
3154
  }
3046
3155
  }
3047
3156
  }
3048
3157
 
3158
+ // 策略 3(v5.x): 搜索包含 toolbar class 的元素
3159
+ var chatContainer = textarea.closest('[class*="chat"]');
3160
+ if (chatContainer) {
3161
+ var toolbarEl = chatContainer.querySelector('[class*="toolbar"]');
3162
+ if (toolbarEl) return toolbarEl;
3163
+ }
3164
+
3165
+ // 策略 4(v5.x fallback): 搜索 voice/mic 按钮定位其容器
3166
+ var voiceBtn = document.querySelector(
3167
+ 'button[aria-label*="voice"], button[aria-label*="语音"], ' +
3168
+ 'button[aria-label*="mic"], button[aria-label*="麦克风"]'
3169
+ );
3170
+ if (voiceBtn) return voiceBtn.parentElement;
3171
+
3172
+ return null;
3173
+ }
3174
+
3175
+ function tryInject() {
3176
+ const textarea = _getTextarea();
3177
+
3178
+ if (!textarea) return false;
3179
+ _installSendClickInterceptor();
3180
+ if (document.querySelector('.openagent-at-btn')) return true; // 已注入
3181
+
3182
+ // ── 定位插入点:麦克风图标所在的 toolbar 行(多策略)──
3183
+ var toolbarRow = _findToolbarRow(textarea);
3184
+
3049
3185
  // ── 创建 Agent Book 按钮 ──
3050
3186
  const atBtn = document.createElement('button');
3051
3187
  atBtn.className = 'openagent-at-btn';
@@ -3079,10 +3215,15 @@ function tryInject() {
3079
3215
  if (toolbarRow) {
3080
3216
  const toolbarLeft = toolbarRow.querySelector('[class*="toolbar-left"]') || toolbarRow;
3081
3217
  const iconButtons = toolbarLeft.querySelectorAll('button, [role="button"]');
3218
+ // 优先按 aria-label 匹配 voice/mic 按钮,回退到位置假设
3082
3219
  let micBtn = null;
3083
- if (iconButtons.length >= 2) {
3084
- micBtn = iconButtons[1];
3085
- } else if (iconButtons.length === 1) {
3220
+ for (var i = 0; i < iconButtons.length; i++) {
3221
+ var label = (iconButtons[i].getAttribute('aria-label') || '').toLowerCase();
3222
+ if (/voice|mic|语音|麦克风/.test(label)) { micBtn = iconButtons[i]; break; }
3223
+ }
3224
+ if (!micBtn && iconButtons.length >= 2) {
3225
+ micBtn = iconButtons[1]; // fallback: v4.x 中 mic 一般是第 2 个按钮
3226
+ } else if (!micBtn && iconButtons.length === 1) {
3086
3227
  micBtn = iconButtons[0];
3087
3228
  }
3088
3229
 
@@ -3092,16 +3233,19 @@ function tryInject() {
3092
3233
  toolbarLeft.appendChild(atBtn);
3093
3234
  }
3094
3235
  } else {
3095
- (textareaParent || textarea.parentElement).appendChild(atBtn);
3236
+ // 无工具栏时回退到 textarea 的父级插入
3237
+ var fallbackParent = textarea.parentElement;
3238
+ if (fallbackParent) fallbackParent.appendChild(atBtn);
3096
3239
  }
3097
3240
 
3098
3241
  // ── 给输入区域加 position: relative 包裹层 ──
3242
+ var inputParent = textarea.parentElement;
3099
3243
  const inputContainer = textarea.closest('.agent-chat__composer-combobox')
3100
3244
  || textarea.closest('.chat-input')
3101
3245
  || textarea.closest('[class*="chat-composer"]')
3102
3246
  || textarea.closest('[class*="chat"]')
3103
- || textareaParent?.parentElement
3104
- || textareaParent;
3247
+ || (inputParent ? inputParent.parentElement : null)
3248
+ || inputParent;
3105
3249
  if (inputContainer) {
3106
3250
  inputContainer.classList.add('openagent-input-wrapper');
3107
3251
  inputWrapperRef = inputContainer;
@@ -5418,14 +5562,20 @@ function _clScan() {
5418
5562
  var SHELL_SELECTORS = [
5419
5563
  '.chat-tool-card:not(:has(.cl-remote-agent-card))',
5420
5564
  '.chat-tool-msg-collapse:not(:has(.cl-remote-agent-card))',
5421
- '.chat-tools-collapse:not(:has(.cl-remote-agent-card))'
5565
+ '.chat-tools-collapse:not(:has(.cl-remote-agent-card))',
5566
+ // v5.x+ variants
5567
+ '.tool-card:not(:has(.cl-remote-agent-card))',
5568
+ '.chat-tool-card__wrapper:not(:has(.cl-remote-agent-card))'
5422
5569
  ];
5423
5570
 
5424
5571
  var SUMMARY_SELECTORS = [
5425
5572
  '.chat-tool-card__header',
5426
5573
  '.chat-tool-card__title',
5427
5574
  '.chat-tool-msg-summary:not(.cl-remote-agent-host-shell *)',
5428
- '.chat-tools-summary:not(.cl-remote-agent-host-shell *)'
5575
+ '.chat-tools-summary:not(.cl-remote-agent-host-shell *)',
5576
+ // v5.x+ variants
5577
+ '.tool-card__header',
5578
+ '.tool-card__title'
5429
5579
  ];
5430
5580
 
5431
5581
  var INDENT_SELECTORS = [
@@ -25,6 +25,7 @@ const MANIFEST = [
25
25
  // ── agent-book/panel ──
26
26
  'agent-book/panel/agent-data.js',
27
27
  'agent-book/panel/agent-card.js',
28
+ 'agent-book/panel/theme-adapter.js', // 必须在 styles.js 之前,设置 --oa-* CSS 变量
28
29
  'agent-book/panel/styles.js',
29
30
  'agent-book/panel/mention-state.js',
30
31
  'agent-book/panel/agent-book.js',
@@ -9,8 +9,8 @@
9
9
  //
10
10
  // tryInject() 只做 DOM 注入(@ 按钮 + CSS class),不绑定任何事件。
11
11
 
12
- // ── textarea 选择器(全局复用)──
13
- // 多策略回退:v3 "Message..." / v4 "发消息..." / 结构化选择器
12
+ // ── textarea 选择器(多版本兼容)──
13
+ // v3 "Message..." / v4 "发消息..." / v5.x 结构化选择器
14
14
  const _CL_TEXTAREA_SELECTOR = [
15
15
  'textarea[placeholder*="发消息"]',
16
16
  'textarea[placeholder*="Message"]',
@@ -19,6 +19,8 @@ const _CL_TEXTAREA_SELECTOR = [
19
19
  '.chat-composer textarea',
20
20
  '.chat-input textarea',
21
21
  '[class*="composer"] textarea:not([placeholder*="Search"])',
22
+ '[class*="chat-input"] textarea',
23
+ '[data-component="chat-input"] textarea',
22
24
  ].join(', ');
23
25
 
24
26
  function _getTextarea() {
@@ -134,37 +136,59 @@ function _installSendClickInterceptor() {
134
136
  }, true);
135
137
  }
136
138
 
137
- function tryInject() {
138
- const textarea = _getTextarea();
139
-
140
- if (!textarea) return false;
141
- _installSendClickInterceptor();
142
- if (document.querySelector('.openagent-at-btn')) return true; // 已注入
143
-
144
- // ── 定位插入点:麦克风图标所在的 toolbar 行 ──
145
- const textareaParent = textarea.parentElement;
146
- let toolbarRow = null;
139
+ /**
140
+ * 多策略定位 textarea 所在输入区域的工具栏行。
141
+ * v4.x: 工具栏在 textarea.parentElement.children 中
142
+ * v5.x: 工具栏可能嵌套在祖父容器或包含 toolbar class
143
+ * 返回 null 表示未找到则回退到 textarea 父级插入
144
+ */
145
+ function _findToolbarRow(textarea) {
146
+ var parent = textarea.parentElement;
147
147
 
148
- if (textareaParent) {
149
- for (const child of textareaParent.children) {
150
- if (child !== textarea && child.querySelector('svg')) {
151
- toolbarRow = child;
152
- break;
153
- }
148
+ // 策略 1(v4.x): textarea 兄弟元素含 SVG 图标
149
+ if (parent) {
150
+ for (var i = 0; i < parent.children.length; i++) {
151
+ if (parent.children[i] !== textarea && parent.children[i].querySelector('svg')) return parent.children[i];
154
152
  }
155
153
  }
156
- if (!toolbarRow && textareaParent) {
157
- const grandparent = textareaParent.parentElement;
154
+
155
+ // 策略 2(v4.x): 祖父级兄弟元素含 SVG 图标
156
+ if (parent) {
157
+ var grandparent = parent.parentElement;
158
158
  if (grandparent) {
159
- for (const child of grandparent.children) {
160
- if (child !== textareaParent && child.querySelector('svg')) {
161
- toolbarRow = child;
162
- break;
163
- }
159
+ for (var j = 0; j < grandparent.children.length; j++) {
160
+ if (grandparent.children[j] !== parent && grandparent.children[j].querySelector('svg')) return grandparent.children[j];
164
161
  }
165
162
  }
166
163
  }
167
164
 
165
+ // 策略 3(v5.x): 搜索包含 toolbar class 的元素
166
+ var chatContainer = textarea.closest('[class*="chat"]');
167
+ if (chatContainer) {
168
+ var toolbarEl = chatContainer.querySelector('[class*="toolbar"]');
169
+ if (toolbarEl) return toolbarEl;
170
+ }
171
+
172
+ // 策略 4(v5.x fallback): 搜索 voice/mic 按钮定位其容器
173
+ var voiceBtn = document.querySelector(
174
+ 'button[aria-label*="voice"], button[aria-label*="语音"], ' +
175
+ 'button[aria-label*="mic"], button[aria-label*="麦克风"]'
176
+ );
177
+ if (voiceBtn) return voiceBtn.parentElement;
178
+
179
+ return null;
180
+ }
181
+
182
+ function tryInject() {
183
+ const textarea = _getTextarea();
184
+
185
+ if (!textarea) return false;
186
+ _installSendClickInterceptor();
187
+ if (document.querySelector('.openagent-at-btn')) return true; // 已注入
188
+
189
+ // ── 定位插入点:麦克风图标所在的 toolbar 行(多策略)──
190
+ var toolbarRow = _findToolbarRow(textarea);
191
+
168
192
  // ── 创建 Agent Book 按钮 ──
169
193
  const atBtn = document.createElement('button');
170
194
  atBtn.className = 'openagent-at-btn';
@@ -198,10 +222,15 @@ function tryInject() {
198
222
  if (toolbarRow) {
199
223
  const toolbarLeft = toolbarRow.querySelector('[class*="toolbar-left"]') || toolbarRow;
200
224
  const iconButtons = toolbarLeft.querySelectorAll('button, [role="button"]');
225
+ // 优先按 aria-label 匹配 voice/mic 按钮,回退到位置假设
201
226
  let micBtn = null;
202
- if (iconButtons.length >= 2) {
203
- micBtn = iconButtons[1];
204
- } else if (iconButtons.length === 1) {
227
+ for (var i = 0; i < iconButtons.length; i++) {
228
+ var label = (iconButtons[i].getAttribute('aria-label') || '').toLowerCase();
229
+ if (/voice|mic|语音|麦克风/.test(label)) { micBtn = iconButtons[i]; break; }
230
+ }
231
+ if (!micBtn && iconButtons.length >= 2) {
232
+ micBtn = iconButtons[1]; // fallback: v4.x 中 mic 一般是第 2 个按钮
233
+ } else if (!micBtn && iconButtons.length === 1) {
205
234
  micBtn = iconButtons[0];
206
235
  }
207
236
 
@@ -211,16 +240,19 @@ function tryInject() {
211
240
  toolbarLeft.appendChild(atBtn);
212
241
  }
213
242
  } else {
214
- (textareaParent || textarea.parentElement).appendChild(atBtn);
243
+ // 无工具栏时回退到 textarea 的父级插入
244
+ var fallbackParent = textarea.parentElement;
245
+ if (fallbackParent) fallbackParent.appendChild(atBtn);
215
246
  }
216
247
 
217
248
  // ── 给输入区域加 position: relative 包裹层 ──
249
+ var inputParent = textarea.parentElement;
218
250
  const inputContainer = textarea.closest('.agent-chat__composer-combobox')
219
251
  || textarea.closest('.chat-input')
220
252
  || textarea.closest('[class*="chat-composer"]')
221
253
  || textarea.closest('[class*="chat"]')
222
- || textareaParent?.parentElement
223
- || textareaParent;
254
+ || (inputParent ? inputParent.parentElement : null)
255
+ || inputParent;
224
256
  if (inputContainer) {
225
257
  inputContainer.classList.add('openagent-input-wrapper');
226
258
  inputWrapperRef = inputContainer;