openclaw-openagent 1.0.12 → 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 (132) hide show
  1. package/dist/index.js +2 -0
  2. package/dist/src/config/config-schema.d.ts +9 -0
  3. package/dist/src/config/config-schema.js +6 -0
  4. package/dist/src/messaging/executor.d.ts +2 -3
  5. package/dist/src/messaging/executor.js +4 -31
  6. package/dist/src/plugin-ui/assets/openagent-override.js +124 -18
  7. package/dist/src/proxy/auth-proxy.js +5 -0
  8. package/dist/src/runtime/update-checker.d.ts +18 -0
  9. package/dist/src/runtime/update-checker.js +216 -0
  10. package/index.ts +3 -0
  11. package/package.json +1 -3
  12. package/src/app/channel-tools.ts +0 -249
  13. package/src/app/discovery-tools.ts +0 -273
  14. package/src/app/download-file-tool.ts +0 -133
  15. package/src/app/hooks.ts +0 -144
  16. package/src/app/index.ts +0 -84
  17. package/src/app/messaging-tools.ts +0 -79
  18. package/src/app/ops-tools.ts +0 -155
  19. package/src/app/remote-agent-tool.ts +0 -762
  20. package/src/app/types.ts +0 -68
  21. package/src/app/upload-file-tool.ts +0 -411
  22. package/src/app/verbose-preflight.ts +0 -190
  23. package/src/auth/config.ts +0 -266
  24. package/src/auth/credential-manager.ts +0 -146
  25. package/src/auth/index.ts +0 -24
  26. package/src/auth/verify.ts +0 -99
  27. package/src/channel.ts +0 -566
  28. package/src/compat.ts +0 -94
  29. package/src/config/config-schema.ts +0 -39
  30. package/src/messaging/aggregator.ts +0 -120
  31. package/src/messaging/collector.ts +0 -89
  32. package/src/messaging/executor.ts +0 -72
  33. package/src/messaging/inbound.ts +0 -150
  34. package/src/messaging/index.ts +0 -11
  35. package/src/messaging/mention-protocol.ts +0 -94
  36. package/src/messaging/process-c2c-request.ts +0 -564
  37. package/src/messaging/process-message.ts +0 -373
  38. package/src/messaging/scheduler.ts +0 -55
  39. package/src/messaging/types.ts +0 -38
  40. package/src/plugin-ui/adapters/oc-2026-04.js +0 -103
  41. package/src/plugin-ui/adapters/oc-2026-05.js +0 -125
  42. package/src/plugin-ui/adapters/oc-2026-06.js +0 -125
  43. package/src/plugin-ui/adapters/oc-unknown.js +0 -48
  44. package/src/plugin-ui/assets/agentbook-icon.svg +0 -5
  45. package/src/plugin-ui/assets/bg.png +0 -0
  46. package/src/plugin-ui/assets/icon.png +0 -0
  47. package/src/plugin-ui/assets/magic.svg +0 -5
  48. package/src/plugin-ui/assets/openagent-override.js +0 -11156
  49. package/src/plugin-ui/build.cjs +0 -463
  50. package/src/plugin-ui/index.ts +0 -18
  51. package/src/plugin-ui/modules/agent-book/panel/agent-book.js +0 -570
  52. package/src/plugin-ui/modules/agent-book/panel/agent-card.js +0 -170
  53. package/src/plugin-ui/modules/agent-book/panel/agent-data.js +0 -643
  54. package/src/plugin-ui/modules/agent-book/panel/inject-ui.js +0 -665
  55. package/src/plugin-ui/modules/agent-book/panel/mention-state.js +0 -206
  56. package/src/plugin-ui/modules/agent-book/panel/styles.js +0 -833
  57. package/src/plugin-ui/modules/agent-book/panel/theme-adapter.js +0 -86
  58. package/src/plugin-ui/modules/agent-book/remote-agent-tool/components-core.js +0 -295
  59. package/src/plugin-ui/modules/agent-book/remote-agent-tool/thought-chain-card.js +0 -211
  60. package/src/plugin-ui/modules/agent-book/scanner.js +0 -131
  61. package/src/plugin-ui/modules/agent-book/travelcard/travel-cards.js +0 -500
  62. package/src/plugin-ui/modules/agent-book/travelcard/travel-engine.js +0 -652
  63. package/src/plugin-ui/modules/agent-book/travelcard/travel-styles.js +0 -256
  64. package/src/plugin-ui/modules/loader/bootstrap.js +0 -38
  65. package/src/plugin-ui/modules/loader/shared-state.js +0 -838
  66. package/src/plugin-ui/modules/loader/ws-intercept.js +0 -199
  67. package/src/plugin-ui/modules/remote-agent/chunk-parser.js +0 -161
  68. package/src/plugin-ui/modules/remote-agent/execution-card.js +0 -530
  69. package/src/plugin-ui/modules/remote-agent/markdown-renderer.js +0 -256
  70. package/src/plugin-ui/modules/remote-agent/media-links.js +0 -151
  71. package/src/plugin-ui/modules/remote-agent/native-style-adapter.js +0 -134
  72. package/src/plugin-ui/modules/remote-agent/output-card.js +0 -342
  73. package/src/plugin-ui/modules/remote-agent/progress-store.js +0 -363
  74. package/src/plugin-ui/modules/remote-agent/render-hooks.js +0 -1700
  75. package/src/plugin-ui/modules/remote-agent/styles.js +0 -896
  76. package/src/plugin-ui/modules/remote-agent/tool-card-model.js +0 -130
  77. package/src/plugin-ui/postinstall-deploy.cjs +0 -52
  78. package/src/plugin-ui/ui-extension-loader/backup.ts +0 -92
  79. package/src/plugin-ui/ui-extension-loader/index.ts +0 -276
  80. package/src/plugin-ui/ui-extension-loader/locator.ts +0 -152
  81. package/src/plugin-ui/ui-extension-loader/manifest.ts +0 -57
  82. package/src/plugin-ui/ui-extension-loader/registry-regex.ts +0 -944
  83. package/src/plugin-ui/ui-extension-loader/removed-extensions.ts +0 -70
  84. package/src/plugin-ui/ui-extension-loader/types.ts +0 -72
  85. package/src/proxy/auth-proxy.ts +0 -409
  86. package/src/runtime/account.ts +0 -549
  87. package/src/runtime/index.ts +0 -7
  88. package/src/runtime/plugin-runtime.ts +0 -94
  89. package/src/runtime/registry.ts +0 -71
  90. package/src/sdk/CLASS_MAP.md +0 -143
  91. package/src/sdk/index.d.ts +0 -126
  92. package/src/sdk/index.js +0 -23990
  93. package/src/sdk/modules/cloud-search-module.js +0 -1117
  94. package/src/sdk/modules/follow-module.js +0 -1069
  95. package/src/sdk/modules/group-module.js +0 -7397
  96. package/src/sdk/modules/relationship-module.js +0 -2269
  97. package/src/sdk/modules/signaling-module.js +0 -1468
  98. package/src/sdk/modules/tim-upload-plugin.js +0 -730
  99. package/src/sdk/node-env/http-request.js +0 -90
  100. package/src/sdk/node-env/index.js +0 -57
  101. package/src/sdk/node-env/storage.js +0 -114
  102. package/src/sdk/package.json +0 -10
  103. package/src/sdk/tsconfig.json +0 -16
  104. package/src/state/pending-invocation-store.ts +0 -43
  105. package/src/state/store.ts +0 -756
  106. package/src/tim/c2c.ts +0 -451
  107. package/src/tim/channels.ts +0 -364
  108. package/src/tim/client.ts +0 -330
  109. package/src/tim/index.ts +0 -18
  110. package/src/tim/messages.ts +0 -166
  111. package/src/tim/sdk-logger-init.ts +0 -50
  112. package/src/tools.ts +0 -10
  113. package/src/transport/factory.ts +0 -95
  114. package/src/transport/oasn/index.ts +0 -17
  115. package/src/transport/oasn/oasn-agent-card.ts +0 -111
  116. package/src/transport/oasn/oasn-discovery.ts +0 -108
  117. package/src/transport/oasn/oasn-files.ts +0 -210
  118. package/src/transport/oasn/oasn-http.ts +0 -529
  119. package/src/transport/oasn/oasn-invocation.ts +0 -604
  120. package/src/transport/oasn/oasn-normalize.ts +0 -133
  121. package/src/transport/oasn/oasn-register.ts +0 -33
  122. package/src/transport/oasn/oasn-transport.ts +0 -318
  123. package/src/transport/oasn/oasn-types.ts +0 -404
  124. package/src/transport/tim/index.ts +0 -8
  125. package/src/transport/tim/tim-transport.ts +0 -515
  126. package/src/transport/types.ts +0 -538
  127. package/src/types/openclaw-plugin-sdk-media-store.d.ts +0 -9
  128. package/src/types/openclaw.d.ts +0 -97
  129. package/src/types/tencentcloud-chat.d.ts +0 -15
  130. package/src/util/http.ts +0 -113
  131. package/src/util/logger.ts +0 -131
  132. package/src/util/url-resolver.ts +0 -77
@@ -1,1700 +0,0 @@
1
- // ── OpenAgent Render Hook v2 — 单一数据源 + 纯渲染 ──
2
- //
3
- // 架构:
4
- // __cl_toolStates: 当前页面生命周期内的 DOM/phase 状态
5
- // _clRemoteAgentProgressStore: 可替换存储层,保存卡片快照参数
6
- // 正常流式渲染只读写 __cl_toolStates;store 只是旁路记录,刷新后才用于恢复
7
-
8
- // ═══════════════════════════════════════════════════════════════
9
- // 单一数据源
10
- // ═══════════════════════════════════════════════════════════════
11
-
12
- var __cl_toolStates = new Map();
13
- // 每个 entry: { steps: [], phase: 'active'|'completed', cardNode: null, args: {} }
14
-
15
- var __cl_lastCallModel = null; // 同一 render cycle 里 call card 的 model(供 result card 读取 agent 身份)
16
- var __cl_outputCardCache = new Map(); // toolCallId → cached output DOM
17
- var __cl_executionCardCache = new Map(); // toolCallId → cached historical execution DOM
18
- var __cl_cardUiStates = new Map(); // execution/output 展开状态,避免 OpenClaw rerender 覆盖用户操作
19
- var __cl_progressMessageMap = new Map(); // toolCallId → 动态进度文字(来自 response.progress?.safe_status_message)
20
-
21
- // ── 诊断:渲染频率计数器 ──
22
- var __clDiag = { renderCalls: 0, lastDiagTs: 0, version: 'v4-oneshot-20260630' };
23
- console.log('[cl-diag] render-hooks loaded, version=' + __clDiag.version);
24
-
25
- var CL_REMOTE_AGENT_PROGRESS_MODE_TRAVEL_TITLE = 'travel-title';
26
- var CL_REMOTE_AGENT_PROGRESS_MODE_GENERIC_STREAM = 'generic-stream';
27
- var CL_TRAVEL_TITLE_AGENT_IDS = {
28
- '82bc858c-5021-4bda-83bc-1badd7f49049': true
29
- };
30
- var CL_TRAVEL_TITLE_AGENT_NAMES = {
31
- '旅游梦想家': true
32
- };
33
-
34
- window.__openagentOwnsToolCards = function(cards) {
35
- if (!Array.isArray(cards)) return false;
36
- for (var i = 0; i < cards.length; i++) {
37
- var card = cards[i] || {};
38
- var name = String(card.name || '').toLowerCase();
39
- var tcid = card.toolCallId || '';
40
- var kind = typeof _clInferToolCardKind === 'function' ? _clInferToolCardKind(card) : (card.kind || '');
41
- if (name.includes('call_remote_agent') || name.includes('openagent_delegate')) {
42
- return true;
43
- }
44
- if (kind === 'result' && tcid && __cl_lastCallModel && tcid === __cl_lastCallModel.tcid) {
45
- return true;
46
- }
47
- }
48
- return false;
49
- };
50
-
51
- // ═══════════════════════════════════════════════════════════════
52
- // WS 流式事件处理器 — 只写 __cl_toolStates
53
- // ═══════════════════════════════════════════════════════════════
54
-
55
- window.__cl_handleStreamEvent = function(msg) {
56
- if (!msg || msg.stream !== 'tool') return;
57
-
58
- var d = msg.data || {};
59
- var phase = d.phase;
60
- var toolCallId = d.toolCallId || '';
61
- if (!toolCallId) return;
62
-
63
- // ── start ──
64
- if (phase === 'start') {
65
- var name = d.name || (d.call && d.call.name) || '';
66
- if (!_cl_isRemoteToolEvent(d, null)) return;
67
-
68
- var wsArgs = _cl_getStreamArgs(d);
69
- var existingState = __cl_toolStates.get(toolCallId);
70
- if (existingState) {
71
- existingState.args = _cl_mergeArgs(existingState.args, wsArgs);
72
- existingState.progressMode = _cl_getRemoteAgentProgressMode(existingState.args, existingState.progressMode);
73
- if (existingState.thinkChunks && existingState.thinkChunks.length > 0) {
74
- _cl_rebuildThinkStepsByAgentMode(existingState);
75
- }
76
- existingState.phase = 'active';
77
- if (typeof _clRemoteAgentProgressStore !== 'undefined' && _clRemoteAgentProgressStore) {
78
- _clRemoteAgentProgressStore.recordStart(toolCallId, { name: name, args: existingState.args || wsArgs });
79
- }
80
- return;
81
- }
82
-
83
- var state = _cl_createToolState(wsArgs);
84
- __cl_toolStates.set(toolCallId, state);
85
- if (typeof _clRemoteAgentProgressStore !== 'undefined' && _clRemoteAgentProgressStore) {
86
- _clRemoteAgentProgressStore.recordStart(toolCallId, { name: name, args: wsArgs });
87
- }
88
- console.log('[cl-stream] 🚀 start tcid=' + toolCallId.substring(0, 8));
89
- return;
90
- }
91
-
92
- // ── update ──
93
- if (phase === 'update') {
94
- var rawText = _cl_extractText(d.partialResult || d.content);
95
- if (!rawText) return;
96
-
97
- _cl_logRawSseUpdate(toolCallId, rawText);
98
-
99
- var chunks = _cl_parseLiveUpdateChunks(rawText, { toolCallId: toolCallId, rawText: rawText });
100
- if (!chunks.length) {
101
- console.log('[cl-stream] update without think chunks tcid=' + toolCallId.substring(0, 8)
102
- + ' rawLen=' + rawText.length);
103
- return;
104
- }
105
-
106
- var state = _cl_ensureRemoteToolState(toolCallId, d, rawText);
107
- if (!state) return;
108
-
109
- var changed = false;
110
- var lastChunk = null;
111
- var wasCompleted = state.phase === 'completed';
112
- for (var ci = 0; ci < chunks.length; ci++) {
113
- var chunk = chunks[ci];
114
- lastChunk = chunk;
115
-
116
- // 正常流程只用内存 state 驱动 UI;store 只是旁路记录,避免影响直播路径。
117
- if (_cl_applyThinkChunkToLiveState(state, chunk)) changed = true;
118
- if (typeof _clRemoteAgentProgressStore !== 'undefined' && _clRemoteAgentProgressStore) {
119
- _clRemoteAgentProgressStore.recordChunk(toolCallId, chunk);
120
- }
121
- }
122
-
123
- console.log('[cl-diag][step] tcid=' + toolCallId.substring(0, 8)
124
- + ' chunks=' + chunks.length
125
- + ' lastIndex=' + (lastChunk && lastChunk.index !== undefined ? lastChunk.index : '')
126
- + ' lastType=' + (lastChunk && lastChunk.thinkType || '')
127
- + ' changed=' + changed
128
- + ' steps=' + state.steps.length);
129
-
130
- // 直接更新已渲染的 DOM(replay 进行中不直接更新,tick 会自然追上)
131
- if (changed && state.cardNode && !state._replayActive) {
132
- _cl_updateCardSteps(state.cardNode, state.steps);
133
- if (wasCompleted) {
134
- _cl_finalizeCard(state.cardNode, state.steps);
135
- }
136
- }
137
- return;
138
- }
139
-
140
- // ── result ──
141
- if (phase === 'result') {
142
- var state = _cl_ensureRemoteToolState(toolCallId, d, null);
143
- if (!state) return;
144
-
145
- state.phase = 'completed';
146
- if (typeof _clRemoteAgentProgressStore !== 'undefined' && _clRemoteAgentProgressStore) {
147
- _clRemoteAgentProgressStore.recordResult(toolCallId);
148
- }
149
- console.log('[cl-stream] ✅ result: steps=' + state.steps.length
150
- + ' tcid=' + toolCallId.substring(0, 8)
151
- + ' hasCard=' + !!state.cardNode
152
- + ' replayActive=' + !!state._replayActive);
153
-
154
- // 如果 replay 未完成或 cardNode 未创建,延迟 finalize
155
- if (state._replayActive || !state.cardNode) {
156
- state.pendingFinalize = true;
157
- console.log('[cl-playback] finalize deferred tcid=' + toolCallId.substring(0, 8));
158
- } else {
159
- _cl_finalizeCard(state.cardNode, state.steps);
160
- console.log('[cl-playback] finalize now tcid=' + toolCallId.substring(0, 8));
161
- }
162
- return;
163
- }
164
- };
165
-
166
- function _cl_logRawSseUpdate(toolCallId, rawText) {
167
- try {
168
- if (!_cl_isRawSseDebugEnabled()) return;
169
- var entry = {
170
- ts: new Date().toISOString(),
171
- toolCallId: toolCallId || '',
172
- rawText: rawText || ''
173
- };
174
- window.__openagentRawSseLog = window.__openagentRawSseLog || [];
175
- window.__openagentRawSseLog.push(entry);
176
- if (window.__openagentRawSseLog.length > 200) {
177
- window.__openagentRawSseLog.splice(0, window.__openagentRawSseLog.length - 200);
178
- }
179
- _cl_persistRawDebugLog('openagent.rawSseLog.v1', 'openagent-raw-sse-log', window.__openagentRawSseLog);
180
- console.log('[cl-raw-sse] tcid=' + String(toolCallId || '').substring(0, 8) + ' raw=' + rawText);
181
- } catch (err) {}
182
- }
183
-
184
- function _cl_isRawSseDebugEnabled() {
185
- try {
186
- return window.localStorage && window.localStorage.getItem('openagent.debug.rawSse') === '1';
187
- } catch (err) {
188
- return false;
189
- }
190
- }
191
-
192
- function _cl_persistRawDebugLog(storageKey, elementId, entries) {
193
- try {
194
- var json = JSON.stringify(entries || []);
195
- try {
196
- window.localStorage && window.localStorage.setItem(storageKey, json);
197
- } catch (storageErr) {}
198
-
199
- var doc = window.document;
200
- if (!doc || !doc.body) return;
201
- var node = doc.getElementById(elementId);
202
- if (!node) {
203
- node = doc.createElement('pre');
204
- node.id = elementId;
205
- node.style.display = 'none';
206
- doc.body.appendChild(node);
207
- }
208
- node.textContent = json;
209
- } catch (err) {}
210
- }
211
-
212
- function _cl_isRemoteToolEvent(data, rawText) {
213
- data = data || {};
214
- var name = String(data.name || (data.call && data.call.name) || '').toLowerCase();
215
- if (name.includes('call_remote_agent') || name.includes('openagent_delegate')) return true;
216
-
217
- var args = _cl_getStreamArgs(data);
218
- var agentId = args.agent_id || args.agentId || '';
219
- var agentName = args.agent_name || args.agentName || '';
220
- if (agentId || agentName) return true;
221
-
222
- if (rawText && /openagent_call_remote_agent|openagent_delegate|call_remote_agent/.test(rawText)) return true;
223
- if (rawText && rawText.indexOf('"dataType"') !== -1 && rawText.indexOf('"thinkType"') !== -1) return true;
224
- if (rawText && rawText.indexOf('"dataType"') !== -1 && rawText.indexOf('"msgType"') !== -1) return true;
225
- return false;
226
- }
227
-
228
- function _cl_getStreamArgs(data) {
229
- data = data || {};
230
- return data.args || (data.call && (data.call.args || data.call.arguments)) || {};
231
- }
232
-
233
- function _cl_mergeArgs(current, incoming) {
234
- current = current && typeof current === 'object' ? current : {};
235
- incoming = incoming && typeof incoming === 'object' ? incoming : {};
236
- var merged = {};
237
- var k;
238
- for (k in current) {
239
- if (Object.prototype.hasOwnProperty.call(current, k)) merged[k] = current[k];
240
- }
241
- for (k in incoming) {
242
- if (Object.prototype.hasOwnProperty.call(incoming, k) && merged[k] === undefined) {
243
- merged[k] = incoming[k];
244
- }
245
- }
246
- return merged;
247
- }
248
-
249
- function _cl_getRemoteAgentProgressMode(args, currentMode) {
250
- args = args && typeof args === 'object' ? args : {};
251
- currentMode = currentMode || '';
252
-
253
- var agentId = String(args.agent_id || args.agentId || '').trim();
254
- var agentName = String(args.agent_name || args.agentName || '').trim();
255
-
256
- if (agentId && CL_TRAVEL_TITLE_AGENT_IDS[agentId]) {
257
- return CL_REMOTE_AGENT_PROGRESS_MODE_TRAVEL_TITLE;
258
- }
259
- if (agentName && CL_TRAVEL_TITLE_AGENT_NAMES[agentName]) {
260
- return CL_REMOTE_AGENT_PROGRESS_MODE_TRAVEL_TITLE;
261
- }
262
- if (currentMode === CL_REMOTE_AGENT_PROGRESS_MODE_TRAVEL_TITLE) {
263
- return currentMode;
264
- }
265
- return CL_REMOTE_AGENT_PROGRESS_MODE_GENERIC_STREAM;
266
- }
267
-
268
- function _cl_createToolState(args) {
269
- var normalizedArgs = args || {};
270
- return {
271
- steps: [],
272
- phase: 'active',
273
- progressMode: _cl_getRemoteAgentProgressMode(normalizedArgs, ''),
274
- thinkChunks: [],
275
- lastThinkType: '',
276
- lastTitleIndex: -1,
277
- seenChunkKeys: Object.create(null),
278
- unindexedSeq: 0,
279
- cardNode: null,
280
- args: normalizedArgs,
281
- // ── 播放控制 ──
282
- _replayActive: false,
283
- _replayTimer: null,
284
- pendingFinalize: false
285
- };
286
- }
287
-
288
- function _cl_ensureRemoteToolState(toolCallId, data, rawText) {
289
- var state = __cl_toolStates.get(toolCallId);
290
- if (state) {
291
- state.args = _cl_mergeArgs(state.args, _cl_getStreamArgs(data));
292
- state.progressMode = _cl_getRemoteAgentProgressMode(state.args, state.progressMode);
293
- if (state.thinkChunks && state.thinkChunks.length > 0) {
294
- _cl_rebuildThinkStepsByAgentMode(state);
295
- }
296
- return state;
297
- }
298
-
299
- if (!_cl_isRemoteToolEvent(data, rawText)) return null;
300
-
301
- var name = data && (data.name || (data.call && data.call.name)) || '';
302
- var args = _cl_getStreamArgs(data);
303
- state = _cl_createToolState(args);
304
- __cl_toolStates.set(toolCallId, state);
305
-
306
- if (typeof _clRemoteAgentProgressStore !== 'undefined' && _clRemoteAgentProgressStore) {
307
- _clRemoteAgentProgressStore.recordStart(toolCallId, { name: name, args: args });
308
- }
309
- console.log('[cl-stream] 🧩 recovered missing start tcid=' + toolCallId.substring(0, 8)
310
- + ' phase=' + ((data && data.phase) || ''));
311
- return state;
312
- }
313
-
314
- // ═══════════════════════════════════════════════════════════════
315
- // 渲染入口 — 只读 __cl_toolStates
316
- // ═══════════════════════════════════════════════════════════════
317
-
318
- window.__openagentRenderToolCard = function(card, onOpenSidebar) {
319
- // ── 诊断:渲染频率计数器 ──
320
- __clDiag.renderCalls++;
321
- var diagNow = Date.now();
322
- if (diagNow - __clDiag.lastDiagTs > 2000) {
323
- var rps = (__clDiag.renderCalls / ((diagNow - __clDiag.lastDiagTs) / 1000)).toFixed(1);
324
- console.log('[cl-diag] render rate: ' + rps + ' calls/sec, total=' + __clDiag.renderCalls + ', version=' + __clDiag.version);
325
- __clDiag.renderCalls = 0;
326
- __clDiag.lastDiagTs = diagNow;
327
- }
328
-
329
- var n = (card && card.name || '').toLowerCase();
330
- var cardTcid = card && (card.toolCallId || '');
331
- var cardKind = typeof _clInferToolCardKind === 'function' ? _clInferToolCardKind(card) : (card && card.kind || '');
332
- var liveState = cardTcid ? __cl_toolStates.get(cardTcid) : null;
333
- if (cardKind === 'result' && liveState && liveState.phase === 'active' && card && card.inputText) {
334
- cardKind = 'call';
335
- }
336
- var isRemoteName = n.includes('call_remote_agent') || n.includes('openagent_delegate');
337
- var isLinkedRemoteResult = card && cardKind === 'result' && cardTcid
338
- && __cl_lastCallModel && cardTcid === __cl_lastCallModel.tcid;
339
- if (!isRemoteName && !isLinkedRemoteResult) return null;
340
-
341
- // Phase 2: lazy 采样原生 ToolCard 尺寸变量
342
- if (typeof _clEnsureNativeToolStyleVars === 'function') {
343
- _clEnsureNativeToolStyleVars();
344
- }
345
-
346
- var model = _clParseToolCardModel(card);
347
-
348
- try {
349
- if (cardKind === 'call') {
350
- var tcid = card.toolCallId || '';
351
- var state = liveState || (tcid ? __cl_toolStates.get(tcid) : null);
352
-
353
- // 保存 call card 的 model,供同一 message 的 result card 使用
354
- __cl_lastCallModel = { tcid: tcid, agentId: model.agentId, agentName: model.agentName };
355
-
356
- console.log('[cl-render] 🔍 call: tcid=' + tcid.substring(0, 8)
357
- + ' hasState=' + !!state
358
- + (state ? ' phase=' + state.phase + ' steps=' + state.steps.length + ' hasCard=' + !!state.cardNode : ''));
359
-
360
- if (state) {
361
- if (state.args) {
362
- _cl_applyStoredRecordToModel(model, { args: state.args });
363
- }
364
- if (state.phase === 'active') {
365
- model.status = 'executing';
366
- if (typeof _clUpdateAgentStatusTextV2 === 'function' && state.cardNode && state.cardNode.classList.contains('cl-exec-v2')) {
367
- _clUpdateAgentStatusTextV2(state.cardNode, 'executing');
368
- } else if (typeof _clUpdateAgentStatusText === 'function') {
369
- _clUpdateAgentStatusText(state.cardNode, 'executing');
370
- }
371
- }
372
- // 有 streaming 状态 — 复用或新建
373
- if (state.cardNode) {
374
- // Lit 重复调用会复用同一个 DOM 节点;但 OpenClaw 外层 native
375
- // <details> 可能被 rerender 成 closed。这里重新打开的是外层原生
376
- // details,不影响卡片内部的”思考中/思考完成”折叠状态。
377
- _cl_applyCardUiState(state.cardNode, { toolCallId: tcid, cardKind: 'call' });
378
- if (state.phase === 'active') {
379
- _cl_autoOpenStepsIfUncontrolled(state.cardNode);
380
- }
381
- // ── 更新进度文字:来自 response.progress?.safe_status_message ──
382
- var progressEl = state.cardNode.querySelector('.cl-exec-progress-text');
383
- if (progressEl && model.rawText) {
384
- var msg = (model.rawText || '').trim();
385
- if (msg) progressEl.textContent = msg;
386
- }
387
- // ── 一次性外壳初始化:首次渲染时打开 native details + 隐藏 raw text,之后永久跳过 ──
388
- if (!state.cardNode.__clShellReady) {
389
- state.cardNode.__clShellReady = true;
390
- _cl_forceOpenNativeToolDetails(state.cardNode, 'execution-cache', tcid);
391
- if (typeof _cl_scheduleHostShellMark === 'function') _cl_scheduleHostShellMark(state.cardNode);
392
- _cl_hideHostRawText(state.cardNode);
393
- }
394
- return state.cardNode;
395
- }
396
-
397
- // 首次渲染:创建卡片
398
- model.toolCallId = tcid;
399
- model.cardKind = 'call';
400
- // 不直接灌 bufferedSteps,由 replay 逐条展示
401
- model._bufferedSteps = [];
402
- var node = _clRenderExecutionCard(model);
403
- state.cardNode = node;
404
- _cl_forceOpenNativeToolDetails(node, 'execution', tcid);
405
- if (typeof _cl_scheduleHostShellMark === 'function') _cl_scheduleHostShellMark(node);
406
- _cl_hideHostRawText(node);
407
-
408
- if (state.phase === 'completed') {
409
- // result 已到 — 如果有积压步骤,replay 后 finalize
410
- if (state.steps.length > 0) {
411
- state.pendingFinalize = true;
412
- _cl_replayBufferedSteps(tcid, state);
413
- } else {
414
- _cl_finalizeCard(node, state.steps);
415
- }
416
- console.log('[cl-render] 🆕 built completed card, tcid=' + tcid.substring(0, 8)
417
- + ' steps=' + state.steps.length);
418
- } else {
419
- // 仍在活跃 — replay 积压步骤(如有)
420
- if (state.steps.length > 0) {
421
- _cl_replayBufferedSteps(tcid, state);
422
- }
423
- console.log('[cl-render] 📥 bound active card, tcid=' + tcid.substring(0, 8)
424
- + ' buffered=' + state.steps.length);
425
- }
426
- return node;
427
- }
428
-
429
- // 无状态 → 刷新/历史态。前端 store 如果有同一 toolCallId 的卡片快照,
430
- // 就用快照参数重建 steps;没有 store 时仍展示完成态外壳。
431
-
432
- // 缓存:历史态 execution card 只创建一次,Lit 重复调用时复用(与 output card 逻辑一致)
433
- var cachedExecution = tcid ? __cl_executionCardCache.get(tcid) : null;
434
- if (cachedExecution && cachedExecution.isConnected) {
435
- _cl_applyCardUiState(cachedExecution, { toolCallId: tcid, cardKind: 'call' });
436
- if (!cachedExecution.__clShellReady) {
437
- cachedExecution.__clShellReady = true;
438
- _cl_forceOpenNativeToolDetails(cachedExecution, 'execution-history-cache', tcid);
439
- if (typeof _cl_scheduleHostShellMark === 'function') _cl_scheduleHostShellMark(cachedExecution);
440
- _cl_hideHostRawText(cachedExecution);
441
- }
442
- return cachedExecution;
443
- }
444
-
445
- var storedCallRecord = tcid && typeof _clRemoteAgentProgressStore !== 'undefined' && _clRemoteAgentProgressStore
446
- ? _clRemoteAgentProgressStore.get(tcid)
447
- : null;
448
- if (storedCallRecord) {
449
- _cl_applyStoredRecordToModel(model, storedCallRecord);
450
- model._bufferedSteps = _clRemoteAgentProgressStore.rebuildSteps(storedCallRecord);
451
- console.log('[cl-store] restore call model tcid=' + tcid.substring(0, 8)
452
- + ' steps=' + model._bufferedSteps.length
453
- + ' agent=' + JSON.stringify(model.agentName));
454
- } else {
455
- console.log('[cl-store] historical call has no frontend progress record: tcid=' + tcid.substring(0, 8));
456
- }
457
- model.toolCallId = tcid;
458
- model.cardKind = 'call';
459
- model.status = 'completed';
460
- var historyNode = _clRenderExecutionCard(model);
461
- if (historyNode && historyNode.classList && historyNode.classList.contains('cl-exec-v2')) {
462
- if (typeof _clUpdateAgentStatusTextV2 === 'function') _clUpdateAgentStatusTextV2(historyNode, 'completed');
463
- } else if (typeof _clUpdateAgentStatusText === 'function') {
464
- _clUpdateAgentStatusText(historyNode, 'completed');
465
- }
466
- _cl_finalizeCard(historyNode, model._bufferedSteps || []);
467
- _cl_forceOpenNativeToolDetails(historyNode, 'execution-history', tcid);
468
- if (typeof _cl_scheduleHostShellMark === 'function') _cl_scheduleHostShellMark(historyNode);
469
- _cl_hideHostRawText(historyNode);
470
- if (tcid) __cl_executionCardCache.set(tcid, historyNode);
471
- console.log('[cl-render] 🗂️ built historical call card, tcid=' + tcid.substring(0, 8));
472
- return historyNode;
473
- }
474
-
475
- if (cardKind === 'result') {
476
- var resTcid = card.toolCallId || '';
477
-
478
- // 从 toolState 合并 agent 身份信息(live 场景)
479
- var resState = resTcid ? __cl_toolStates.get(resTcid) : null;
480
- if (resState && resState.args) {
481
- var ra = resState.args;
482
- if (!model.agentId && (ra.agent_id || ra.agentId)) {
483
- model.agentId = ra.agent_id || ra.agentId;
484
- }
485
- if (model.agentName === 'Remote Agent') {
486
- model.agentName = ra.agent_name || ra.agentName || model.agentName;
487
- }
488
- }
489
- var storedResultRecord = resTcid && typeof _clRemoteAgentProgressStore !== 'undefined' && _clRemoteAgentProgressStore
490
- ? _clRemoteAgentProgressStore.get(resTcid)
491
- : null;
492
- if (storedResultRecord) {
493
- _cl_applyStoredRecordToModel(model, storedResultRecord);
494
- console.log('[cl-store] apply result model tcid=' + resTcid.substring(0, 8)
495
- + ' agent=' + JSON.stringify(model.agentName));
496
- }
497
-
498
- // 从同一 message 的 call card 复制 agent 身份(历史场景 / WS 漏接)
499
- // 仅当 toolCallId 匹配时才使用,防止跨消息串身份
500
- if (model.agentName === 'Remote Agent' && __cl_lastCallModel
501
- && resTcid && resTcid === __cl_lastCallModel.tcid) {
502
- model.agentId = model.agentId || __cl_lastCallModel.agentId;
503
- model.agentName = __cl_lastCallModel.agentName || model.agentName;
504
- }
505
-
506
- var chunkCounts = _cl_countChunkTypes(model.chunks || []);
507
- console.log('[cl-diag][result] render tcid=' + resTcid.substring(0, 8)
508
- + ' rawLen=' + ((model.rawText || '').length)
509
- + ' chunks=' + (model.chunks || []).length
510
- + ' steps=' + chunkCounts.step
511
- + ' text=' + chunkCounts.text
512
- + ' result=' + chunkCounts.result
513
- + ' hasState=' + !!resState
514
- + ' agent=' + JSON.stringify(model.agentName));
515
-
516
- model.toolCallId = resTcid;
517
- model.cardKind = 'result';
518
- if (model.isError) {
519
- var executionNodeForResult = (resState && resState.cardNode)
520
- || (resTcid ? __cl_executionCardCache.get(resTcid) : null);
521
- if (executionNodeForResult && executionNodeForResult.classList && executionNodeForResult.classList.contains('cl-exec-v2')) {
522
- if (typeof _clUpdateAgentStatusTextV2 === 'function') _clUpdateAgentStatusTextV2(executionNodeForResult, 'failed');
523
- } else if (executionNodeForResult && typeof _clUpdateAgentStatusText === 'function') {
524
- _clUpdateAgentStatusText(executionNodeForResult, 'failed');
525
- }
526
- }
527
-
528
- // 缓存:同一 toolCallId 的 output card 只创建一次,Lit 重复调用时复用
529
- var cachedOutput = resTcid ? __cl_outputCardCache.get(resTcid) : null;
530
- if (cachedOutput && cachedOutput.isConnected) {
531
- _cl_applyCardUiState(cachedOutput, { toolCallId: resTcid, cardKind: 'result' });
532
- // ── 一次性外壳初始化:首次渲染时打开 native details + 隐藏 raw text,之后永久跳过 ──
533
- if (!cachedOutput.__clShellReady) {
534
- cachedOutput.__clShellReady = true;
535
- _cl_forceOpenNativeToolDetails(cachedOutput, 'output-cache', resTcid);
536
- if (typeof _cl_scheduleHostShellMark === 'function') _cl_scheduleHostShellMark(cachedOutput);
537
- _cl_hideHostRawText(cachedOutput);
538
- }
539
- return cachedOutput;
540
- }
541
-
542
- var outputNode = _clRenderOutputCard(model, onOpenSidebar);
543
- if (!outputNode.__clEmptyOutput) {
544
- _cl_forceOpenNativeToolDetails(outputNode, 'output', resTcid);
545
- if (typeof _cl_scheduleHostShellMark === 'function') _cl_scheduleHostShellMark(outputNode);
546
- if (resTcid) {
547
- __cl_outputCardCache.set(resTcid, outputNode);
548
- }
549
- }
550
- _cl_hideHostRawText(outputNode);
551
- return outputNode;
552
- }
553
- } catch (err) {
554
- console.error('[cl-render] 💥', err.message);
555
- }
556
-
557
- return null;
558
- };
559
-
560
- // ═══════════════════════════════════════════════════════════════
561
- // Buffered Step Replay — cardNode 创建前积压的 steps 逐条展示
562
- // ═══════════════════════════════════════════════════════════════
563
-
564
- function _cl_replayBufferedSteps(toolCallId, state) {
565
- if (!state || !state.cardNode || state._replayActive) return;
566
-
567
- var totalSteps = state.steps.length;
568
- if (totalSteps === 0) return;
569
-
570
- state._replayActive = true;
571
- var revealed = 0;
572
-
573
- console.log('[cl-playback] start replay tcid=' + String(toolCallId || '').substring(0, 8)
574
- + ' steps=' + totalSteps);
575
-
576
- function tick() {
577
- state._replayTimer = null;
578
-
579
- if (!state.cardNode) {
580
- state._replayActive = false;
581
- return;
582
- }
583
-
584
- revealed++;
585
- // 展示前 revealed 步(从 canonical state 切片)
586
- var visibleSteps = state.steps.slice(0, revealed);
587
- _cl_updateCardSteps(state.cardNode, visibleSteps);
588
-
589
- console.log('[cl-playback] tick tcid=' + String(toolCallId || '').substring(0, 8)
590
- + ' revealed=' + revealed + '/' + state.steps.length);
591
-
592
- if (revealed < state.steps.length) {
593
- // 还有步骤未展示
594
- state._replayTimer = setTimeout(tick, 120);
595
- } else {
596
- // 播放完毕
597
- state._replayActive = false;
598
- console.log('[cl-playback] done tcid=' + String(toolCallId || '').substring(0, 8)
599
- + ' steps=' + state.steps.length);
600
-
601
- if (state.pendingFinalize) {
602
- state.pendingFinalize = false;
603
- _cl_finalizeCard(state.cardNode, state.steps);
604
- console.log('[cl-playback] finalize after replay tcid=' + String(toolCallId || '').substring(0, 8));
605
- }
606
- }
607
- }
608
-
609
- // 第一步立即展示,后续逐条
610
- tick();
611
- }
612
-
613
- // ═══════════════════════════════════════════════════════════════
614
- // DOM 操作
615
- // ═══════════════════════════════════════════════════════════════
616
-
617
- // ── Phase A: 主动隐藏宿主 raw text ──
618
-
619
- function _cl_hideHostRawText(cardNode) {
620
- if (!cardNode) return;
621
- // 幂等性守卫:如果父链已经标记为隐藏,跳过重复操作
622
- if (cardNode.closest && cardNode.closest('.cl-remote-agent-raw-hidden')) return;
623
- function hide() {
624
- var hosts = _cl_collectRawTextHosts(cardNode);
625
- for (var h = 0; h < hosts.length; h++) {
626
- _cl_hideRawTextInHost(hosts[h]);
627
- }
628
- }
629
- hide();
630
- if (typeof requestAnimationFrame === 'function') requestAnimationFrame(hide);
631
- setTimeout(hide, 0);
632
- setTimeout(hide, 200);
633
- }
634
-
635
- function _cl_isRemoteToolNameText(text) {
636
- var value = String(text || '').toLowerCase();
637
- return value.indexOf('call_remote_agent') !== -1 || value.indexOf('openagent_delegate') !== -1;
638
- }
639
-
640
- function _cl_isRemoteRawTextContent(text) {
641
- var value = String(text || '');
642
- if (!value) return false;
643
- if (/"msgType"\s*:/.test(value) && /"dataType"\s*:/.test(value)) return true;
644
- if (/"itineraryId"\s*:/.test(value) && /"unitList"\s*:/.test(value)) return true;
645
- if (/"dayList"\s*:/.test(value) && /"unitContexts"\s*:/.test(value)) return true;
646
- if (/(?:<|&lt;)(?:sh_card|tr_card|i_card|bg_card|fr_card|time_data_card)(?:>|&gt;)/.test(value)) return true;
647
- if (value.indexOf('[TRIP_CARDS_READY]') !== -1) return true;
648
- return false;
649
- }
650
-
651
- function _cl_getHostAdapter() {
652
- return (typeof CL !== 'undefined' && CL.hostAdapter) ? CL.hostAdapter : {};
653
- }
654
-
655
- function _cl_collectRawTextHosts(node) {
656
- var hosts = [];
657
- var adapter = _cl_getHostAdapter();
658
- var selectors = adapter.selectors && adapter.selectors.rawTextHostClosest;
659
- function add(host) {
660
- if (!host || hosts.indexOf(host) !== -1) return;
661
- hosts.push(host);
662
- }
663
- add(node.parentElement);
664
- if (node.closest && Array.isArray(selectors)) {
665
- for (var i = 0; i < selectors.length; i++) {
666
- add(node.closest(selectors[i]));
667
- }
668
- }
669
- return hosts;
670
- }
671
-
672
- function _cl_hideRawTextInHost(host) {
673
- if (!host || !host.querySelectorAll) return;
674
- var adapter = _cl_getHostAdapter();
675
- host.classList.add('cl-remote-agent-raw-hidden');
676
- if (adapter.isToolBody && adapter.isToolBody(host)) {
677
- host.classList.add('cl-remote-agent-host-shell');
678
- }
679
- var texts = host.querySelectorAll(':scope > .chat-text');
680
- for (var i = 0; i < texts.length; i++) {
681
- texts[i].style.display = 'none';
682
- texts[i].dataset.openagentRawHidden = '1';
683
- }
684
- }
685
-
686
- function _cl_findRemoteNativeToolShell(node) {
687
- var adapter = _cl_getHostAdapter();
688
- var cur = node && (node.nodeType === 1 ? node : node.parentElement);
689
- var hops = 0;
690
- while (cur && cur !== document.body && hops < 12) {
691
- if (adapter.isRemoteNativeToolShellCandidate && adapter.isRemoteNativeToolShellCandidate(cur)) {
692
- if (_cl_isRemoteNativeToolShell(cur)) return cur;
693
- }
694
- cur = cur.parentElement;
695
- hops++;
696
- }
697
- return null;
698
- }
699
-
700
- function _cl_isRemoteNativeToolShell(shell) {
701
- if (!shell) return false;
702
- var adapter = _cl_getHostAdapter();
703
- var summarySelector = adapter.selectors && adapter.selectors.summary;
704
- var summaryText = '';
705
- var summary = shell.querySelector && summarySelector
706
- ? shell.querySelector(summarySelector)
707
- : null;
708
- if (summary) summaryText = summary.textContent || '';
709
- if (_cl_isRemoteToolNameText(summaryText)) return true;
710
-
711
- var ownText = shell.textContent || '';
712
- // Limit the expensive fallback to the beginning of the host text so huge
713
- // raw JSON payloads do not become a broad "contains" matcher.
714
- return _cl_isRemoteToolNameText(ownText.slice(0, 800));
715
- }
716
-
717
- function _cl_tryHideRemoteRawTextNode(node) {
718
- if (!node) return;
719
- if (node.nodeType !== 1) {
720
- node = node.parentElement;
721
- }
722
- if (!node || node.nodeType !== 1) return;
723
- var textNodes = [];
724
- if (node.matches && node.matches('.chat-text')) {
725
- textNodes.push(node);
726
- }
727
- if (node.querySelectorAll) {
728
- var found = node.querySelectorAll('.chat-text');
729
- for (var i = 0; i < found.length; i++) textNodes.push(found[i]);
730
- }
731
- for (var j = 0; j < textNodes.length; j++) {
732
- var textNode = textNodes[j];
733
- var shell = _cl_findRemoteNativeToolShell(textNode);
734
- if (!shell && _cl_isRemoteRawTextContent(textNode.textContent || '')) {
735
- shell = _cl_findNearestToolLikeShell(textNode);
736
- }
737
- if (shell && _cl_isRemoteRawTextContent(textNode.textContent || '')) {
738
- _cl_hideRawTextInHost(textNode.parentElement);
739
- continue;
740
- }
741
- if (!shell) continue;
742
- var host = textNode.parentElement;
743
- _cl_hideRawTextInHost(host);
744
- }
745
-
746
- var shell = _cl_findRemoteNativeToolShell(node);
747
- if (shell && shell.querySelectorAll) {
748
- var adapter = _cl_getHostAdapter();
749
- var hostSelector = adapter.selectors && adapter.selectors.rawTextHostsWithinShell;
750
- if (hostSelector) {
751
- var hosts = shell.querySelectorAll(hostSelector);
752
- for (var h = 0; h < hosts.length; h++) {
753
- _cl_hideRawTextInHost(hosts[h]);
754
- }
755
- }
756
- }
757
- }
758
-
759
- function _cl_findNearestToolLikeShell(node) {
760
- var adapter = _cl_getHostAdapter();
761
- var cur = node && (node.nodeType === 1 ? node : node.parentElement);
762
- var hops = 0;
763
- while (cur && cur !== document.body && hops < 12) {
764
- if (adapter.isToolLikeShell && adapter.isToolLikeShell(cur)) {
765
- return cur;
766
- }
767
- cur = cur.parentElement;
768
- hops++;
769
- }
770
- return null;
771
- }
772
-
773
- function _cl_installRemoteRawTextObserver() {
774
- if (window.__clRemoteRawTextObserverInstalled) return;
775
- if (!document.body) {
776
- setTimeout(_cl_installRemoteRawTextObserver, 0);
777
- if (document.addEventListener) {
778
- document.addEventListener('DOMContentLoaded', _cl_installRemoteRawTextObserver, { once: true });
779
- }
780
- return;
781
- }
782
- window.__clRemoteRawTextObserverInstalled = true;
783
- function scanExisting() {
784
- if (!document.querySelectorAll) return;
785
- var texts = document.querySelectorAll('.chat-text');
786
- for (var i = 0; i < texts.length; i++) {
787
- _cl_tryHideRemoteRawTextNode(texts[i]);
788
- }
789
- }
790
- scanExisting();
791
- if (typeof MutationObserver !== 'function' || !document.body) return;
792
- var observer = new MutationObserver(function(records) {
793
- for (var i = 0; i < records.length; i++) {
794
- var record = records[i];
795
- for (var j = 0; j < record.addedNodes.length; j++) {
796
- var added = record.addedNodes[j];
797
- _cl_tryHideRemoteRawTextNode(added.nodeType === 1 ? added : added.parentElement);
798
- }
799
- if (record.type === 'characterData') {
800
- _cl_tryHideRemoteRawTextNode(record.target && record.target.parentElement);
801
- }
802
- }
803
- });
804
- observer.observe(document.body, { childList: true, subtree: true, characterData: true });
805
- window.__clRemoteRawTextObserver = observer;
806
- }
807
-
808
- _cl_installRemoteRawTextObserver();
809
-
810
- // ── 局部打开最近宿主 wrapper(不做全局扫描)──
811
-
812
- function _cl_findNearestNativeToolShell(node) {
813
- var adapter = _cl_getHostAdapter();
814
- var cur = node && node.parentElement;
815
- var hops = 0;
816
- while (cur && cur !== document.body && hops < 10) {
817
- if (adapter.isNativeToolShell && adapter.isNativeToolShell(cur)) {
818
- return cur;
819
- }
820
- cur = cur.parentElement;
821
- hops++;
822
- }
823
- return null;
824
- }
825
-
826
- function _cl_forceOpenNativeToolDetails(node, source, tcid) {
827
- if (!node) return;
828
-
829
- function openNearest() {
830
- var shell = _cl_findNearestNativeToolShell(node);
831
- if (!shell) return false;
832
-
833
- if (shell.tagName && shell.tagName.toLowerCase() === 'details') {
834
- if (!shell.open) {
835
- shell.open = true;
836
- shell.setAttribute('open', '');
837
- console.log('[cl-render] 🔓 opened native details source=' + source
838
- + ' tcid=' + String(tcid || '').substring(0, 8));
839
- }
840
- return true;
841
- }
842
-
843
- if (shell.classList && !shell.classList.contains('is-open')) {
844
- shell.classList.add('is-open');
845
- var adapter = _cl_getHostAdapter();
846
- var headerSelector = (adapter.selectors && adapter.selectors.forceOpenHeader) || '';
847
- var header = headerSelector ? shell.querySelector(headerSelector) : null;
848
- if (header) header.setAttribute('aria-expanded', 'true');
849
- console.log('[cl-render] 🔓 opened native collapse source=' + source
850
- + ' tcid=' + String(tcid || '').substring(0, 8));
851
- }
852
- return true;
853
- }
854
-
855
- // Lit 先拿 DOM node,稍后才插入 OpenClaw details body,所以需要延迟重试。
856
- // openNearest 是幂等的(已 open 不重复操作),不需要防抖。
857
- openNearest();
858
- if (typeof requestAnimationFrame === 'function') requestAnimationFrame(openNearest);
859
- setTimeout(openNearest, 300); // 兜底:慢速 Lit render / 异步 wrapper 挂载
860
- }
861
-
862
- function _cl_cardUiKey(modelOrNode) {
863
- if (!modelOrNode) return '';
864
-
865
- var tcid = '';
866
- var kind = '';
867
-
868
- if (modelOrNode.nodeType === 1) {
869
- tcid = modelOrNode.dataset && (modelOrNode.dataset.clCardTcid || '');
870
- kind = modelOrNode.dataset && (modelOrNode.dataset.clCardSource || '');
871
- } else {
872
- tcid = modelOrNode.toolCallId || modelOrNode.tcid || '';
873
- kind = modelOrNode.cardKind || modelOrNode.kind || '';
874
- }
875
-
876
- if (!tcid) return '';
877
- if (kind === 'call') kind = 'execution';
878
- if (kind === 'result') kind = 'output';
879
- if (!kind) kind = 'card';
880
- return kind + ':' + tcid;
881
- }
882
-
883
- function _cl_getCardUiState(modelOrNode) {
884
- var key = _cl_cardUiKey(modelOrNode);
885
- if (!key) return null;
886
- return __cl_cardUiStates.get(key) || null;
887
- }
888
-
889
- function _cl_setCardUiState(modelOrNode, patch) {
890
- var key = _cl_cardUiKey(modelOrNode);
891
- if (!key || !patch) return;
892
-
893
- var current = __cl_cardUiStates.get(key) || {};
894
- var next = {};
895
- for (var k in current) {
896
- if (Object.prototype.hasOwnProperty.call(current, k)) next[k] = current[k];
897
- }
898
- for (var p in patch) {
899
- if (Object.prototype.hasOwnProperty.call(patch, p)) next[p] = patch[p];
900
- }
901
- __cl_cardUiStates.set(key, next);
902
- console.log('[cl-ui-state] save key=' + key
903
- + ' contentCollapsed=' + (next.contentCollapsed === undefined ? '' : next.contentCollapsed)
904
- + ' stepsExpanded=' + (next.stepsExpanded === undefined ? '' : next.stepsExpanded));
905
- }
906
-
907
- function _cl_applyCardUiState(container, modelOrNode) {
908
- if (!container) return;
909
-
910
- var state = _cl_getCardUiState(modelOrNode || container);
911
- if (!state) return;
912
-
913
- var content = container.querySelector('.cl-thought-chain-content');
914
- var caret = container.querySelector('.cl-openagent-header-caret');
915
- if (content && state.contentCollapsed !== undefined) {
916
- if (state.contentCollapsed) {
917
- content.classList.add('cl-content-collapsed');
918
- if (caret) caret.textContent = '▶';
919
- } else {
920
- content.classList.remove('cl-content-collapsed');
921
- if (caret) caret.textContent = '▼';
922
- }
923
- if (typeof _clSyncOutputFooterVisibility === 'function') {
924
- _clSyncOutputFooterVisibility(container, state.contentCollapsed);
925
- }
926
- }
927
-
928
- var body = container.querySelector('.cl-tools-collapse-body');
929
- var stepIcon = container.querySelector('.cl-tools-collapse-header-icon');
930
- if (body && state.stepsExpanded !== undefined) {
931
- if (state.stepsExpanded) {
932
- body.classList.remove('cl-collapsed');
933
- body.classList.add('cl-expanded');
934
- if (stepIcon) stepIcon.classList.remove('cl-collapsed');
935
- } else {
936
- body.classList.remove('cl-expanded');
937
- body.classList.add('cl-collapsed');
938
- if (stepIcon) stepIcon.classList.add('cl-collapsed');
939
- }
940
- }
941
- }
942
-
943
- function _cl_applyStoredRecordToModel(model, record) {
944
- if (!model || !record) return;
945
- var args = record.args || {};
946
- if (!model.agentId && (args.agent_id || args.agentId)) {
947
- model.agentId = args.agent_id || args.agentId;
948
- }
949
- if (model.agentName === 'Remote Agent' || !model.agentName) {
950
- model.agentName = args.agent_name || args.agentName || model.agentId || 'Remote Agent';
951
- }
952
- if (!model.agentAvatar) {
953
- model.agentAvatar = args.agent_avatar || args.agentAvatar || args.avatar || args.avatar_url || args.avatarUrl || null;
954
- }
955
- if (!model.agentBio) {
956
- model.agentBio = typeof _clPickAgentDescription === 'function'
957
- ? _clPickAgentDescription(args)
958
- : (args.agent_bio || args.agentBio || args.description || args.agent_description || args.MOM || '');
959
- }
960
- if (!model.agentStars) {
961
- model.agentStars = args.agent_stars || args.agentStars || '3.2万';
962
- }
963
- var identity = CL && typeof CL.getAgentIdentity === 'function'
964
- ? CL.getAgentIdentity(model.agentId, model.agentName)
965
- : null;
966
- if (identity) {
967
- model.agentId = model.agentId || identity.id || identity.agent_id;
968
- model.agentName = (model.agentName && model.agentName !== 'Remote Agent') ? model.agentName : (identity.name || model.agentName);
969
- model.agentAvatar = model.agentAvatar || identity.avatar || null;
970
- if (!model.agentBio) {
971
- model.agentBio = typeof _clPickAgentDescription === 'function'
972
- ? _clPickAgentDescription(identity)
973
- : (identity.bio || identity.description || identity.agent_bio || identity.agentBio || identity.MOM || '');
974
- }
975
- }
976
- model.taskText = model.taskText || args.task || args.message || args.instruction || '';
977
- }
978
-
979
- function _cl_applyThinkChunkToLiveState(state, chunk) {
980
- if (!state || !chunk || chunk.dataType !== 'think') return false;
981
-
982
- if (chunk._clProgressStep) {
983
- var beforeProgressSig = _cl_stepsSignature(state.steps);
984
- var stepIndex = Number(chunk.stepIndex);
985
- if (!Number.isFinite(stepIndex) || stepIndex < 0) return false;
986
- stepIndex = Math.floor(stepIndex);
987
-
988
- if (chunk._clTripProgressRoot) {
989
- state.steps = _cl_pruneGenericProgressSteps(state.steps);
990
- stepIndex = 0;
991
- }
992
-
993
- while (state.steps.length <= stepIndex) {
994
- state.steps.push({ title: '', detail: '' });
995
- }
996
-
997
- var progressStep = state.steps[stepIndex];
998
- var progressTitle = _cl_normalizeStepTitle(chunk.text || '');
999
- var progressChanged = false;
1000
-
1001
- if (progressTitle && progressStep.title !== progressTitle) {
1002
- progressStep.title = progressTitle;
1003
- progressChanged = true;
1004
- }
1005
- if (progressStep.detail) progressStep.detail = '';
1006
-
1007
- state.steps = _cl_compactThoughtSteps(state.steps);
1008
- var afterProgressSig = _cl_stepsSignature(state.steps);
1009
- if (!_cl_isGenericProgressTitle(progressTitle)) {
1010
- state.lastThinkType = 'progress-step';
1011
- }
1012
- return progressChanged || beforeProgressSig !== afterProgressSig;
1013
- }
1014
-
1015
- var key = _cl_getLiveChunkKey(state, chunk);
1016
- if (key && state.seenChunkKeys && state.seenChunkKeys[key]) {
1017
- return false;
1018
- }
1019
- if (key) {
1020
- if (!state.seenChunkKeys) state.seenChunkKeys = Object.create(null);
1021
- state.seenChunkKeys[key] = true;
1022
- }
1023
-
1024
- var type = chunk.thinkType || '';
1025
- var changed = _cl_applyThinkChunkByAgentMode(state, chunk);
1026
- state.lastThinkType = type;
1027
- return changed;
1028
- }
1029
-
1030
- function _cl_applyThinkChunkByAgentMode(target, chunk) {
1031
- if (!target || !chunk || chunk.dataType !== 'think') return false;
1032
-
1033
- var beforeSig = _cl_stepsSignature(target.steps);
1034
- _cl_storeThinkChunk(target, chunk);
1035
- _cl_rebuildThinkStepsByAgentMode(target);
1036
-
1037
- return beforeSig !== _cl_stepsSignature(target.steps);
1038
- }
1039
-
1040
- function _cl_rebuildThinkStepsByAgentMode(target) {
1041
- if (!target) return;
1042
- target.progressMode = _cl_getRemoteAgentProgressMode(target.args, target.progressMode);
1043
- if (target.progressMode === CL_REMOTE_AGENT_PROGRESS_MODE_TRAVEL_TITLE) {
1044
- target.steps = _cl_rebuildTravelTitleSteps(target.thinkChunks || []);
1045
- } else {
1046
- target.steps = _cl_rebuildGenericStreamSteps(target.thinkChunks || []);
1047
- }
1048
- }
1049
-
1050
- function _cl_storeThinkChunk(target, chunk) {
1051
- if (!target.thinkChunks) target.thinkChunks = [];
1052
-
1053
- var text = typeof chunk.text === 'string' ? chunk.text : '';
1054
- var item = {
1055
- dataType: chunk.dataType,
1056
- thinkType: chunk.thinkType || '',
1057
- text: text,
1058
- index: chunk.index,
1059
- _clPlainTextUpdate: !!chunk._clPlainTextUpdate,
1060
- _clLiveUpdateFallback: !!chunk._clLiveUpdateFallback,
1061
- _clSeq: target.unindexedSeq || 0
1062
- };
1063
- target.unindexedSeq = (target.unindexedSeq || 0) + 1;
1064
-
1065
- var idx = _cl_getChunkIndex(item);
1066
- if (idx >= 0) {
1067
- for (var i = 0; i < target.thinkChunks.length; i++) {
1068
- var existing = target.thinkChunks[i];
1069
- if (!existing || _cl_getChunkIndex(existing) !== idx || (existing.thinkType || '') !== item.thinkType) {
1070
- continue;
1071
- }
1072
- var existingText = typeof existing.text === 'string' ? existing.text : '';
1073
- if (item.text && existingText && item.text.indexOf(existingText) === 0) {
1074
- existing.text = item.text;
1075
- existing._clSeq = item._clSeq;
1076
- return;
1077
- }
1078
- if (existingText && item.text && existingText.indexOf(item.text) === 0) {
1079
- return;
1080
- }
1081
- }
1082
- }
1083
-
1084
- target.thinkChunks.push(item);
1085
- }
1086
-
1087
- function _cl_sortThinkChunks(chunks) {
1088
- var sorted = (chunks || []).slice();
1089
- sorted.sort(function(a, b) {
1090
- var ai = _cl_getChunkIndex(a);
1091
- var bi = _cl_getChunkIndex(b);
1092
- if (ai >= 0 && bi >= 0 && ai !== bi) return ai - bi;
1093
- if (ai >= 0 && bi < 0) return -1;
1094
- if (ai < 0 && bi >= 0) return 1;
1095
- return ((a && a._clSeq) || 0) - ((b && b._clSeq) || 0);
1096
- });
1097
- return sorted;
1098
- }
1099
-
1100
- function _cl_rebuildTravelTitleSteps(chunks) {
1101
- var sorted = _cl_sortThinkChunks(chunks);
1102
- var steps = [];
1103
- var pendingTitle = '';
1104
-
1105
- function flushPending() {
1106
- var title = _cl_normalizeStepTitle(pendingTitle);
1107
- if (title) steps.push({ title: title, detail: '' });
1108
- pendingTitle = '';
1109
- }
1110
-
1111
- for (var i = 0; i < sorted.length; i++) {
1112
- var chunk = sorted[i] || {};
1113
- var type = chunk.thinkType || '';
1114
- var text = _cl_normalizeStepTitle(chunk.text || '');
1115
-
1116
- if (type === 'title') {
1117
- if (text) pendingTitle = _cl_mergeStepTitle(pendingTitle, text);
1118
- continue;
1119
- }
1120
-
1121
- // 旅游梦想家的 content 是 title 之间的过程说明,只作为 title 分组边界。
1122
- flushPending();
1123
- }
1124
-
1125
- flushPending();
1126
- return _cl_compactThoughtSteps(steps);
1127
- }
1128
-
1129
- function _cl_rebuildGenericStreamSteps(chunks) {
1130
- var sorted = _cl_sortThinkChunks(chunks);
1131
- var text = '';
1132
- var shortPlainSteps = [];
1133
- var allShortPlain = sorted.length > 0 && sorted.length <= 20;
1134
- var hasFallbackUpdates = false;
1135
-
1136
- for (var i = 0; i < sorted.length; i++) {
1137
- var chunk = sorted[i] || {};
1138
- var part = typeof chunk.text === 'string' ? chunk.text : '';
1139
- if (!part) continue;
1140
- var normalizedPart = _cl_normalizeStepTitle(part);
1141
- if (chunk._clLiveUpdateFallback) {
1142
- hasFallbackUpdates = true;
1143
- }
1144
- if (!chunk._clPlainTextUpdate || !normalizedPart || normalizedPart.length > 20) {
1145
- allShortPlain = false;
1146
- } else if (shortPlainSteps.length === 0 || shortPlainSteps[shortPlainSteps.length - 1].title !== normalizedPart) {
1147
- shortPlainSteps.push({ title: normalizedPart, detail: '' });
1148
- }
1149
- text = _cl_mergeStepTitle(text, part);
1150
- }
1151
-
1152
- if (hasFallbackUpdates) {
1153
- return _cl_rebuildFallbackUpdateSteps(sorted);
1154
- }
1155
-
1156
- if (allShortPlain && shortPlainSteps.length > 0) {
1157
- return shortPlainSteps;
1158
- }
1159
-
1160
- text = _cl_normalizeStepTitle(text);
1161
- return text ? [{ title: text, detail: '' }] : [];
1162
- }
1163
-
1164
- function _cl_rebuildFallbackUpdateSteps(chunks) {
1165
- var steps = [];
1166
- for (var i = 0; i < chunks.length; i++) {
1167
- var chunk = chunks[i] || {};
1168
- var parts = _cl_splitKnownFallbackStatusLine(chunk.text || '');
1169
- for (var j = 0; j < parts.length; j++) {
1170
- var part = _cl_normalizeStepTitle(parts[j] || '');
1171
- if (!part) continue;
1172
- if (steps.length > 0 && steps[steps.length - 1].title === part) continue;
1173
- steps.push({ title: part, detail: '', _clKeepSeparate: true });
1174
- }
1175
- }
1176
- return steps;
1177
- }
1178
-
1179
- function _cl_getLiveChunkKey(state, chunk) {
1180
- if (chunk && chunk._clProgressStep) {
1181
- return 'p:' + String(chunk.stepIndex) + ':' + (chunk.text || '') + ':' + (chunk.detail || '');
1182
- }
1183
- if (chunk && Object.prototype.hasOwnProperty.call(chunk, 'index')
1184
- && chunk.index !== null && chunk.index !== undefined && chunk.index !== '') {
1185
- // 去重 key 包含完整内容:同 index 不同 text 是累计更新,不应跳过
1186
- return 'i:' + String(chunk.index) + ':' + (chunk.thinkType || '') + ':' + (chunk.text || '');
1187
- }
1188
- return 'u:' + (chunk.thinkType || '') + ':' + (chunk.text || '');
1189
- }
1190
-
1191
- function _cl_getChunkIndex(chunk) {
1192
- if (!chunk || chunk.index === null || chunk.index === undefined || chunk.index === '') return -1;
1193
- var n = Number(chunk.index);
1194
- if (!Number.isFinite(n) || n < 0) return -1;
1195
- return Math.floor(n);
1196
- }
1197
-
1198
- function _cl_normalizeStepTitle(text) {
1199
- return String(text || '').replace(/[\u2705\u2611\uFE0F\u2714\uFE0F]/g, '').trim();
1200
- }
1201
-
1202
- function _cl_stepsSignature(steps) {
1203
- if (!Array.isArray(steps) || steps.length === 0) return '';
1204
- return steps.map(function(step) {
1205
- return _cl_normalizeStepTitle(step && step.title || '');
1206
- }).join('|');
1207
- }
1208
-
1209
- function _cl_isGenericProgressTitle(title) {
1210
- title = _cl_normalizeStepTitle(title);
1211
- if (!title) return true;
1212
- var genericTitles = {
1213
- '正在整理行程卡片': true,
1214
- '远端 Agent 已开始生成': true,
1215
- '远端 Agent 正在回复': true,
1216
- '远端 Agent 正在思考': true,
1217
- '远端 Agent 正在生成回复': true,
1218
- '正在生成回复内容': true,
1219
- '远端 Agent 已完成结果生成': true
1220
- };
1221
- return !!genericTitles[title];
1222
- }
1223
-
1224
- function _cl_isShortTitleFragment(title) {
1225
- title = _cl_normalizeStepTitle(title).replace(/\s+/g, '');
1226
- if (!title) return false;
1227
- if (_cl_normalizeKnownTitlePhrase(title) !== title) return false;
1228
- if (title.indexOf('查找') !== -1
1229
- && (title.indexOf('目的地') !== -1 || title.indexOf('信息') !== -1)
1230
- && !(title.indexOf('目的地') !== -1 && title.indexOf('信息') !== -1)
1231
- && title.length <= 6) {
1232
- return true;
1233
- }
1234
- return title.length <= 3;
1235
- }
1236
-
1237
- function _cl_shouldMergeStepTitle(current, incoming) {
1238
- current = _cl_normalizeStepTitle(current);
1239
- incoming = _cl_normalizeStepTitle(incoming);
1240
- if (!current || !incoming) return false;
1241
- if (incoming.length > current.length && incoming.indexOf(current) === 0) return true;
1242
- return _cl_isShortTitleFragment(current) && _cl_isShortTitleFragment(incoming);
1243
- }
1244
-
1245
- function _cl_normalizeKnownTitlePhrase(title) {
1246
- var compact = _cl_normalizeStepTitle(title).replace(/\s+/g, '');
1247
- if (!compact) return '';
1248
-
1249
- // 远端有时会把同一个 title 拆成乱序短片段:查找 / 信息 / 目的地。
1250
- // UI 只展示 title 时,需要把这个产品语义还原成一句话。
1251
- if (compact.indexOf('了解') !== -1
1252
- && compact.indexOf('你的') !== -1
1253
- && compact.indexOf('需求') !== -1
1254
- && compact.length <= 6) {
1255
- return '了解你的需求';
1256
- }
1257
-
1258
- if (compact.indexOf('查找') !== -1
1259
- && compact.indexOf('目的地') !== -1
1260
- && compact.indexOf('信息') !== -1
1261
- && compact.length <= 8) {
1262
- return '查找目的地信息';
1263
- }
1264
-
1265
- return compact;
1266
- }
1267
-
1268
- function _cl_compactThoughtSteps(steps) {
1269
- if (!Array.isArray(steps) || steps.length === 0) return [];
1270
-
1271
- var compacted = [];
1272
- var pendingTitle = '';
1273
-
1274
- function flushPending() {
1275
- var normalized = _cl_normalizeKnownTitlePhrase(pendingTitle);
1276
- if (normalized) compacted.push({ title: normalized, detail: '' });
1277
- pendingTitle = '';
1278
- }
1279
-
1280
- for (var i = 0; i < steps.length; i++) {
1281
- var title = _cl_normalizeStepTitle(steps[i] && steps[i].title || '');
1282
- if (!title || _cl_isGenericProgressTitle(title)) continue;
1283
-
1284
- if (steps[i] && steps[i]._clKeepSeparate) {
1285
- flushPending();
1286
- compacted.push({ title: title, detail: steps[i].detail || '', _clKeepSeparate: true });
1287
- continue;
1288
- }
1289
-
1290
- var knownTitle = _cl_normalizeKnownTitlePhrase(title);
1291
- if (knownTitle && knownTitle !== title) {
1292
- flushPending();
1293
- compacted.push({ title: knownTitle, detail: '' });
1294
- continue;
1295
- }
1296
-
1297
- if (_cl_isShortTitleFragment(title)) {
1298
- pendingTitle = _cl_mergeStepTitle(pendingTitle, title);
1299
- continue;
1300
- }
1301
-
1302
- flushPending();
1303
- compacted.push({ title: title, detail: '' });
1304
- }
1305
-
1306
- flushPending();
1307
- return compacted;
1308
- }
1309
-
1310
- function _cl_pruneGenericProgressSteps(steps) {
1311
- if (!Array.isArray(steps) || steps.length === 0) return [];
1312
- return steps.filter(function(step) {
1313
- var title = _cl_normalizeStepTitle(step && step.title || '');
1314
- return !_cl_isGenericProgressTitle(title);
1315
- });
1316
- }
1317
-
1318
- function _cl_mergeStepTitle(current, incoming) {
1319
- current = current || '';
1320
- incoming = incoming || '';
1321
- if (!incoming) return current;
1322
- if (!current) return incoming;
1323
- if (incoming.length > current.length && incoming.indexOf(current) === 0) return incoming;
1324
-
1325
- var maxOverlap = Math.min(current.length, incoming.length);
1326
- for (var len = maxOverlap; len > 0; len--) {
1327
- if (current.slice(current.length - len) === incoming.slice(0, len)) {
1328
- return current + incoming.slice(len);
1329
- }
1330
- }
1331
- return current + incoming;
1332
- }
1333
-
1334
- function _cl_updateCardSteps(card, steps) {
1335
- var body = card.querySelector('.cl-tools-collapse-body');
1336
- var header = card.querySelector('.cl-tools-collapse-header-title');
1337
- if (!body || !header) return;
1338
-
1339
- steps = _cl_compactThoughtSteps(steps || []);
1340
-
1341
- header.textContent = '思考中...';
1342
-
1343
- // 默认产品行为:SSE 传输时步骤应自动展开展示。
1344
- // 只有用户明确点过“思考中/思考完成”后,才尊重用户保存的折叠状态。
1345
- _cl_autoOpenStepsIfUncontrolled(card);
1346
-
1347
- var existing = body.querySelectorAll('.cl-tools-collapse-step');
1348
-
1349
- // 已存在步骤可能继续收到 title/content token,逐个刷新文本和详情。
1350
- var updateCount = Math.min(existing.length, steps.length);
1351
- for (var ei = 0; ei < updateCount; ei++) {
1352
- _cl_updateStepElement(existing[ei], steps[ei], ei === steps.length - 1);
1353
- }
1354
-
1355
- // 合并/过滤后 steps 可能变少,旧 DOM 行必须删除,否则页面会残留错误断句。
1356
- for (var ri = existing.length - 1; ri >= steps.length; ri--) {
1357
- existing[ri].remove();
1358
- }
1359
- existing = body.querySelectorAll('.cl-tools-collapse-step');
1360
-
1361
- // 差量添加新步骤
1362
- for (var i = existing.length; i < steps.length; i++) {
1363
- if (i > 0) {
1364
- var prev = body.querySelectorAll('.cl-tools-collapse-step')[i - 1];
1365
- if (prev) {
1366
- var prevDot = prev.querySelector('.cl-tools-collapse-step-dot');
1367
- if (prevDot) prevDot.className = 'cl-tools-collapse-step-check';
1368
- }
1369
- }
1370
- var stepEl = _clCreateStepElement(steps[i], i === steps.length - 1);
1371
- body.appendChild(stepEl);
1372
- }
1373
- }
1374
-
1375
- function _cl_autoOpenStepsIfUncontrolled(card) {
1376
- if (!card) return;
1377
- var uiState = _cl_getCardUiState(card);
1378
- if (uiState && uiState.stepsExpanded !== undefined) return;
1379
-
1380
- // 缓存 body 引用,避免 5-16Hz Lit rerender 时重复 querySelector
1381
- if (!card.__clStepsBody) {
1382
- card.__clStepsBody = card.querySelector('.cl-tools-collapse-body');
1383
- }
1384
- var body = card.__clStepsBody;
1385
- if (!body) return;
1386
-
1387
- body.classList.remove('cl-collapsed');
1388
- body.classList.add('cl-expanded');
1389
-
1390
- var headerIcon = card.querySelector('.cl-tools-collapse-header-icon');
1391
- if (headerIcon) headerIcon.classList.remove('cl-collapsed');
1392
- }
1393
-
1394
- function _cl_updateStepElement(stepEl, step, isLatest) {
1395
- if (!stepEl || !step) return;
1396
-
1397
- var titleEl = stepEl.querySelector('.cl-tools-collapse-step-title');
1398
- if (titleEl) {
1399
- titleEl.textContent = (step.title || '').replace(/[\u2705\u2611\uFE0F\u2714\uFE0F]/g, '').trim();
1400
- }
1401
- var detailEl = stepEl.querySelector('.cl-tools-collapse-step-detail');
1402
- if (detailEl) {
1403
- detailEl.remove();
1404
- }
1405
-
1406
- var iconWrap = stepEl.querySelector('.cl-tools-collapse-step-icon');
1407
- if (iconWrap) {
1408
- var activeDot = iconWrap.querySelector('.cl-tools-collapse-step-dot');
1409
- var check = iconWrap.querySelector('.cl-tools-collapse-step-check');
1410
- if (isLatest) {
1411
- if (check) {
1412
- iconWrap.innerHTML = '';
1413
- var dot = document.createElement('div');
1414
- dot.className = 'cl-tools-collapse-step-dot cl-active';
1415
- iconWrap.appendChild(dot);
1416
- } else if (activeDot) {
1417
- activeDot.className = 'cl-tools-collapse-step-dot cl-active';
1418
- }
1419
- } else if (activeDot) {
1420
- iconWrap.innerHTML = '';
1421
- var done = document.createElement('div');
1422
- done.className = 'cl-tools-collapse-step-check';
1423
- iconWrap.appendChild(done);
1424
- }
1425
- }
1426
- }
1427
-
1428
- function _cl_countChunkTypes(chunks) {
1429
- var counts = { step: 0, text: 0, result: 0, content: 0, error: 0, data: 0, other: 0 };
1430
- for (var i = 0; i < chunks.length; i++) {
1431
- var type = chunks[i] && chunks[i].type;
1432
- if (Object.prototype.hasOwnProperty.call(counts, type)) counts[type]++;
1433
- else counts.other++;
1434
- }
1435
- return counts;
1436
- }
1437
-
1438
- function _cl_finalizeCard(card, steps) {
1439
- if (card && card.classList && card.classList.contains('cl-exec-v2')) {
1440
- if (typeof _clUpdateAgentStatusTextV2 === 'function') _clUpdateAgentStatusTextV2(card, 'completed');
1441
- } else if (typeof _clUpdateAgentStatusText === 'function') {
1442
- _clUpdateAgentStatusText(card, 'completed');
1443
- }
1444
- var header = card.querySelector('.cl-tools-collapse-header-title');
1445
- if (header) {
1446
- header.textContent = '思考完成';
1447
- }
1448
-
1449
- var dots = card.querySelectorAll('.cl-tools-collapse-step-dot');
1450
- for (var i = 0; i < dots.length; i++) {
1451
- dots[i].className = 'cl-tools-collapse-step-check';
1452
- }
1453
-
1454
- // 执行完成后隐藏第一个 chat-bubble(execution card),仅保留 output card
1455
- // 仅 V2 启用:V1 的 output card 布局不同,不宜隐藏 execution bubble
1456
- var isV2Finalize = typeof _clIsHostVersionGte === 'function' && _clIsHostVersionGte('2026.6.9');
1457
- if (isV2Finalize && card && !card.__clBubbleHidden) {
1458
- card.__clBubbleHidden = true;
1459
- var bubble = card.closest && card.closest('.chat-bubble');
1460
- if (bubble) {
1461
- // 延迟隐藏,等 output card(第二个 bubble)渲染完毕
1462
- setTimeout(function() {
1463
- bubble.style.display = 'none';
1464
- }, 500);
1465
- }
1466
- }
1467
-
1468
- // Finalize 只负责把 execution card 标记为完成态。
1469
- // 不在这里强制折叠 steps:OpenClaw 会周期性 rerender,若 finalize 每次都
1470
- // 写 cl-collapsed,会覆盖用户手动展开后的状态。
1471
- }
1472
-
1473
- // ═══════════════════════════════════════════════════════════════
1474
- // 解析
1475
- // ═══════════════════════════════════════════════════════════════
1476
-
1477
- function _cl_extractText(value) {
1478
- if (typeof value === 'string') return value;
1479
- if (!value) return '';
1480
- if (Array.isArray(value.content)) {
1481
- return value.content
1482
- .filter(function(c) { return c.type === 'text'; })
1483
- .map(function(c) { return c.text || ''; })
1484
- .join('\n');
1485
- }
1486
- if (typeof value.text === 'string') return value.text;
1487
- try { return JSON.stringify(value, null, 2); } catch(e) { return String(value); }
1488
- }
1489
-
1490
- function _cl_parseThinkChunks(rawText, scope) {
1491
- var chunks = [];
1492
- if (!rawText || typeof rawText !== 'string') return chunks;
1493
- var lines = rawText.trim().split('\n');
1494
- for (var i = 0; i < lines.length; i++) {
1495
- var line = lines[i].trim();
1496
- if (!line) continue;
1497
- try {
1498
- var obj = JSON.parse(line);
1499
- _cl_ingestTripCardData(obj, scope);
1500
- if (obj && obj.dataType === 'think') chunks.push(obj);
1501
- } catch (e) {}
1502
- }
1503
- return chunks;
1504
- }
1505
-
1506
- function _cl_parseLiveUpdateChunks(rawText, scope) {
1507
- _cl_ingestLiveUpdateData(rawText, scope);
1508
- var chunks = _cl_parseThinkChunks(rawText, scope);
1509
- if (chunks.length > 0) return chunks;
1510
- return _cl_extractLiveUpdateFallbackChunks(rawText);
1511
- }
1512
-
1513
- function _cl_extractLiveUpdateFallbackChunks(rawText) {
1514
- var value = String(rawText || '').trim();
1515
- if (!value) return [];
1516
-
1517
- var parts = value.split(/\r?\n/).map(function(line) {
1518
- return String(line || '').trim();
1519
- }).filter(Boolean);
1520
- if (parts.length === 0) parts = [value];
1521
-
1522
- var chunks = [];
1523
- for (var i = 0; i < parts.length; i++) {
1524
- var splitParts = _cl_splitKnownFallbackStatusLine(parts[i]);
1525
- for (var j = 0; j < splitParts.length; j++) {
1526
- var text = splitParts[j];
1527
- if (!text) continue;
1528
- chunks.push({
1529
- dataType: 'think',
1530
- thinkType: 'content',
1531
- text: text,
1532
- _clPlainTextUpdate: _cl_isPlainLiveUpdateText(text),
1533
- _clLiveUpdateFallback: true
1534
- });
1535
- }
1536
- }
1537
- return chunks;
1538
- }
1539
-
1540
- function _cl_splitKnownFallbackStatusLine(line) {
1541
- line = String(line || '').trim();
1542
- if (!line) return [];
1543
-
1544
- var compact = line.replace(/\s+/g, '');
1545
- var statuses = ['已提交', '生成中', '已完成'];
1546
- var rest = compact;
1547
- var pieces = [];
1548
-
1549
- while (rest) {
1550
- var matched = '';
1551
- for (var i = 0; i < statuses.length; i++) {
1552
- if (rest.indexOf(statuses[i]) === 0) {
1553
- matched = statuses[i];
1554
- break;
1555
- }
1556
- }
1557
- if (!matched) return [line];
1558
- pieces.push(matched);
1559
- rest = rest.slice(matched.length);
1560
- }
1561
-
1562
- return pieces.length > 1 ? pieces : [line];
1563
- }
1564
-
1565
- function _cl_isPlainLiveUpdateText(value) {
1566
- value = String(value || '').trim();
1567
- if (!value) return false;
1568
- return value.charAt(0) !== '{' && value.charAt(0) !== '[';
1569
- }
1570
-
1571
- function _cl_ingestLiveUpdateData(rawText, scope) {
1572
- if (!rawText || typeof rawText !== 'string') return;
1573
- var lines = rawText.trim().split('\n');
1574
- for (var i = 0; i < lines.length; i++) {
1575
- var line = lines[i].trim();
1576
- if (!line) continue;
1577
- try {
1578
- var obj = JSON.parse(line);
1579
- if (obj && typeof obj === 'object') _cl_ingestTripCardData(obj, scope);
1580
- } catch (e) {}
1581
- }
1582
- }
1583
-
1584
- function _cl_ingestTripCardData(obj, scope) {
1585
- if (!obj || typeof obj !== 'object') return;
1586
-
1587
- function ensureTripCardData() {
1588
- if (CL && typeof CL.ensureTripCardData === 'function') {
1589
- return CL.ensureTripCardData();
1590
- }
1591
- if (!CL.tripCardData) {
1592
- CL.tripCardData = { unitContexts: {}, dayList: [], foodData: null, budgetData: null };
1593
- }
1594
- if (!CL.tripCardData.unitContexts) CL.tripCardData.unitContexts = {};
1595
- if (!Array.isArray(CL.tripCardData.dayList)) CL.tripCardData.dayList = [];
1596
- return CL.tripCardData;
1597
- }
1598
-
1599
- function mergeTripCardData(data) {
1600
- if (!data || typeof data !== 'object') return false;
1601
- if (CL && typeof CL.mergeTripCardData === 'function') {
1602
- return CL.mergeTripCardData(data, { replaceDayList: true, scope: scope });
1603
- }
1604
-
1605
- var changed = false;
1606
- var target = ensureTripCardData();
1607
-
1608
- if (data.unitContexts && typeof data.unitContexts === 'object') {
1609
- var ids = Object.keys(data.unitContexts);
1610
- for (var i = 0; i < ids.length; i++) {
1611
- target.unitContexts[ids[i]] = data.unitContexts[ids[i]];
1612
- }
1613
- if (ids.length > 0) changed = true;
1614
- }
1615
-
1616
- if (data.unitContext && data.unitContext.id) {
1617
- target.unitContexts[data.unitContext.id] = data.unitContext;
1618
- changed = true;
1619
- }
1620
-
1621
- if (Array.isArray(data.dayList)) {
1622
- target.dayList = data.dayList;
1623
- changed = true;
1624
- }
1625
-
1626
- if (data.foodData) {
1627
- target.foodData = data.foodData;
1628
- changed = true;
1629
- }
1630
-
1631
- var incomingBudget = data.budgetData
1632
- || data.budget
1633
- || (data.result && (data.result.budgetData || data.result.budget));
1634
- if (incomingBudget) {
1635
- target.budgetData = incomingBudget;
1636
- changed = true;
1637
- }
1638
-
1639
- return changed;
1640
- }
1641
-
1642
- function persistTripCardData() {
1643
- try {
1644
- if (CL && CL.tripCardData) {
1645
- localStorage.setItem('openagent_tripCardData', JSON.stringify(CL.tripCardData));
1646
- }
1647
- } catch (e) {}
1648
- }
1649
-
1650
- var changed = false;
1651
-
1652
- if (obj.fullJson && typeof obj.fullJson === 'string') {
1653
- try {
1654
- var parsedData = JSON.parse(obj.fullJson);
1655
- changed = mergeTripCardData(parsedData) || changed;
1656
- } catch (e) {}
1657
- } else if (obj.fullJson && typeof obj.fullJson === 'object') {
1658
- changed = mergeTripCardData(obj.fullJson) || changed;
1659
- }
1660
-
1661
- if (obj.tripCardData && typeof obj.tripCardData === 'object') {
1662
- changed = mergeTripCardData(obj.tripCardData) || changed;
1663
- }
1664
-
1665
- if (obj.itineraryVersionInfo && typeof obj.itineraryVersionInfo === 'object') {
1666
- changed = mergeTripCardData(obj.itineraryVersionInfo) || changed;
1667
- }
1668
-
1669
- if (obj.itinerary && typeof obj.itinerary === 'object') {
1670
- changed = mergeTripCardData(obj.itinerary) || changed;
1671
- }
1672
-
1673
- var nestedFields = ['data', 'text', 'content', 'result', 'output'];
1674
- for (var fi = 0; fi < nestedFields.length; fi++) {
1675
- var nested = obj[nestedFields[fi]];
1676
- if (!nested) continue;
1677
- if (typeof nested === 'object') {
1678
- changed = mergeTripCardData(nested) || changed;
1679
- continue;
1680
- }
1681
- if (typeof nested === 'string'
1682
- && nested.charAt(0) === '{'
1683
- && /"unitContexts"|"dayList"|"foodData"|"budgetData"|"budget"/.test(nested)) {
1684
- try {
1685
- changed = mergeTripCardData(JSON.parse(nested)) || changed;
1686
- } catch (e) {}
1687
- }
1688
- }
1689
-
1690
- changed = mergeTripCardData(obj) || changed;
1691
-
1692
- if (changed) {
1693
- CL.tripCardDataFetchFailed = false;
1694
- persistTripCardData();
1695
- }
1696
- }
1697
-
1698
-
1699
-
1700
- console.log('[openagent] ✅ render hook v2 registered');