openclaw-openagent 1.0.13 → 1.0.14

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 (128) hide show
  1. package/dist/src/messaging/executor.d.ts +2 -3
  2. package/dist/src/messaging/executor.js +4 -31
  3. package/dist/src/plugin-ui/assets/openagent-override.js +4 -5
  4. package/dist/src/runtime/update-checker.d.ts +1 -1
  5. package/dist/src/runtime/update-checker.js +9 -46
  6. package/package.json +1 -3
  7. package/src/app/channel-tools.ts +0 -249
  8. package/src/app/discovery-tools.ts +0 -273
  9. package/src/app/download-file-tool.ts +0 -133
  10. package/src/app/hooks.ts +0 -144
  11. package/src/app/index.ts +0 -84
  12. package/src/app/messaging-tools.ts +0 -79
  13. package/src/app/ops-tools.ts +0 -155
  14. package/src/app/remote-agent-tool.ts +0 -762
  15. package/src/app/types.ts +0 -68
  16. package/src/app/upload-file-tool.ts +0 -411
  17. package/src/app/verbose-preflight.ts +0 -190
  18. package/src/auth/config.ts +0 -266
  19. package/src/auth/credential-manager.ts +0 -146
  20. package/src/auth/index.ts +0 -24
  21. package/src/auth/verify.ts +0 -99
  22. package/src/channel.ts +0 -566
  23. package/src/compat.ts +0 -94
  24. package/src/config/config-schema.ts +0 -45
  25. package/src/messaging/aggregator.ts +0 -120
  26. package/src/messaging/collector.ts +0 -89
  27. package/src/messaging/executor.ts +0 -72
  28. package/src/messaging/inbound.ts +0 -150
  29. package/src/messaging/index.ts +0 -11
  30. package/src/messaging/mention-protocol.ts +0 -94
  31. package/src/messaging/process-c2c-request.ts +0 -564
  32. package/src/messaging/process-message.ts +0 -373
  33. package/src/messaging/scheduler.ts +0 -55
  34. package/src/messaging/types.ts +0 -38
  35. package/src/plugin-ui/adapters/oc-2026-04.js +0 -103
  36. package/src/plugin-ui/adapters/oc-2026-05.js +0 -125
  37. package/src/plugin-ui/adapters/oc-2026-06.js +0 -125
  38. package/src/plugin-ui/adapters/oc-unknown.js +0 -48
  39. package/src/plugin-ui/assets/agentbook-icon.svg +0 -5
  40. package/src/plugin-ui/assets/bg.png +0 -0
  41. package/src/plugin-ui/assets/icon.png +0 -0
  42. package/src/plugin-ui/assets/magic.svg +0 -5
  43. package/src/plugin-ui/assets/openagent-override.js +0 -11263
  44. package/src/plugin-ui/build.cjs +0 -463
  45. package/src/plugin-ui/index.ts +0 -18
  46. package/src/plugin-ui/modules/agent-book/panel/agent-book.js +0 -570
  47. package/src/plugin-ui/modules/agent-book/panel/agent-card.js +0 -170
  48. package/src/plugin-ui/modules/agent-book/panel/agent-data.js +0 -643
  49. package/src/plugin-ui/modules/agent-book/panel/inject-ui.js +0 -665
  50. package/src/plugin-ui/modules/agent-book/panel/mention-state.js +0 -206
  51. package/src/plugin-ui/modules/agent-book/panel/styles.js +0 -832
  52. package/src/plugin-ui/modules/agent-book/panel/theme-adapter.js +0 -86
  53. package/src/plugin-ui/modules/agent-book/remote-agent-tool/components-core.js +0 -295
  54. package/src/plugin-ui/modules/agent-book/remote-agent-tool/thought-chain-card.js +0 -211
  55. package/src/plugin-ui/modules/agent-book/scanner.js +0 -131
  56. package/src/plugin-ui/modules/agent-book/travelcard/travel-cards.js +0 -500
  57. package/src/plugin-ui/modules/agent-book/travelcard/travel-engine.js +0 -652
  58. package/src/plugin-ui/modules/agent-book/travelcard/travel-styles.js +0 -262
  59. package/src/plugin-ui/modules/loader/bootstrap.js +0 -133
  60. package/src/plugin-ui/modules/loader/shared-state.js +0 -838
  61. package/src/plugin-ui/modules/loader/ws-intercept.js +0 -199
  62. package/src/plugin-ui/modules/remote-agent/chunk-parser.js +0 -161
  63. package/src/plugin-ui/modules/remote-agent/execution-card.js +0 -530
  64. package/src/plugin-ui/modules/remote-agent/markdown-renderer.js +0 -256
  65. package/src/plugin-ui/modules/remote-agent/media-links.js +0 -151
  66. package/src/plugin-ui/modules/remote-agent/native-style-adapter.js +0 -134
  67. package/src/plugin-ui/modules/remote-agent/output-card.js +0 -342
  68. package/src/plugin-ui/modules/remote-agent/progress-store.js +0 -363
  69. package/src/plugin-ui/modules/remote-agent/render-hooks.js +0 -1700
  70. package/src/plugin-ui/modules/remote-agent/styles.js +0 -903
  71. package/src/plugin-ui/modules/remote-agent/tool-card-model.js +0 -130
  72. package/src/plugin-ui/postinstall-deploy.cjs +0 -52
  73. package/src/plugin-ui/ui-extension-loader/backup.ts +0 -92
  74. package/src/plugin-ui/ui-extension-loader/index.ts +0 -276
  75. package/src/plugin-ui/ui-extension-loader/locator.ts +0 -152
  76. package/src/plugin-ui/ui-extension-loader/manifest.ts +0 -57
  77. package/src/plugin-ui/ui-extension-loader/registry-regex.ts +0 -944
  78. package/src/plugin-ui/ui-extension-loader/removed-extensions.ts +0 -70
  79. package/src/plugin-ui/ui-extension-loader/types.ts +0 -72
  80. package/src/proxy/auth-proxy.ts +0 -414
  81. package/src/runtime/account.ts +0 -549
  82. package/src/runtime/index.ts +0 -7
  83. package/src/runtime/plugin-runtime.ts +0 -94
  84. package/src/runtime/registry.ts +0 -71
  85. package/src/runtime/update-checker.ts +0 -286
  86. package/src/sdk/CLASS_MAP.md +0 -143
  87. package/src/sdk/index.d.ts +0 -126
  88. package/src/sdk/index.js +0 -23990
  89. package/src/sdk/modules/cloud-search-module.js +0 -1117
  90. package/src/sdk/modules/follow-module.js +0 -1069
  91. package/src/sdk/modules/group-module.js +0 -7397
  92. package/src/sdk/modules/relationship-module.js +0 -2269
  93. package/src/sdk/modules/signaling-module.js +0 -1468
  94. package/src/sdk/modules/tim-upload-plugin.js +0 -730
  95. package/src/sdk/node-env/http-request.js +0 -90
  96. package/src/sdk/node-env/index.js +0 -57
  97. package/src/sdk/node-env/storage.js +0 -114
  98. package/src/sdk/package.json +0 -10
  99. package/src/sdk/tsconfig.json +0 -16
  100. package/src/state/pending-invocation-store.ts +0 -43
  101. package/src/state/store.ts +0 -756
  102. package/src/tim/c2c.ts +0 -451
  103. package/src/tim/channels.ts +0 -364
  104. package/src/tim/client.ts +0 -330
  105. package/src/tim/index.ts +0 -18
  106. package/src/tim/messages.ts +0 -166
  107. package/src/tim/sdk-logger-init.ts +0 -50
  108. package/src/tools.ts +0 -10
  109. package/src/transport/factory.ts +0 -95
  110. package/src/transport/oasn/index.ts +0 -17
  111. package/src/transport/oasn/oasn-agent-card.ts +0 -111
  112. package/src/transport/oasn/oasn-discovery.ts +0 -108
  113. package/src/transport/oasn/oasn-files.ts +0 -210
  114. package/src/transport/oasn/oasn-http.ts +0 -529
  115. package/src/transport/oasn/oasn-invocation.ts +0 -604
  116. package/src/transport/oasn/oasn-normalize.ts +0 -133
  117. package/src/transport/oasn/oasn-register.ts +0 -33
  118. package/src/transport/oasn/oasn-transport.ts +0 -318
  119. package/src/transport/oasn/oasn-types.ts +0 -404
  120. package/src/transport/tim/index.ts +0 -8
  121. package/src/transport/tim/tim-transport.ts +0 -515
  122. package/src/transport/types.ts +0 -538
  123. package/src/types/openclaw-plugin-sdk-media-store.d.ts +0 -9
  124. package/src/types/openclaw.d.ts +0 -97
  125. package/src/types/tencentcloud-chat.d.ts +0 -15
  126. package/src/util/http.ts +0 -113
  127. package/src/util/logger.ts +0 -131
  128. package/src/util/url-resolver.ts +0 -77
@@ -1,665 +0,0 @@
1
-
2
- // ── 注入逻辑(v3 — OC handleKeyDown/handleInput 钩子驱动)──
3
- //
4
- // 事件处理策略:
5
- // keydown / input → 由 OC 注入钩子(mention-hooks UIExtension)调用 window.__clMention
6
- // selectionchange → document 级事件委托(不绑 textarea,不怕 Lit 重建)
7
- // beforeinput → document 级事件委托(同上)
8
- // compositionstart/end → document 级事件委托(同上)
9
- //
10
- // tryInject() 只做 DOM 注入(@ 按钮 + CSS class),不绑定任何事件。
11
-
12
- // ── textarea 选择器(多版本兼容)──
13
- // v3 "Message..." / v4 "发消息..." / v5.x 结构化选择器
14
- const _CL_TEXTAREA_SELECTOR = [
15
- 'textarea[placeholder*="发消息"]',
16
- 'textarea[placeholder*="Message"]',
17
- 'textarea[placeholder*="Enter to send"]',
18
- '.agent-chat__composer-combobox textarea',
19
- '.chat-composer textarea',
20
- '.chat-input textarea',
21
- '[class*="composer"] textarea:not([placeholder*="Search"])',
22
- '[class*="chat-input"] textarea',
23
- '[data-component="chat-input"] textarea',
24
- ].join(', ');
25
-
26
- function _getTextarea() {
27
- return document.querySelector(_CL_TEXTAREA_SELECTOR);
28
- }
29
-
30
- function _setTextareaValue(textarea, value) {
31
- const setter = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value')?.set;
32
- if (setter) setter.call(textarea, value);
33
- else textarea.value = value;
34
- textarea.dispatchEvent(new Event('input', { bubbles: true }));
35
- }
36
-
37
- function _escapeRemoteAgentMarkerValue(value) {
38
- return String(value || '')
39
- .replace(/\s+/g, ' ')
40
- .trim()
41
- .replace(/"/g, "'");
42
- }
43
-
44
- function _buildRemoteAgentMarker(mention, userText) {
45
- const hasChinese = /[\u4e00-\u9fff]/.test(userText || '');
46
- const instruction = hasChinese
47
- ? '请调用 openagent_call_remote_agent 工具将此请求转发给远端 Agent 处理'
48
- : 'Please invoke the openagent_call_remote_agent tool to forward this request to the remote agent';
49
- const attrs = [
50
- `agent_id="${_escapeRemoteAgentMarkerValue(mention.agent_id)}"`,
51
- `agent_name="${_escapeRemoteAgentMarkerValue(mention.name)}"`,
52
- ];
53
- if (mention.agent_bio) {
54
- attrs.push(`agent_bio="${_escapeRemoteAgentMarkerValue(mention.agent_bio)}"`);
55
- }
56
- attrs.push(`instruction="${_escapeRemoteAgentMarkerValue(instruction)}"`);
57
- return `{openagent_call_remote_agent:${attrs.join(',')}}`;
58
- }
59
-
60
- function _composeRemoteAgentMessage(textarea, mention) {
61
- const currentVal = textarea.value || '';
62
- const marker = _buildRemoteAgentMarker(mention, currentVal);
63
- const escapedName = mention.name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
64
- const atPattern = new RegExp('@' + escapedName + '\\s*');
65
- return {
66
- currentVal,
67
- marker,
68
- message: currentVal.replace(atPattern, marker + ' ')
69
- };
70
- }
71
-
72
- function _isLikelyNativeSendButton(target, textarea) {
73
- const button = target && target.closest ? target.closest('button, [role="button"]') : null;
74
- if (!button || !textarea) return false;
75
- if (button.classList && button.classList.contains('openagent-at-btn')) return false;
76
-
77
- const inputContainer = textarea.closest('.agent-chat__composer-combobox')
78
- || textarea.closest('.chat-input')
79
- || textarea.closest('[class*="chat-composer"]')
80
- || textarea.closest('[class*="chat"]')
81
- || textarea.parentElement;
82
- if (!inputContainer || !inputContainer.contains(button)) return false;
83
-
84
- const label = [
85
- button.getAttribute('aria-label'),
86
- button.getAttribute('title'),
87
- button.textContent
88
- ].filter(Boolean).join(' ').toLowerCase();
89
- if (/send|发送/.test(label)) return true;
90
-
91
- const buttons = Array.from(inputContainer.querySelectorAll('button, [role="button"]'))
92
- .filter(function(btn) {
93
- if (btn.classList && btn.classList.contains('openagent-at-btn')) return false;
94
- if (btn.disabled || btn.getAttribute('aria-disabled') === 'true') return false;
95
- return btn.offsetParent !== null;
96
- });
97
- return buttons.length > 0 && buttons[buttons.length - 1] === button;
98
- }
99
-
100
- function _sendPendingMention(textarea) {
101
- if (!textarea || !CL.pendingMention) return false;
102
- if (CL.remoteSendInFlight) return true;
103
-
104
- const mention = CL.pendingMention;
105
- const payload = _composeRemoteAgentMessage(textarea, mention);
106
- CL.remoteSendInFlight = true;
107
-
108
- console.log('[cl-diag][send-marker]', {
109
- agentId: mention.agent_id,
110
- agentName: mention.name,
111
- userTextPreview: payload.currentVal.slice(0, 200),
112
- markerPreview: payload.marker.slice(0, 240),
113
- sendValuePreview: payload.message.slice(0, 260),
114
- });
115
-
116
- // 不把内部 marker 写入 textarea。可见输入框按原生发送行为立即清空;
117
- // 真正发给 OpenClaw 的带 marker 文本只作为 handleSendChat 参数传入。
118
- _setTextareaValue(textarea, '');
119
-
120
- (async () => {
121
- try {
122
- CL.lastMentionAgentId = mention.agent_id;
123
- CL.pendingMention = null;
124
- await CL.sendRemoteAgentMessage(payload.message);
125
- console.log('[openagent] remote call sent');
126
- } catch (err) {
127
- CL.pendingMention = mention;
128
- _setTextareaValue(textarea, payload.currentVal);
129
- console.error('[openagent] remote call send failed', err);
130
- window.alert?.('OpenAgent remote call 未发送:请检查 gateway 连接后重试。');
131
- } finally {
132
- CL.remoteSendInFlight = false;
133
- }
134
- })();
135
-
136
- return true;
137
- }
138
-
139
- function _installSendClickInterceptor() {
140
- if (window.__openagentSendClickInterceptorInstalled) return;
141
- window.__openagentSendClickInterceptorInstalled = true;
142
- document.addEventListener('click', function(e) {
143
- if (!CL.pendingMention || CL.remoteSendInFlight) return;
144
- const textarea = _getTextarea();
145
- if (!_isLikelyNativeSendButton(e.target, textarea)) return;
146
-
147
- e.preventDefault();
148
- e.stopPropagation();
149
- if (typeof e.stopImmediatePropagation === 'function') e.stopImmediatePropagation();
150
- _sendPendingMention(textarea);
151
- }, true);
152
- }
153
-
154
- function _installSendKeydownInterceptor() {
155
- if (window.__openagentSendKeydownInterceptorInstalled) return;
156
- window.__openagentSendKeydownInterceptorInstalled = true;
157
- document.addEventListener('keydown', function(e) {
158
- if (!CL.pendingMention || CL.remoteSendInFlight) return;
159
- if (e.key !== 'Enter' || e.shiftKey || e.isComposing) return;
160
-
161
- const textarea = _getTextarea();
162
- if (!textarea || e.target !== textarea) return;
163
-
164
- e.preventDefault();
165
- e.stopPropagation();
166
- if (typeof e.stopImmediatePropagation === 'function') e.stopImmediatePropagation();
167
- _sendPendingMention(textarea);
168
- }, true);
169
- }
170
-
171
- /**
172
- * 多策略定位 textarea 所在输入区域的工具栏行。
173
- * v4.x: 工具栏在 textarea.parentElement.children 中
174
- * v5.x: 工具栏可能嵌套在祖父容器或包含 toolbar class
175
- * 返回 null 表示未找到则回退到 textarea 父级插入
176
- */
177
- function _findToolbarRow(textarea) {
178
- var parent = textarea.parentElement;
179
-
180
- // 策略 1(v4.x): textarea 兄弟元素含 SVG 图标
181
- if (parent) {
182
- for (var i = 0; i < parent.children.length; i++) {
183
- if (parent.children[i] !== textarea && parent.children[i].querySelector('svg')) return parent.children[i];
184
- }
185
- }
186
-
187
- // 策略 2(v4.x): 祖父级兄弟元素含 SVG 图标
188
- if (parent) {
189
- var grandparent = parent.parentElement;
190
- if (grandparent) {
191
- for (var j = 0; j < grandparent.children.length; j++) {
192
- if (grandparent.children[j] !== parent && grandparent.children[j].querySelector('svg')) return grandparent.children[j];
193
- }
194
- }
195
- }
196
-
197
- // 策略 3(v5.x): 搜索包含 toolbar class 的元素
198
- var chatContainer = textarea.closest('[class*="chat"]');
199
- if (chatContainer) {
200
- var toolbarEl = chatContainer.querySelector('[class*="toolbar"]');
201
- if (toolbarEl) return toolbarEl;
202
- }
203
-
204
- // 策略 4(v5.x fallback): 搜索 voice/mic 按钮定位其容器
205
- var voiceBtn = document.querySelector(
206
- 'button[aria-label*="voice"], button[aria-label*="语音"], ' +
207
- 'button[aria-label*="mic"], button[aria-label*="麦克风"]'
208
- );
209
- if (voiceBtn) return voiceBtn.parentElement;
210
-
211
- return null;
212
- }
213
-
214
- function tryInject() {
215
- const textarea = _getTextarea();
216
-
217
- if (!textarea) return false;
218
- _installSendClickInterceptor();
219
- _installSendKeydownInterceptor();
220
- if (document.querySelector('.openagent-at-btn')) return true; // 已注入
221
-
222
- // ── 定位插入点:麦克风图标所在的 toolbar 行(多策略)──
223
- var toolbarRow = _findToolbarRow(textarea);
224
-
225
- // ── 创建 Agent Book 按钮 ──
226
- const atBtn = document.createElement('button');
227
- atBtn.className = 'openagent-at-btn';
228
- atBtn.innerHTML = '<span class="at-sym">@</span> Agent Book';
229
- atBtn.addEventListener('click', () => {
230
- // 在 textarea 中插入 @ 字符,让状态机自然检测 → openPanel
231
- const ta = _getTextarea();
232
- if (!ta) return;
233
- ta.focus();
234
- const val = ta.value;
235
- const pos = ta.selectionStart;
236
- // 如果光标前已经是 @,不重复插入
237
- if (pos > 0 && val[pos - 1] === '@') {
238
- // 按钮主动触发 → 清除 dismissed 状态,确保弹窗能打开
239
- if (typeof resetMention === 'function') resetMention();
240
- _handleStateChange(ta);
241
- return;
242
- }
243
- // 插入 @ 字符(前面加空格分隔,除非行首/空白后)
244
- const needSpace = pos > 0 && !/[\s\n]/.test(val[pos - 1]);
245
- const insert = needSpace ? ' @' : '@';
246
- const newVal = val.slice(0, pos) + insert + val.slice(pos);
247
- const setter = Object.getOwnPropertyDescriptor(
248
- HTMLTextAreaElement.prototype, 'value'
249
- )?.set;
250
- if (setter) setter.call(ta, newVal);
251
- else ta.value = newVal;
252
- ta.selectionStart = ta.selectionEnd = pos + insert.length;
253
- ta.dispatchEvent(new Event('input', { bubbles: true }));
254
- // input 事件 → OC handleInput → __clMention.onInput → 状态机打开面板
255
- });
256
-
257
- if (toolbarRow) {
258
- const toolbarLeft = toolbarRow.querySelector('[class*="toolbar-left"]') || toolbarRow;
259
- const iconButtons = toolbarLeft.querySelectorAll('button, [role="button"]');
260
- // 优先按 aria-label 匹配 voice/mic 按钮,回退到位置假设
261
- let micBtn = null;
262
- for (var i = 0; i < iconButtons.length; i++) {
263
- var label = (iconButtons[i].getAttribute('aria-label') || '').toLowerCase();
264
- if (/voice|mic|语音|麦克风/.test(label)) { micBtn = iconButtons[i]; break; }
265
- }
266
- if (!micBtn && iconButtons.length >= 2) {
267
- micBtn = iconButtons[1]; // fallback: v4.x 中 mic 一般是第 2 个按钮
268
- } else if (!micBtn && iconButtons.length === 1) {
269
- micBtn = iconButtons[0];
270
- }
271
-
272
- if (micBtn) {
273
- micBtn.after(atBtn);
274
- } else {
275
- toolbarLeft.appendChild(atBtn);
276
- }
277
- } else {
278
- // 无工具栏时回退到 textarea 的父级插入
279
- var fallbackParent = textarea.parentElement;
280
- if (fallbackParent) fallbackParent.appendChild(atBtn);
281
- }
282
-
283
- // ── 给输入区域加 position: relative 包裹层 ──
284
- var inputParent = textarea.parentElement;
285
- const inputContainer = textarea.closest('.agent-chat__composer-combobox')
286
- || textarea.closest('.chat-input')
287
- || textarea.closest('[class*="chat-composer"]')
288
- || textarea.closest('[class*="chat"]')
289
- || (inputParent ? inputParent.parentElement : null)
290
- || inputParent;
291
- if (inputContainer) {
292
- inputContainer.classList.add('openagent-input-wrapper');
293
- inputWrapperRef = inputContainer;
294
- }
295
-
296
- console.log('[openagent] ✅ Agent Book UI injected (v3 hook-driven)');
297
-
298
- // ── 提示栏(Figma 141:3293):页面加载即显示 ──
299
- _injectHintBar(inputContainer || textarea.parentElement, toolbarRow);
300
-
301
- return true;
302
- }
303
-
304
- /** 注入提示栏到输入区域上方 */
305
- function _injectHintBar(anchorEl, toolbarEl) {
306
- if (!anchorEl || document.querySelector('.openagent-hint-bar')) return;
307
-
308
- const hintBar = document.createElement('div');
309
- hintBar.className = 'openagent-hint-bar';
310
- hintBar.innerHTML = '' +
311
- '<div class="openagent-hint-bar__content">' +
312
- '<div class="openagent-hint-bar__icon">' +
313
- '<svg xmlns="http://www.w3.org/2000/svg" width="27" height="27" viewBox="0 0 27 27" fill="none">' +
314
- '<rect width="27" height="27" rx="6" fill="url(#paint0_linear_141_3277)" fill-opacity="0.1"/>' +
315
- '<path d="M13.5 6.75098C16.8137 6.75098 19.5 9.43727 19.5 12.751C19.5 14.3288 18.8894 15.7629 17.8936 16.834L20.7803 19.7207C21.0732 20.0136 21.0732 20.4884 20.7803 20.7812C20.4874 21.0741 20.0126 21.0741 19.7197 20.7812L16.7383 17.7998C15.8038 18.4004 14.6933 18.751 13.5 18.751C10.1863 18.751 7.5 16.0647 7.5 12.751C7.5 9.43727 10.1863 6.75098 13.5 6.75098ZM13.5 8.25098C11.0147 8.25098 9 10.2657 9 12.751C9 15.2363 11.0147 17.251 13.5 17.251C15.9853 17.251 18 15.2363 18 12.751C18 10.2657 15.9853 8.25098 13.5 8.25098Z" fill="url(#paint1_linear_141_3277)"/>' +
316
- '<defs>' +
317
- '<linearGradient id="paint0_linear_141_3277" x1="0" y1="13.5" x2="27" y2="13.5" gradientUnits="userSpaceOnUse">' +
318
- '<stop stop-color="#8274FF"/>' +
319
- '<stop offset="1" stop-color="#3890FF"/>' +
320
- '</linearGradient>' +
321
- '<linearGradient id="paint1_linear_141_3277" x1="7.5" y1="13.8759" x2="20.9999" y2="13.8759" gradientUnits="userSpaceOnUse">' +
322
- '<stop stop-color="#8274FF"/>' +
323
- '<stop offset="1" stop-color="#3890FF"/>' +
324
- '</linearGradient>' +
325
- '</defs>' +
326
- '</svg>' +
327
- '</div>' +
328
- '<span class="openagent-hint-bar__text">输入你的需求,调配远端Agent为你解决</span>' +
329
- '</div>' +
330
- '<button class="openagent-hint-bar__close">' +
331
- '<svg xmlns="http://www.w3.org/2000/svg" width="21" height="21" viewBox="0 0 21 21" fill="none">' +
332
- '<path d="M5.625 5.625L15.375 15.375" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>' +
333
- '<path d="M15.375 5.625L5.625 15.375" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>' +
334
- '</svg>' +
335
- '</button>';
336
-
337
- hintBar.querySelector('.openagent-hint-bar__close').addEventListener('click', () => {
338
- hintBar.style.display = 'none';
339
- });
340
-
341
- // ── V2 版本兼容:≥ 2026.6.9 使用 absolute 定位,bottom 动态跟随输入区高度 ──
342
- var isV2 = typeof _clIsHostVersionGte === 'function' && _clIsHostVersionGte('2026.6.9');
343
- if (isV2) {
344
- hintBar.style.position = 'absolute';
345
- }
346
-
347
- // 插入到 wrapper 外面、上方
348
- anchorEl.parentElement.insertBefore(hintBar, anchorEl);
349
-
350
- function _findHintBarToolbar() {
351
- if (toolbarEl) return toolbarEl;
352
- var parentEl = anchorEl.parentElement;
353
- if (parentEl) {
354
- for (var i = 0; i < parentEl.children.length; i++) {
355
- var child = parentEl.children[i];
356
- if (child !== anchorEl && child.className && String(child.className).indexOf('agent-chat__toolbar') !== -1) {
357
- return child;
358
- }
359
- }
360
- }
361
- return anchorEl.closest && anchorEl.closest('.agent-chat__input')
362
- ? anchorEl.closest('.agent-chat__input').querySelector('.agent-chat__toolbar')
363
- : null;
364
- }
365
-
366
- function _readElementHeight(el) {
367
- if (!el) return 0;
368
- var rect = el.getBoundingClientRect ? el.getBoundingClientRect() : null;
369
- if (rect && rect.height > 0) return rect.height;
370
- return typeof el.offsetHeight === 'number' ? el.offsetHeight : 0;
371
- }
372
-
373
- // 跟随 composer wrapper 的实际尺寸。侧栏拖拽/布局重排不一定触发 window resize。
374
- function _syncHintBarLayout() {
375
- var rect = anchorEl.getBoundingClientRect();
376
- if (rect.width > 0) {
377
- hintBar.style.width = rect.width + 'px';
378
- var rootStyle = document.documentElement && document.documentElement.style;
379
- if (rootStyle) {
380
- rootStyle.setProperty('--openagent-input-wrapper-right-offset', Math.max(0, window.innerWidth - rect.right) + 'px');
381
- rootStyle.setProperty('--openagent-input-wrapper-right-edge', Math.max(0, rect.right) + 'px');
382
- rootStyle.setProperty('--openagent-input-wrapper-width', rect.width + 'px');
383
- }
384
- _syncSidebarRightEdge(rect);
385
- }
386
- if (isV2) {
387
- var toolbar = _findHintBarToolbar();
388
- var bottom = _readElementHeight(anchorEl) + _readElementHeight(toolbar) + 6;
389
- hintBar.style.bottom = bottom + 'px';
390
- }
391
- }
392
-
393
- function _syncSidebarRightEdge(inputRect) {
394
- var sidebars = document.querySelectorAll('.chat-sidebar');
395
- for (var i = 0; i < sidebars.length; i++) {
396
- var sidebar = sidebars[i];
397
- var sidebarRect = sidebar.getBoundingClientRect();
398
- if (!sidebarRect || sidebarRect.width <= 0) continue;
399
- var left = Math.max(0, sidebarRect.left);
400
- var targetRight = Math.min(window.innerWidth, Math.max(0, inputRect.right));
401
- var targetWidth = Math.floor(targetRight - left);
402
- if (targetWidth <= 0) continue;
403
- sidebar.style.setProperty('--openagent-sidebar-target-width', targetWidth + 'px');
404
- sidebar.style.setProperty('width', targetWidth + 'px', 'important');
405
- sidebar.style.setProperty('max-width', targetWidth + 'px', 'important');
406
- sidebar.style.setProperty('flex-basis', targetWidth + 'px', 'important');
407
- sidebar.style.setProperty('margin-right', '0px', 'important');
408
- }
409
- }
410
-
411
- function _scheduleHintBarLayoutSync() {
412
- window.requestAnimationFrame
413
- ? window.requestAnimationFrame(_syncHintBarLayout)
414
- : setTimeout(_syncHintBarLayout, 0);
415
- }
416
-
417
- _syncHintBarLayout();
418
- _scheduleHintBarLayoutSync();
419
- window.addEventListener('resize', _scheduleHintBarLayoutSync);
420
- if (typeof ResizeObserver === 'function') {
421
- var hintBarResizeObserver = new ResizeObserver(_scheduleHintBarLayoutSync);
422
- hintBarResizeObserver.observe(anchorEl);
423
- var toolbarForResize = _findHintBarToolbar();
424
- if (toolbarForResize) hintBarResizeObserver.observe(toolbarForResize);
425
- }
426
- if (typeof MutationObserver === 'function' && document.body) {
427
- var sidebarLayoutObserver = new MutationObserver(_scheduleHintBarLayoutSync);
428
- sidebarLayoutObserver.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['class'] });
429
- }
430
- }
431
-
432
- // ════════════════════════════════════════════════════════════════
433
- // ── window.__clMention — OC 注入钩子的桥接对象 ──
434
- // ════════════════════════════════════════════════════════════════
435
- //
436
- // OC 的 handleKeyDown 中注入了:
437
- // if (window.__clMention?.onKeyDown(e)) return;
438
- //
439
- // OC 的 handleInput 中注入了:
440
- // window.__clMention?.onInput?.(target);
441
- //
442
- // 所有业务逻辑在此处理,注入端只做调用转发。
443
-
444
- window.__clMention = {
445
-
446
- /**
447
- * OC handleKeyDown 钩子。
448
- * 返回 true → OC 跳过后续处理(包括 Enter 发送)。
449
- * 返回 false → OC 继续正常处理。
450
- */
451
- onKeyDown(e) {
452
- const textarea = e.target;
453
-
454
- // ── 1. 面板打开时:键盘导航 ──
455
- if (isActive()) {
456
- switch (e.key) {
457
- case 'ArrowDown':
458
- e.preventDefault();
459
- moveSelectionDown();
460
- return true;
461
-
462
- case 'ArrowUp':
463
- e.preventDefault();
464
- moveSelectionUp();
465
- return true;
466
-
467
- case 'Enter':
468
- if (e.shiftKey) return false; // Shift+Enter = 换行,交给 OC
469
- e.preventDefault();
470
- selectHighlightedAgent(textarea);
471
- return true; // 阻止 OC 发送
472
-
473
- case 'Escape':
474
- e.preventDefault();
475
- setDismissed();
476
- closePanel();
477
- return true;
478
- }
479
- // 其他键(字符输入):不拦截,让 OC 继续 → handleInput → onInput
480
- return false;
481
- }
482
-
483
- // ── 2. Backspace 原子删除 @mention ──
484
- // 对标 TIM mention.ts:321-356
485
- // 整体删除 @AgentName(含 @ 符号),不保留 @,不重新弹出面板
486
- if (e.key === 'Backspace' && CL.pendingMention) {
487
- const val = textarea.value;
488
- const pos = textarea.selectionStart;
489
- const name = CL.pendingMention.name;
490
- const mention = '@' + name;
491
-
492
- const searchStart = Math.max(0, pos - mention.length - 1);
493
- const nearText = val.substring(searchStart, pos);
494
- const idx = nearText.lastIndexOf(mention);
495
-
496
- if (idx !== -1) {
497
- const mentionStart = searchStart + idx;
498
- let mentionEnd = mentionStart + mention.length;
499
- if (val[mentionEnd] === ' ') mentionEnd++; // 包含尾部空格
500
-
501
- if (pos > mentionStart && pos <= mentionEnd) {
502
- e.preventDefault();
503
- // 整体删除 @AgentName(含 @ 符号)
504
- const newVal = val.slice(0, mentionStart) + val.slice(mentionEnd);
505
-
506
- const setter = Object.getOwnPropertyDescriptor(
507
- HTMLTextAreaElement.prototype, 'value'
508
- )?.set;
509
- if (setter) setter.call(textarea, newVal);
510
- else textarea.value = newVal;
511
-
512
- // 光标放在删除位置
513
- textarea.selectionStart = textarea.selectionEnd = mentionStart;
514
- textarea.dispatchEvent(new Event('input', { bubbles: true }));
515
- CL.pendingMention = null;
516
- // @ 已被删除,状态机不会检测到 @ → 面板不会弹出
517
- return true;
518
- }
519
- }
520
- }
521
-
522
- // ── 3. Enter 发送时:标记替换(面板关闭 + 有 pendingMention)──
523
- // 这里接管原生发送,只是为了把可见 @mention 替换成 OpenAgent 内部 marker。
524
- if (e.key === 'Enter' && !e.shiftKey && CL.pendingMention) {
525
- e.preventDefault();
526
- _sendPendingMention(textarea);
527
-
528
- // 返回 true:OC 注入钩子会停止原生 Enter 发送,避免重复发送。
529
- return true;
530
- }
531
-
532
- return false;
533
- },
534
-
535
- /**
536
- * OC handleInput 钩子。
537
- * 在 OC 的 props.onDraftChange(target.value) 之后调用。
538
- */
539
- onInput(textarea) {
540
- _handleStateChange(textarea);
541
- },
542
-
543
- // ── 暴露给外部查询的辅助方法 ──
544
- active() { return isActive(); },
545
- };
546
-
547
- // ════════════════════════════════════════════════════════════════
548
- // ── 事件委托(document 级,不怕 Lit 重建)──
549
- // ════════════════════════════════════════════════════════════════
550
-
551
- // ── 输入法组合状态跟踪 ──
552
- // 对标 suggestion.ts:508 "editor.view.composing"
553
- // 输入法组合中的 @ 不应触发面板(§1.1 ⑥)
554
- document.addEventListener('compositionstart', (e) => {
555
- if (e.target?.matches?.(_CL_TEXTAREA_SELECTOR)) {
556
- setComposing(true);
557
- }
558
- });
559
- document.addEventListener('compositionend', (e) => {
560
- if (e.target?.matches?.(_CL_TEXTAREA_SELECTOR)) {
561
- setComposing(false);
562
- // 输入法确认后可能产生 @,OC 的 handleInput 会触发 onInput
563
- // 但 compositionend 在 input 之前触发,需要主动评估一次
564
- _handleStateChange(e.target);
565
- }
566
- });
567
-
568
- // ── 光标变化检测 ──
569
- // 对标 suggestion.ts:473 "选区变化也产生 transaction"
570
- // 实现 §1.3 ⑤ "点击输入框内其他位置 → 面板关闭"
571
- let _guardSelection = false;
572
- let _lastCursorPos = -1; // 追踪上次光标位置,用于判断进入 mention 的方向
573
- document.addEventListener('selectionchange', () => {
574
- const textarea = _getTextarea();
575
- if (!textarea || document.activeElement !== textarea) return;
576
- if (_guardSelection) return;
577
-
578
- // mention 光标保护(§1.6 原子标签)
579
- // 对标 TIM:左键从右侧进入 → 跳到 @ 之前;右键从左侧进入 → 跳到末尾
580
- const mentionRange = _findMentionRange(textarea.value);
581
- if (mentionRange) {
582
- const pos = textarea.selectionStart;
583
- if (pos > mentionRange.start && pos < mentionRange.end &&
584
- textarea.selectionStart === textarea.selectionEnd) {
585
- _guardSelection = true;
586
- if (_lastCursorPos >= 0 && _lastCursorPos >= mentionRange.end) {
587
- // 箭头左键从右侧进入 → 跳到 @ 之前
588
- textarea.selectionStart = textarea.selectionEnd = mentionRange.start;
589
- } else if (_lastCursorPos >= 0 && _lastCursorPos <= mentionRange.start) {
590
- // 箭头右键从左侧进入 → 跳到末尾
591
- textarea.selectionStart = textarea.selectionEnd = mentionRange.end;
592
- } else {
593
- // 鼠标点击跳入 或 _lastCursorPos 未知(-1) → 跳到最近边界
594
- const distToStart = pos - mentionRange.start;
595
- const distToEnd = mentionRange.end - pos;
596
- textarea.selectionStart = textarea.selectionEnd =
597
- distToStart <= distToEnd ? mentionRange.start : mentionRange.end;
598
- }
599
- _lastCursorPos = textarea.selectionStart;
600
- _guardSelection = false;
601
- return;
602
- }
603
- }
604
-
605
- _lastCursorPos = textarea.selectionStart;
606
- // 状态机评估(光标移走 → 可能关闭面板)
607
- _handleStateChange(textarea);
608
- });
609
-
610
- // ── mention 原子保护:阻止在 @mention 内部输入字符(§1.6)──
611
- // 事件委托:OC 不监听 beforeinput,不会冲突
612
- document.addEventListener('beforeinput', (e) => {
613
- if (!e.target?.matches?.(_CL_TEXTAREA_SELECTOR)) return;
614
- const textarea = e.target;
615
- const mentionRange = _findMentionRange(textarea.value);
616
- if (!mentionRange) return;
617
- const selStart = textarea.selectionStart;
618
- const selEnd = textarea.selectionEnd;
619
- // 编辑范围与 mention 重叠 → 阻止
620
- if (selStart > mentionRange.start && selStart < mentionRange.end) {
621
- e.preventDefault();
622
- } else if (selEnd > mentionRange.start && selEnd < mentionRange.end) {
623
- e.preventDefault();
624
- }
625
- });
626
-
627
- // ── 辅助函数 ──
628
-
629
- /**
630
- * 统一的状态变化处理:调用状态机 → 根据结果操作面板。
631
- */
632
- function _handleStateChange(textarea) {
633
- const val = textarea.value;
634
- const result = evaluateState(val, textarea.selectionStart, textarea.selectionEnd);
635
-
636
- switch (result.action) {
637
- case 'open':
638
- openPanel(textarea);
639
- break;
640
- case 'update':
641
- updatePanelSearch(textarea);
642
- break;
643
- case 'close':
644
- closePanel();
645
- break;
646
- // 'none': 不做任何操作
647
- }
648
- }
649
-
650
- /**
651
- * 找到 textarea.value 中当前 mention 的范围。
652
- * 用于原子保护(beforeinput / selectionchange)。
653
- *
654
- * @returns {{ start: number, end: number }|null}
655
- */
656
- function _findMentionRange(value) {
657
- if (!CL.pendingMention) return null;
658
- const name = CL.pendingMention.name;
659
- const mention = '@' + name;
660
- const idx = value.lastIndexOf(mention);
661
- if (idx === -1) return null;
662
- let end = idx + mention.length;
663
- if (value[end] === ' ') end++; // 包含尾部空格
664
- return { start: idx, end };
665
- }