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