openclaw-openagent 1.0.5 → 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.
package/dist/index.js CHANGED
@@ -20,6 +20,7 @@ import { logger } from './src/util/logger.js';
20
20
  // ── Runtime reference ─────────────────────────────────────────────────────
21
21
  // Saved from api.runtime during register(), accessible by other modules.
22
22
  let _runtime;
23
+ let _fullModeRegistered = false;
23
24
  export function getOpenagentRuntime() {
24
25
  return _runtime;
25
26
  }
@@ -63,8 +64,15 @@ export default {
63
64
  logger.info('[entry] Channel adapter registered');
64
65
  // P1-2: registrationMode exists in 2026.3.22+; skip heavy registrations in
65
66
  // setup-only mode (e.g. `openclaw channels list`).
67
+ // IMPORTANT: once registered in full mode, a subsequent hot-reload may
68
+ // re-invoke register() with mode='tool-discovery'. In that case we must
69
+ // still re-register tools/hooks so the gateway doesn't drop them.
66
70
  const mode = api.registrationMode;
67
- if (mode && mode !== 'full') {
71
+ if (mode && mode !== 'full' && _fullModeRegistered) {
72
+ logger.info(`[entry] Re-registering in ${mode} mode (full mode preserved)`);
73
+ // Fall through — re-register tools to avoid gateway dropping them.
74
+ }
75
+ else if (mode && mode !== 'full') {
68
76
  logger.info(`[entry] Setup-only mode (registrationMode=${mode}), skipping tools/hooks`);
69
77
  return;
70
78
  }
@@ -97,6 +105,7 @@ export default {
97
105
  logger.warn(`[entry] Control UI injection failed: ${err.message}`);
98
106
  });
99
107
  logger.info('[entry] Tools + hooks registered (full mode)');
108
+ _fullModeRegistered = true;
100
109
  },
101
110
  };
102
111
  // Named exports for direct imports
@@ -21,7 +21,7 @@ import { toolResult, } from './types.js';
21
21
  import { sendC2CCustomMessage, waitForC2CReply } from '../tim/c2c.js';
22
22
  import fs from 'node:fs';
23
23
  import nodePath from 'node:path';
24
- import { resolveOasnAccessUrl } from '../util/url-resolver.js';
24
+ import { resolveOasnAccessUrl, resolveOasnWebuiUrl } from '../util/url-resolver.js';
25
25
  // ── Helpers ──
26
26
  function fail(error) {
27
27
  return toolResult(JSON.stringify({ success: false, error }));
@@ -291,7 +291,8 @@ export function createCallRemoteAgentTool() {
291
291
  const accessBase = rt.config?.accessApiBase || rt.config?.apiBase;
292
292
  if (result.webuiContinuation?.launchUrl) {
293
293
  const rawLink = result.webuiContinuation.launchUrl;
294
- const link = resolveOasnAccessUrl(accessBase, rawLink);
294
+ const claimUrl = rt.config?.claimUrl;
295
+ const link = resolveOasnWebuiUrl(accessBase, claimUrl, rawLink);
295
296
  const label = result.webuiContinuation.displayName || 'Open WebUI';
296
297
  content = content.trim()
297
298
  ? `${content}\n\n${label}: ${link}`
@@ -10,6 +10,8 @@
10
10
  */
11
11
  export declare const PLUGIN_VERSION = "1.0.0";
12
12
  export declare const SUPPORTED_HOST_MIN = "2026.3.22";
13
+ /** Plugin has been verified up to this OpenClaw host version */
14
+ export declare const MAX_TESTED_VERSION = "2026.6.30";
13
15
  export interface OpenClawVersion {
14
16
  year: number;
15
17
  month: number;
@@ -11,6 +11,8 @@
11
11
  import { logger } from './util/logger.js';
12
12
  export const PLUGIN_VERSION = '1.0.0';
13
13
  export const SUPPORTED_HOST_MIN = '2026.3.22';
14
+ /** Plugin has been verified up to this OpenClaw host version */
15
+ export const MAX_TESTED_VERSION = '2026.6.30';
14
16
  /**
15
17
  * Parse an OpenClaw date version string (e.g. "2026.3.22") into components.
16
18
  * Returns null for unparseable strings.
@@ -61,6 +63,13 @@ export function assertHostCompatibility(hostVersion) {
61
63
  }
62
64
  if (isHostVersionSupported(hostVersion)) {
63
65
  logger.info(`[compat] Host OpenClaw ${hostVersion} >= ${SUPPORTED_HOST_MIN}, OK.`);
66
+ // Warn but don't block for versions beyond tested range
67
+ const host = parseOpenClawVersion(hostVersion);
68
+ const max = parseOpenClawVersion(MAX_TESTED_VERSION);
69
+ if (host && max && compareVersions(host, max) > 0) {
70
+ logger.warn(`[compat] Host OpenClaw ${hostVersion} is beyond max tested version ${MAX_TESTED_VERSION}. ` +
71
+ `The plugin may encounter compatibility issues.`);
72
+ }
64
73
  return;
65
74
  }
66
75
  throw new Error(`openclaw-openagent@${PLUGIN_VERSION} requires OpenClaw >=${SUPPORTED_HOST_MIN}, ` +
@@ -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-25T07:20:29.082Z
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 = [
@@ -235,27 +235,28 @@ const toolcardRender = {
235
235
  if (hasManualCollapseWrapper) {
236
236
  const wrapperFn = findFunctionByAnchor(content, wrapperAnchor);
237
237
  if (!wrapperFn) {
238
- logger.warn('[ui-ext:regex] toolcard-render: manual collapse wrapper function not found');
239
- return false;
238
+ logger.warn('[ui-ext:regex] toolcard-render: manual collapse wrapper function not found, skipping wrapper patch');
240
239
  }
241
- if (!wrapperFn.body.includes('chat-tool-msg-body') || !wrapperFn.body.includes('onToggleExpanded')) {
242
- logger.warn('[ui-ext:regex] toolcard-render: manual collapse wrapper shape mismatch');
243
- return false;
240
+ else if (!wrapperFn.body.includes('chat-tool-msg-body') || !wrapperFn.body.includes('onToggleExpanded')) {
241
+ logger.warn('[ui-ext:regex] toolcard-render: manual collapse wrapper shape mismatch, skipping wrapper patch');
244
242
  }
245
- const wrapperReturnTagMatch = wrapperFn.body.match(/return\s+([a-zA-Z_$]\w*)\s*`/);
246
- if (!wrapperReturnTagMatch) {
247
- logger.warn('[ui-ext:regex] toolcard-render: manual collapse Lit html tag not found');
248
- return false;
243
+ else {
244
+ const wrapperReturnTagMatch = wrapperFn.body.match(/return\s+([a-zA-Z_$]\w*)\s*`/);
245
+ if (!wrapperReturnTagMatch) {
246
+ logger.warn('[ui-ext:regex] toolcard-render: manual collapse Lit html tag not found, skipping wrapper patch');
247
+ }
248
+ else {
249
+ const wrapperHtmlVar = wrapperReturnTagMatch[1];
250
+ const wrapperParams = wrapperFn.params.split(',').map((s) => s.trim());
251
+ const wrapperCardParam = wrapperParams[0] || 'e';
252
+ const wrapperOptionsParam = wrapperParams[1] || '';
253
+ const wrapperSidebarArg = wrapperOptionsParam ? `${wrapperOptionsParam}&&${wrapperOptionsParam}.onOpenSidebar` : 'undefined';
254
+ const wrapperHook = `${marker}if(window.__openagentRenderToolCard){let __cl=window.__openagentRenderToolCard(${wrapperCardParam},${wrapperSidebarArg});if(__cl)return ${wrapperHtmlVar}\`\${__cl}\`}`;
255
+ content = content.substring(0, wrapperFn.bodyStart) + wrapperHook + content.substring(wrapperFn.bodyStart);
256
+ patchedWrapper = true;
257
+ logger.info(`[ui-ext:regex] toolcard-render: patched manual wrapper ${wrapperFn.funcName}(${wrapperFn.params.substring(0, 20)}) html=${wrapperHtmlVar}`);
258
+ }
249
259
  }
250
- const wrapperHtmlVar = wrapperReturnTagMatch[1];
251
- const wrapperParams = wrapperFn.params.split(',').map((s) => s.trim());
252
- const wrapperCardParam = wrapperParams[0] || 'e';
253
- const wrapperOptionsParam = wrapperParams[1] || '';
254
- const wrapperSidebarArg = wrapperOptionsParam ? `${wrapperOptionsParam}&&${wrapperOptionsParam}.onOpenSidebar` : 'undefined';
255
- const wrapperHook = `${marker}if(window.__openagentRenderToolCard){let __cl=window.__openagentRenderToolCard(${wrapperCardParam},${wrapperSidebarArg});if(__cl)return ${wrapperHtmlVar}\`\${__cl}\`}`;
256
- content = content.substring(0, wrapperFn.bodyStart) + wrapperHook + content.substring(wrapperFn.bodyStart);
257
- patchedWrapper = true;
258
- logger.info(`[ui-ext:regex] toolcard-render: patched manual wrapper ${wrapperFn.funcName}(${wrapperFn.params.substring(0, 20)}) html=${wrapperHtmlVar}`);
259
260
  }
260
261
  // 4.11+ also renders tool-result messages through the grouped message
261
262
  // renderer itself instead of nb(). That path only calls the inner ToolCard
@@ -267,12 +268,12 @@ const toolcardRender = {
267
268
  let toolMessageOk = true;
268
269
  const toolMessageFn = findFunctionByAnchor(content, toolMessageAnchor, 8000);
269
270
  if (!toolMessageFn) {
270
- logger.warn('[ui-ext:regex] toolcard-render: tool message wrapper function not found');
271
+ logger.warn('[ui-ext:regex] toolcard-render: tool message wrapper function not found, skipping');
271
272
  toolMessageOk = false;
272
273
  }
273
274
  if (toolMessageOk && (!toolMessageFn.body.includes('chat-tool-msg-collapse--manual')
274
275
  || !toolMessageFn.body.includes('chat-tool-msg-body'))) {
275
- logger.warn('[ui-ext:regex] toolcard-render: tool message wrapper shape mismatch');
276
+ logger.warn('[ui-ext:regex] toolcard-render: tool message wrapper shape mismatch, skipping');
276
277
  toolMessageOk = false;
277
278
  }
278
279
  let toolMessageHtmlVar = '';
@@ -613,11 +614,36 @@ const remoteAgentMessageShortCircuit = {
613
614
  logger.info(`[ui-ext:regex] remote-agent-msg: patched (strategy C, sc=${scVar})`);
614
615
  return true;
615
616
  }
617
+ // Strategy D (v5.17+ fallback): search for any if-condition referencing
618
+ // hasToolCards alongside a role-like variable — v5.17+ may replace the
619
+ // ternary/compound patterns of A/B/C with a different expression shape.
620
+ const strategyDRe = new RegExp(`if\\(!` + `([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*&&\\s*` +
621
+ hasToolCardsVar + `\\s*&&\\s*([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*\\)`);
622
+ const strategyDMatch = content.match(strategyDRe);
623
+ if (strategyDMatch) {
624
+ const markdownVarD = strategyDMatch[1];
625
+ const isToolResultVarD = strategyDMatch[2];
626
+ const dMatchFull = strategyDMatch[0];
627
+ const dIdx = strategyDMatch.index;
628
+ const patched = `if(` + marker +
629
+ `(!` + markdownVarD + `||window.__openagentOwnsToolCards&&window.__openagentOwnsToolCards(` +
630
+ toolCardsVar + `))&&` + hasToolCardsVar + `&&` + isToolResultVarD + `)`;
631
+ content = content.substring(0, dIdx) + patched + content.substring(dIdx + dMatchFull.length);
632
+ writeFileSync(bundlePath, content, 'utf-8');
633
+ logger.info(`[ui-ext:regex] remote-agent-msg: patched (strategy D, md=${markdownVarD})`);
634
+ return true;
635
+ }
616
636
  // 策略B (v4.11~v4.1x fallback): compound = hasToolCards && (showToolCalls ?? !0)
617
637
  const compoundRe = new RegExp(`(\\w+)=${hasToolCardsVar}&&\\(\\w+\\.showToolCalls\\?\\?!0\\)`);
618
638
  const compoundMatch = content.match(compoundRe);
619
639
  if (!compoundMatch) {
620
- logger.warn('[ui-ext:regex] remote-agent-msg: all strategies (A/C/B) failed');
640
+ // All strategies (A/C/D/B) failed — dump context for diagnostics
641
+ const anchorHintIdx = content.indexOf(hasToolCardsVar);
642
+ const ctxStart = anchorHintIdx > 200 ? anchorHintIdx - 200 : 0;
643
+ const ctxEnd = Math.min(anchorHintIdx + 200, content.length);
644
+ logger.warn(`[ui-ext:regex] remote-agent-msg: all strategies (A/C/D/B) failed for toolCards=${toolCardsVar}. ` +
645
+ `Context around hasToolCardsVar (at ${anchorHintIdx}): ` +
646
+ content.substring(ctxStart, ctxEnd));
621
647
  return false;
622
648
  }
623
649
  const compoundVar = compoundMatch[1];
@@ -670,14 +696,15 @@ const mentionHooks = {
670
696
  const markerA = makeMarker(this.id);
671
697
  const hookA = `${markerA}if(window.__clMention?.onKeyDown(${eventParam}))return;`;
672
698
  content = content.substring(0, keydownInsertIdx) + hookA + content.substring(keydownInsertIdx);
673
- // 注入点 B: handleInput — 支持三种形态
699
+ // 注入点 B: handleInput — 四种形态兼容
674
700
  // Shape A (old): .onDraftChange(target.value)}}
675
701
  // Shape B (4.11~4.x): .onDraftChange(target.value) (arrow function 内)
676
- // Shape C (2026.6.x): be=t=>{let n=t.target;pL(e,n.value)} — 引入 pL helper
677
- // arrow body 末尾不再出现 onDraftChange 字面
702
+ // Shape C (2026.6.x): be=t=>{let n=t.target;pL(e,n.value)} — 引入 pL helper
703
+ // Shape D (generic): .onDraftChange(任意.target.value) 不加结尾限制
678
704
  const inputReA = /\.onDraftChange\(([a-zA-Z_$][a-zA-Z0-9_$]*)\.value\)\}\}/;
679
705
  const inputReB = /\.onDraftChange\(([a-zA-Z_$][a-zA-Z0-9_$]*)\.value\)/;
680
706
  const inputReC = /let ([a-zA-Z_$][a-zA-Z0-9_$]*)=[a-zA-Z_$][a-zA-Z0-9_$]*\.target;[a-zA-Z_$][a-zA-Z0-9_$]*\([a-zA-Z_$][a-zA-Z0-9_$]*,\1\.value\)\}/;
707
+ const inputReD = /\.onDraftChange\(([a-zA-Z_$][a-zA-Z0-9_$]*)\.target\.value\)/;
681
708
  let inputMatch = content.match(inputReA);
682
709
  let inputShape = 'A';
683
710
  if (!inputMatch) {
@@ -689,12 +716,22 @@ const mentionHooks = {
689
716
  inputShape = 'B';
690
717
  }
691
718
  if (!inputMatch) {
692
- logger.warn('[ui-ext:regex] mention-hooks: input anchor not found (Shape A/B/C all missed)');
719
+ inputMatch = content.match(inputReD);
720
+ inputShape = 'D';
721
+ }
722
+ if (!inputMatch) {
723
+ // All shapes missed — dump context for diagnostics
724
+ const draftHintIdx = content.indexOf('.onDraftChange');
725
+ const ctxStart = draftHintIdx > 200 ? draftHintIdx - 200 : 0;
726
+ const ctxEnd = Math.min(draftHintIdx + 200, content.length);
727
+ logger.warn(`[ui-ext:regex] mention-hooks: input anchor not found (Shape A/B/C/D all missed). ` +
728
+ `Context around onDraftChange (at ${draftHintIdx}): ` +
729
+ content.substring(ctxStart, ctxEnd));
693
730
  return false;
694
731
  }
695
732
  const targetVar = inputMatch[1];
696
733
  const inputAnchorIdx = content.indexOf(inputMatch[0]);
697
- // Shape A: }} 之前;Shape C: 末尾 } 之前;Shape B: ) 之后
734
+ // Shape A: }} 之前;Shape C: 末尾 } 之前;Shape B/D: ) 之后
698
735
  const draftCallEnd = inputShape === 'A'
699
736
  ? inputAnchorIdx + inputMatch[0].length - 2
700
737
  : inputShape === 'C'