openclaw-openagent 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.ts +114 -0
- package/openclaw.plugin.json +159 -0
- package/package.json +79 -0
- package/skills/clawlink/SKILL.md +145 -0
- package/skills/clawlink/SKILL.md.bak +165 -0
- package/src/app/channel-tools.ts +249 -0
- package/src/app/discovery-tools.ts +273 -0
- package/src/app/hooks.ts +60 -0
- package/src/app/index.ts +78 -0
- package/src/app/messaging-tools.ts +79 -0
- package/src/app/ops-tools.ts +155 -0
- package/src/app/remote-agent-tool.ts +476 -0
- package/src/app/types.ts +67 -0
- package/src/app/verbose-preflight.ts +190 -0
- package/src/auth/config.ts +197 -0
- package/src/auth/credential-manager.ts +146 -0
- package/src/auth/index.ts +24 -0
- package/src/auth/verify.ts +99 -0
- package/src/channel.ts +565 -0
- package/src/compat.ts +82 -0
- package/src/config/config-schema.ts +39 -0
- package/src/messaging/aggregator.ts +120 -0
- package/src/messaging/collector.ts +89 -0
- package/src/messaging/executor.ts +72 -0
- package/src/messaging/inbound.ts +150 -0
- package/src/messaging/index.ts +11 -0
- package/src/messaging/mention-protocol.ts +94 -0
- package/src/messaging/process-c2c-request.ts +564 -0
- package/src/messaging/process-message.ts +373 -0
- package/src/messaging/scheduler.ts +55 -0
- package/src/messaging/types.ts +38 -0
- package/src/plugin-ui/assets/agentbook-icon.svg +5 -0
- package/src/plugin-ui/assets/magic.svg +5 -0
- package/src/plugin-ui/assets/openagent-override.js +9329 -0
- package/src/plugin-ui/build.cjs +175 -0
- package/src/plugin-ui/index.ts +18 -0
- package/src/plugin-ui/modules/agent-book/panel/agent-book.js +458 -0
- package/src/plugin-ui/modules/agent-book/panel/agent-card.js +154 -0
- package/src/plugin-ui/modules/agent-book/panel/agent-data.js +644 -0
- package/src/plugin-ui/modules/agent-book/panel/inject-ui.js +456 -0
- package/src/plugin-ui/modules/agent-book/panel/mention-state.js +206 -0
- package/src/plugin-ui/modules/agent-book/panel/styles.js +670 -0
- package/src/plugin-ui/modules/agent-book/remote-agent-tool/components-core.js +293 -0
- package/src/plugin-ui/modules/agent-book/remote-agent-tool/thought-chain-card.js +208 -0
- package/src/plugin-ui/modules/agent-book/scanner.js +119 -0
- package/src/plugin-ui/modules/agent-book/travelcard/travel-cards.js +500 -0
- package/src/plugin-ui/modules/agent-book/travelcard/travel-engine.js +652 -0
- package/src/plugin-ui/modules/agent-book/travelcard/travel-styles.js +251 -0
- package/src/plugin-ui/modules/loader/bootstrap.js +38 -0
- package/src/plugin-ui/modules/loader/shared-state.js +560 -0
- package/src/plugin-ui/modules/loader/ws-intercept.js +199 -0
- package/src/plugin-ui/modules/remote-agent/chunk-parser.js +161 -0
- package/src/plugin-ui/modules/remote-agent/execution-card.js +269 -0
- package/src/plugin-ui/modules/remote-agent/markdown-renderer.js +256 -0
- package/src/plugin-ui/modules/remote-agent/native-style-adapter.js +146 -0
- package/src/plugin-ui/modules/remote-agent/output-card.js +259 -0
- package/src/plugin-ui/modules/remote-agent/progress-store.js +363 -0
- package/src/plugin-ui/modules/remote-agent/render-hooks.js +1609 -0
- package/src/plugin-ui/modules/remote-agent/styles.js +668 -0
- package/src/plugin-ui/modules/remote-agent/tool-card-model.js +56 -0
- package/src/plugin-ui/ui-extension-loader/backup.ts +92 -0
- package/src/plugin-ui/ui-extension-loader/index.ts +276 -0
- package/src/plugin-ui/ui-extension-loader/locator.ts +152 -0
- package/src/plugin-ui/ui-extension-loader/manifest.ts +57 -0
- package/src/plugin-ui/ui-extension-loader/registry-regex.ts +729 -0
- package/src/plugin-ui/ui-extension-loader/removed-extensions.ts +70 -0
- package/src/plugin-ui/ui-extension-loader/types.ts +68 -0
- package/src/proxy/auth-proxy.ts +356 -0
- package/src/runtime/account.ts +572 -0
- package/src/runtime/index.ts +7 -0
- package/src/runtime/plugin-runtime.ts +94 -0
- package/src/runtime/registry.ts +71 -0
- package/src/sdk/CLASS_MAP.md +143 -0
- package/src/sdk/index.d.ts +126 -0
- package/src/sdk/index.js +23990 -0
- package/src/sdk/modules/cloud-search-module.js +1117 -0
- package/src/sdk/modules/follow-module.js +1069 -0
- package/src/sdk/modules/group-module.js +7397 -0
- package/src/sdk/modules/relationship-module.js +2269 -0
- package/src/sdk/modules/signaling-module.js +1468 -0
- package/src/sdk/modules/tim-upload-plugin.js +730 -0
- package/src/sdk/node-env/http-request.js +90 -0
- package/src/sdk/node-env/index.js +57 -0
- package/src/sdk/node-env/storage.js +114 -0
- package/src/sdk/package.json +10 -0
- package/src/sdk/tsconfig.json +16 -0
- package/src/state/pending-invocation-store.ts +43 -0
- package/src/state/store.ts +676 -0
- package/src/tim/c2c.ts +451 -0
- package/src/tim/channels.ts +364 -0
- package/src/tim/client.ts +330 -0
- package/src/tim/index.ts +18 -0
- package/src/tim/messages.ts +166 -0
- package/src/tim/sdk-logger-init.ts +50 -0
- package/src/tools.ts +10 -0
- package/src/transport/factory.ts +95 -0
- package/src/transport/oasn/index.ts +17 -0
- package/src/transport/oasn/oasn-agent-card.ts +111 -0
- package/src/transport/oasn/oasn-discovery.ts +108 -0
- package/src/transport/oasn/oasn-files.ts +210 -0
- package/src/transport/oasn/oasn-http.ts +483 -0
- package/src/transport/oasn/oasn-invocation.ts +527 -0
- package/src/transport/oasn/oasn-normalize.ts +159 -0
- package/src/transport/oasn/oasn-register.ts +106 -0
- package/src/transport/oasn/oasn-transport.ts +341 -0
- package/src/transport/oasn/oasn-types.ts +353 -0
- package/src/transport/tim/index.ts +8 -0
- package/src/transport/tim/tim-transport.ts +515 -0
- package/src/transport/types.ts +541 -0
- package/src/types/openclaw.d.ts +97 -0
- package/src/types/tencentcloud-chat.d.ts +15 -0
- package/src/util/http.ts +113 -0
- package/src/util/logger.ts +131 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
// ── Agent Card 内嵌卡片 ──
|
|
2
|
+
|
|
3
|
+
/** 通过 agent_id / name / query 获取单个 Agent 详情 */
|
|
4
|
+
async function fetchAgentById(identifier, fallbackName) {
|
|
5
|
+
const keys = [];
|
|
6
|
+
const pushKey = (value) => {
|
|
7
|
+
const key = String(value || '').trim();
|
|
8
|
+
if (key && !keys.includes(key)) keys.push(key);
|
|
9
|
+
};
|
|
10
|
+
pushKey(identifier);
|
|
11
|
+
pushKey(fallbackName);
|
|
12
|
+
|
|
13
|
+
// 1. 先从缓存精确匹配
|
|
14
|
+
const identity = CL && typeof CL.getAgentIdentity === 'function'
|
|
15
|
+
? CL.getAgentIdentity(identifier, fallbackName)
|
|
16
|
+
: null;
|
|
17
|
+
if (identity) return identity;
|
|
18
|
+
|
|
19
|
+
const cached = CL.agentCache.find(a =>
|
|
20
|
+
keys.includes(a.id) || keys.includes(a.tag) || keys.includes(a.name)
|
|
21
|
+
);
|
|
22
|
+
if (cached) return cached;
|
|
23
|
+
|
|
24
|
+
// 2. 用列表 API 加载全量数据(如果缓存为空),按 UUID / 名字精确匹配
|
|
25
|
+
try {
|
|
26
|
+
const agents = await fetchAgents(); // 会更新 agentCache
|
|
27
|
+
const exact = agents.find(a =>
|
|
28
|
+
keys.includes(a.id) || keys.includes(a.tag) || keys.includes(a.name)
|
|
29
|
+
);
|
|
30
|
+
if (exact) return exact;
|
|
31
|
+
} catch (err) {
|
|
32
|
+
console.warn('[openagent] fetchAgentById list error:', err);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 3. 精确匹配失败 → 逐个关键词搜索。当前后端不支持 UUID 语义搜索时,
|
|
36
|
+
// fallbackName 仍能命中真实 Agent,从而拿到 avatar 等展示字段。
|
|
37
|
+
for (let i = 0; i < keys.length; i++) {
|
|
38
|
+
const key = keys[i];
|
|
39
|
+
try {
|
|
40
|
+
const res = await fetch(`${SEARCH_API}?q=${encodeURIComponent(key)}&top_k=5`);
|
|
41
|
+
if (!res.ok) continue;
|
|
42
|
+
const data = await res.json();
|
|
43
|
+
const list = (data.results || []).map(mapAgent);
|
|
44
|
+
const exact = list.find(a =>
|
|
45
|
+
keys.includes(a.id) || keys.includes(a.tag) || keys.includes(a.name)
|
|
46
|
+
);
|
|
47
|
+
if (exact) {
|
|
48
|
+
if (CL && typeof CL.storeAgentIdentity === 'function') CL.storeAgentIdentity(exact);
|
|
49
|
+
return exact;
|
|
50
|
+
}
|
|
51
|
+
if (list[0]) {
|
|
52
|
+
if (CL && typeof CL.storeAgentIdentity === 'function') CL.storeAgentIdentity(list[0]);
|
|
53
|
+
return list[0];
|
|
54
|
+
}
|
|
55
|
+
} catch (err) {
|
|
56
|
+
console.warn('[openagent] fetchAgentById search error:', err);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** 构建 Agent 卡片 DOM(使用统一 AgentCard 组件) */
|
|
63
|
+
function buildAgentCard(agent) {
|
|
64
|
+
return AgentCard(agent, {
|
|
65
|
+
mode: 'inline',
|
|
66
|
+
onAction: (a) => {
|
|
67
|
+
const textarea = document.querySelector('.agent-chat__input textarea');
|
|
68
|
+
if (textarea) {
|
|
69
|
+
const current = textarea.value;
|
|
70
|
+
textarea.value = current + (current && !current.endsWith(' ') ? ' ' : '') + `@${a.name} `;
|
|
71
|
+
textarea.focus();
|
|
72
|
+
textarea.dispatchEvent(new Event('input', { bubbles: true }));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ── 组件注册表 ──
|
|
79
|
+
|
|
80
|
+
const OPENAGENT_COMPONENTS = {};
|
|
81
|
+
|
|
82
|
+
/** 注册一个自定义组件类型 */
|
|
83
|
+
function registerComponent(type, handler) {
|
|
84
|
+
OPENAGENT_COMPONENTS[type] = handler;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 注册:Agent Card
|
|
88
|
+
registerComponent('agent-card', async (data) => {
|
|
89
|
+
const searchTerm = data.agent_id || data.agentId || data.id || data.name || data.query;
|
|
90
|
+
if (!searchTerm) return null;
|
|
91
|
+
const agent = await fetchAgentById(searchTerm);
|
|
92
|
+
return agent ? buildAgentCard(agent) : null;
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// ── 组件协议:[[type:{...}]] 和 [[trip-data:...]] ──
|
|
96
|
+
//
|
|
97
|
+
// 不再使用宿主 marked.js 扩展。统一使用 DOM 文本扫描:
|
|
98
|
+
// [[agent-card:{"name":"..."}]] → 替换为 Agent Card 组件
|
|
99
|
+
// [[trip-data:file]] → 触发 trip card 数据加载
|
|
100
|
+
// [[trip-data:<base64>]] → 内联 trip card 数据注入
|
|
101
|
+
|
|
102
|
+
/** 处理 [[trip-data:...]] 文本协议 */
|
|
103
|
+
function _clHandleTripDataSignal(payload, contextNode) {
|
|
104
|
+
if (!payload) return;
|
|
105
|
+
console.log('[trip-data] detected signal, payload:', payload.substring(0, 40));
|
|
106
|
+
|
|
107
|
+
if (payload === 'file') {
|
|
108
|
+
const chatText = contextNode && contextNode.closest
|
|
109
|
+
? contextNode.closest('.chat-text, .chat-tool-card__detail, .chat-tools-collapse__body')
|
|
110
|
+
: null;
|
|
111
|
+
if (chatText) {
|
|
112
|
+
chatText.dataset.clProcessed = '';
|
|
113
|
+
if (typeof _processCustomCards === 'function') _processCustomCards(chatText);
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
// base64 payload
|
|
117
|
+
try {
|
|
118
|
+
const cardData = JSON.parse(atob(payload));
|
|
119
|
+
if (typeof CL !== 'undefined' && typeof CL.mergeTripCardData === 'function') {
|
|
120
|
+
CL.mergeTripCardData(cardData, { replaceDayList: true });
|
|
121
|
+
} else if (typeof CL !== 'undefined') {
|
|
122
|
+
CL.tripCardData = cardData;
|
|
123
|
+
}
|
|
124
|
+
console.log('[trip-data] ✅ Card data from base64');
|
|
125
|
+
const chatText = contextNode && contextNode.closest
|
|
126
|
+
? contextNode.closest('.chat-text, .chat-tool-card__detail, .chat-tools-collapse__body')
|
|
127
|
+
: null;
|
|
128
|
+
if (chatText) {
|
|
129
|
+
chatText.dataset.clProcessed = '';
|
|
130
|
+
if (typeof _processCustomCards === 'function') _processCustomCards(chatText);
|
|
131
|
+
}
|
|
132
|
+
} catch (e) {
|
|
133
|
+
console.warn('[trip-data] ❌ base64 decode failed:', e && e.message);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** 扫描 DOM 中的组件占位(纯文本扫描,不依赖 marked) */
|
|
139
|
+
function scanForComponents(root) {
|
|
140
|
+
if (!root) return;
|
|
141
|
+
scanTextNodesForComponents(root);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** DOM 文本扫描降级方案 */
|
|
145
|
+
function scanTextNodesForComponents(root) {
|
|
146
|
+
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null);
|
|
147
|
+
const matchedNodes = [];
|
|
148
|
+
let node;
|
|
149
|
+
while (node = walker.nextNode()) {
|
|
150
|
+
if (node.textContent.includes('[[') && node.textContent.includes(']]')) {
|
|
151
|
+
if (node.parentElement?.dataset?.openagentProcessed) continue;
|
|
152
|
+
matchedNodes.push(node);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
matchedNodes.forEach(replaceComponentTextNode);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** 替换文本节点中的 [[type:{...}]] 或处理 [[trip-data:...]] */
|
|
159
|
+
function replaceComponentTextNode(textNode) {
|
|
160
|
+
const text = textNode.textContent;
|
|
161
|
+
const match = /\[\[([\w][\w-]*):(.*?)\]\]/.exec(text);
|
|
162
|
+
if (!match) return;
|
|
163
|
+
|
|
164
|
+
const type = match[1];
|
|
165
|
+
|
|
166
|
+
// ── trip-data 信号:不替换 DOM,直接处理数据 ──
|
|
167
|
+
if (type === 'trip-data') {
|
|
168
|
+
_clHandleTripDataSignal(match[2].trim(), textNode);
|
|
169
|
+
// 隐藏信号文本
|
|
170
|
+
const parentEl = textNode.parentElement;
|
|
171
|
+
if (parentEl) parentEl.style.display = 'none';
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const handler = OPENAGENT_COMPONENTS[type];
|
|
176
|
+
if (!handler) return;
|
|
177
|
+
|
|
178
|
+
let data;
|
|
179
|
+
try {
|
|
180
|
+
data = JSON.parse(match[2].trim());
|
|
181
|
+
} catch {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const parentEl = textNode.parentElement;
|
|
186
|
+
if (!parentEl) return;
|
|
187
|
+
parentEl.dataset.openagentProcessed = 'true';
|
|
188
|
+
|
|
189
|
+
const before = text.substring(0, match.index);
|
|
190
|
+
const after = text.substring(match.index + match[0].length);
|
|
191
|
+
const fragment = document.createDocumentFragment();
|
|
192
|
+
if (before.trim()) fragment.appendChild(document.createTextNode(before));
|
|
193
|
+
|
|
194
|
+
const placeholder = document.createElement('div');
|
|
195
|
+
placeholder.className = 'agent-card__loading';
|
|
196
|
+
placeholder.textContent = '🔍 Loading...';
|
|
197
|
+
fragment.appendChild(placeholder);
|
|
198
|
+
|
|
199
|
+
if (after.trim()) fragment.appendChild(document.createTextNode(after));
|
|
200
|
+
parentEl.replaceChild(fragment, textNode);
|
|
201
|
+
|
|
202
|
+
handler(data).then(component => {
|
|
203
|
+
if (component) {
|
|
204
|
+
placeholder.replaceWith(component);
|
|
205
|
+
} else {
|
|
206
|
+
placeholder.textContent = `⚠️ Component "${type}" load failed`;
|
|
207
|
+
}
|
|
208
|
+
}).catch(err => {
|
|
209
|
+
console.warn(`[openagent] Component "${type}" handler error:`, err);
|
|
210
|
+
placeholder.textContent = `⚠️ Component "${type}" error`;
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// ── 组件 Observer ──
|
|
215
|
+
|
|
216
|
+
function startComponentObserver() {
|
|
217
|
+
const chatThread = document.querySelector('.chat-thread');
|
|
218
|
+
if (!chatThread) return;
|
|
219
|
+
|
|
220
|
+
// 先扫描已有的
|
|
221
|
+
scanForComponents(chatThread);
|
|
222
|
+
|
|
223
|
+
const observer = new MutationObserver(() => {
|
|
224
|
+
scanForComponents(chatThread);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
observer.observe(chatThread, { childList: true, subtree: true });
|
|
228
|
+
|
|
229
|
+
// 周期扫描兜底(DOM mutation 可能漏掉某些 SPA 渲染)
|
|
230
|
+
setInterval(() => {
|
|
231
|
+
const thread = document.querySelector('.chat-thread');
|
|
232
|
+
if (thread) scanForComponents(thread);
|
|
233
|
+
}, 1500);
|
|
234
|
+
console.log('[openagent] ✅ Component observer started (text scan mode)');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// ── 启动(需要 DOM 就绪) ──
|
|
238
|
+
|
|
239
|
+
function bootstrap() {
|
|
240
|
+
injectStyles();
|
|
241
|
+
|
|
242
|
+
// 初次加载轮询(兼容 textarea 延迟渲染)
|
|
243
|
+
let attempts = 0;
|
|
244
|
+
const pollTimer = setInterval(() => {
|
|
245
|
+
attempts++;
|
|
246
|
+
if (tryInject() || attempts >= CL.MAX_POLL) {
|
|
247
|
+
clearInterval(pollTimer);
|
|
248
|
+
if (attempts >= CL.MAX_POLL) {
|
|
249
|
+
console.warn('[openagent] ⚠️ textarea not found after', CL.MAX_POLL, 'attempts, observer will keep watching');
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}, CL.POLL_INTERVAL);
|
|
253
|
+
|
|
254
|
+
// 持续监听 DOM 变化:无论路由切换、SPA 重渲染、还是延迟加载,
|
|
255
|
+
// 只要 textarea 出现且未注入,就自动注入 Agent Book
|
|
256
|
+
let _injectDebounce = null;
|
|
257
|
+
const routeObserver = new MutationObserver(() => {
|
|
258
|
+
// debounce 300ms 避免高频触发
|
|
259
|
+
if (_injectDebounce) clearTimeout(_injectDebounce);
|
|
260
|
+
_injectDebounce = setTimeout(() => {
|
|
261
|
+
// 如果还没注入成功,持续尝试
|
|
262
|
+
if (!document.querySelector('.openagent-at-btn')) {
|
|
263
|
+
tryInject();
|
|
264
|
+
}
|
|
265
|
+
// 检查是否需要启动 component observer
|
|
266
|
+
const thread = document.querySelector('.chat-thread');
|
|
267
|
+
if (thread && !thread._openagentCompObs) {
|
|
268
|
+
thread._openagentCompObs = true;
|
|
269
|
+
startComponentObserver();
|
|
270
|
+
}
|
|
271
|
+
}, 300);
|
|
272
|
+
});
|
|
273
|
+
if (document.body) {
|
|
274
|
+
routeObserver.observe(document.body, { childList: true, subtree: true });
|
|
275
|
+
} else {
|
|
276
|
+
const bodyWaiter = setInterval(() => {
|
|
277
|
+
if (document.body) {
|
|
278
|
+
clearInterval(bodyWaiter);
|
|
279
|
+
routeObserver.observe(document.body, { childList: true, subtree: true });
|
|
280
|
+
}
|
|
281
|
+
}, 100);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// 首次启动 component observer
|
|
285
|
+
setTimeout(() => startComponentObserver(), 2000);
|
|
286
|
+
} // end bootstrap()
|
|
287
|
+
|
|
288
|
+
// 等 DOM 就绪后再执行 bootstrap
|
|
289
|
+
if (document.readyState === 'loading') {
|
|
290
|
+
document.addEventListener('DOMContentLoaded', bootstrap);
|
|
291
|
+
} else {
|
|
292
|
+
bootstrap();
|
|
293
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
// ── Shared remote-agent visual primitives ───────────────────────────────
|
|
2
|
+
//
|
|
3
|
+
// Remote-agent ToolCard rendering is owned by remote-agent/render-hooks.js.
|
|
4
|
+
// This module keeps the shared styles/helpers and forwards WS events only.
|
|
5
|
+
|
|
6
|
+
// ── 1. CSS 注入 ──
|
|
7
|
+
|
|
8
|
+
(function injectThoughtChainStyles() {
|
|
9
|
+
if (document.getElementById('cl-tools-collapse-css')) return;
|
|
10
|
+
const style = document.createElement('style');
|
|
11
|
+
style.id = 'cl-tools-collapse-css';
|
|
12
|
+
style.textContent = `
|
|
13
|
+
/* ── Agent Pill(通用身份标签) ── */
|
|
14
|
+
.cl-agent-pill {
|
|
15
|
+
display: inline-flex;
|
|
16
|
+
align-items: center;
|
|
17
|
+
gap: 6px;
|
|
18
|
+
padding: 6px 10px;
|
|
19
|
+
border: 0.8px solid rgba(47,47,51,0.15);
|
|
20
|
+
border-radius: 6px;
|
|
21
|
+
background: #fff;
|
|
22
|
+
margin-bottom: 4px;
|
|
23
|
+
width: fit-content;
|
|
24
|
+
}
|
|
25
|
+
.cl-agent-pill-avatar {
|
|
26
|
+
width: 18px;
|
|
27
|
+
height: 18px;
|
|
28
|
+
border-radius: 50%;
|
|
29
|
+
object-fit: cover;
|
|
30
|
+
flex-shrink: 0;
|
|
31
|
+
}
|
|
32
|
+
.cl-agent-pill-name {
|
|
33
|
+
font-family: "PingFang SC", sans-serif;
|
|
34
|
+
font-size: 10px;
|
|
35
|
+
font-weight: 600;
|
|
36
|
+
color: #2F2F33;
|
|
37
|
+
white-space: nowrap;
|
|
38
|
+
line-height: 12px;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* ── ThoughtChain 步骤容器 ── */
|
|
42
|
+
.cl-thought-chain {
|
|
43
|
+
margin: 0;
|
|
44
|
+
width: 100%;
|
|
45
|
+
box-sizing: border-box;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* ── 分隔线 ── */
|
|
49
|
+
.cl-thought-chain-divider {
|
|
50
|
+
height: 0;
|
|
51
|
+
border: none;
|
|
52
|
+
border-top: 0.2px solid rgba(219, 219, 219, 1);
|
|
53
|
+
margin: 4px 0;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* ── 标题栏 (状态文字) ── */
|
|
57
|
+
.cl-tools-collapse-header {
|
|
58
|
+
display: flex;
|
|
59
|
+
align-items: center;
|
|
60
|
+
gap: 4px;
|
|
61
|
+
cursor: pointer;
|
|
62
|
+
padding: 4px 0 2px 0;
|
|
63
|
+
user-select: none;
|
|
64
|
+
}
|
|
65
|
+
.cl-tools-collapse-header:hover { opacity: 0.8; }
|
|
66
|
+
|
|
67
|
+
.cl-tools-collapse-header-icon {
|
|
68
|
+
display: inline-block;
|
|
69
|
+
font-size: 8px;
|
|
70
|
+
color: #999;
|
|
71
|
+
transition: transform 0.25s ease;
|
|
72
|
+
line-height: 1;
|
|
73
|
+
}
|
|
74
|
+
.cl-tools-collapse-header-icon.cl-collapsed { transform: rotate(-90deg); }
|
|
75
|
+
|
|
76
|
+
.cl-tools-collapse-header-title {
|
|
77
|
+
font-family: "PingFang SC", sans-serif;
|
|
78
|
+
font-size: 10px;
|
|
79
|
+
font-weight: 600;
|
|
80
|
+
color: #2F2F33;
|
|
81
|
+
line-height: 12px;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* ── 内容区 (折叠动画) ── */
|
|
85
|
+
.cl-tools-collapse-body {
|
|
86
|
+
overflow: hidden;
|
|
87
|
+
transition: max-height 0.35s ease, opacity 0.3s ease;
|
|
88
|
+
padding-left: 0;
|
|
89
|
+
}
|
|
90
|
+
.cl-tools-collapse-body.cl-expanded { max-height: 2000px; opacity: 1; }
|
|
91
|
+
.cl-tools-collapse-body.cl-collapsed { max-height: 0; opacity: 0; }
|
|
92
|
+
|
|
93
|
+
/* ── 步骤项 ── */
|
|
94
|
+
.cl-tools-collapse-step {
|
|
95
|
+
display: flex;
|
|
96
|
+
align-items: center;
|
|
97
|
+
gap: 8px;
|
|
98
|
+
padding: 3px 0;
|
|
99
|
+
position: relative;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.cl-tools-collapse-step-row {
|
|
103
|
+
display: flex;
|
|
104
|
+
align-items: center;
|
|
105
|
+
gap: 8px;
|
|
106
|
+
min-height: 16px;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* ── 步骤圆点/图标 ── */
|
|
110
|
+
.cl-tools-collapse-step-icon {
|
|
111
|
+
width: 14px;
|
|
112
|
+
height: 14px;
|
|
113
|
+
flex-shrink: 0;
|
|
114
|
+
display: flex;
|
|
115
|
+
align-items: center;
|
|
116
|
+
justify-content: center;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* 加载中:三点脉冲动画 (未完成/进行中) */
|
|
120
|
+
.cl-tools-collapse-step-dot {
|
|
121
|
+
display: flex;
|
|
122
|
+
align-items: center;
|
|
123
|
+
gap: 2px;
|
|
124
|
+
width: 14px;
|
|
125
|
+
height: 14px;
|
|
126
|
+
justify-content: center;
|
|
127
|
+
}
|
|
128
|
+
.cl-tools-collapse-step-dot::before,
|
|
129
|
+
.cl-tools-collapse-step-dot::after,
|
|
130
|
+
.cl-tools-collapse-step-dot span {
|
|
131
|
+
content: '';
|
|
132
|
+
display: block;
|
|
133
|
+
width: 3px;
|
|
134
|
+
height: 3px;
|
|
135
|
+
border-radius: 50%;
|
|
136
|
+
background: #bbb;
|
|
137
|
+
animation: cl-dot-pulse 1.2s ease-in-out infinite;
|
|
138
|
+
}
|
|
139
|
+
.cl-tools-collapse-step-dot::after { animation-delay: 0.2s; }
|
|
140
|
+
.cl-tools-collapse-step-dot span { animation-delay: 0.4s; }
|
|
141
|
+
@keyframes cl-dot-pulse {
|
|
142
|
+
0%, 80%, 100% { opacity: 0.3; transform: scale(0.8); }
|
|
143
|
+
40% { opacity: 1; transform: scale(1.1); }
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* 转圈 (当前步骤) */
|
|
147
|
+
.cl-tools-collapse-step-spinner {
|
|
148
|
+
width: 10px;
|
|
149
|
+
height: 10px;
|
|
150
|
+
border: 1.5px solid #999;
|
|
151
|
+
border-top-color: transparent;
|
|
152
|
+
border-radius: 50%;
|
|
153
|
+
animation: cl-spin 0.8s linear infinite;
|
|
154
|
+
}
|
|
155
|
+
@keyframes cl-spin { to { transform: rotate(360deg); } }
|
|
156
|
+
|
|
157
|
+
/* 完成 — 灰色实心圆点(Figma: Ellipse 12871, bg #dcdcdc) */
|
|
158
|
+
.cl-tools-collapse-step-check {
|
|
159
|
+
width: 10px;
|
|
160
|
+
height: 10px;
|
|
161
|
+
border-radius: 50%;
|
|
162
|
+
background: #dcdcdc;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/* ── 步骤标题文本 ── */
|
|
166
|
+
.cl-tools-collapse-step-title {
|
|
167
|
+
font-family: "PingFang SC", sans-serif;
|
|
168
|
+
font-size: 9.5px;
|
|
169
|
+
font-weight: 400;
|
|
170
|
+
color: #2F2F33;
|
|
171
|
+
line-height: 12.54px;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/* ── 步骤描述文本 ── */
|
|
175
|
+
.cl-tools-collapse-step-detail {
|
|
176
|
+
font-family: "PingFang SC", sans-serif;
|
|
177
|
+
font-size: 9px;
|
|
178
|
+
color: #999;
|
|
179
|
+
line-height: 12px;
|
|
180
|
+
margin-top: 1px;
|
|
181
|
+
padding-left: 22px;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
`;
|
|
185
|
+
document.head.appendChild(style);
|
|
186
|
+
})();
|
|
187
|
+
|
|
188
|
+
// ── 2. WebSocket 事件分发 ──
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
var _clWarnedMissingRenderHookHandler = false;
|
|
192
|
+
|
|
193
|
+
CL.handleDelegateWsEvent = function (msg) {
|
|
194
|
+
if (window.__cl_handleStreamEvent) {
|
|
195
|
+
window.__cl_handleStreamEvent(msg);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// RenderHook is the only remote-agent ToolCard path. Do not fall back to
|
|
200
|
+
// mutating native .chat-tool-card DOM here; missing handler should be visible
|
|
201
|
+
// during debugging instead of being masked by the legacy path.
|
|
202
|
+
if (!_clWarnedMissingRenderHookHandler && msg && msg.stream === 'tool') {
|
|
203
|
+
console.warn('[cl-ws-gate] RenderHook stream handler missing; remote-agent tool event ignored');
|
|
204
|
+
_clWarnedMissingRenderHookHandler = true;
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
// JSON.parse 拦截已移至 IIFE 顶部(在 bundle 加载前执行)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// ── OpenAgent DOM Scanner — lightweight non-ToolCard DOM helpers ──
|
|
2
|
+
//
|
|
3
|
+
// RenderHook is the only supported rendering path for remote-agent ToolCards.
|
|
4
|
+
// This scanner must not decorate, restore, or replace call_remote_agent cards.
|
|
5
|
+
|
|
6
|
+
// CL.clScanCount lives on CL.clScanCount
|
|
7
|
+
|
|
8
|
+
function _clScan() {
|
|
9
|
+
CL.clScanCount++;
|
|
10
|
+
|
|
11
|
+
// ── 0.5 清理标记 → 显示为 @agentName ──
|
|
12
|
+
// 新格式: {openagent_call_remote_agent:agent_id="...",agent_name="...",instruction="..."}
|
|
13
|
+
// 旧格式: {openagent:agent_id="...",agent_name="..."}
|
|
14
|
+
// 两种都要匹配清理;这里只处理普通聊天气泡,不处理 ToolCard。
|
|
15
|
+
var CL_MENTION_STYLE = '';
|
|
16
|
+
document.querySelectorAll('.chat-bubble').forEach(function(el) {
|
|
17
|
+
if (el.dataset.clMarkerCleaned) return;
|
|
18
|
+
if (el.closest('.chat-tool-card, .chat-tools-collapse, details')) return;
|
|
19
|
+
|
|
20
|
+
var text = el.textContent || '';
|
|
21
|
+
var rawRegex = /\{openagent(?:_call_remote_agent)?:agent_id="([^"]*)",agent_name="([^"]*)"(?:,instruction="[^"]*")?\}\s*/g;
|
|
22
|
+
if (!rawRegex.test(text)) return;
|
|
23
|
+
|
|
24
|
+
if (el.querySelector('.cl-remote-agent-card')) {
|
|
25
|
+
el.dataset.clMarkerCleaned = '1';
|
|
26
|
+
return; // 包含活 DOM,跳过 innerHTML 重写
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
var htmlRegex = /\{openagent(?:_call_remote_agent)?:agent_id=(?:"|")([^&"]*)(?:"|"),agent_name=(?:"|")([^&"]*)(?:"|")(?:,instruction=(?:"|")[^&"]*(?:"|"))?\}\s*/g;
|
|
30
|
+
if (CL && typeof CL.diagHtmlRewrite === 'function') {
|
|
31
|
+
CL.diagHtmlRewrite('scanner.marker.before', el, { scan: CL.clScanCount });
|
|
32
|
+
}
|
|
33
|
+
el.innerHTML = el.innerHTML.replace(htmlRegex, function(_, agentId, agentName) {
|
|
34
|
+
var safeAgentId = escHtml(agentId);
|
|
35
|
+
var safeAgentName = escHtml(agentName);
|
|
36
|
+
return '<code style="' + CL_MENTION_STYLE + '" data-agent-id="' + safeAgentId + '" data-agent-name="' + safeAgentName + '">@' + safeAgentName + '</code> ';
|
|
37
|
+
});
|
|
38
|
+
if (CL && typeof CL.diagHtmlRewrite === 'function') {
|
|
39
|
+
CL.diagHtmlRewrite('scanner.marker.after', el, { scan: CL.clScanCount });
|
|
40
|
+
}
|
|
41
|
+
el.dataset.clMarkerCleaned = '1';
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// ── 0.6 LLM 回复中的 agent 名称也加底色标签 ──
|
|
45
|
+
var mentionedAgents = [];
|
|
46
|
+
document.querySelectorAll('code').forEach(function(c) {
|
|
47
|
+
var t = (c.textContent || '').trim();
|
|
48
|
+
if (t.startsWith('@') && t.length > 1) {
|
|
49
|
+
var name = t.substring(1);
|
|
50
|
+
if (mentionedAgents.indexOf(name) === -1) mentionedAgents.push(name);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
if (CL.agentCache.length > 0) {
|
|
55
|
+
for (var ai = 0; ai < CL.agentCache.length; ai++) {
|
|
56
|
+
var n = CL.agentCache[ai].name;
|
|
57
|
+
if (n && n.length >= 2 && mentionedAgents.indexOf(n) === -1) mentionedAgents.push(n);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (mentionedAgents.length > 0) {
|
|
62
|
+
document.querySelectorAll('.chat-bubble').forEach(function(el) {
|
|
63
|
+
if (el.dataset.clAgentStyled) return;
|
|
64
|
+
if (el.closest('.chat-tool-card, .chat-tools-collapse, details')) return;
|
|
65
|
+
|
|
66
|
+
var text = el.textContent || '';
|
|
67
|
+
var html = el.innerHTML;
|
|
68
|
+
var changed = false;
|
|
69
|
+
for (var mi = 0; mi < mentionedAgents.length; mi++) {
|
|
70
|
+
var aName = mentionedAgents[mi];
|
|
71
|
+
if (html.includes('>@' + aName + '</code>')) continue;
|
|
72
|
+
if (!text.includes(aName)) continue;
|
|
73
|
+
|
|
74
|
+
var escaped = aName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
75
|
+
var quoteWrap = new RegExp(
|
|
76
|
+
'(?:[\u201C\u201D"\u300C]|")' + escaped + '(?:[\u201C\u201D"\u300D]|")',
|
|
77
|
+
'g'
|
|
78
|
+
);
|
|
79
|
+
var newHtml = html.replace(quoteWrap, '<code>@' + aName + '</code>');
|
|
80
|
+
if (newHtml !== html) {
|
|
81
|
+
html = newHtml;
|
|
82
|
+
changed = true;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// 包含活 DOM 时不重写 innerHTML
|
|
86
|
+
if (el.querySelector('.cl-remote-agent-card')) {
|
|
87
|
+
el.dataset.clAgentStyled = '1';
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (changed && CL && typeof CL.diagHtmlRewrite === 'function') {
|
|
91
|
+
CL.diagHtmlRewrite('scanner.agent-style.before', el, {
|
|
92
|
+
scan: CL.clScanCount,
|
|
93
|
+
agents: mentionedAgents
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
if (changed) el.innerHTML = html;
|
|
97
|
+
if (changed && CL && typeof CL.diagHtmlRewrite === 'function') {
|
|
98
|
+
CL.diagHtmlRewrite('scanner.agent-style.after', el, {
|
|
99
|
+
scan: CL.clScanCount,
|
|
100
|
+
agents: mentionedAgents
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
el.dataset.clAgentStyled = '1';
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Remote-agent ToolCards intentionally have no scanner fallback.
|
|
108
|
+
// call/result cards, thought steps, completion state, and Agent pill rendering
|
|
109
|
+
// must all come from remote-agent/render-hooks.js.
|
|
110
|
+
|
|
111
|
+
// Travel card tags (<tr_card>, <sh_card>, etc.) are processed exclusively
|
|
112
|
+
// by the RenderHook pipeline via _clScheduleOutputCardProcessing in output-card.js.
|
|
113
|
+
// No scanner polling needed.
|
|
114
|
+
|
|
115
|
+
// Continue scanning for 2 minutes (mention cleanup only).
|
|
116
|
+
if (CL.clScanCount < 60) {
|
|
117
|
+
setTimeout(_clScan, 2000);
|
|
118
|
+
}
|
|
119
|
+
}
|