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,256 @@
|
|
|
1
|
+
// ── OpenAgent Markdown Renderer — 安全的 line-based markdown → HTML 转换 ──
|
|
2
|
+
//
|
|
3
|
+
// 不依赖宿主 marked.js,也不依赖 window.__openagentMarked。
|
|
4
|
+
// 只支持 remote agent output 需要的 markdown 子集。
|
|
5
|
+
// 所有输出经过白名单 sanitize,防止 XSS。
|
|
6
|
+
//
|
|
7
|
+
// 支持的语法:
|
|
8
|
+
// # / ## / ### / #### → 标题
|
|
9
|
+
// - item / * item → 无序列表
|
|
10
|
+
// 1. item → 有序列表
|
|
11
|
+
// **bold** → 粗体
|
|
12
|
+
// *italic* → 斜体
|
|
13
|
+
// `code` → 行内代码
|
|
14
|
+
// ```lang ... ``` → fenced code block
|
|
15
|
+
// [text](url) → 链接(仅 http/https/mailto)
|
|
16
|
+
// --- → 水平分隔线
|
|
17
|
+
// 段落 / 换行
|
|
18
|
+
//
|
|
19
|
+
// OpenAgent travel card tags(sh_card, tr_card 等)作为 literal custom tags 保留。
|
|
20
|
+
// la_card 是住宿区域卡片占位,目前不再展示,渲染前静默剔除。
|
|
21
|
+
|
|
22
|
+
// ── 安全工具 ──
|
|
23
|
+
|
|
24
|
+
var _CL_SAFE_PROTOCOLS = ['http:', 'https:', 'mailto:'];
|
|
25
|
+
|
|
26
|
+
// OpenAgent travel card tag 白名单
|
|
27
|
+
var _CL_TRAVEL_TAGS = ['sh_card', 'tr_card', 'i_card', 'la_card', 'fr_card', 'bg_card', 'time_data_card'];
|
|
28
|
+
|
|
29
|
+
function _clStripSuppressedTravelTags(text) {
|
|
30
|
+
return String(text || '')
|
|
31
|
+
.replace(/<la_card(?:\s[^>]*)?>[\s\S]*?<\/la_card>/gi, '')
|
|
32
|
+
.replace(/<la_card(?:\s[\s\S]*?)?>[\s\S]*?<\/la_card>/gi, '')
|
|
33
|
+
.replace(/<la_card(?:\s[^>]*)?\/?>/gi, '')
|
|
34
|
+
.replace(/<la_card(?:\s[\s\S]*?)?\/?>/gi, '');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function _clEscapeHtml(str) {
|
|
38
|
+
return str
|
|
39
|
+
.replace(/&/g, '&')
|
|
40
|
+
.replace(/</g, '<')
|
|
41
|
+
.replace(/>/g, '>')
|
|
42
|
+
.replace(/"/g, '"')
|
|
43
|
+
.replace(/'/g, ''');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function _clIsSafeUrl(url) {
|
|
47
|
+
try {
|
|
48
|
+
var parsed = new URL(url, 'https://placeholder.invalid');
|
|
49
|
+
return _CL_SAFE_PROTOCOLS.indexOf(parsed.protocol) >= 0;
|
|
50
|
+
} catch (e) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ── inline markdown 解析 ──
|
|
56
|
+
|
|
57
|
+
function _clRenderInlineMarkdown(text) {
|
|
58
|
+
// 先 escape HTML
|
|
59
|
+
var escaped = _clEscapeHtml(text);
|
|
60
|
+
|
|
61
|
+
// inline code: `code` — 最先处理,code 内部不做进一步解析
|
|
62
|
+
escaped = escaped.replace(/`([^`]+)`/g, '<code>$1</code>');
|
|
63
|
+
|
|
64
|
+
// bold: **text**
|
|
65
|
+
escaped = escaped.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
|
|
66
|
+
|
|
67
|
+
// italic: *text* (不匹配 ** 开头的)
|
|
68
|
+
escaped = escaped.replace(/(?<!\*)\*([^*]+)\*(?!\*)/g, '<em>$1</em>');
|
|
69
|
+
|
|
70
|
+
// links: [text](url) — 只允许安全协议
|
|
71
|
+
escaped = escaped.replace(/\[([^\]]+)\]\(([^)]+)\)/g, function(match, linkText, url) {
|
|
72
|
+
// url 已经被 escape 过,需要还原来检查协议
|
|
73
|
+
var rawUrl = url.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, "'");
|
|
74
|
+
if (_clIsSafeUrl(rawUrl)) {
|
|
75
|
+
return '<a href="' + _clEscapeHtml(rawUrl) + '" target="_blank" rel="noopener noreferrer">' + linkText + '</a>';
|
|
76
|
+
}
|
|
77
|
+
return linkText;
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
return escaped;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ── 主渲染函数 ──
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 将 markdown 文本转换为安全的 HTML 字符串。
|
|
87
|
+
*
|
|
88
|
+
* OpenAgent travel card tags 作为 literal DOM 标签保留(无属性透传),
|
|
89
|
+
* 供 travel-engine.js 后续处理。
|
|
90
|
+
*
|
|
91
|
+
* @param {string} text - 原始 markdown 文本
|
|
92
|
+
* @returns {string} 安全的 HTML 字符串
|
|
93
|
+
*/
|
|
94
|
+
function _clRenderOutputMarkdown(text) {
|
|
95
|
+
if (!text || typeof text !== 'string') return '';
|
|
96
|
+
text = _clStripSuppressedTravelTags(text);
|
|
97
|
+
|
|
98
|
+
// ── 第 1 步:提取并保护 OpenAgent travel card tags ──
|
|
99
|
+
// 将 <tag_name>...</tag_name> 替换为占位符,防止被 HTML escape
|
|
100
|
+
var travelPlaceholders = [];
|
|
101
|
+
var tagPattern = new RegExp(
|
|
102
|
+
'<(' + _CL_TRAVEL_TAGS.join('|') + ')(?:\\s[^>]*)?>([\\s\\S]*?)<\\/\\1>',
|
|
103
|
+
'gi'
|
|
104
|
+
);
|
|
105
|
+
var protectedText = text.replace(tagPattern, function(match, tagName, innerContent) {
|
|
106
|
+
var idx = travelPlaceholders.length;
|
|
107
|
+
// 保留 tag name(小写),escape inner content,不透传任何 attribute
|
|
108
|
+
var safeTag = '<' + tagName.toLowerCase() + '>' + _clEscapeHtml(innerContent) + '</' + tagName.toLowerCase() + '>';
|
|
109
|
+
travelPlaceholders.push(safeTag);
|
|
110
|
+
return '\x00CL_TRAVEL_' + idx + '\x00';
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// 也处理 self-closing 或空 travel tags: <tag_name> 或 <tag_name/>
|
|
114
|
+
var selfClosePattern = new RegExp(
|
|
115
|
+
'<(' + _CL_TRAVEL_TAGS.join('|') + ')(?:\\s[^>]*)?\\/?>',
|
|
116
|
+
'gi'
|
|
117
|
+
);
|
|
118
|
+
protectedText = protectedText.replace(selfClosePattern, function(match, tagName) {
|
|
119
|
+
var idx = travelPlaceholders.length;
|
|
120
|
+
travelPlaceholders.push('<' + tagName.toLowerCase() + '></' + tagName.toLowerCase() + '>');
|
|
121
|
+
return '\x00CL_TRAVEL_' + idx + '\x00';
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// ── 第 2 步:逐行解析 markdown ──
|
|
125
|
+
var lines = protectedText.split('\n');
|
|
126
|
+
var html = [];
|
|
127
|
+
var inCodeBlock = false;
|
|
128
|
+
var codeBlockContent = [];
|
|
129
|
+
var codeBlockLang = '';
|
|
130
|
+
var inList = false;
|
|
131
|
+
var listType = ''; // 'ul' or 'ol'
|
|
132
|
+
var paragraphLines = [];
|
|
133
|
+
|
|
134
|
+
function flushParagraph() {
|
|
135
|
+
if (paragraphLines.length > 0) {
|
|
136
|
+
var combined = paragraphLines.join('<br>');
|
|
137
|
+
html.push('<p>' + combined + '</p>');
|
|
138
|
+
paragraphLines = [];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function flushList() {
|
|
143
|
+
if (inList) {
|
|
144
|
+
html.push('</' + listType + '>');
|
|
145
|
+
inList = false;
|
|
146
|
+
listType = '';
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
for (var i = 0; i < lines.length; i++) {
|
|
151
|
+
var line = lines[i];
|
|
152
|
+
|
|
153
|
+
// fenced code block: ``` or ```lang
|
|
154
|
+
if (/^```/.test(line)) {
|
|
155
|
+
if (!inCodeBlock) {
|
|
156
|
+
flushParagraph();
|
|
157
|
+
flushList();
|
|
158
|
+
inCodeBlock = true;
|
|
159
|
+
codeBlockLang = line.replace(/^```\s*/, '').trim();
|
|
160
|
+
codeBlockContent = [];
|
|
161
|
+
continue;
|
|
162
|
+
} else {
|
|
163
|
+
// end of code block — content is already collected and will be escaped
|
|
164
|
+
var codeHtml = _clEscapeHtml(codeBlockContent.join('\n'));
|
|
165
|
+
if (codeBlockLang) {
|
|
166
|
+
html.push('<pre><code class="language-' + _clEscapeHtml(codeBlockLang) + '">' + codeHtml + '</code></pre>');
|
|
167
|
+
} else {
|
|
168
|
+
html.push('<pre><code>' + codeHtml + '</code></pre>');
|
|
169
|
+
}
|
|
170
|
+
inCodeBlock = false;
|
|
171
|
+
codeBlockContent = [];
|
|
172
|
+
codeBlockLang = '';
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (inCodeBlock) {
|
|
178
|
+
codeBlockContent.push(line);
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// horizontal rule: --- or *** or ___
|
|
183
|
+
if (/^(\s*[-*_]\s*){3,}$/.test(line)) {
|
|
184
|
+
flushParagraph();
|
|
185
|
+
flushList();
|
|
186
|
+
html.push('<hr>');
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// headings: # to ####
|
|
191
|
+
var headingMatch = /^(#{1,4})\s+(.+)$/.exec(line);
|
|
192
|
+
if (headingMatch) {
|
|
193
|
+
flushParagraph();
|
|
194
|
+
flushList();
|
|
195
|
+
var level = headingMatch[1].length;
|
|
196
|
+
html.push('<h' + level + '>' + _clRenderInlineMarkdown(headingMatch[2]) + '</h' + level + '>');
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// unordered list: - item or * item
|
|
201
|
+
var ulMatch = /^(\s*)[-*]\s+(.+)$/.exec(line);
|
|
202
|
+
if (ulMatch) {
|
|
203
|
+
flushParagraph();
|
|
204
|
+
if (!inList || listType !== 'ul') {
|
|
205
|
+
flushList();
|
|
206
|
+
html.push('<ul>');
|
|
207
|
+
inList = true;
|
|
208
|
+
listType = 'ul';
|
|
209
|
+
}
|
|
210
|
+
html.push('<li>' + _clRenderInlineMarkdown(ulMatch[2]) + '</li>');
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// ordered list: 1. item
|
|
215
|
+
var olMatch = /^(\s*)\d+\.\s+(.+)$/.exec(line);
|
|
216
|
+
if (olMatch) {
|
|
217
|
+
flushParagraph();
|
|
218
|
+
if (!inList || listType !== 'ol') {
|
|
219
|
+
flushList();
|
|
220
|
+
html.push('<ol>');
|
|
221
|
+
inList = true;
|
|
222
|
+
listType = 'ol';
|
|
223
|
+
}
|
|
224
|
+
html.push('<li>' + _clRenderInlineMarkdown(olMatch[2]) + '</li>');
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// empty line → flush paragraph and list
|
|
229
|
+
if (line.trim() === '') {
|
|
230
|
+
flushParagraph();
|
|
231
|
+
flushList();
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// regular text → accumulate into paragraph
|
|
236
|
+
flushList();
|
|
237
|
+
paragraphLines.push(_clRenderInlineMarkdown(line));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// handle unclosed code block
|
|
241
|
+
if (inCodeBlock) {
|
|
242
|
+
var remainingCode = _clEscapeHtml(codeBlockContent.join('\n'));
|
|
243
|
+
html.push('<pre><code>' + remainingCode + '</code></pre>');
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
flushParagraph();
|
|
247
|
+
flushList();
|
|
248
|
+
|
|
249
|
+
// ── 第 3 步:还原 travel card 占位符 ──
|
|
250
|
+
var result = html.join('\n');
|
|
251
|
+
for (var j = 0; j < travelPlaceholders.length; j++) {
|
|
252
|
+
result = result.replace('\x00CL_TRAVEL_' + j + '\x00', travelPlaceholders[j]);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return result;
|
|
256
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// ── OpenAgent Native Style Adapter ──
|
|
2
|
+
//
|
|
3
|
+
// 从当前页面的 OpenClaw 原生 ToolCard 读取尺寸参数,
|
|
4
|
+
// 写入 CSS variables 供 OpenAgent 折叠态消费。
|
|
5
|
+
//
|
|
6
|
+
// 颜色不做 computed style,继续使用 OpenClaw 宿主 CSS variables(var(--card) 等)。
|
|
7
|
+
// 只读取:padding, borderRadius, gap, minHeight, fontSize, lineHeight。
|
|
8
|
+
|
|
9
|
+
(function _clInitNativeStyleAdapter() {
|
|
10
|
+
var _clNativeStyleSampled = false;
|
|
11
|
+
|
|
12
|
+
var SHELL_SELECTORS = [
|
|
13
|
+
'.chat-tool-card:not(:has(.cl-remote-agent-card))',
|
|
14
|
+
'.chat-tool-msg-collapse:not(:has(.cl-remote-agent-card))',
|
|
15
|
+
'.chat-tools-collapse:not(:has(.cl-remote-agent-card))'
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
var SUMMARY_SELECTORS = [
|
|
19
|
+
'.chat-tool-card__header',
|
|
20
|
+
'.chat-tool-card__title',
|
|
21
|
+
'.chat-tool-msg-summary:not(.cl-remote-agent-host-shell *)',
|
|
22
|
+
'.chat-tools-summary:not(.cl-remote-agent-host-shell *)'
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
var INDENT_SELECTORS = [
|
|
26
|
+
'.chat-group.tool:not(:has(.cl-remote-agent-card))',
|
|
27
|
+
'.chat-bubble:not(:has(.cl-remote-agent-card))'
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
// Fallback 默认值(和当前 styles.js 折叠态写死值保持一致,避免跳变)
|
|
31
|
+
var FALLBACKS = {
|
|
32
|
+
'--cl-native-shell-padding': '8px',
|
|
33
|
+
'--cl-native-shell-radius': '14px',
|
|
34
|
+
'--cl-native-shell-gap': '4px',
|
|
35
|
+
'--cl-native-summary-padding': '6px 10px',
|
|
36
|
+
'--cl-native-summary-radius': '10px',
|
|
37
|
+
'--cl-native-summary-gap': '6px',
|
|
38
|
+
'--cl-native-summary-min-height': '34px',
|
|
39
|
+
'--cl-native-summary-font-size': '13px',
|
|
40
|
+
'--cl-native-summary-line-height': '1.2',
|
|
41
|
+
'--cl-native-tool-indent': '0px'
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
function findFirst(selectors) {
|
|
45
|
+
for (var i = 0; i < selectors.length; i++) {
|
|
46
|
+
var el = document.querySelector(selectors[i]);
|
|
47
|
+
if (el) return el;
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function readSize(cs, prop) {
|
|
53
|
+
var val = cs ? cs[prop] : '';
|
|
54
|
+
// 过滤无意义值
|
|
55
|
+
if (!val || val === 'auto' || val === 'none' || val === 'normal' || val === '0px') return '';
|
|
56
|
+
return val;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function setVar(name, value) {
|
|
60
|
+
try {
|
|
61
|
+
document.documentElement.style.setProperty(name, value);
|
|
62
|
+
} catch (err) {}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function applyFallbacks() {
|
|
66
|
+
for (var key in FALLBACKS) {
|
|
67
|
+
if (Object.prototype.hasOwnProperty.call(FALLBACKS, key)) {
|
|
68
|
+
setVar(key, FALLBACKS[key]);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
console.log('[cl-adapter] applied fallback native style variables');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function sampleFromDOM() {
|
|
75
|
+
if (_clNativeStyleSampled) return;
|
|
76
|
+
|
|
77
|
+
var shellEl = findFirst(SHELL_SELECTORS);
|
|
78
|
+
var summaryEl = findFirst(SUMMARY_SELECTORS);
|
|
79
|
+
|
|
80
|
+
if (!shellEl && !summaryEl) {
|
|
81
|
+
// 页面没有原生 ToolCard,使用 fallback
|
|
82
|
+
applyFallbacks();
|
|
83
|
+
// 不设 _clNativeStyleSampled = true,下次 render hook 再尝试
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
_clNativeStyleSampled = true;
|
|
88
|
+
|
|
89
|
+
if (shellEl) {
|
|
90
|
+
var scs = getComputedStyle(shellEl);
|
|
91
|
+
setVar('--cl-native-shell-padding', readSize(scs, 'padding') || FALLBACKS['--cl-native-shell-padding']);
|
|
92
|
+
setVar('--cl-native-shell-radius', readSize(scs, 'borderRadius') || FALLBACKS['--cl-native-shell-radius']);
|
|
93
|
+
setVar('--cl-native-shell-gap', readSize(scs, 'gap') || FALLBACKS['--cl-native-shell-gap']);
|
|
94
|
+
console.log('[cl-adapter] sampled shell from ' + (shellEl.className || '').substring(0, 40));
|
|
95
|
+
} else {
|
|
96
|
+
setVar('--cl-native-shell-padding', FALLBACKS['--cl-native-shell-padding']);
|
|
97
|
+
setVar('--cl-native-shell-radius', FALLBACKS['--cl-native-shell-radius']);
|
|
98
|
+
setVar('--cl-native-shell-gap', FALLBACKS['--cl-native-shell-gap']);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (summaryEl) {
|
|
102
|
+
var hcs = getComputedStyle(summaryEl);
|
|
103
|
+
setVar('--cl-native-summary-padding', readSize(hcs, 'padding') || FALLBACKS['--cl-native-summary-padding']);
|
|
104
|
+
setVar('--cl-native-summary-radius', readSize(hcs, 'borderRadius') || FALLBACKS['--cl-native-summary-radius']);
|
|
105
|
+
setVar('--cl-native-summary-gap', readSize(hcs, 'gap') || FALLBACKS['--cl-native-summary-gap']);
|
|
106
|
+
setVar('--cl-native-summary-min-height', readSize(hcs, 'minHeight') || FALLBACKS['--cl-native-summary-min-height']);
|
|
107
|
+
setVar('--cl-native-summary-font-size', readSize(hcs, 'fontSize') || FALLBACKS['--cl-native-summary-font-size']);
|
|
108
|
+
setVar('--cl-native-summary-line-height', readSize(hcs, 'lineHeight') || FALLBACKS['--cl-native-summary-line-height']);
|
|
109
|
+
console.log('[cl-adapter] sampled summary from ' + (summaryEl.className || '').substring(0, 40));
|
|
110
|
+
} else {
|
|
111
|
+
setVar('--cl-native-summary-padding', FALLBACKS['--cl-native-summary-padding']);
|
|
112
|
+
setVar('--cl-native-summary-radius', FALLBACKS['--cl-native-summary-radius']);
|
|
113
|
+
setVar('--cl-native-summary-gap', FALLBACKS['--cl-native-summary-gap']);
|
|
114
|
+
setVar('--cl-native-summary-min-height', FALLBACKS['--cl-native-summary-min-height']);
|
|
115
|
+
setVar('--cl-native-summary-font-size', FALLBACKS['--cl-native-summary-font-size']);
|
|
116
|
+
setVar('--cl-native-summary-line-height', FALLBACKS['--cl-native-summary-line-height']);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ── 缩进采样 ──
|
|
120
|
+
var indentEl = findFirst(INDENT_SELECTORS);
|
|
121
|
+
if (indentEl) {
|
|
122
|
+
// 二次确认不含 OpenAgent card(防止 :has() 时序问题)
|
|
123
|
+
if (indentEl.querySelector && indentEl.querySelector('.cl-remote-agent-card')) {
|
|
124
|
+
indentEl = null;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (indentEl) {
|
|
128
|
+
var ics = getComputedStyle(indentEl);
|
|
129
|
+
var plVal = parseFloat(ics.paddingLeft) || 0;
|
|
130
|
+
var mlVal = parseFloat(ics.marginLeft) || 0;
|
|
131
|
+
var indent = Math.max(plVal, mlVal);
|
|
132
|
+
setVar('--cl-native-tool-indent', indent > 0 ? indent + 'px' : '0px');
|
|
133
|
+
console.log('[cl-adapter] sampled indent=' + indent + 'px from ' + (indentEl.className || '').substring(0, 40));
|
|
134
|
+
} else {
|
|
135
|
+
setVar('--cl-native-tool-indent', FALLBACKS['--cl-native-tool-indent']);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// 暴露为全局函数,render hook 首次命中 remote-agent 时 lazy 调用
|
|
140
|
+
window._clEnsureNativeToolStyleVars = sampleFromDOM;
|
|
141
|
+
|
|
142
|
+
// 写入 fallback(确保即使未采样也有值可用)
|
|
143
|
+
applyFallbacks();
|
|
144
|
+
|
|
145
|
+
console.log('[cl-adapter] ✅ native style adapter initialized');
|
|
146
|
+
})();
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
// ── OpenAgent Output Card — Agent 完成态 UI ──
|
|
2
|
+
//
|
|
3
|
+
// 输入: OpenAgentCardModel (from tool-card-model.js), onOpenSidebar callback
|
|
4
|
+
// 输出: DOM Node (嵌入 Lit template)
|
|
5
|
+
//
|
|
6
|
+
// 显示内容:
|
|
7
|
+
// - Agent Pill(头像 + @名称)
|
|
8
|
+
// - 分隔线
|
|
9
|
+
// - 最终结果(如有)
|
|
10
|
+
//
|
|
11
|
+
// UI 结构复用 shared thought-chain styles;数据来源是 RenderHook model。
|
|
12
|
+
|
|
13
|
+
function _clRenderOutputCard(model, onOpenSidebar) {
|
|
14
|
+
// ── 从 chunks 提取最终结果。SSE 思考链只由 call/execution card 展示,避免重复。──
|
|
15
|
+
var resultTexts = [];
|
|
16
|
+
for (var i = 0; i < model.chunks.length; i++) {
|
|
17
|
+
var chunk = model.chunks[i];
|
|
18
|
+
if (chunk.type === 'result') {
|
|
19
|
+
if (_clShouldRenderOutputText(chunk.text)) resultTexts.push(chunk.text);
|
|
20
|
+
} else if (chunk.type === 'text') {
|
|
21
|
+
// 远端 Agent 最终回复是普通文本(toolResult(reply.content)),
|
|
22
|
+
// chunk-parser 归为 type='text',也应作为最终结果显示
|
|
23
|
+
if (_clShouldRenderOutputText(chunk.text)) resultTexts.push(chunk.text);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (resultTexts.length === 0) {
|
|
28
|
+
var placeholder = document.createElement('span');
|
|
29
|
+
placeholder.className = 'cl-remote-agent-card cl-remote-agent-output-placeholder';
|
|
30
|
+
placeholder.style.display = 'none';
|
|
31
|
+
placeholder.__clEmptyOutput = true;
|
|
32
|
+
if (model.toolCallId) {
|
|
33
|
+
placeholder.dataset.clCardTcid = model.toolCallId;
|
|
34
|
+
}
|
|
35
|
+
console.log('[cl-output] hidden empty output tcid=' + String(model.toolCallId || '').substring(0, 8));
|
|
36
|
+
return placeholder;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
var container = document.createElement('div');
|
|
40
|
+
container.className = 'cl-remote-agent-card cl-thought-chain cl-output-card cl-output-card--pending';
|
|
41
|
+
container.dataset.clCardRole = 'output';
|
|
42
|
+
if (CL && typeof CL.diagMarkCard === 'function') {
|
|
43
|
+
CL.diagMarkCard(container, 'output', { tcid: model.toolCallId || '' });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
var shellHeader = _clCreateOpenAgentHeader(model);
|
|
47
|
+
container.appendChild(shellHeader);
|
|
48
|
+
if (typeof _clSetOpenAgentHeaderAgent === 'function') {
|
|
49
|
+
_clSetOpenAgentHeaderAgent(container, model.agentName || model.agentId || 'Remote Agent');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
var content = document.createElement('div');
|
|
53
|
+
content.className = 'cl-thought-chain-content';
|
|
54
|
+
container.appendChild(content);
|
|
55
|
+
|
|
56
|
+
// ── Agent Pill ──
|
|
57
|
+
var pillSlot = document.createElement('div');
|
|
58
|
+
pillSlot.className = 'cl-thought-chain-pill-slot';
|
|
59
|
+
var pill = _clCreateAgentPill(model.agentName, model.agentId, model.agentAvatar);
|
|
60
|
+
pillSlot.appendChild(pill);
|
|
61
|
+
content.appendChild(pillSlot);
|
|
62
|
+
|
|
63
|
+
// ── 分隔线 ──
|
|
64
|
+
var divider = document.createElement('div');
|
|
65
|
+
divider.className = 'cl-thought-chain-divider';
|
|
66
|
+
content.appendChild(divider);
|
|
67
|
+
|
|
68
|
+
// ── 最终结果展示 ──
|
|
69
|
+
if (resultTexts.length > 0) {
|
|
70
|
+
var resultSection = document.createElement('div');
|
|
71
|
+
resultSection.className = 'cl-output-result cl-output-result--pending';
|
|
72
|
+
resultSection.dataset.clRenderState = 'pending';
|
|
73
|
+
var resultText = resultTexts.join('\n\n');
|
|
74
|
+
resultSection.dataset.clToolCallId = model.toolCallId || '';
|
|
75
|
+
|
|
76
|
+
if (CL && typeof CL.extractTripCardRefsFromText === 'function') {
|
|
77
|
+
var refs = CL.extractTripCardRefsFromText(resultText);
|
|
78
|
+
resultSection.dataset.clRouteIds = refs.routeIds.join(',');
|
|
79
|
+
resultSection.dataset.clCardRefs = refs.cardRefs.join(',');
|
|
80
|
+
resultSection.dataset.clCardIds = refs.cardIds.join(',');
|
|
81
|
+
resultSection.dataset.clUnitIds = refs.unitIds.join(',');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// 使用 OpenAgent 自有 markdown renderer(不依赖宿主 marked.js)
|
|
85
|
+
if (typeof _clRenderOutputMarkdown === 'function') {
|
|
86
|
+
try {
|
|
87
|
+
resultSection.innerHTML = _clRenderOutputMarkdown(resultText);
|
|
88
|
+
} catch (e) {
|
|
89
|
+
resultSection.textContent = resultText;
|
|
90
|
+
}
|
|
91
|
+
} else {
|
|
92
|
+
resultSection.textContent = resultText;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
content.appendChild(resultSection);
|
|
96
|
+
_clScheduleOutputCardProcessing(resultSection, model);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ── 错误状态 ──
|
|
100
|
+
if (model.isError) {
|
|
101
|
+
container.classList.add('cl-remote-agent-card--error');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ── onOpenSidebar 支持:点击展开到侧边栏 ──
|
|
105
|
+
if (onOpenSidebar && model.rawText) {
|
|
106
|
+
container.style.cursor = 'pointer';
|
|
107
|
+
container.addEventListener('click', function(e) {
|
|
108
|
+
// 不拦截折叠/展开点击
|
|
109
|
+
if (e.target.closest('.cl-tools-collapse-header')) return;
|
|
110
|
+
onOpenSidebar(model.rawText);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ── 异步更新真实 Agent 身份 ──
|
|
115
|
+
if ((model.agentId || model.agentName) && typeof fetchAgentById === 'function') {
|
|
116
|
+
fetchAgentById(model.agentId, model.agentName).then(function(agent) {
|
|
117
|
+
if (!agent) return;
|
|
118
|
+
var pa = container.querySelector('.cl-agent-pill-avatar');
|
|
119
|
+
var pn = container.querySelector('.cl-agent-pill-name');
|
|
120
|
+
if (pa && typeof avatarUrl === 'function') {
|
|
121
|
+
var resolvedAvatar = avatarUrl(agent);
|
|
122
|
+
console.log('[cl-diag][pill] fetch-update agentId=' + JSON.stringify(model.agentId)
|
|
123
|
+
+ ' name=' + JSON.stringify(agent.name || model.agentName)
|
|
124
|
+
+ ' src=' + JSON.stringify(resolvedAvatar));
|
|
125
|
+
pa.src = resolvedAvatar;
|
|
126
|
+
pa.style.display = '';
|
|
127
|
+
}
|
|
128
|
+
_clSetAgentPillName(container, agent.name || model.agentName);
|
|
129
|
+
_clSetOpenAgentHeaderAgent(container, agent.name || model.agentName);
|
|
130
|
+
}).catch(function() {});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (typeof _cl_applyCardUiState === 'function') {
|
|
134
|
+
_cl_applyCardUiState(container, model);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
console.log('[cl-output] ✅ output card built: results=' + resultTexts.length);
|
|
138
|
+
return container;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function _clShouldRenderOutputText(text) {
|
|
142
|
+
var value = String(text || '').trim();
|
|
143
|
+
if (!value) return false;
|
|
144
|
+
|
|
145
|
+
if (_clLooksLikeStructuredDataFragment(value)) return false;
|
|
146
|
+
|
|
147
|
+
// output 卡片只展示给用户看的最终内容。远端 SSE 的 data/think 事件
|
|
148
|
+
// 是旅行卡片原料或执行过程,不应该作为正文出现在 output 里。
|
|
149
|
+
if (/"msgType"\s*:/.test(value) && /"dataType"\s*:\s*"(data|think)"/.test(value)) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (value.charAt(0) === '{' && value.charAt(value.length - 1) === '}') {
|
|
154
|
+
try {
|
|
155
|
+
var obj = JSON.parse(value);
|
|
156
|
+
if (obj && (obj.dataType === 'data' || obj.dataType === 'think')) return false;
|
|
157
|
+
if (obj && obj.msgType != null && obj.dataType && obj.dataType !== 'result') return false;
|
|
158
|
+
} catch (e) {}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function _clLooksLikeStructuredDataFragment(value) {
|
|
165
|
+
value = String(value || '').trim();
|
|
166
|
+
if (!value) return false;
|
|
167
|
+
|
|
168
|
+
// Pretty/truncated JSON payloads may be split into text chunks before they can
|
|
169
|
+
// be parsed as full objects. Those fragments are data source material, not UI
|
|
170
|
+
// output. Keep this narrow to Trip/itinerary-style payload keys.
|
|
171
|
+
if (/^[,}\]\s]+$/.test(value)) return true;
|
|
172
|
+
if (/^\.\.\.\s*\(truncated\)\s*\.\.\.$/.test(value)) return true;
|
|
173
|
+
|
|
174
|
+
var dataKeyRe = /"(?:msgType|dataType|sessionId|queryMsgId|itineraryId|itineraryVersionInfo|unitList|unitType|unitContexts?|dayList|trafficInfo|trafficPlanInfo|poiInfo|hotelInfo|foodData|budgetData|rankName|hotScore|remarkText|keywords|sourceType|departure|arrival)"\s*:/;
|
|
175
|
+
if (!dataKeyRe.test(value)) return false;
|
|
176
|
+
|
|
177
|
+
var structuralChars = (value.match(/[{}\[\]",:]/g) || []).length;
|
|
178
|
+
if (structuralChars >= 6) return true;
|
|
179
|
+
|
|
180
|
+
// Some split lines start midway through an object, for example
|
|
181
|
+
// `"trafficPlanInfo":{...` or `,"arrival":{...`.
|
|
182
|
+
if (/^\s*,?\s*"(?:unitType|trafficInfo|trafficPlanInfo|poiInfo|hotelInfo|remarkText|keywords|departure|arrival)"\s*:/.test(value)) {
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function _clRevealOutputResult(resultSection, reason) {
|
|
190
|
+
if (!resultSection) return;
|
|
191
|
+
resultSection.classList.remove('cl-output-result--pending');
|
|
192
|
+
var card = resultSection.closest && resultSection.closest('.cl-output-card');
|
|
193
|
+
if (card) {
|
|
194
|
+
card.classList.remove('cl-output-card--pending');
|
|
195
|
+
}
|
|
196
|
+
if (resultSection.dataset) {
|
|
197
|
+
resultSection.dataset.clRenderState = 'ready';
|
|
198
|
+
resultSection.dataset.clRenderReadyReason = reason || 'ready';
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function _clHasTripCardContent(el) {
|
|
203
|
+
if (!el) return false;
|
|
204
|
+
var html = el.innerHTML || '';
|
|
205
|
+
var text = el.textContent || '';
|
|
206
|
+
// Raw card tags (HTML-encoded or literal)
|
|
207
|
+
if (/(?:<|<)(?:sh_card|tr_card|i_card|bg_card|fr_card|time_data_card)(?:>|>)/.test(html)) return true;
|
|
208
|
+
if (text.includes('<sh_card>') || text.includes('<tr_card>') || text.includes('<i_card>') ||
|
|
209
|
+
text.includes('<bg_card>') || text.includes('<fr_card>') ||
|
|
210
|
+
text.includes('<time_data_card>')) return true;
|
|
211
|
+
// Ready marker
|
|
212
|
+
if (html.includes('[TRIP_CARDS_READY]')) return true;
|
|
213
|
+
// Unprocessed travel links
|
|
214
|
+
if (el.querySelector && el.querySelector('a[href*="poi_"], a[href*="hotel_"], a[href*="lodgingArea_"], a[href*="ctx_"]')) return true;
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function _clScheduleOutputCardProcessing(resultSection, model) {
|
|
219
|
+
if (!resultSection) return;
|
|
220
|
+
|
|
221
|
+
var attempts = 0;
|
|
222
|
+
function run() {
|
|
223
|
+
attempts++;
|
|
224
|
+
|
|
225
|
+
// Only process if the output actually contains travel card content.
|
|
226
|
+
// This prevents non-travel remote-agent outputs from triggering
|
|
227
|
+
// trip-card-data.json fetches and polluting CL.tripCardDataFetchFailed.
|
|
228
|
+
if (!_clHasTripCardContent(resultSection)) {
|
|
229
|
+
_clRevealOutputResult(resultSection, 'plain-output');
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (typeof _processCustomCards === 'function') {
|
|
234
|
+
try {
|
|
235
|
+
if (model && model.toolCallId && resultSection.dataset) {
|
|
236
|
+
resultSection.dataset.clToolCallId = resultSection.dataset.clToolCallId || model.toolCallId;
|
|
237
|
+
}
|
|
238
|
+
_processCustomCards(resultSection);
|
|
239
|
+
} catch (err) {
|
|
240
|
+
console.warn('[cl-output] trip card processing failed:', err && err.message);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
var processed = resultSection.dataset && resultSection.dataset.clProcessed;
|
|
245
|
+
if (processed === '1') {
|
|
246
|
+
_clRevealOutputResult(resultSection, 'trip-rendered');
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if (attempts >= 20) {
|
|
250
|
+
_clRevealOutputResult(resultSection, processed === 'partial' ? 'trip-partial-timeout' : 'trip-timeout');
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
var delay = attempts < 4 ? 250 : 1000;
|
|
255
|
+
setTimeout(run, delay);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
setTimeout(run, 0);
|
|
259
|
+
}
|