@yumiai/chat-widget 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,3235 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ ChatWidget: () => ChatWidget_default,
34
+ ChatWidgetAuthError: () => ChatWidgetAuthError,
35
+ ChatWidgetNetworkError: () => ChatWidgetNetworkError,
36
+ ChildAgentCard: () => ChildAgentCard_default,
37
+ DEFAULT_CONFIG: () => DEFAULT_CONFIG,
38
+ DefaultJetAgentsAdapter: () => DefaultJetAgentsAdapter,
39
+ MessageContent: () => MessageContent_default,
40
+ PinnedArea: () => PinnedArea_default,
41
+ PlanCard: () => PlanCard_default,
42
+ RoundHeader: () => RoundHeader_default,
43
+ SchemaFormRenderer: () => SchemaFormRenderer_default,
44
+ TodoCard: () => TodoCard_default,
45
+ ToolCardBuffering: () => ToolCardBuffering_default,
46
+ isTodoNotificationContent: () => isTodoNotificationContent,
47
+ mergeConsecutiveThinkMessages: () => mergeConsecutiveThinkMessages,
48
+ useMessageAggregator: () => useMessageAggregator,
49
+ useSSE: () => useSSE
50
+ });
51
+ module.exports = __toCommonJS(index_exports);
52
+
53
+ // src/ChatWidget.tsx
54
+ var import_react21 = __toESM(require("react"), 1);
55
+
56
+ // src/types.ts
57
+ function isTodoNotificationContent(data) {
58
+ if (!data || typeof data !== "object") return false;
59
+ const obj = data;
60
+ return obj.operation === "checklist_edit" && obj.checklist != null;
61
+ }
62
+ var DEFAULT_CONFIG = {
63
+ showPinnedArea: true,
64
+ showPlan: true,
65
+ enableTypewriter: true,
66
+ typewriterSpeed: 30,
67
+ autoScroll: true,
68
+ childAgentMaxHeight: 200,
69
+ theme: "auto",
70
+ displayLevels: [0, 1, 2]
71
+ };
72
+ function mergeConsecutiveThinkMessages(messages) {
73
+ if (messages.length === 0) return messages;
74
+ const result = [];
75
+ let pendingThink = null;
76
+ for (const msg of messages) {
77
+ if (msg.contentType === "think") {
78
+ if (pendingThink) {
79
+ const prev = pendingThink;
80
+ pendingThink = {
81
+ ...prev,
82
+ contentChunks: [...prev.contentChunks, ...msg.contentChunks]
83
+ };
84
+ } else {
85
+ pendingThink = { ...msg, contentChunks: [...msg.contentChunks] };
86
+ }
87
+ } else {
88
+ if (pendingThink) {
89
+ result.push(pendingThink);
90
+ pendingThink = null;
91
+ }
92
+ result.push(msg);
93
+ }
94
+ }
95
+ if (pendingThink) {
96
+ result.push(pendingThink);
97
+ }
98
+ return result;
99
+ }
100
+
101
+ // src/hooks/useMessageAggregator.ts
102
+ var import_react = require("react");
103
+ function extractTaskPurposeFromPreview(preview) {
104
+ const m = preview.match(/"_?task_purpose"\s*:\s*"([^"]*)"/);
105
+ return m?.[1] || void 0;
106
+ }
107
+ function useMessageAggregator() {
108
+ const [state, setState] = (0, import_react.useState)({
109
+ rounds: /* @__PURE__ */ new Map(),
110
+ currentInteractionId: null
111
+ });
112
+ const [todoMap, setTodoMap] = (0, import_react.useState)(/* @__PURE__ */ new Map());
113
+ const todoMapRef = (0, import_react.useRef)(/* @__PURE__ */ new Map());
114
+ const pendingUpdates = (0, import_react.useRef)([]);
115
+ const rafId = (0, import_react.useRef)(null);
116
+ const messageIndexRef = (0, import_react.useRef)(/* @__PURE__ */ new Map());
117
+ const roundIndexRef = (0, import_react.useRef)(/* @__PURE__ */ new Map());
118
+ const processedChunksRef = (0, import_react.useRef)(/* @__PURE__ */ new Set());
119
+ const callBatchToRoundRef = (0, import_react.useRef)(/* @__PURE__ */ new Map());
120
+ const parsePlan = (0, import_react.useCallback)((content) => {
121
+ try {
122
+ const parsed = JSON.parse(content);
123
+ if (parsed.items && Array.isArray(parsed.items)) {
124
+ return {
125
+ title: parsed.title,
126
+ items: parsed.items.map((item) => ({
127
+ text: item.text || "",
128
+ status: item.status || "pending"
129
+ })),
130
+ progress: {
131
+ completed: parsed.items.filter((i) => i.status === "completed").length,
132
+ total: parsed.items.length
133
+ }
134
+ };
135
+ }
136
+ } catch {
137
+ const lines = content.split("\n").filter(Boolean);
138
+ return {
139
+ items: lines.map((line) => ({
140
+ text: line.replace(/^[-*]\s*/, ""),
141
+ status: "pending"
142
+ })),
143
+ progress: { completed: 0, total: lines.length }
144
+ };
145
+ }
146
+ return null;
147
+ }, []);
148
+ const parseSingleArtifact = (0, import_react.useCallback)((parsed) => {
149
+ if (parsed.type === "file" && parsed.data) {
150
+ const data = parsed.data;
151
+ const fileSize = data.file_size;
152
+ const sizeStr = fileSize ? fileSize > 1024 ? `${(fileSize / 1024).toFixed(1)} KB` : `${fileSize} B` : void 0;
153
+ return {
154
+ id: parsed.identifier || data.file_id || crypto.randomUUID(),
155
+ name: data.file_name || parsed.description || "Untitled",
156
+ type: data.mime_type || "text/plain",
157
+ size: sizeStr,
158
+ content: parsed.description,
159
+ preview: `${parsed.intention}: ${parsed.description}`,
160
+ metadata: {
161
+ gitPath: data.git_path,
162
+ gitCommitHash: data.git_commit_hash,
163
+ createdByAgentId: data.created_by_agent_id,
164
+ operationType: data.operation_type
165
+ }
166
+ };
167
+ }
168
+ return {
169
+ id: parsed.id || crypto.randomUUID(),
170
+ name: parsed.name || "Untitled",
171
+ type: parsed.type || "text/plain",
172
+ size: parsed.size,
173
+ content: parsed.content,
174
+ preview: parsed.preview
175
+ };
176
+ }, []);
177
+ const parseArtifact = (0, import_react.useCallback)((content) => {
178
+ try {
179
+ const parsed = JSON.parse(content);
180
+ return parseSingleArtifact(parsed);
181
+ } catch {
182
+ const firstJsonEnd = content.indexOf("}{");
183
+ if (firstJsonEnd > 0) {
184
+ try {
185
+ const firstJson = content.substring(0, firstJsonEnd + 1);
186
+ const parsed = JSON.parse(firstJson);
187
+ return parseSingleArtifact(parsed);
188
+ } catch {
189
+ return null;
190
+ }
191
+ }
192
+ return null;
193
+ }
194
+ }, [parseSingleArtifact]);
195
+ const parseArtifacts = (0, import_react.useCallback)((content) => {
196
+ const artifacts = [];
197
+ const jsonStrings = content.split(/\}\s*\{/).map((str, index, arr) => {
198
+ if (index === 0) return str + (arr.length > 1 ? "}" : "");
199
+ if (index === arr.length - 1) return "{" + str;
200
+ return "{" + str + "}";
201
+ });
202
+ for (const jsonStr of jsonStrings) {
203
+ try {
204
+ const parsed = JSON.parse(jsonStr);
205
+ const artifact = parseSingleArtifact(parsed);
206
+ if (artifact) {
207
+ artifacts.push(artifact);
208
+ }
209
+ } catch {
210
+ }
211
+ }
212
+ return artifacts;
213
+ }, [parseSingleArtifact]);
214
+ const parseHITLRequest = (0, import_react.useCallback)((content, contentType) => {
215
+ try {
216
+ const parsed = JSON.parse(content);
217
+ return {
218
+ await_command_uuid: parsed.await_command_uuid || "",
219
+ schema_type: contentType === "html_schema" ? "html_schema" : "json_schema",
220
+ schema: parsed.schema || parsed,
221
+ title: parsed.title,
222
+ description: parsed.description
223
+ };
224
+ } catch {
225
+ return null;
226
+ }
227
+ }, []);
228
+ const flushUpdates = (0, import_react.useCallback)(() => {
229
+ if (pendingUpdates.current.length === 0) return;
230
+ const updates = [...pendingUpdates.current];
231
+ pendingUpdates.current = [];
232
+ let latestInteractionId = null;
233
+ let todoMapChanged = false;
234
+ for (const msg of updates) {
235
+ const { agent_instance_id, call_batch_id, content_type, content } = msg;
236
+ let { interaction_id } = msg;
237
+ if (msg.notification_type === "node_summary") {
238
+ continue;
239
+ }
240
+ if (msg.notification_type === "compression_started" || msg.notification_type === "compression_completed") {
241
+ continue;
242
+ }
243
+ if (msg.notification_type === "plan_result" && content) {
244
+ try {
245
+ const parsed = typeof content === "string" ? JSON.parse(content) : content;
246
+ if (isTodoNotificationContent(parsed)) {
247
+ const planId = parsed.plan_id || "default";
248
+ todoMapRef.current = new Map(todoMapRef.current).set(planId, parsed.checklist);
249
+ todoMapChanged = true;
250
+ continue;
251
+ }
252
+ } catch {
253
+ }
254
+ }
255
+ const existingRoundForBatch = callBatchToRoundRef.current.get(call_batch_id);
256
+ if (existingRoundForBatch && existingRoundForBatch !== interaction_id) {
257
+ interaction_id = existingRoundForBatch;
258
+ } else if (!existingRoundForBatch) {
259
+ callBatchToRoundRef.current.set(call_batch_id, interaction_id);
260
+ }
261
+ let round = roundIndexRef.current.get(interaction_id);
262
+ if (!round) {
263
+ round = {
264
+ interactionId: interaction_id,
265
+ index: roundIndexRef.current.size + 1,
266
+ userMessage: "",
267
+ timestamp: msg.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
268
+ status: "running",
269
+ messages: []
270
+ };
271
+ roundIndexRef.current.set(interaction_id, round);
272
+ }
273
+ latestInteractionId = interaction_id;
274
+ const notification_type = msg.notification_type;
275
+ const isMcpMessage = notification_type && ["mcp_generating", "mcp_start", "mcp_result", "mcp_end"].includes(notification_type);
276
+ let messageKey = isMcpMessage ? `${interaction_id}-${agent_instance_id}-mcp-${msg.tool_call_id || call_batch_id}` : `${interaction_id}-${agent_instance_id}-${call_batch_id}-${content_type}`;
277
+ if (isMcpMessage && notification_type !== "mcp_generating" && !messageIndexRef.current.has(messageKey)) {
278
+ const fallback = round.messages.find(
279
+ (m) => m.toolPhase && m.toolPhase !== "complete" && m.toolPhase !== "error" && m.callBatchId === call_batch_id && (msg.tool_name ? m.toolName === msg.tool_name : true)
280
+ );
281
+ if (fallback) {
282
+ messageKey = fallback.id;
283
+ }
284
+ }
285
+ if (!isMcpMessage && messageIndexRef.current.has(messageKey)) {
286
+ const baseKey = messageKey;
287
+ const candidateKeys = [baseKey];
288
+ let segIdx = 1;
289
+ while (messageIndexRef.current.has(`${baseKey}-${segIdx}`)) {
290
+ candidateKeys.push(`${baseKey}-${segIdx}`);
291
+ segIdx++;
292
+ }
293
+ const lastMsgId = round.messages.length > 0 ? round.messages[round.messages.length - 1].id : null;
294
+ const mergeTarget = candidateKeys.find((k) => k === lastMsgId);
295
+ if (mergeTarget) {
296
+ messageKey = mergeTarget;
297
+ } else {
298
+ const lastMsg = round.messages.length > 0 ? round.messages[round.messages.length - 1] : null;
299
+ const isAfterToolCard = lastMsg && lastMsg.toolPhase !== void 0;
300
+ const isShortFragment = content.length < 10;
301
+ if (isAfterToolCard && isShortFragment) {
302
+ const latestTextKey = candidateKeys[candidateKeys.length - 1];
303
+ messageKey = latestTextKey;
304
+ } else {
305
+ messageKey = `${baseKey}-${segIdx}`;
306
+ }
307
+ }
308
+ }
309
+ let message = messageIndexRef.current.get(messageKey);
310
+ if (!message) {
311
+ message = {
312
+ id: messageKey,
313
+ agentInstanceId: agent_instance_id,
314
+ callBatchId: call_batch_id,
315
+ parentCallBatchId: msg.parent_call_batch_id,
316
+ level: msg.level,
317
+ agentName: msg.agent_name,
318
+ taskPurpose: msg.task_purpose,
319
+ // 任务目标
320
+ notificationType: msg.notification_type,
321
+ // 顶层通知类型
322
+ toolName: msg.tool_name,
323
+ // 工具名称
324
+ toolCallId: msg.tool_call_id,
325
+ // 工具调用 ID
326
+ toolStatus: msg.tool_status,
327
+ // 工具执行状态
328
+ toolPhase: notification_type === "mcp_generating" ? "generating" : notification_type === "mcp_start" ? "executing" : notification_type === "mcp_end" ? msg.tool_status === "error" ? "error" : "complete" : void 0,
329
+ argsPreview: msg.args_preview,
330
+ uiConfig: msg.ui_config,
331
+ parentToolCallId: msg.parent_tool_call_id,
332
+ contentType: content_type,
333
+ contentChunks: [],
334
+ timestamp: msg.timestamp || (/* @__PURE__ */ new Date()).toISOString()
335
+ };
336
+ messageIndexRef.current.set(messageKey, message);
337
+ if (!round.messages.some((m) => m.id === messageKey)) {
338
+ round.messages.push(message);
339
+ }
340
+ } else if (isMcpMessage) {
341
+ const prev = message;
342
+ message = { ...message, contentChunks: [...message.contentChunks] };
343
+ message.notificationType = msg.notification_type;
344
+ if (msg.tool_status) {
345
+ message.toolStatus = msg.tool_status;
346
+ }
347
+ if (msg.task_purpose && !message.taskPurpose) {
348
+ message.taskPurpose = msg.task_purpose;
349
+ }
350
+ if (!message.taskPurpose && msg.args_preview) {
351
+ const tp = extractTaskPurposeFromPreview(msg.args_preview);
352
+ if (tp) message.taskPurpose = tp;
353
+ }
354
+ const isTerminal = message.toolPhase === "complete" || message.toolPhase === "error";
355
+ if (notification_type === "mcp_generating") {
356
+ if (!isTerminal) {
357
+ message.toolPhase = "generating";
358
+ }
359
+ if (msg.args_preview) {
360
+ message.argsPreview = msg.args_preview;
361
+ }
362
+ if (msg.ui_config && !message.uiConfig) {
363
+ message.uiConfig = msg.ui_config;
364
+ }
365
+ } else if (notification_type === "mcp_start") {
366
+ if (!isTerminal) {
367
+ message.toolPhase = "executing";
368
+ }
369
+ } else if (notification_type === "mcp_end") {
370
+ message.toolPhase = msg.tool_status === "error" ? "error" : "complete";
371
+ }
372
+ messageIndexRef.current.set(messageKey, message);
373
+ const prevIdx = round.messages.indexOf(prev);
374
+ if (prevIdx >= 0) round.messages[prevIdx] = message;
375
+ }
376
+ const skipContent = isMcpMessage && notification_type !== "mcp_result";
377
+ if (!skipContent) {
378
+ const chunkIndex = message.contentChunks.length;
379
+ const chunkId = `${messageKey}-chunk-${chunkIndex}`;
380
+ if (!processedChunksRef.current.has(chunkId)) {
381
+ processedChunksRef.current.add(chunkId);
382
+ const prevMsg = message;
383
+ message = { ...message, contentChunks: [...message.contentChunks, content] };
384
+ messageIndexRef.current.set(messageKey, message);
385
+ const idx = round.messages.indexOf(prevMsg);
386
+ if (idx >= 0) round.messages[idx] = message;
387
+ }
388
+ }
389
+ if (content_type === "plan") {
390
+ const plan = parsePlan(message.contentChunks.join(""));
391
+ if (plan) {
392
+ message.plan = plan;
393
+ round.plan = plan;
394
+ }
395
+ } else if (content_type === "artifact") {
396
+ const allContent = message.contentChunks.join("");
397
+ const artifacts = parseArtifacts(allContent);
398
+ if (artifacts.length > 0) {
399
+ message.artifacts = artifacts;
400
+ message.artifact = artifacts[0];
401
+ }
402
+ } else if (content_type === "json_schema" || content_type === "html_schema" || content_type === "json_schema_wait") {
403
+ message.hitlRequest = parseHITLRequest(message.contentChunks.join(""), content_type) ?? void 0;
404
+ }
405
+ }
406
+ setState(() => {
407
+ const newRounds = new Map(roundIndexRef.current);
408
+ return {
409
+ rounds: newRounds,
410
+ currentInteractionId: latestInteractionId
411
+ };
412
+ });
413
+ if (todoMapChanged) {
414
+ setTodoMap(new Map(todoMapRef.current));
415
+ }
416
+ rafId.current = null;
417
+ }, [parsePlan, parseArtifact, parseHITLRequest]);
418
+ const processMessage = (0, import_react.useCallback)((message) => {
419
+ pendingUpdates.current.push(message);
420
+ if (rafId.current === null) {
421
+ rafId.current = requestAnimationFrame(flushUpdates);
422
+ }
423
+ }, [flushUpdates]);
424
+ const getRoundsList = (0, import_react.useCallback)(() => {
425
+ return Array.from(state.rounds.values()).sort((a, b) => a.index - b.index);
426
+ }, [state.rounds]);
427
+ const getCurrentRound = (0, import_react.useCallback)(() => {
428
+ if (!state.currentInteractionId) return null;
429
+ return state.rounds.get(state.currentInteractionId) || null;
430
+ }, [state.rounds, state.currentInteractionId]);
431
+ const startNewRound = (0, import_react.useCallback)((interactionId, userMessage) => {
432
+ const round = {
433
+ interactionId,
434
+ index: roundIndexRef.current.size + 1,
435
+ userMessage,
436
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
437
+ status: "running",
438
+ messages: []
439
+ };
440
+ roundIndexRef.current.set(interactionId, round);
441
+ setState((prevState) => {
442
+ const newRounds = new Map(prevState.rounds);
443
+ newRounds.set(interactionId, round);
444
+ return {
445
+ rounds: newRounds,
446
+ currentInteractionId: interactionId
447
+ };
448
+ });
449
+ }, []);
450
+ const loadRounds = (0, import_react.useCallback)((rounds) => {
451
+ roundIndexRef.current.clear();
452
+ messageIndexRef.current.clear();
453
+ callBatchToRoundRef.current.clear();
454
+ processedChunksRef.current.clear();
455
+ const newRounds = /* @__PURE__ */ new Map();
456
+ for (const round of rounds) {
457
+ newRounds.set(round.interactionId, round);
458
+ roundIndexRef.current.set(round.interactionId, round);
459
+ for (const msg of round.messages) {
460
+ messageIndexRef.current.set(msg.id, msg);
461
+ if (msg.callBatchId) {
462
+ callBatchToRoundRef.current.set(msg.callBatchId, round.interactionId);
463
+ }
464
+ }
465
+ }
466
+ const lastId = rounds.length > 0 ? rounds[rounds.length - 1].interactionId : null;
467
+ setState({ rounds: newRounds, currentInteractionId: lastId });
468
+ }, []);
469
+ const finalizeRound = (0, import_react.useCallback)((interactionId) => {
470
+ const round = roundIndexRef.current.get(interactionId);
471
+ if (!round) return;
472
+ let changed = false;
473
+ for (let i = 0; i < round.messages.length; i++) {
474
+ const msg = round.messages[i];
475
+ if (msg.toolPhase && msg.toolPhase !== "complete" && msg.toolPhase !== "error") {
476
+ const updated = { ...msg, toolPhase: "complete" };
477
+ round.messages[i] = updated;
478
+ messageIndexRef.current.set(msg.id, updated);
479
+ changed = true;
480
+ }
481
+ }
482
+ if (changed) {
483
+ setState(() => ({
484
+ rounds: new Map(roundIndexRef.current),
485
+ currentInteractionId: interactionId
486
+ }));
487
+ }
488
+ }, []);
489
+ const clear = (0, import_react.useCallback)(() => {
490
+ pendingUpdates.current = [];
491
+ messageIndexRef.current.clear();
492
+ roundIndexRef.current.clear();
493
+ processedChunksRef.current.clear();
494
+ callBatchToRoundRef.current.clear();
495
+ todoMapRef.current.clear();
496
+ if (rafId.current) {
497
+ cancelAnimationFrame(rafId.current);
498
+ rafId.current = null;
499
+ }
500
+ setState({
501
+ rounds: /* @__PURE__ */ new Map(),
502
+ currentInteractionId: null
503
+ });
504
+ setTodoMap(/* @__PURE__ */ new Map());
505
+ }, []);
506
+ return {
507
+ state,
508
+ todoMap,
509
+ processMessage,
510
+ getRoundsList,
511
+ getCurrentRound,
512
+ startNewRound,
513
+ loadRounds,
514
+ finalizeRound,
515
+ clear
516
+ };
517
+ }
518
+
519
+ // src/hooks/useSSE.ts
520
+ var import_react2 = require("react");
521
+ function useSSE(options) {
522
+ const {
523
+ adapter,
524
+ autoConnect = false,
525
+ reconnectInterval = 3e3,
526
+ maxReconnectAttempts = 3,
527
+ onMessage,
528
+ onStatusChange,
529
+ onError
530
+ } = options;
531
+ const [status, setStatus] = (0, import_react2.useState)("idle");
532
+ const eventSourceRef = (0, import_react2.useRef)(null);
533
+ const abortControllerRef = (0, import_react2.useRef)(null);
534
+ const reconnectAttemptsRef = (0, import_react2.useRef)(0);
535
+ const reconnectTimerRef = (0, import_react2.useRef)(null);
536
+ const updateStatus = (0, import_react2.useCallback)((newStatus) => {
537
+ setStatus(newStatus);
538
+ onStatusChange?.(newStatus);
539
+ }, [onStatusChange]);
540
+ const disconnect = (0, import_react2.useCallback)(() => {
541
+ if (reconnectTimerRef.current) {
542
+ clearTimeout(reconnectTimerRef.current);
543
+ reconnectTimerRef.current = null;
544
+ }
545
+ if (eventSourceRef.current) {
546
+ eventSourceRef.current.close();
547
+ eventSourceRef.current = null;
548
+ }
549
+ if (abortControllerRef.current) {
550
+ abortControllerRef.current.abort();
551
+ abortControllerRef.current = null;
552
+ }
553
+ reconnectAttemptsRef.current = 0;
554
+ updateStatus("disconnected");
555
+ }, [updateStatus]);
556
+ const connect = (0, import_react2.useCallback)((url, body) => {
557
+ disconnect();
558
+ updateStatus("connecting");
559
+ if (body) {
560
+ abortControllerRef.current = new AbortController();
561
+ const doFetch = () => {
562
+ const headers = {
563
+ "Content-Type": "application/json",
564
+ "Accept": "text/event-stream",
565
+ ...adapter.getAuthHeaders(),
566
+ ...adapter.getExtraHeaders?.() ?? {}
567
+ };
568
+ return fetch(url, {
569
+ method: "POST",
570
+ headers,
571
+ body: JSON.stringify(body),
572
+ signal: abortControllerRef.current.signal
573
+ });
574
+ };
575
+ doFetch().then(async (response) => {
576
+ if (response.status === 401) {
577
+ console.warn("[useSSE] Got 401, attempting token refresh via adapter...");
578
+ const refreshed = await adapter.refreshAuth?.();
579
+ if (refreshed) {
580
+ console.log("[useSSE] Token refreshed, retrying SSE connection");
581
+ const retryResponse = await doFetch();
582
+ if (!retryResponse.ok) {
583
+ throw new Error(`HTTP ${retryResponse.status}: ${retryResponse.statusText}`);
584
+ }
585
+ return retryResponse;
586
+ }
587
+ adapter.onAuthFailure?.();
588
+ throw new Error("HTTP 401: Unauthorized");
589
+ }
590
+ if (!response.ok) {
591
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
592
+ }
593
+ return response;
594
+ }).then(async (response) => {
595
+ updateStatus("connected");
596
+ reconnectAttemptsRef.current = 0;
597
+ const reader = response.body?.getReader();
598
+ if (!reader) {
599
+ throw new Error("Response body is not readable");
600
+ }
601
+ const decoder = new TextDecoder();
602
+ let buffer = "";
603
+ while (true) {
604
+ const { done, value } = await reader.read();
605
+ if (done) break;
606
+ buffer += decoder.decode(value, { stream: true });
607
+ const lines = buffer.split("\n");
608
+ buffer = lines.pop() || "";
609
+ for (const line of lines) {
610
+ if (line.startsWith("data:")) {
611
+ const data = line.slice(5).trim();
612
+ if (data && data !== "[DONE]") {
613
+ try {
614
+ const message = JSON.parse(data);
615
+ onMessage?.(message);
616
+ } catch (e) {
617
+ console.warn("Failed to parse SSE message:", data, e);
618
+ }
619
+ }
620
+ }
621
+ }
622
+ }
623
+ updateStatus("disconnected");
624
+ }).catch((error) => {
625
+ if (error.name === "AbortError") {
626
+ updateStatus("disconnected");
627
+ } else {
628
+ const is401 = error instanceof Error && error.message.includes("401");
629
+ console.error("SSE connection error:", error);
630
+ updateStatus("error");
631
+ onError?.(error instanceof Error ? error : new Error(String(error)));
632
+ if (is401) return;
633
+ if (reconnectAttemptsRef.current < maxReconnectAttempts) {
634
+ reconnectAttemptsRef.current++;
635
+ reconnectTimerRef.current = window.setTimeout(() => {
636
+ connect(url, body);
637
+ }, reconnectInterval);
638
+ }
639
+ }
640
+ });
641
+ } else {
642
+ try {
643
+ const eventSource = new EventSource(url);
644
+ eventSourceRef.current = eventSource;
645
+ eventSource.onopen = () => {
646
+ updateStatus("connected");
647
+ reconnectAttemptsRef.current = 0;
648
+ };
649
+ eventSource.onmessage = (event) => {
650
+ if (event.data && event.data !== "[DONE]") {
651
+ try {
652
+ const message = JSON.parse(event.data);
653
+ onMessage?.(message);
654
+ } catch (e) {
655
+ console.warn("Failed to parse SSE message:", event.data, e);
656
+ }
657
+ }
658
+ };
659
+ eventSource.onerror = () => {
660
+ eventSource.close();
661
+ updateStatus("error");
662
+ if (reconnectAttemptsRef.current < maxReconnectAttempts) {
663
+ reconnectAttemptsRef.current++;
664
+ reconnectTimerRef.current = window.setTimeout(() => {
665
+ connect(url);
666
+ }, reconnectInterval);
667
+ }
668
+ };
669
+ } catch (error) {
670
+ updateStatus("error");
671
+ onError?.(error instanceof Error ? error : new Error(String(error)));
672
+ }
673
+ }
674
+ }, [adapter, disconnect, updateStatus, onMessage, onError, maxReconnectAttempts, reconnectInterval]);
675
+ (0, import_react2.useEffect)(() => {
676
+ if (autoConnect) {
677
+ connect(adapter.getStreamUrl());
678
+ }
679
+ return () => {
680
+ disconnect();
681
+ };
682
+ }, []);
683
+ return {
684
+ status,
685
+ connect,
686
+ disconnect
687
+ };
688
+ }
689
+
690
+ // src/components/PinnedArea.tsx
691
+ var import_react5 = require("react");
692
+
693
+ // src/components/PlanCard.tsx
694
+ var import_react3 = require("react");
695
+ var import_jsx_runtime = require("react/jsx-runtime");
696
+ var PlanCard = ({
697
+ plan,
698
+ status,
699
+ compact = false
700
+ }) => {
701
+ const statusIcons = {
702
+ completed: "\u2713",
703
+ active: "\u2192",
704
+ pending: "\u25CB",
705
+ error: "\u2717"
706
+ };
707
+ if (compact) {
708
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "plan-card-compact", children: [
709
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "plan-icon", children: "\u{1F4CB}" }),
710
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "plan-items-inline", children: plan.items.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: `plan-item-inline ${item.status}`, children: [
711
+ "[",
712
+ statusIcons[item.status],
713
+ " ",
714
+ item.text,
715
+ "]"
716
+ ] }, i)) })
717
+ ] });
718
+ }
719
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `plan-card ${status === "completed" ? "completed" : ""}`, children: [
720
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "plan-header", children: [
721
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "plan-title", children: [
722
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "plan-icon", children: "\u{1F4CB}" }),
723
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u4EFB\u52A1\u8BA1\u5212" })
724
+ ] }),
725
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "plan-progress", children: [
726
+ "(",
727
+ plan.progress.completed,
728
+ "/",
729
+ plan.progress.total,
730
+ ")"
731
+ ] })
732
+ ] }),
733
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "plan-items", children: plan.items.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: `plan-item ${item.status}`, children: [
734
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "plan-item-icon", children: statusIcons[item.status] }),
735
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "plan-item-text", children: item.text }),
736
+ item.status === "active" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "plan-item-status", children: "\u6267\u884C\u4E2D" })
737
+ ] }, index)) })
738
+ ] });
739
+ };
740
+ var PlanCard_default = PlanCard;
741
+
742
+ // src/components/TodoCard.tsx
743
+ var import_react4 = require("react");
744
+ var import_jsx_runtime2 = require("react/jsx-runtime");
745
+ var STATUS_ICON = {
746
+ pending: "\u25CB",
747
+ in_progress: "\u2192",
748
+ completed: "\u2713",
749
+ blocked: "\u26A0"
750
+ };
751
+ var MAX_VISIBLE_ITEMS = 8;
752
+ function flattenTasks(todo) {
753
+ const result = [];
754
+ for (const phase of todo.phases) {
755
+ for (const task of phase.tasks) {
756
+ result.push(task);
757
+ }
758
+ for (const group of phase.groups) {
759
+ for (const task of group.tasks) {
760
+ result.push(task);
761
+ }
762
+ }
763
+ }
764
+ return result;
765
+ }
766
+ function findCurrentTask(tasks) {
767
+ return tasks.find((t) => t.status === "in_progress") || null;
768
+ }
769
+ var TaskRow = ({ task, depth = 0 }) => {
770
+ const [expanded, setExpanded] = (0, import_react4.useState)(
771
+ task.status === "in_progress" && task.children.length > 0
772
+ );
773
+ const hasChildren = task.children.length > 0;
774
+ const toggle = (0, import_react4.useCallback)(() => setExpanded((v) => !v), []);
775
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
776
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
777
+ "div",
778
+ {
779
+ className: `todo-task-row todo-status-${task.status}`,
780
+ style: { paddingLeft: `${12 + depth * 16}px` },
781
+ onClick: hasChildren ? toggle : void 0,
782
+ role: hasChildren ? "button" : void 0,
783
+ children: [
784
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "todo-status-icon", children: STATUS_ICON[task.status] }),
785
+ hasChildren && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: `todo-expand-arrow ${expanded ? "expanded" : ""}`, children: "\u25B8" }),
786
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "todo-task-desc", children: task.description })
787
+ ]
788
+ }
789
+ ),
790
+ expanded && task.children.map((child, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TaskRow, { task: child, depth: depth + 1 }, child.id || i))
791
+ ] });
792
+ };
793
+ var TodoCardCompact = ({ todo }) => {
794
+ const tasks = (0, import_react4.useMemo)(() => flattenTasks(todo), [todo]);
795
+ const current = findCurrentTask(tasks);
796
+ const { stats } = todo;
797
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "todo-card-compact", children: [
798
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "todo-compact-icon", children: "\u2630" }),
799
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "todo-compact-title", children: todo.title }),
800
+ current && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
801
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "todo-compact-sep", children: "\u2192" }),
802
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "todo-compact-current", children: current.description })
803
+ ] }),
804
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "todo-compact-progress", children: [
805
+ "\u5DF2\u5B8C\u6210 ",
806
+ stats.completed,
807
+ "/",
808
+ stats.total
809
+ ] })
810
+ ] });
811
+ };
812
+ var TodoCard = ({ todo, compact = false }) => {
813
+ const [expanded, setExpanded] = (0, import_react4.useState)(false);
814
+ const [showAll, setShowAll] = (0, import_react4.useState)(false);
815
+ const tasks = (0, import_react4.useMemo)(() => flattenTasks(todo), [todo]);
816
+ const { stats } = todo;
817
+ const progress = stats.total > 0 ? stats.completed / stats.total : 0;
818
+ if (compact) {
819
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TodoCardCompact, { todo });
820
+ }
821
+ const visibleTasks = showAll ? tasks : tasks.slice(0, MAX_VISIBLE_ITEMS);
822
+ const hiddenCount = tasks.length - MAX_VISIBLE_ITEMS;
823
+ const current = (0, import_react4.useMemo)(() => findCurrentTask(tasks), [tasks]);
824
+ const isAllDone = stats.completed === stats.total && stats.total > 0;
825
+ const hasBlocked = stats.blocked > 0;
826
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "todo-card", children: [
827
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "todo-header", onClick: () => setExpanded((v) => !v), role: "button", children: [
828
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "todo-title", children: [
829
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: `todo-header-arrow ${expanded ? "expanded" : ""}`, children: "\u25B8" }),
830
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "todo-title-icon", children: "\u2630" }),
831
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "todo-title-text", children: todo.title })
832
+ ] }),
833
+ !expanded && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "todo-header-status", children: isAllDone ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "todo-header-done", children: "\u2713 \u5DF2\u5B8C\u6210" }) : hasBlocked ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "todo-header-blocked", children: [
834
+ "\u26A0 ",
835
+ stats.blocked,
836
+ " \u9879\u963B\u585E"
837
+ ] }) : current ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
838
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "todo-header-current-sep", children: "\u2192" }),
839
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "todo-header-current", children: current.description })
840
+ ] }) : null }),
841
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "todo-header-count", children: [
842
+ "\u5DF2\u5B8C\u6210 ",
843
+ stats.completed,
844
+ "/",
845
+ stats.total
846
+ ] })
847
+ ] }),
848
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "todo-progress-bar", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
849
+ "div",
850
+ {
851
+ className: "todo-progress-fill",
852
+ style: { width: `${progress * 100}%` }
853
+ }
854
+ ) }),
855
+ expanded && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
856
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "todo-list", children: visibleTasks.map((task, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TaskRow, { task }, task.id || i)) }),
857
+ !showAll && hiddenCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "todo-more", onClick: () => setShowAll(true), children: [
858
+ "+ ",
859
+ hiddenCount,
860
+ " more"
861
+ ] })
862
+ ] })
863
+ ] });
864
+ };
865
+ var TodoCardMulti = ({ todoMap, compact = false }) => {
866
+ const entries = (0, import_react4.useMemo)(() => Array.from(todoMap.entries()), [todoMap]);
867
+ const [activeId, setActiveId] = (0, import_react4.useState)(null);
868
+ if (entries.length === 0) return null;
869
+ if (entries.length === 1) {
870
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TodoCard, { todo: entries[0][1], compact });
871
+ }
872
+ const selectedId = activeId || entries[entries.length - 1][0];
873
+ const selectedTodo = todoMap.get(selectedId);
874
+ if (compact) {
875
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "todo-multi-compact", children: entries.map(([id, todo]) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TodoCardCompact, { todo }, id)) });
876
+ }
877
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "todo-card", children: [
878
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "todo-multi-tabs", children: entries.map(([id, todo]) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
879
+ "button",
880
+ {
881
+ className: `todo-tab ${id === selectedId ? "active" : ""}`,
882
+ onClick: () => setActiveId(id),
883
+ children: [
884
+ todo.title,
885
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "todo-tab-progress", children: [
886
+ todo.stats.completed,
887
+ "/",
888
+ todo.stats.total
889
+ ] })
890
+ ]
891
+ },
892
+ id
893
+ )) }),
894
+ selectedTodo && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TodoCard, { todo: selectedTodo })
895
+ ] });
896
+ };
897
+ var TodoCard_default = TodoCard;
898
+
899
+ // src/components/PinnedArea.tsx
900
+ var import_jsx_runtime3 = require("react/jsx-runtime");
901
+ var PinnedArea = ({
902
+ round,
903
+ isTransitioning,
904
+ todoMap
905
+ }) => {
906
+ if (!round) return null;
907
+ const hasTodo = todoMap && todoMap.size > 0;
908
+ const formatTime = (timestamp) => {
909
+ try {
910
+ return new Date(timestamp).toLocaleTimeString("zh-CN", {
911
+ hour: "2-digit",
912
+ minute: "2-digit",
913
+ second: "2-digit"
914
+ });
915
+ } catch {
916
+ return "";
917
+ }
918
+ };
919
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "chat-pinned-area", children: [
920
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: `pinned-content ${isTransitioning ? "fade-out" : ""}`, children: [
921
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "pinned-user-message", children: round.userMessage || "(\u65E0\u6D88\u606F\u5185\u5BB9)" }),
922
+ hasTodo && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TodoCardMulti, { todoMap }),
923
+ !hasTodo && round.plan && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
924
+ PlanCard_default,
925
+ {
926
+ plan: round.plan,
927
+ status: round.status
928
+ }
929
+ )
930
+ ] }),
931
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "pinned-timestamp", children: formatTime(round.timestamp) })
932
+ ] });
933
+ };
934
+ var PinnedArea_default = PinnedArea;
935
+
936
+ // src/components/RoundHeader.tsx
937
+ var import_react6 = require("react");
938
+ var import_jsx_runtime4 = require("react/jsx-runtime");
939
+ var RoundHeader = ({
940
+ round,
941
+ isPinned,
942
+ todoMap
943
+ }) => {
944
+ const statusLabels = {
945
+ running: { icon: "\u23F3", text: "\u6267\u884C\u4E2D", className: "running" },
946
+ completed: { icon: "\u2713", text: "\u5DF2\u5B8C\u6210", className: "completed" },
947
+ error: { icon: "\u2717", text: "\u6267\u884C\u5931\u8D25", className: "error" }
948
+ };
949
+ const status = statusLabels[round.status];
950
+ const hasTodo = todoMap && todoMap.size > 0;
951
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `round-header ${isPinned ? "pinned" : ""}`, children: [
952
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "round-header-title", children: [
953
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "user-avatar", children: [
954
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "avatar-icon", children: "\u{1F464}" }),
955
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "user-message-text", children: round.userMessage || "(\u65E0\u6D88\u606F)" })
956
+ ] }),
957
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: `round-status ${status.className}`, children: [
958
+ status.icon,
959
+ " ",
960
+ status.text
961
+ ] })
962
+ ] }),
963
+ hasTodo && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TodoCardMulti, { todoMap, compact: true }),
964
+ !hasTodo && round.plan && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PlanCard_default, { plan: round.plan, status: round.status, compact: true })
965
+ ] });
966
+ };
967
+ var RoundHeader_default = RoundHeader;
968
+
969
+ // src/components/MessageContent.tsx
970
+ var import_react19 = require("react");
971
+ var import_streamdown = require("streamdown");
972
+ var import_code = require("@streamdown/code");
973
+ var import_mermaid = require("@streamdown/mermaid");
974
+ var import_math = require("@streamdown/math");
975
+ var import_cjk = require("@streamdown/cjk");
976
+
977
+ // src/components/SchemaFormRenderer.tsx
978
+ var import_react7 = require("react");
979
+ var import_jsx_runtime5 = require("react/jsx-runtime");
980
+ var renderNode = (node, values, onChange, errors) => {
981
+ if (!node) return null;
982
+ const { type, key, name, props, children, options, schema } = node;
983
+ const nodeKey = key || name || Math.random().toString();
984
+ switch (type) {
985
+ case "Card":
986
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "schema-card", children: children?.map((child) => renderNode(child, values, onChange, errors)) }, nodeKey);
987
+ case "FormChunk":
988
+ if (schema) {
989
+ return renderNode(schema, values, onChange, errors);
990
+ }
991
+ return children?.map((child) => renderNode(child, values, onChange, errors));
992
+ case "Form":
993
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "schema-form", children: children?.map((child) => renderNode(child, values, onChange, errors)) }, nodeKey);
994
+ case "FormItem": {
995
+ const label = props?.label || "";
996
+ const required = node.rules?.some((r) => r.required);
997
+ const error = name ? errors[name] : void 0;
998
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: `schema-form-item ${error ? "has-error" : ""}`, children: [
999
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "schema-form-label", children: [
1000
+ required && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "required-mark", children: "*" }),
1001
+ label
1002
+ ] }),
1003
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "schema-form-control", children: children?.map((child) => renderNode(
1004
+ { ...child, name: name || child.name },
1005
+ values,
1006
+ onChange,
1007
+ errors
1008
+ )) }),
1009
+ error && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "schema-form-error", children: error })
1010
+ ] }, nodeKey);
1011
+ }
1012
+ case "RadioGroup": {
1013
+ const fieldName = name || "";
1014
+ const currentValue = values[fieldName];
1015
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "schema-radio-group", children: options?.map((option) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "schema-radio-option", children: [
1016
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1017
+ "input",
1018
+ {
1019
+ type: "radio",
1020
+ name: fieldName,
1021
+ value: option.value,
1022
+ checked: currentValue === option.value,
1023
+ onChange: () => onChange(fieldName, option.value)
1024
+ }
1025
+ ),
1026
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "radio-checkmark" }),
1027
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "radio-label", children: option.label })
1028
+ ] }, option.value)) }, nodeKey);
1029
+ }
1030
+ case "CheckboxGroup": {
1031
+ const fieldName = name || "";
1032
+ const currentValues = values[fieldName] || [];
1033
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "schema-checkbox-group", children: options?.map((option) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "schema-checkbox-option", children: [
1034
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1035
+ "input",
1036
+ {
1037
+ type: "checkbox",
1038
+ value: option.value,
1039
+ checked: currentValues.includes(option.value),
1040
+ onChange: (e) => {
1041
+ const newValues = e.target.checked ? [...currentValues, option.value] : currentValues.filter((v) => v !== option.value);
1042
+ onChange(fieldName, newValues);
1043
+ }
1044
+ }
1045
+ ),
1046
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "checkbox-checkmark" }),
1047
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "checkbox-label", children: option.label })
1048
+ ] }, option.value)) }, nodeKey);
1049
+ }
1050
+ case "Input": {
1051
+ const fieldName = name || "";
1052
+ const placeholder = props?.placeholder || "";
1053
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1054
+ "input",
1055
+ {
1056
+ type: "text",
1057
+ className: "schema-input",
1058
+ placeholder,
1059
+ value: values[fieldName] || "",
1060
+ onChange: (e) => onChange(fieldName, e.target.value)
1061
+ },
1062
+ nodeKey
1063
+ );
1064
+ }
1065
+ case "TextArea": {
1066
+ const fieldName = name || "";
1067
+ const placeholder = props?.placeholder || "";
1068
+ const rows = props?.rows || 3;
1069
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1070
+ "textarea",
1071
+ {
1072
+ className: "schema-textarea",
1073
+ placeholder,
1074
+ rows,
1075
+ value: values[fieldName] || "",
1076
+ onChange: (e) => onChange(fieldName, e.target.value)
1077
+ },
1078
+ nodeKey
1079
+ );
1080
+ }
1081
+ case "Select": {
1082
+ const fieldName = name || "";
1083
+ const placeholder = props?.placeholder || "\u8BF7\u9009\u62E9";
1084
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1085
+ "select",
1086
+ {
1087
+ className: "schema-select",
1088
+ value: values[fieldName] || "",
1089
+ onChange: (e) => onChange(fieldName, e.target.value),
1090
+ children: [
1091
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: "", disabled: true, children: placeholder }),
1092
+ options?.map((option) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: option.value, children: option.label }, option.value))
1093
+ ]
1094
+ },
1095
+ nodeKey
1096
+ );
1097
+ }
1098
+ default:
1099
+ if (children && children.length > 0) {
1100
+ return children.map((child) => renderNode(child, values, onChange, errors));
1101
+ }
1102
+ return null;
1103
+ }
1104
+ };
1105
+ var extractRules = (node) => {
1106
+ const rules = {};
1107
+ const traverse = (n) => {
1108
+ if (n.name && n.rules) {
1109
+ rules[n.name] = n.rules;
1110
+ }
1111
+ if (n.children) {
1112
+ n.children.forEach(traverse);
1113
+ }
1114
+ if (n.schema) {
1115
+ traverse(n.schema);
1116
+ }
1117
+ };
1118
+ traverse(node);
1119
+ return rules;
1120
+ };
1121
+ var SchemaFormRenderer = ({
1122
+ schema,
1123
+ onSubmit,
1124
+ awaitCommandUuid
1125
+ }) => {
1126
+ const [values, setValues] = (0, import_react7.useState)({});
1127
+ const [errors, setErrors] = (0, import_react7.useState)({});
1128
+ const [isSubmitting, setIsSubmitting] = (0, import_react7.useState)(false);
1129
+ const handleChange = (0, import_react7.useCallback)((name, value) => {
1130
+ setValues((prev) => ({ ...prev, [name]: value }));
1131
+ setErrors((prev) => {
1132
+ const newErrors = { ...prev };
1133
+ delete newErrors[name];
1134
+ return newErrors;
1135
+ });
1136
+ }, []);
1137
+ const validate = (0, import_react7.useCallback)(() => {
1138
+ const rules = extractRules(schema);
1139
+ const newErrors = {};
1140
+ Object.entries(rules).forEach(([fieldName, fieldRules]) => {
1141
+ fieldRules.forEach((rule) => {
1142
+ if (rule.required) {
1143
+ const value = values[fieldName];
1144
+ const isEmpty = value === void 0 || value === null || value === "" || Array.isArray(value) && value.length === 0;
1145
+ if (isEmpty) {
1146
+ newErrors[fieldName] = rule.message || "\u6B64\u9879\u4E3A\u5FC5\u586B\u9879";
1147
+ }
1148
+ }
1149
+ });
1150
+ });
1151
+ setErrors(newErrors);
1152
+ return Object.keys(newErrors).length === 0;
1153
+ }, [schema, values]);
1154
+ const handleSubmit = (0, import_react7.useCallback)(async () => {
1155
+ if (!validate()) return;
1156
+ setIsSubmitting(true);
1157
+ try {
1158
+ await onSubmit?.(values);
1159
+ } finally {
1160
+ setIsSubmitting(false);
1161
+ }
1162
+ }, [validate, values, onSubmit]);
1163
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "schema-form-renderer", children: [
1164
+ renderNode(schema, values, handleChange, errors),
1165
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "schema-form-actions", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1166
+ "button",
1167
+ {
1168
+ className: "schema-submit-btn",
1169
+ onClick: handleSubmit,
1170
+ disabled: isSubmitting,
1171
+ children: isSubmitting ? "\u63D0\u4EA4\u4E2D..." : "\u63D0\u4EA4"
1172
+ }
1173
+ ) }),
1174
+ awaitCommandUuid && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "schema-form-meta", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "meta-uuid", children: [
1175
+ "\u8BF7\u6C42 ID: ",
1176
+ awaitCommandUuid.slice(0, 8),
1177
+ "..."
1178
+ ] }) })
1179
+ ] });
1180
+ };
1181
+ var SchemaFormRenderer_default = SchemaFormRenderer;
1182
+
1183
+ // src/components/ToolCardBuffering.tsx
1184
+ var import_react18 = require("react");
1185
+
1186
+ // src/components/renderers/utils.tsx
1187
+ var import_react8 = require("react");
1188
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1189
+ function parseArgsIncremental(argsPreview, cache) {
1190
+ if (!argsPreview) return null;
1191
+ try {
1192
+ cache.current = null;
1193
+ return JSON.parse(argsPreview);
1194
+ } catch {
1195
+ }
1196
+ const prev = cache.current;
1197
+ if (prev && prev.truncatedKey && prev.truncatedValueStart > 0 && argsPreview.length > prev.lastArgsLen) {
1198
+ prev.lastArgsLen = argsPreview.length;
1199
+ const rawValue = argsPreview.slice(prev.truncatedValueStart);
1200
+ return { ...prev.stableFields, [prev.truncatedKey]: decodeJsonEscapes(rawValue) };
1201
+ }
1202
+ const meta = parsePartialJsonWithMeta(argsPreview);
1203
+ if (!meta) return null;
1204
+ const stableFields = { ...meta.fields };
1205
+ if (meta.truncatedKey) {
1206
+ delete stableFields[meta.truncatedKey];
1207
+ }
1208
+ cache.current = {
1209
+ stableFields,
1210
+ truncatedKey: meta.truncatedKey,
1211
+ truncatedValueStart: meta.truncatedValueStart,
1212
+ lastArgsLen: argsPreview.length
1213
+ };
1214
+ return meta.fields;
1215
+ }
1216
+ function parsePartialJsonWithMeta(raw) {
1217
+ const fields = {};
1218
+ let i = raw.indexOf("{");
1219
+ if (i < 0) return null;
1220
+ i++;
1221
+ let foundAny = false;
1222
+ let truncatedKey = null;
1223
+ let truncatedValueStart = -1;
1224
+ while (i < raw.length) {
1225
+ while (i < raw.length && " \r\n,".includes(raw[i])) i++;
1226
+ if (i >= raw.length || raw[i] === "}") break;
1227
+ if (raw[i] !== '"') break;
1228
+ const keyEnd = findClosingQuote(raw, i);
1229
+ if (keyEnd < 0) break;
1230
+ const key = raw.slice(i + 1, keyEnd);
1231
+ i = keyEnd + 1;
1232
+ while (i < raw.length && " \r\n".includes(raw[i])) i++;
1233
+ if (i >= raw.length || raw[i] !== ":") break;
1234
+ i++;
1235
+ while (i < raw.length && " \r\n".includes(raw[i])) i++;
1236
+ if (i >= raw.length) break;
1237
+ const valueStartPos = raw[i] === '"' ? i + 1 : i;
1238
+ const [value, nextPos, complete] = parseValue(raw, i);
1239
+ if (value !== void 0) {
1240
+ fields[key] = value;
1241
+ foundAny = true;
1242
+ }
1243
+ if (!complete) {
1244
+ truncatedKey = key;
1245
+ truncatedValueStart = valueStartPos;
1246
+ break;
1247
+ }
1248
+ i = nextPos;
1249
+ }
1250
+ return foundAny ? { fields, truncatedKey, truncatedValueStart } : null;
1251
+ }
1252
+ function decodeJsonEscapes(raw) {
1253
+ let result = "";
1254
+ let i = 0;
1255
+ while (i < raw.length) {
1256
+ if (raw[i] === "\\" && i + 1 < raw.length) {
1257
+ const next = raw[i + 1];
1258
+ switch (next) {
1259
+ case "n":
1260
+ result += "\n";
1261
+ break;
1262
+ case "t":
1263
+ result += " ";
1264
+ break;
1265
+ case "r":
1266
+ result += "\r";
1267
+ break;
1268
+ case '"':
1269
+ result += '"';
1270
+ break;
1271
+ case "\\":
1272
+ result += "\\";
1273
+ break;
1274
+ case "/":
1275
+ result += "/";
1276
+ break;
1277
+ default:
1278
+ result += "\\" + next;
1279
+ break;
1280
+ }
1281
+ i += 2;
1282
+ } else {
1283
+ result += raw[i];
1284
+ i++;
1285
+ }
1286
+ }
1287
+ return result;
1288
+ }
1289
+ function findClosingQuote(s, openPos) {
1290
+ let i = openPos + 1;
1291
+ while (i < s.length) {
1292
+ if (s[i] === "\\") {
1293
+ i += 2;
1294
+ continue;
1295
+ }
1296
+ if (s[i] === '"') return i;
1297
+ i++;
1298
+ }
1299
+ return -1;
1300
+ }
1301
+ function parseValue(s, pos) {
1302
+ if (pos >= s.length) return [void 0, pos, false];
1303
+ const ch = s[pos];
1304
+ if (ch === '"') {
1305
+ const end = findClosingQuote(s, pos);
1306
+ if (end < 0) {
1307
+ return [decodeJsonEscapes(s.slice(pos + 1)), s.length, false];
1308
+ }
1309
+ try {
1310
+ const val = JSON.parse(s.slice(pos, end + 1));
1311
+ return [val, end + 1, true];
1312
+ } catch {
1313
+ return [decodeJsonEscapes(s.slice(pos + 1, end)), end + 1, true];
1314
+ }
1315
+ }
1316
+ if ("-0123456789".includes(ch)) {
1317
+ let end = pos + 1;
1318
+ while (end < s.length && "0123456789.eE+-".includes(s[end])) end++;
1319
+ const numStr = s.slice(pos, end);
1320
+ const num = Number(numStr);
1321
+ if (!isNaN(num) && numStr.length > 0) return [num, end, true];
1322
+ return [void 0, end, end < s.length];
1323
+ }
1324
+ if (s.startsWith("true", pos)) return [true, pos + 4, true];
1325
+ if (s.startsWith("false", pos)) return [false, pos + 5, true];
1326
+ if (s.startsWith("null", pos)) return [null, pos + 4, true];
1327
+ if (ch === "{" || ch === "[") {
1328
+ const close = ch === "{" ? "}" : "]";
1329
+ let depth = 1, j = pos + 1, inStr = false;
1330
+ while (j < s.length && depth > 0) {
1331
+ if (inStr) {
1332
+ if (s[j] === "\\") {
1333
+ j += 2;
1334
+ continue;
1335
+ }
1336
+ if (s[j] === '"') inStr = false;
1337
+ } else {
1338
+ if (s[j] === '"') inStr = true;
1339
+ else if (s[j] === ch) depth++;
1340
+ else if (s[j] === close) depth--;
1341
+ }
1342
+ j++;
1343
+ }
1344
+ if (depth === 0) {
1345
+ try {
1346
+ return [JSON.parse(s.slice(pos, j)), j, true];
1347
+ } catch {
1348
+ return [s.slice(pos, j), j, true];
1349
+ }
1350
+ }
1351
+ return [void 0, j, false];
1352
+ }
1353
+ return [void 0, pos + 1, false];
1354
+ }
1355
+ function extractField(args, fieldName) {
1356
+ if (!args || !fieldName) return void 0;
1357
+ return args[fieldName];
1358
+ }
1359
+ function formatValue(value) {
1360
+ if (value === void 0 || value === null) return "";
1361
+ if (typeof value === "string") return value;
1362
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
1363
+ return JSON.stringify(value, null, 2);
1364
+ }
1365
+ function buildCompactTags(args, compactFields, hideFields) {
1366
+ if (!args || !compactFields) return [];
1367
+ const hidden = new Set(hideFields || []);
1368
+ return compactFields.filter((f) => !hidden.has(f) && args[f] !== void 0).map((f) => ({ key: f, value: formatValue(args[f]) }));
1369
+ }
1370
+ var phaseConfig = {
1371
+ generating: { icon: "\u2699\uFE0F", label: "\u751F\u6210\u4E2D", className: "phase-generating" },
1372
+ executing: { icon: "\u23F3", label: "\u6267\u884C\u4E2D", className: "phase-executing" },
1373
+ complete: { icon: "\u2705", label: "\u5B8C\u6210", className: "phase-complete" },
1374
+ error: { icon: "\u274C", label: "\u5931\u8D25", className: "phase-error" }
1375
+ };
1376
+ var PhaseLabel = ({ phase }) => {
1377
+ const cfg = phaseConfig[phase];
1378
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "tool-phase-label", children: [
1379
+ cfg?.label || phase,
1380
+ phase === "generating" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "tool-card-dots", children: [
1381
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", {}),
1382
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", {}),
1383
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", {})
1384
+ ] })
1385
+ ] });
1386
+ };
1387
+ var CompactTags = ({ tags }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "compact-tags", children: [
1388
+ tags.slice(0, 5).map((t) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "compact-tag", children: [
1389
+ t.key,
1390
+ ": ",
1391
+ t.value
1392
+ ] }, t.key)),
1393
+ tags.length > 5 && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "compact-tag", children: [
1394
+ "+",
1395
+ tags.length - 5
1396
+ ] })
1397
+ ] });
1398
+
1399
+ // src/components/renderers/ContentRenderer.tsx
1400
+ var import_react9 = require("react");
1401
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1402
+ var MAX_PREVIEW_LINES = 2;
1403
+ var ContentRenderer = ({ message, uiConfig, parsedArgs, phase }) => {
1404
+ const [expanded, setExpanded] = (0, import_react9.useState)(false);
1405
+ const headerValue = extractField(parsedArgs, uiConfig.header_field);
1406
+ const primaryValue = extractField(parsedArgs, uiConfig.primary_field);
1407
+ const toolName = message.toolName || "tool";
1408
+ const taskPurpose = message.taskPurpose;
1409
+ const isEdit = toolName === "edit" || toolName === "edit_block";
1410
+ const contentStr = primaryValue ? formatValue(primaryValue) : "";
1411
+ const hasContent = contentStr.length > 0;
1412
+ const isGenerating = phase === "generating";
1413
+ const shortPath = (0, import_react9.useMemo)(() => {
1414
+ if (!headerValue) return void 0;
1415
+ const full = String(headerValue);
1416
+ const parts = full.split("/");
1417
+ return parts.length > 2 ? `.../${parts.slice(-2).join("/")}` : full;
1418
+ }, [headerValue]);
1419
+ const { previewLines, totalLines } = (0, import_react9.useMemo)(() => {
1420
+ if (!contentStr) return { previewLines: [], totalLines: 0 };
1421
+ const lines = contentStr.split("\n").filter((l) => l.trim());
1422
+ const total = lines.length;
1423
+ if (isGenerating) {
1424
+ return { previewLines: lines.slice(-MAX_PREVIEW_LINES), totalLines: total };
1425
+ }
1426
+ return { previewLines: lines.slice(0, MAX_PREVIEW_LINES), totalLines: total };
1427
+ }, [contentStr, isGenerating]);
1428
+ const oldValue = isEdit ? extractField(parsedArgs, "old_string") : void 0;
1429
+ const oldPreviewLines = (0, import_react9.useMemo)(() => {
1430
+ if (!oldValue) return [];
1431
+ const s = formatValue(oldValue);
1432
+ return s.split("\n").filter((l) => l.trim()).slice(0, 1);
1433
+ }, [oldValue]);
1434
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1435
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1436
+ "div",
1437
+ {
1438
+ className: "tool-card-header",
1439
+ onClick: () => hasContent && setExpanded(!expanded),
1440
+ style: { cursor: hasContent ? "pointer" : "default" },
1441
+ children: [
1442
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "tool-phase-icon", children: "\u{1F4C4}" }),
1443
+ taskPurpose ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1444
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "tool-card-name", children: taskPurpose }),
1445
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "tool-card-fn", children: [
1446
+ "(",
1447
+ toolName,
1448
+ ")"
1449
+ ] })
1450
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "tool-card-name", children: toolName }),
1451
+ shortPath && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("code", { className: "tool-card-path-inline", children: shortPath }),
1452
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PhaseLabel, { phase }),
1453
+ hasContent && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "tool-card-expand", children: expanded ? "\u25BC" : "\u25B6" })
1454
+ ]
1455
+ }
1456
+ ),
1457
+ (hasContent || isGenerating) && !expanded && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: `content-diff-preview${isGenerating ? " streaming" : ""}`, children: [
1458
+ isEdit && !isGenerating && oldPreviewLines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "diff-line diff-remove", children: [
1459
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "diff-sign", children: "-" }),
1460
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "diff-text", children: line })
1461
+ ] }, `old-${i}`)),
1462
+ previewLines.length > 0 ? previewLines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "diff-line diff-add", children: [
1463
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "diff-sign", children: "+" }),
1464
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "diff-text", children: [
1465
+ line,
1466
+ isGenerating && i === previewLines.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "streaming-block-cursor" })
1467
+ ] })
1468
+ ] }, i)) : isGenerating && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "diff-line diff-add", children: [
1469
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "diff-sign", children: "+" }),
1470
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "diff-text", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "streaming-block-cursor" }) })
1471
+ ] }),
1472
+ !isGenerating && totalLines > MAX_PREVIEW_LINES && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "diff-more", children: [
1473
+ "... +",
1474
+ totalLines - MAX_PREVIEW_LINES,
1475
+ " lines"
1476
+ ] })
1477
+ ] }),
1478
+ expanded && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "content-renderer-body", children: [
1479
+ headerValue && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "content-renderer-path", children: headerValue }),
1480
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("pre", { className: "content-renderer-code", children: contentStr })
1481
+ ] })
1482
+ ] });
1483
+ };
1484
+ var ContentRenderer_default = ContentRenderer;
1485
+
1486
+ // src/components/renderers/CommandRenderer.tsx
1487
+ var import_react10 = require("react");
1488
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1489
+ var CommandRenderer = ({ message, uiConfig, parsedArgs, phase }) => {
1490
+ const command = extractField(parsedArgs, uiConfig.primary_field);
1491
+ const toolName = message.toolName || "tool";
1492
+ const taskPurpose = message.taskPurpose;
1493
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1494
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "tool-card-header", children: [
1495
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "tool-phase-icon", children: "\u{1F4BB}" }),
1496
+ taskPurpose ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1497
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "tool-card-name", children: taskPurpose }),
1498
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "tool-card-fn", children: [
1499
+ "(",
1500
+ toolName,
1501
+ ")"
1502
+ ] })
1503
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "tool-card-name", children: toolName }),
1504
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PhaseLabel, { phase })
1505
+ ] }),
1506
+ command && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "command-renderer-terminal", children: [
1507
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "command-prompt", children: "$" }),
1508
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("code", { className: "command-text", children: formatValue(command) })
1509
+ ] })
1510
+ ] });
1511
+ };
1512
+ var CommandRenderer_default = CommandRenderer;
1513
+
1514
+ // src/components/renderers/BrowserRenderer.tsx
1515
+ var import_react11 = require("react");
1516
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1517
+ var BROWSER_ICONS = {
1518
+ browser_navigate: "\u{1F310}",
1519
+ browser_navigate1: "\u{1F310}",
1520
+ browser_click: "\u{1F5B1}\uFE0F",
1521
+ browser_click1: "\u{1F5B1}\uFE0F",
1522
+ browser_type: "\u2328\uFE0F",
1523
+ browser_hover: "\u{1F446}",
1524
+ browser_snapshot: "\u{1F4F8}",
1525
+ browser_take_screenshot: "\u{1F4F8}",
1526
+ browser_press_key: "\u2328\uFE0F",
1527
+ browser_drag: "\u2194\uFE0F",
1528
+ browser_tab_new: "\u2795",
1529
+ browser_tab_close: "\u2716\uFE0F",
1530
+ browser_wait: "\u23F1\uFE0F",
1531
+ browser_wait_for: "\u23F1\uFE0F"
1532
+ };
1533
+ var BrowserRenderer = ({ message, uiConfig, parsedArgs, phase }) => {
1534
+ const toolName = message.toolName || "";
1535
+ const taskPurpose = message.taskPurpose;
1536
+ const icon = BROWSER_ICONS[toolName] || "\u{1F310}";
1537
+ const primaryValue = extractField(parsedArgs, uiConfig.primary_field);
1538
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "tool-card-header", children: [
1539
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "tool-phase-icon", children: icon }),
1540
+ taskPurpose ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
1541
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "tool-card-name", children: taskPurpose }),
1542
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "tool-card-fn", children: [
1543
+ "(",
1544
+ toolName,
1545
+ ")"
1546
+ ] })
1547
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "tool-card-name", children: toolName }),
1548
+ primaryValue && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "browser-primary-value", children: formatValue(primaryValue) }),
1549
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PhaseLabel, { phase })
1550
+ ] }) });
1551
+ };
1552
+ var BrowserRenderer_default = BrowserRenderer;
1553
+
1554
+ // src/components/renderers/QueryRenderer.tsx
1555
+ var import_react12 = require("react");
1556
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1557
+ var QueryRenderer = ({ message, uiConfig, parsedArgs, phase }) => {
1558
+ const queryValue = extractField(parsedArgs, uiConfig.primary_field);
1559
+ const headerValue = extractField(parsedArgs, uiConfig.header_field);
1560
+ const toolName = message.toolName || "tool";
1561
+ const taskPurpose = message.taskPurpose;
1562
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_jsx_runtime10.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "tool-card-header", children: [
1563
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "tool-phase-icon", children: "\u{1F50D}" }),
1564
+ taskPurpose ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
1565
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "tool-card-name", children: taskPurpose }),
1566
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "tool-card-fn", children: [
1567
+ "(",
1568
+ toolName,
1569
+ ")"
1570
+ ] })
1571
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "tool-card-name", children: toolName }),
1572
+ queryValue && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "query-inline", children: [
1573
+ '"',
1574
+ formatValue(queryValue),
1575
+ '"'
1576
+ ] }),
1577
+ headerValue && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "query-scope-inline", children: [
1578
+ "in ",
1579
+ formatValue(headerValue)
1580
+ ] }),
1581
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PhaseLabel, { phase })
1582
+ ] }) });
1583
+ };
1584
+ var QueryRenderer_default = QueryRenderer;
1585
+
1586
+ // src/components/renderers/PathRenderer.tsx
1587
+ var import_react13 = require("react");
1588
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1589
+ var PathRenderer = ({ message, uiConfig, parsedArgs, phase }) => {
1590
+ const pathValue = extractField(parsedArgs, uiConfig.primary_field);
1591
+ const toolName = message.toolName || "tool";
1592
+ const taskPurpose = message.taskPurpose;
1593
+ const isDir = ["list_directory", "create_directory", "mkdir", "rmdir"].includes(toolName);
1594
+ const icon = isDir ? "\u{1F4C2}" : "\u{1F4C4}";
1595
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_jsx_runtime11.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "tool-card-header", children: [
1596
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "tool-phase-icon", children: icon }),
1597
+ taskPurpose ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
1598
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "tool-card-name", children: taskPurpose }),
1599
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "tool-card-fn", children: [
1600
+ "(",
1601
+ toolName,
1602
+ ")"
1603
+ ] })
1604
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "tool-card-name", children: toolName }),
1605
+ pathValue && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("code", { className: "tool-card-path-inline", children: formatValue(pathValue) }),
1606
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PhaseLabel, { phase })
1607
+ ] }) });
1608
+ };
1609
+ var PathRenderer_default = PathRenderer;
1610
+
1611
+ // src/components/renderers/TriggerRenderer.tsx
1612
+ var import_react14 = require("react");
1613
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1614
+ var TriggerRenderer = ({ message, uiConfig, parsedArgs, phase }) => {
1615
+ const [expanded, setExpanded] = (0, import_react14.useState)(false);
1616
+ const headerValue = extractField(parsedArgs, uiConfig.header_field);
1617
+ const primaryValue = extractField(parsedArgs, uiConfig.primary_field);
1618
+ const tags = buildCompactTags(parsedArgs, uiConfig.compact_fields, uiConfig.hide_fields);
1619
+ const toolLabel = message.taskPurpose || message.toolName || "tool";
1620
+ const hasDetail = !!parsedArgs && Object.keys(parsedArgs).length > 0;
1621
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
1622
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1623
+ "div",
1624
+ {
1625
+ className: "tool-card-header",
1626
+ onClick: () => hasDetail && setExpanded(!expanded),
1627
+ style: { cursor: hasDetail ? "pointer" : "default" },
1628
+ children: [
1629
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "tool-phase-icon", children: "\u23F0" }),
1630
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "tool-card-name", children: toolLabel }),
1631
+ (headerValue || primaryValue) && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "trigger-title", children: formatValue(headerValue || primaryValue) }),
1632
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PhaseLabel, { phase }),
1633
+ hasDetail && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "tool-card-expand", children: expanded ? "\u25BC" : "\u25B6" })
1634
+ ]
1635
+ }
1636
+ ),
1637
+ tags.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(CompactTags, { tags }),
1638
+ expanded && parsedArgs && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "trigger-detail", children: Object.entries(parsedArgs).filter(([k]) => !uiConfig.hide_fields?.includes(k)).map(([k, v]) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "trigger-row", children: [
1639
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "trigger-key", children: k }),
1640
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "trigger-value", children: formatValue(v) })
1641
+ ] }, k)) })
1642
+ ] });
1643
+ };
1644
+ var TriggerRenderer_default = TriggerRenderer;
1645
+
1646
+ // src/components/renderers/ThinkingRenderer.tsx
1647
+ var import_react15 = require("react");
1648
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1649
+ var ThinkingRenderer = ({ message, uiConfig, parsedArgs, phase }) => {
1650
+ const thought = extractField(parsedArgs, uiConfig.primary_field);
1651
+ const stepInfo = parsedArgs ? `${parsedArgs.thoughtNumber || "?"}/${parsedArgs.totalThoughts || "?"}` : "";
1652
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
1653
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "tool-card-header", children: [
1654
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "tool-phase-icon", children: "\u{1F4AD}" }),
1655
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "tool-card-name", children: "\u601D\u8003\u63A8\u7406" }),
1656
+ stepInfo && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "thinking-step", children: [
1657
+ "\u6B65\u9AA4 ",
1658
+ stepInfo
1659
+ ] }),
1660
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PhaseLabel, { phase })
1661
+ ] }),
1662
+ thought && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "thinking-bubble", children: formatValue(thought) })
1663
+ ] });
1664
+ };
1665
+ var ThinkingRenderer_default = ThinkingRenderer;
1666
+
1667
+ // src/components/renderers/ParamsRenderer.tsx
1668
+ var import_react16 = require("react");
1669
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1670
+ var ParamsRenderer = ({ message, uiConfig, parsedArgs, phase }) => {
1671
+ const [expanded, setExpanded] = (0, import_react16.useState)(false);
1672
+ const headerValue = extractField(parsedArgs, uiConfig?.header_field);
1673
+ const toolName = message.toolName || "tool";
1674
+ const taskPurpose = message.taskPurpose;
1675
+ const hasArgs = !!parsedArgs && Object.keys(parsedArgs).length > 0;
1676
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
1677
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
1678
+ "div",
1679
+ {
1680
+ className: "tool-card-header",
1681
+ onClick: () => hasArgs && setExpanded(!expanded),
1682
+ style: { cursor: hasArgs ? "pointer" : "default" },
1683
+ children: [
1684
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "tool-phase-icon", children: "\u2699\uFE0F" }),
1685
+ taskPurpose ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
1686
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "tool-card-name", children: taskPurpose }),
1687
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "tool-card-fn", children: [
1688
+ "(",
1689
+ toolName,
1690
+ ")"
1691
+ ] })
1692
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "tool-card-name", children: toolName }),
1693
+ headerValue && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("code", { className: "tool-card-path-inline", children: formatValue(headerValue) }),
1694
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PhaseLabel, { phase }),
1695
+ hasArgs && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "tool-card-expand", children: expanded ? "\u25BC" : "\u25B6" })
1696
+ ]
1697
+ }
1698
+ ),
1699
+ expanded && parsedArgs && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "params-detail", children: Object.entries(parsedArgs).filter(([k]) => !uiConfig?.hide_fields?.includes(k)).map(([k, v]) => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "params-row", children: [
1700
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "params-key", children: [
1701
+ k,
1702
+ ":"
1703
+ ] }),
1704
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "params-value", children: formatValue(v) })
1705
+ ] }, k)) })
1706
+ ] });
1707
+ };
1708
+ var ParamsRenderer_default = ParamsRenderer;
1709
+
1710
+ // src/components/renderers/ToolResultRenderer.tsx
1711
+ var import_react17 = require("react");
1712
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1713
+ function tryParseJson(raw) {
1714
+ try {
1715
+ return JSON.parse(raw);
1716
+ } catch {
1717
+ return null;
1718
+ }
1719
+ }
1720
+ function isSearchResultsShape(data) {
1721
+ if (!data || typeof data !== "object") return false;
1722
+ const obj = data;
1723
+ const sr = obj.search_results;
1724
+ return sr != null && Array.isArray(sr.results) && sr.results.length > 0;
1725
+ }
1726
+ function isScrapeResultShape(data) {
1727
+ if (!data || typeof data !== "object") return false;
1728
+ const obj = data;
1729
+ return typeof obj.url === "string" && obj.url.length > 0 && !("search_results" in obj) && (typeof obj.title === "string" || typeof obj.snippet === "string");
1730
+ }
1731
+ var SearchResultsView = ({ query, results }) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "tool-result-search", children: [
1732
+ query && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "tool-result-search-query", children: [
1733
+ "\u{1F50D} ",
1734
+ query
1735
+ ] }),
1736
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "tool-result-search-list", children: [
1737
+ results.slice(0, 8).map((r, i) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
1738
+ "a",
1739
+ {
1740
+ className: "tool-result-search-item",
1741
+ href: r.link,
1742
+ target: "_blank",
1743
+ rel: "noopener noreferrer",
1744
+ children: [
1745
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "search-item-title", children: r.title }),
1746
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "search-item-snippet", children: r.snippet }),
1747
+ r.date && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "search-item-date", children: r.date })
1748
+ ]
1749
+ },
1750
+ i
1751
+ )),
1752
+ results.length > 8 && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "search-item-more", children: [
1753
+ "+",
1754
+ results.length - 8,
1755
+ " more"
1756
+ ] })
1757
+ ] })
1758
+ ] });
1759
+ var ScrapeResultView = ({ result }) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "tool-result-scrape", children: [
1760
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
1761
+ "a",
1762
+ {
1763
+ className: "scrape-result-link",
1764
+ href: result.url,
1765
+ target: "_blank",
1766
+ rel: "noopener noreferrer",
1767
+ children: [
1768
+ result.title && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "scrape-result-title", children: result.title }),
1769
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "scrape-result-url", children: result.url })
1770
+ ]
1771
+ }
1772
+ ),
1773
+ result.snippet && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "scrape-result-snippet", children: result.snippet })
1774
+ ] });
1775
+ var JsonView = ({ data }) => {
1776
+ const formatted = JSON.stringify(data, null, 2);
1777
+ const lines = formatted.split("\n");
1778
+ const needsTruncation = lines.length > 12;
1779
+ const [showAll, setShowAll] = (0, import_react17.useState)(false);
1780
+ const display = showAll ? formatted : lines.slice(0, 12).join("\n");
1781
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "tool-result-json", children: [
1782
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("pre", { className: "tool-result-json-code", children: display }),
1783
+ needsTruncation && !showAll && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "tool-result-json-more", onClick: () => setShowAll(true), children: [
1784
+ "\u5C55\u5F00\u5168\u90E8 (",
1785
+ lines.length,
1786
+ " \u884C)"
1787
+ ] })
1788
+ ] });
1789
+ };
1790
+ var ToolResultRenderer = ({ content, defaultExpanded = false }) => {
1791
+ const [isExpanded, setIsExpanded] = (0, import_react17.useState)(defaultExpanded);
1792
+ if (!content.trim()) return null;
1793
+ const parsed = tryParseJson(content);
1794
+ const summaryText = (() => {
1795
+ if (parsed && isSearchResultsShape(parsed)) {
1796
+ const count = parsed.search_results.results.length;
1797
+ return `\u641C\u7D22\u7ED3\u679C \xB7 ${count} \u6761`;
1798
+ }
1799
+ if (parsed && isScrapeResultShape(parsed)) {
1800
+ const title = parsed.title || new URL(parsed.url).hostname;
1801
+ return `\u6293\u53D6\u7ED3\u679C \xB7 ${title}`;
1802
+ }
1803
+ if (parsed) {
1804
+ const str = JSON.stringify(parsed);
1805
+ return str.length > 60 ? str.slice(0, 60) + "\u2026" : str;
1806
+ }
1807
+ return content.length > 80 ? content.slice(0, 80) + "\u2026" : content;
1808
+ })();
1809
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "tool-result-section", children: [
1810
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "tool-result-header", onClick: () => setIsExpanded(!isExpanded), children: [
1811
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "tool-result-label", children: "\u{1F4CB} \u6267\u884C\u7ED3\u679C" }),
1812
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "tool-result-summary", children: summaryText }),
1813
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: `collapse-arrow ${isExpanded ? "expanded" : ""}`, children: "\u25BC" })
1814
+ ] }),
1815
+ isExpanded && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "tool-result-body", children: parsed && isSearchResultsShape(parsed) ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SearchResultsView, { query: parsed.query, results: parsed.search_results.results }) : parsed && isScrapeResultShape(parsed) ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ScrapeResultView, { result: parsed }) : parsed ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(JsonView, { data: parsed }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "tool-result-text", children: content }) })
1816
+ ] });
1817
+ };
1818
+ var ToolResultRenderer_default = ToolResultRenderer;
1819
+
1820
+ // src/components/ToolCardBuffering.tsx
1821
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1822
+ var renderers = {
1823
+ content: ContentRenderer_default,
1824
+ command: CommandRenderer_default,
1825
+ browser: BrowserRenderer_default,
1826
+ query: QueryRenderer_default,
1827
+ path: PathRenderer_default,
1828
+ trigger: TriggerRenderer_default,
1829
+ thinking: ThinkingRenderer_default,
1830
+ params: ParamsRenderer_default
1831
+ };
1832
+ var ToolCardBuffering = (0, import_react18.memo)(({ message }) => {
1833
+ const phase = message.toolPhase || "generating";
1834
+ const config = phaseConfig[phase];
1835
+ const uiConfig = message.uiConfig || { display_type: "params" };
1836
+ const parseCacheRef = (0, import_react18.useRef)(null);
1837
+ const parsedArgs = (0, import_react18.useMemo)(
1838
+ () => parseArgsIncremental(message.argsPreview, parseCacheRef),
1839
+ [message.argsPreview]
1840
+ );
1841
+ const displayType = uiConfig.display_type || "params";
1842
+ const Renderer = renderers[displayType] || ParamsRenderer_default;
1843
+ const resultContent = message.contentChunks.join("");
1844
+ const hasResult = resultContent.trim().length > 0;
1845
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: `tool-card-buffering ${config.className}`, children: [
1846
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1847
+ Renderer,
1848
+ {
1849
+ message,
1850
+ uiConfig,
1851
+ parsedArgs,
1852
+ phase
1853
+ }
1854
+ ),
1855
+ hasResult && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ToolResultRenderer_default, { content: resultContent })
1856
+ ] });
1857
+ });
1858
+ var ToolCardBuffering_default = ToolCardBuffering;
1859
+
1860
+ // src/components/MessageContent.tsx
1861
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1862
+ var math = (0, import_math.createMathPlugin)({ singleDollarTextMath: true });
1863
+ var streamdownPlugins = { code: import_code.code, mermaid: import_mermaid.mermaid, math, cjk: import_cjk.cjk };
1864
+ function countOccurrences(text, needle) {
1865
+ let count = 0;
1866
+ let pos = 0;
1867
+ while (pos < text.length) {
1868
+ const idx = text.indexOf(needle, pos);
1869
+ if (idx === -1) break;
1870
+ count++;
1871
+ pos = idx + needle.length;
1872
+ }
1873
+ return count;
1874
+ }
1875
+ function snapToSafeBoundary(content, length) {
1876
+ if (length >= content.length) return content.length;
1877
+ if (length === 0) return 0;
1878
+ const slice = content.slice(0, length);
1879
+ const lastNL = slice.lastIndexOf("\n");
1880
+ const currentLine = slice.slice(lastNL + 1);
1881
+ if (/^`{3,}/.test(currentLine)) {
1882
+ const lineEnd = content.indexOf("\n", lastNL + 1);
1883
+ return lineEnd !== -1 ? lineEnd + 1 : content.length;
1884
+ }
1885
+ if (/`{1,2}$/.test(currentLine) && length < content.length && content[length] === "`") {
1886
+ const lineEnd = content.indexOf("\n", lastNL + 1);
1887
+ return lineEnd !== -1 ? lineEnd + 1 : content.length;
1888
+ }
1889
+ if (slice.endsWith("$") && length < content.length && content[length] === "$") {
1890
+ return snapToSafeBoundary(content, length + 1);
1891
+ }
1892
+ const ddCount = countOccurrences(slice, "$$");
1893
+ if (ddCount % 2 === 1) {
1894
+ const closeIdx = content.indexOf("$$", length);
1895
+ if (closeIdx !== -1) {
1896
+ return Math.min(closeIdx + 2, content.length);
1897
+ }
1898
+ }
1899
+ const totalDollars = countOccurrences(slice, "$");
1900
+ const singleDollars = totalDollars - 2 * ddCount;
1901
+ if (singleDollars % 2 === 1) {
1902
+ let searchPos = length;
1903
+ while (searchPos < content.length) {
1904
+ const idx = content.indexOf("$", searchPos);
1905
+ if (idx === -1) break;
1906
+ if (idx + 1 < content.length && content[idx + 1] === "$") {
1907
+ searchPos = idx + 2;
1908
+ continue;
1909
+ }
1910
+ if (idx > 0 && content[idx - 1] === "$") {
1911
+ searchPos = idx + 1;
1912
+ continue;
1913
+ }
1914
+ return Math.min(idx + 1, content.length);
1915
+ }
1916
+ }
1917
+ return length;
1918
+ }
1919
+ function useTypewriter(content, enabled, speed = 30) {
1920
+ const [displayedLength, setDisplayedLength] = (0, import_react19.useState)(0);
1921
+ const [isTyping, setIsTyping] = (0, import_react19.useState)(false);
1922
+ const rafRef = (0, import_react19.useRef)(null);
1923
+ const targetLengthRef = (0, import_react19.useRef)(0);
1924
+ const displayedLengthRef = (0, import_react19.useRef)(0);
1925
+ displayedLengthRef.current = displayedLength;
1926
+ (0, import_react19.useEffect)(() => {
1927
+ if (!enabled) {
1928
+ setDisplayedLength(content.length);
1929
+ targetLengthRef.current = content.length;
1930
+ return;
1931
+ }
1932
+ targetLengthRef.current = content.length;
1933
+ if (rafRef.current) return;
1934
+ if (displayedLengthRef.current >= content.length) return;
1935
+ setIsTyping(true);
1936
+ let lastTime = performance.now();
1937
+ const animate = (currentTime) => {
1938
+ const deltaTime = currentTime - lastTime;
1939
+ lastTime = currentTime;
1940
+ const charsToAdd = Math.max(1, Math.ceil(deltaTime / speed));
1941
+ const currentDisplayed = displayedLengthRef.current;
1942
+ const target = targetLengthRef.current;
1943
+ const raw = Math.min(currentDisplayed + charsToAdd, target);
1944
+ const newLength = snapToSafeBoundary(content, raw);
1945
+ setDisplayedLength(newLength);
1946
+ displayedLengthRef.current = newLength;
1947
+ if (newLength >= target) {
1948
+ setIsTyping(false);
1949
+ rafRef.current = null;
1950
+ } else {
1951
+ rafRef.current = requestAnimationFrame(animate);
1952
+ }
1953
+ };
1954
+ rafRef.current = requestAnimationFrame(animate);
1955
+ return () => {
1956
+ if (rafRef.current) {
1957
+ cancelAnimationFrame(rafRef.current);
1958
+ rafRef.current = null;
1959
+ }
1960
+ };
1961
+ }, [content, enabled, speed]);
1962
+ return [content.slice(0, displayedLength), isTyping];
1963
+ }
1964
+ var MessageContent = (0, import_react19.memo)(({
1965
+ message,
1966
+ enableTypewriter = true,
1967
+ typewriterSpeed = 30,
1968
+ onArtifactClick
1969
+ }) => {
1970
+ const content = message.contentChunks.join("");
1971
+ const isTextType = message.contentType === "text";
1972
+ const [displayedContent, isTyping] = useTypewriter(
1973
+ content,
1974
+ enableTypewriter && isTextType,
1975
+ typewriterSpeed
1976
+ );
1977
+ if (message.contentType === "think") {
1978
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ThinkChunk, { content, isThinking: isTyping });
1979
+ }
1980
+ if (message.contentType === "json_schema_wait" || message.contentType === "json_schema" || message.contentType === "html_schema") {
1981
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(HITLWaitCard, { content, hitlRequest: message.hitlRequest });
1982
+ }
1983
+ if (message.contentType === "artifact" && (message.artifacts || message.artifact)) {
1984
+ const artifacts = message.artifacts || (message.artifact ? [message.artifact] : []);
1985
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "artifact-list", children: artifacts.map((artifact, index) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1986
+ ArtifactCard,
1987
+ {
1988
+ artifact,
1989
+ onClick: () => onArtifactClick?.(artifact)
1990
+ },
1991
+ artifact.id || index
1992
+ )) });
1993
+ }
1994
+ if (message.toolPhase) {
1995
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ToolCardBuffering_default, { message });
1996
+ }
1997
+ const isMcpEvent = message.notificationType && ["mcp_start", "mcp_result", "mcp_end"].includes(message.notificationType);
1998
+ if (isMcpEvent) {
1999
+ const toolName = message.toolName || (() => {
2000
+ const match = content.match(/[🔧\s]*(.+?)\s*\((\w+)\)/) || content.match(/[🔧\s]*execute tool:\s*(\S+)/);
2001
+ return match ? match[2] || match[1] : null;
2002
+ })();
2003
+ const toolLabelMatch = content.match(/[🔧\s]*(.+?)\s*\(/);
2004
+ const toolLabel = toolLabelMatch ? toolLabelMatch[1].trim() : message.taskPurpose || toolName || content;
2005
+ let statusIcon = "\u{1F527}";
2006
+ let statusClass = "";
2007
+ let statusText = "";
2008
+ if (message.notificationType === "mcp_start") {
2009
+ statusIcon = "\u23F3";
2010
+ statusClass = "tool-running";
2011
+ statusText = "\u6267\u884C\u4E2D";
2012
+ } else if (message.notificationType === "mcp_end") {
2013
+ if (message.toolStatus === "error") {
2014
+ statusIcon = "\u274C";
2015
+ statusClass = "tool-error";
2016
+ statusText = "\u5931\u8D25";
2017
+ } else {
2018
+ statusIcon = "\u2705";
2019
+ statusClass = "tool-success";
2020
+ statusText = "\u5B8C\u6210";
2021
+ }
2022
+ } else {
2023
+ statusIcon = "\u{1F527}";
2024
+ statusClass = "tool-running";
2025
+ }
2026
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: `tool-execution ${statusClass}`, children: [
2027
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "tool-icon", children: statusIcon }),
2028
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: "tool-text", children: [
2029
+ toolLabel,
2030
+ toolName && toolName !== toolLabel ? ` (${toolName})` : ""
2031
+ ] }),
2032
+ statusText && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "tool-status", children: statusText })
2033
+ ] });
2034
+ }
2035
+ const toolLineRegex = /^[🔧\s]*execute tool:\s*(\S+)\s*$/m;
2036
+ const toolMatch = content.match(toolLineRegex);
2037
+ if (toolMatch) {
2038
+ const toolName = toolMatch[1];
2039
+ const matchIndex = content.indexOf(toolMatch[0]);
2040
+ const beforeContent = content.slice(0, matchIndex).trim();
2041
+ const afterContent = content.slice(matchIndex + toolMatch[0].length).trim();
2042
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
2043
+ beforeContent && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "sd-message", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_streamdown.Streamdown, { plugins: streamdownPlugins, children: beforeContent }) }),
2044
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "tool-execution", children: [
2045
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "tool-icon", children: "\u{1F527}" }),
2046
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: "tool-text", children: [
2047
+ "\u6267\u884C\u5DE5\u5177: ",
2048
+ toolName
2049
+ ] })
2050
+ ] }),
2051
+ afterContent && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "sd-message", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_streamdown.Streamdown, { plugins: streamdownPlugins, children: afterContent }) })
2052
+ ] });
2053
+ }
2054
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "sd-message", children: [
2055
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2056
+ import_streamdown.Streamdown,
2057
+ {
2058
+ plugins: streamdownPlugins,
2059
+ isAnimating: isTyping,
2060
+ children: displayedContent
2061
+ }
2062
+ ),
2063
+ isTyping && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "streaming-cursor" })
2064
+ ] });
2065
+ });
2066
+ var ThinkChunk = ({ content, isThinking = false }) => {
2067
+ const [isExpanded, setIsExpanded] = (0, import_react19.useState)(false);
2068
+ const [manualToggled, setManualToggled] = (0, import_react19.useState)(false);
2069
+ const [prevContentLength, setPrevContentLength] = (0, import_react19.useState)(0);
2070
+ const [thinking, setThinking] = (0, import_react19.useState)(true);
2071
+ const previewRef = (0, import_react19.useRef)(null);
2072
+ const isActive = thinking || isThinking;
2073
+ const thinkingTimerRef = (0, import_react19.useRef)(null);
2074
+ (0, import_react19.useEffect)(() => {
2075
+ if (content.length > prevContentLength) {
2076
+ setThinking(true);
2077
+ setPrevContentLength(content.length);
2078
+ if (thinkingTimerRef.current) {
2079
+ clearTimeout(thinkingTimerRef.current);
2080
+ thinkingTimerRef.current = null;
2081
+ }
2082
+ }
2083
+ thinkingTimerRef.current = setTimeout(() => {
2084
+ if (content.length === prevContentLength) {
2085
+ setThinking(false);
2086
+ }
2087
+ }, 5e3);
2088
+ return () => {
2089
+ if (thinkingTimerRef.current) {
2090
+ clearTimeout(thinkingTimerRef.current);
2091
+ }
2092
+ };
2093
+ }, [content, prevContentLength]);
2094
+ (0, import_react19.useEffect)(() => {
2095
+ if (previewRef.current && isActive && !isExpanded) {
2096
+ previewRef.current.scrollTop = previewRef.current.scrollHeight;
2097
+ }
2098
+ }, [content, isActive, isExpanded]);
2099
+ const extractHeading = (text) => {
2100
+ const match = text.match(/^#\s+(.+)$/m);
2101
+ return match ? match[1].trim() : null;
2102
+ };
2103
+ const heading = extractHeading(content);
2104
+ const displayText = heading ? heading : isActive ? "\u601D\u8003\u4E2D..." : "\u601D\u8003\u5B8C\u6210";
2105
+ const showPreview = isActive && !manualToggled && !isExpanded && content.length > 0;
2106
+ const [previewVisible, setPreviewVisible] = (0, import_react19.useState)(false);
2107
+ (0, import_react19.useEffect)(() => {
2108
+ if (showPreview) {
2109
+ setPreviewVisible(true);
2110
+ } else if (previewVisible) {
2111
+ const t = setTimeout(() => setPreviewVisible(false), 600);
2112
+ return () => clearTimeout(t);
2113
+ }
2114
+ }, [showPreview]);
2115
+ const handleToggle = () => {
2116
+ setManualToggled(true);
2117
+ setPreviewVisible(false);
2118
+ setIsExpanded(!isExpanded);
2119
+ };
2120
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: `think-chunk ${isExpanded ? "expanded" : ""} ${isActive ? "thinking" : ""}`, children: [
2121
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
2122
+ "div",
2123
+ {
2124
+ className: "think-header",
2125
+ onClick: handleToggle,
2126
+ children: [
2127
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "think-title", children: [
2128
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: `think-icon ${isActive ? "thinking-animation" : ""}`, children: "\u{1F4AD}" }),
2129
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "think-text", children: displayText }),
2130
+ isActive && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: "thinking-dots", children: [
2131
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { children: "." }),
2132
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { children: "." }),
2133
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { children: "." })
2134
+ ] })
2135
+ ] }),
2136
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: `collapse-arrow ${isExpanded || previewVisible ? "expanded" : ""}`, children: "\u25BC" })
2137
+ ]
2138
+ }
2139
+ ),
2140
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2141
+ "div",
2142
+ {
2143
+ className: `think-preview ${previewVisible ? "think-preview-open" : "think-preview-closed"}`,
2144
+ ref: previewRef,
2145
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "sd-think", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_streamdown.Streamdown, { plugins: streamdownPlugins, isAnimating: isActive, children: content }) })
2146
+ }
2147
+ ),
2148
+ isExpanded && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "think-content", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "sd-think", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_streamdown.Streamdown, { plugins: streamdownPlugins, children: content }) }) })
2149
+ ] });
2150
+ };
2151
+ var HITLWaitCard = ({ content, hitlRequest }) => {
2152
+ const parseFirstJson = (str) => {
2153
+ try {
2154
+ return JSON.parse(str);
2155
+ } catch {
2156
+ let depth = 0;
2157
+ let start = -1;
2158
+ for (let i = 0; i < str.length; i++) {
2159
+ if (str[i] === "{") {
2160
+ if (depth === 0) start = i;
2161
+ depth++;
2162
+ } else if (str[i] === "}") {
2163
+ depth--;
2164
+ if (depth === 0 && start !== -1) {
2165
+ try {
2166
+ return JSON.parse(str.slice(start, i + 1));
2167
+ } catch {
2168
+ start = -1;
2169
+ }
2170
+ }
2171
+ }
2172
+ }
2173
+ return null;
2174
+ }
2175
+ };
2176
+ const getDisplayText = () => {
2177
+ const parsed = parseFirstJson(content);
2178
+ if (!parsed) {
2179
+ return content || "\u7B49\u5F85\u7528\u6237\u8F93\u5165...";
2180
+ }
2181
+ if (parsed.content !== void 0) {
2182
+ let langContent = parsed.content;
2183
+ if (typeof langContent === "string") {
2184
+ try {
2185
+ langContent = JSON.parse(langContent);
2186
+ } catch {
2187
+ return langContent || "\u7B49\u5F85\u7528\u6237\u8F93\u5165...";
2188
+ }
2189
+ }
2190
+ if (typeof langContent === "object" && langContent !== null) {
2191
+ const langObj = langContent;
2192
+ return langObj.zh_CN || langObj.en_US || JSON.stringify(langContent);
2193
+ }
2194
+ return String(langContent) || "\u7B49\u5F85\u7528\u6237\u8F93\u5165...";
2195
+ }
2196
+ if (parsed.zh_CN || parsed.en_US) {
2197
+ return parsed.zh_CN || parsed.en_US;
2198
+ }
2199
+ if (parsed.type && parsed.props?.title) {
2200
+ return parsed.props.title;
2201
+ }
2202
+ return content;
2203
+ };
2204
+ const getFormSchema = () => {
2205
+ const parsed = parseFirstJson(content);
2206
+ if (parsed) {
2207
+ if (parsed.type && (parsed.children || parsed.props)) {
2208
+ return parsed;
2209
+ }
2210
+ }
2211
+ if (hitlRequest?.schema) {
2212
+ return hitlRequest.schema;
2213
+ }
2214
+ return null;
2215
+ };
2216
+ const displayText = getDisplayText();
2217
+ const formSchema = getFormSchema();
2218
+ if (formSchema) {
2219
+ const schemaObj = formSchema;
2220
+ const title = schemaObj.props?.title || hitlRequest?.title || "\u8BF7\u586B\u5199\u8868\u5355";
2221
+ const handleFormSubmit = (values) => {
2222
+ console.log("[HITL] \u8868\u5355\u63D0\u4EA4:", values, "await_command_uuid:", hitlRequest?.await_command_uuid);
2223
+ };
2224
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "hitl-form-card", children: [
2225
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "hitl-form-header", children: [
2226
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "hitl-form-icon", children: "\u{1F4CB}" }),
2227
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "hitl-form-title", children: title })
2228
+ ] }),
2229
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "hitl-form-body", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2230
+ SchemaFormRenderer_default,
2231
+ {
2232
+ schema: formSchema,
2233
+ onSubmit: handleFormSubmit,
2234
+ awaitCommandUuid: hitlRequest?.await_command_uuid
2235
+ }
2236
+ ) })
2237
+ ] });
2238
+ }
2239
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "hitl-wait-card", children: [
2240
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "hitl-icon", children: "\u23F3" }),
2241
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "hitl-content", children: [
2242
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "hitl-text", children: displayText }),
2243
+ hitlRequest?.await_command_uuid && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "hitl-uuid", children: [
2244
+ "ID: ",
2245
+ hitlRequest.await_command_uuid.slice(0, 8),
2246
+ "..."
2247
+ ] })
2248
+ ] })
2249
+ ] });
2250
+ };
2251
+ var ArtifactCard = ({ artifact, onClick }) => {
2252
+ const iconMap = {
2253
+ "text/markdown": "\u{1F4DD}",
2254
+ "text/plain": "\u{1F4C4}",
2255
+ "application/json": "\u{1F4CA}",
2256
+ "image/": "\u{1F5BC}\uFE0F",
2257
+ "application/pdf": "\u{1F4D5}"
2258
+ };
2259
+ const icon = Object.entries(iconMap).find(
2260
+ ([type]) => artifact.type.startsWith(type)
2261
+ )?.[1] || "\u{1F4CE}";
2262
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "artifact-card", onClick, children: [
2263
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "artifact-icon", children: icon }),
2264
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "artifact-info", children: [
2265
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "artifact-name", children: artifact.name }),
2266
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "artifact-meta", children: artifact.type })
2267
+ ] }),
2268
+ artifact.size && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "artifact-size", children: artifact.size })
2269
+ ] });
2270
+ };
2271
+ var MessageContent_default = MessageContent;
2272
+
2273
+ // src/components/ChildAgentCard.tsx
2274
+ var import_react20 = require("react");
2275
+ var import_jsx_runtime18 = require("react/jsx-runtime");
2276
+ var ChildAgentCard = ({
2277
+ message,
2278
+ children,
2279
+ maxHeight = 200,
2280
+ config,
2281
+ onArtifactClick,
2282
+ defaultExpanded = false,
2283
+ parentTaskPurpose
2284
+ }) => {
2285
+ const [isExpanded, setIsExpanded] = (0, import_react20.useState)(defaultExpanded);
2286
+ const contentRef = (0, import_react20.useRef)(null);
2287
+ const hasActiveContent = children.some(
2288
+ (m) => m.contentType === "text" && m.contentChunks.join("").trim()
2289
+ );
2290
+ const status = hasActiveContent ? "completed" : "running";
2291
+ (0, import_react20.useEffect)(() => {
2292
+ if (isExpanded && contentRef.current) {
2293
+ contentRef.current.scrollTop = contentRef.current.scrollHeight;
2294
+ }
2295
+ }, [isExpanded, children]);
2296
+ const cardTitle = parentTaskPurpose || message.taskPurpose || message.agentName;
2297
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: `child-agent-card ${isExpanded ? "expanded" : ""}`, children: [
2298
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
2299
+ "div",
2300
+ {
2301
+ className: "child-agent-header",
2302
+ onClick: () => setIsExpanded(!isExpanded),
2303
+ children: [
2304
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "child-agent-title", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "card-title", children: cardTitle }) }),
2305
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "child-agent-status", children: [
2306
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: `status-badge status-${status}`, children: [
2307
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: `status-icon ${status === "running" ? "spinning" : ""}`, children: status === "running" ? "\u23F3" : "\u2713" }),
2308
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "status-text", children: status === "running" ? "\u6267\u884C\u4E2D" : "\u5DF2\u5B8C\u6210" })
2309
+ ] }),
2310
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: `collapse-arrow ${isExpanded ? "expanded" : ""}`, children: "\u25BC" })
2311
+ ] })
2312
+ ]
2313
+ }
2314
+ ),
2315
+ isExpanded && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
2316
+ "div",
2317
+ {
2318
+ className: "child-agent-content",
2319
+ ref: contentRef,
2320
+ style: { maxHeight },
2321
+ children: [
2322
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "child-agent-inner", children: [
2323
+ message.contentChunks.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2324
+ MessageContent_default,
2325
+ {
2326
+ message,
2327
+ enableTypewriter: config?.enableTypewriter,
2328
+ typewriterSpeed: config?.typewriterSpeed,
2329
+ onArtifactClick
2330
+ },
2331
+ message.id
2332
+ ),
2333
+ mergeConsecutiveThinkMessages(children).map((childMsg) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2334
+ MessageContent_default,
2335
+ {
2336
+ message: childMsg,
2337
+ enableTypewriter: config?.enableTypewriter,
2338
+ typewriterSpeed: config?.typewriterSpeed,
2339
+ onArtifactClick
2340
+ },
2341
+ childMsg.id
2342
+ ))
2343
+ ] }),
2344
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "child-agent-footer", children: [
2345
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "footer-agent-icon", children: "\u{1F916}" }),
2346
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "footer-agent-name", children: message.agentName })
2347
+ ] })
2348
+ ]
2349
+ }
2350
+ )
2351
+ ] });
2352
+ };
2353
+ var ChildAgentCard_default = ChildAgentCard;
2354
+
2355
+ // src/ChatWidget.tsx
2356
+ var import_jsx_runtime19 = require("react/jsx-runtime");
2357
+ var HYSTERESIS_THRESHOLD = 30;
2358
+ var DEBOUNCE_DELAY = 150;
2359
+ var FADE_DURATION = 150;
2360
+ function convertTurnsToRounds(turns) {
2361
+ const groups = /* @__PURE__ */ new Map();
2362
+ for (const t of turns) {
2363
+ const iid = t.interaction_id;
2364
+ if (!groups.has(iid)) groups.set(iid, []);
2365
+ groups.get(iid).push(t);
2366
+ }
2367
+ const rounds = [];
2368
+ let roundIdx = 0;
2369
+ for (const [interactionId, group] of groups) {
2370
+ const userTurn = group.find((t) => t.type === "user_input");
2371
+ const userText = userTurn ? (userTurn.contents || []).map((c) => c.content || "").join("") : "(\u5386\u53F2\u6D88\u606F)";
2372
+ const agentMessages = [];
2373
+ for (const turn of group) {
2374
+ if (turn.type === "user_input") continue;
2375
+ const contents = turn.contents || [];
2376
+ if (contents.length === 0 && !turn.type.startsWith("mcp_")) continue;
2377
+ const ext = turn.ext || {};
2378
+ if (contents.length > 0) {
2379
+ for (let i = 0; i < contents.length; i++) {
2380
+ const part = contents[i];
2381
+ const partType = part.type || "text";
2382
+ let contentType = "text";
2383
+ if (partType === "think") contentType = "think";
2384
+ else if (partType === "plan") contentType = "plan";
2385
+ else if (partType === "artifact") contentType = "artifact";
2386
+ else if (partType === "json_schema") contentType = "json_schema";
2387
+ else if (partType === "html_schema") contentType = "html_schema";
2388
+ agentMessages.push({
2389
+ id: `hist-${turn.turn_id}-${i}`,
2390
+ agentInstanceId: turn.agent_instance_id || 0,
2391
+ callBatchId: String(turn.call_batch_id || 0),
2392
+ parentCallBatchId: turn.parent_call_batch_id != null ? String(turn.parent_call_batch_id) : void 0,
2393
+ level: turn.level ?? 0,
2394
+ agentName: turn.card_title || turn.agent_id || "Assistant",
2395
+ notificationType: turn.type,
2396
+ toolName: ext.tool_name,
2397
+ toolCallId: ext.tool_call_id,
2398
+ toolStatus: ext.status,
2399
+ taskPurpose: ext.task_purpose || ext._task_purpose,
2400
+ uiConfig: ext.ui_config,
2401
+ parentToolCallId: ext.parent_tool_call_id,
2402
+ contentType,
2403
+ contentChunks: [part.content || ""],
2404
+ timestamp: String(turn.timestamp || ""),
2405
+ toolPhase: turn.type.startsWith("mcp_") ? "complete" : void 0
2406
+ });
2407
+ }
2408
+ } else {
2409
+ agentMessages.push({
2410
+ id: `hist-${turn.turn_id}`,
2411
+ agentInstanceId: turn.agent_instance_id || 0,
2412
+ callBatchId: String(turn.call_batch_id || 0),
2413
+ parentCallBatchId: turn.parent_call_batch_id != null ? String(turn.parent_call_batch_id) : void 0,
2414
+ level: turn.level ?? 0,
2415
+ agentName: turn.card_title || turn.agent_id || "Assistant",
2416
+ notificationType: turn.type,
2417
+ toolName: ext.tool_name,
2418
+ toolCallId: ext.tool_call_id,
2419
+ toolStatus: ext.status,
2420
+ taskPurpose: ext.task_purpose || ext._task_purpose,
2421
+ uiConfig: ext.ui_config,
2422
+ parentToolCallId: ext.parent_tool_call_id,
2423
+ contentType: "text",
2424
+ contentChunks: [],
2425
+ timestamp: String(turn.timestamp || ""),
2426
+ toolPhase: "complete"
2427
+ });
2428
+ }
2429
+ }
2430
+ roundIdx++;
2431
+ const ts = group[0]?.timestamp_start || group[0]?.timestamp;
2432
+ rounds.push({
2433
+ interactionId,
2434
+ index: roundIdx,
2435
+ userMessage: userText,
2436
+ timestamp: ts ? new Date(ts).toISOString() : (/* @__PURE__ */ new Date()).toISOString(),
2437
+ status: "completed",
2438
+ messages: agentMessages
2439
+ });
2440
+ }
2441
+ return rounds;
2442
+ }
2443
+ var ChatWidget = ({
2444
+ adapter,
2445
+ sessionId: propSessionId,
2446
+ initialMessages: _initialMessages,
2447
+ config: userConfig,
2448
+ renderHeaderExtra,
2449
+ renderFooter,
2450
+ onArtifactClick,
2451
+ onHITLSubmit: _onHITLSubmit,
2452
+ onStatusChange,
2453
+ onError,
2454
+ onReady,
2455
+ className,
2456
+ style,
2457
+ height = 600
2458
+ }) => {
2459
+ const config = (0, import_react21.useMemo)(() => ({
2460
+ ...DEFAULT_CONFIG,
2461
+ ...userConfig
2462
+ }), [userConfig]);
2463
+ const {
2464
+ state: aggregatorState,
2465
+ todoMap,
2466
+ processMessage,
2467
+ getRoundsList,
2468
+ startNewRound,
2469
+ loadRounds,
2470
+ finalizeRound
2471
+ } = useMessageAggregator();
2472
+ const [pinnedInteractionId, setPinnedInteractionId] = (0, import_react21.useState)(null);
2473
+ const [isTransitioning, setIsTransitioning] = (0, import_react21.useState)(false);
2474
+ const [executionStatus, setExecutionStatus] = (0, import_react21.useState)("idle");
2475
+ const [currentSessionId, setCurrentSessionId] = (0, import_react21.useState)(null);
2476
+ const [sessionTitle, setSessionTitle] = (0, import_react21.useState)("");
2477
+ const [displayedTitle, setDisplayedTitle] = (0, import_react21.useState)("");
2478
+ const [isTitleAnimating, setIsTitleAnimating] = (0, import_react21.useState)(false);
2479
+ const sessionTitleFetchedRef = (0, import_react21.useRef)(false);
2480
+ const agentInstanceMapRef = (0, import_react21.useRef)(/* @__PURE__ */ new Map());
2481
+ const currentAgentIdRef = (0, import_react21.useRef)("agent-666");
2482
+ const historyLoadedRef = (0, import_react21.useRef)(false);
2483
+ (0, import_react21.useEffect)(() => {
2484
+ if (!propSessionId) return;
2485
+ const numericId = typeof propSessionId === "string" ? parseInt(propSessionId, 10) : propSessionId;
2486
+ if (isNaN(numericId)) return;
2487
+ setCurrentSessionId(numericId);
2488
+ if (historyLoadedRef.current) return;
2489
+ historyLoadedRef.current = true;
2490
+ const loadHistory = async () => {
2491
+ try {
2492
+ console.log("[ChatWidget] Loading session history for:", numericId);
2493
+ const [turns, detail] = await Promise.all([
2494
+ adapter.getSessionHistory(numericId),
2495
+ adapter.getSessionDetail(numericId).catch(() => null)
2496
+ ]);
2497
+ if (detail?.session_title) {
2498
+ setSessionTitle(detail.session_title);
2499
+ sessionTitleFetchedRef.current = true;
2500
+ }
2501
+ if (!turns || turns.length === 0) return;
2502
+ for (const t of turns) {
2503
+ if ((t.level === 0 || t.level == null) && t.agent_instance_id && t.agent_id) {
2504
+ agentInstanceMapRef.current.set(t.agent_id, t.agent_instance_id);
2505
+ }
2506
+ }
2507
+ if (agentInstanceMapRef.current.size > 0) {
2508
+ console.log("[ChatWidget] Restored agent instance map from history:", Object.fromEntries(agentInstanceMapRef.current));
2509
+ }
2510
+ const rounds2 = convertTurnsToRounds(turns);
2511
+ if (rounds2.length > 0) {
2512
+ loadRounds(rounds2);
2513
+ setExecutionStatus("completed");
2514
+ setPinnedInteractionId(rounds2[rounds2.length - 1].interactionId);
2515
+ }
2516
+ console.log("[ChatWidget] Loaded", rounds2.length, "history rounds from", turns.length, "turns");
2517
+ } catch (err) {
2518
+ console.warn("[ChatWidget] Failed to load session history:", err);
2519
+ }
2520
+ };
2521
+ loadHistory();
2522
+ }, [propSessionId, loadRounds]);
2523
+ const handleSSEMessage = (0, import_react21.useCallback)((message) => {
2524
+ if (message.type === "session_info" && message.session_id) {
2525
+ setCurrentSessionId(message.session_id);
2526
+ sessionTitleFetchedRef.current = false;
2527
+ return;
2528
+ }
2529
+ if (message.notification_type === "session_title" && message.content) {
2530
+ console.log("[ChatWidget] Received session_title:", message.content);
2531
+ setSessionTitle(message.content);
2532
+ sessionTitleFetchedRef.current = true;
2533
+ return;
2534
+ }
2535
+ const msgType = message.type || message.notification_type;
2536
+ if (msgType === "compression_started") {
2537
+ setExecutionStatus("compressing");
2538
+ return;
2539
+ }
2540
+ if (msgType === "compression_completed") {
2541
+ setExecutionStatus("running");
2542
+ return;
2543
+ }
2544
+ if (message.type === "finish") {
2545
+ const iid = message.interaction_id || aggregatorState.currentInteractionId;
2546
+ if (iid) {
2547
+ finalizeRound(iid);
2548
+ }
2549
+ setExecutionStatus("completed");
2550
+ return;
2551
+ }
2552
+ if (message.type === "error") {
2553
+ setExecutionStatus("error");
2554
+ onError?.(new Error(message.content || "Unknown error"));
2555
+ return;
2556
+ }
2557
+ if (message.interaction_id && message.content_type) {
2558
+ if (message.level === 0 && message.agent_instance_id) {
2559
+ const prevId = agentInstanceMapRef.current.get(currentAgentIdRef.current);
2560
+ if (prevId !== message.agent_instance_id) {
2561
+ agentInstanceMapRef.current.set(currentAgentIdRef.current, message.agent_instance_id);
2562
+ console.log("[ChatWidget] Captured level-0 agent instance:", currentAgentIdRef.current, "\u2192", message.agent_instance_id);
2563
+ }
2564
+ }
2565
+ processMessage(message);
2566
+ }
2567
+ }, [processMessage, onError, finalizeRound, aggregatorState.currentInteractionId]);
2568
+ const { status: connectionStatus, connect, disconnect: _disconnect } = useSSE({
2569
+ adapter,
2570
+ onMessage: handleSSEMessage,
2571
+ onError
2572
+ });
2573
+ const [pendingScrollInteractionId, setPendingScrollInteractionId] = (0, import_react21.useState)(null);
2574
+ const sendMessage = (0, import_react21.useCallback)((message, agentId = "agent-666") => {
2575
+ const interactionId = crypto.randomUUID();
2576
+ console.log("====== [sendMessage v2] \u53D1\u9001\u65B0\u6D88\u606F ======", { interactionId, message });
2577
+ currentAgentIdRef.current = agentId;
2578
+ sessionTitleFetchedRef.current = false;
2579
+ startNewRound(interactionId, message);
2580
+ console.log("[sendMessage] \u5207\u6362\u7F6E\u9876\u5230:", interactionId);
2581
+ setPinnedInteractionId(interactionId);
2582
+ isUserAtBottomRef.current = true;
2583
+ setPendingScrollInteractionId(interactionId);
2584
+ setExecutionStatus("running");
2585
+ const knownInstanceId = agentInstanceMapRef.current.get(agentId);
2586
+ if (knownInstanceId) {
2587
+ console.log("[sendMessage] \u590D\u7528 agent \u5B9E\u4F8B:", agentId, "\u2192", knownInstanceId);
2588
+ }
2589
+ connect(adapter.getStreamUrl(), {
2590
+ message,
2591
+ agent_id: agentId,
2592
+ agent_instance_id: knownInstanceId,
2593
+ session_id: currentSessionId,
2594
+ interaction_id: interactionId
2595
+ });
2596
+ }, [adapter, currentSessionId, startNewRound, connect]);
2597
+ const messageListRef = (0, import_react21.useRef)(null);
2598
+ const scrollDebounceTimer = (0, import_react21.useRef)(null);
2599
+ const lastScrollTop = (0, import_react21.useRef)(0);
2600
+ const isUserAtBottomRef = (0, import_react21.useRef)(true);
2601
+ const isProgrammaticScrollRef = (0, import_react21.useRef)(false);
2602
+ const clearScreenScrollTopRef = (0, import_react21.useRef)(null);
2603
+ const rounds = (0, import_react21.useMemo)(() => getRoundsList(), [getRoundsList, aggregatorState]);
2604
+ const getCurrentRound = (0, import_react21.useCallback)(() => {
2605
+ if (rounds.length === 0) return null;
2606
+ return rounds[rounds.length - 1];
2607
+ }, [rounds]);
2608
+ const pinnedRound = (0, import_react21.useMemo)(() => {
2609
+ if (!pinnedInteractionId && rounds.length > 0) {
2610
+ return rounds[rounds.length - 1];
2611
+ }
2612
+ return rounds.find((r) => r.interactionId === pinnedInteractionId) || null;
2613
+ }, [rounds, pinnedInteractionId]);
2614
+ const calculatePinnedRound = (0, import_react21.useCallback)(() => {
2615
+ if (!messageListRef.current || rounds.length === 0) {
2616
+ return rounds[rounds.length - 1]?.interactionId || null;
2617
+ }
2618
+ const listRect = messageListRef.current.getBoundingClientRect();
2619
+ const viewportTop = listRect.top;
2620
+ for (let i = rounds.length - 1; i >= 0; i--) {
2621
+ const round = rounds[i];
2622
+ const container = messageListRef.current.querySelector(
2623
+ `[data-interaction-id="${round.interactionId}"]`
2624
+ );
2625
+ if (!container) continue;
2626
+ const rect = container.getBoundingClientRect();
2627
+ if (rect.top <= viewportTop + 50) {
2628
+ return round.interactionId;
2629
+ }
2630
+ if (i > 0) {
2631
+ if (rect.top <= viewportTop + 100) {
2632
+ return rounds[i - 1].interactionId;
2633
+ }
2634
+ }
2635
+ }
2636
+ return rounds[0]?.interactionId || null;
2637
+ }, [rounds]);
2638
+ const updatePinnedArea = (0, import_react21.useCallback)((interactionId) => {
2639
+ if (isTransitioning || interactionId === pinnedInteractionId) return;
2640
+ setIsTransitioning(true);
2641
+ setTimeout(() => {
2642
+ setPinnedInteractionId(interactionId);
2643
+ setTimeout(() => {
2644
+ setIsTransitioning(false);
2645
+ }, FADE_DURATION);
2646
+ }, FADE_DURATION);
2647
+ }, [isTransitioning, pinnedInteractionId]);
2648
+ const handleScroll = (0, import_react21.useCallback)((e) => {
2649
+ const target = e.currentTarget;
2650
+ const currentScrollTop = target.scrollTop;
2651
+ const scrollDelta = Math.abs(currentScrollTop - lastScrollTop.current);
2652
+ if (isProgrammaticScrollRef.current) {
2653
+ lastScrollTop.current = currentScrollTop;
2654
+ return;
2655
+ }
2656
+ const scrollBottom = target.scrollHeight - target.scrollTop - target.clientHeight;
2657
+ isUserAtBottomRef.current = scrollBottom < 50;
2658
+ if (scrollBottom < 50 && clearScreenScrollTopRef.current !== null) {
2659
+ clearScreenScrollTopRef.current = null;
2660
+ }
2661
+ if (scrollDebounceTimer.current) {
2662
+ clearTimeout(scrollDebounceTimer.current);
2663
+ }
2664
+ const newPinnedRound = calculatePinnedRound();
2665
+ if (isTransitioning) {
2666
+ lastScrollTop.current = currentScrollTop;
2667
+ return;
2668
+ }
2669
+ if (newPinnedRound !== pinnedInteractionId) {
2670
+ if (scrollDelta >= HYSTERESIS_THRESHOLD) {
2671
+ updatePinnedArea(newPinnedRound);
2672
+ lastScrollTop.current = currentScrollTop;
2673
+ } else {
2674
+ scrollDebounceTimer.current = window.setTimeout(() => {
2675
+ if (!isTransitioning && !isProgrammaticScrollRef.current) {
2676
+ const confirmedRound = calculatePinnedRound();
2677
+ if (confirmedRound !== pinnedInteractionId) {
2678
+ updatePinnedArea(confirmedRound);
2679
+ }
2680
+ }
2681
+ lastScrollTop.current = target.scrollTop;
2682
+ }, DEBOUNCE_DELAY);
2683
+ }
2684
+ } else {
2685
+ lastScrollTop.current = currentScrollTop;
2686
+ }
2687
+ }, [calculatePinnedRound, updatePinnedArea, isTransitioning, pinnedInteractionId]);
2688
+ const organizeMessagesByLevel = (0, import_react21.useCallback)((messages) => {
2689
+ const rootMessages = [];
2690
+ const childrenMap = /* @__PURE__ */ new Map();
2691
+ const isChildMessage = (msg) => {
2692
+ const pcb = msg.parentCallBatchId;
2693
+ if (pcb == null || pcb === "" || pcb === "-1") return false;
2694
+ const pcbNum = Number(pcb);
2695
+ if (pcbNum <= 0 || isNaN(pcbNum)) return false;
2696
+ return true;
2697
+ };
2698
+ messages.forEach((msg) => {
2699
+ if (isChildMessage(msg)) {
2700
+ const key = msg.parentToolCallId || String(msg.parentCallBatchId);
2701
+ const children = childrenMap.get(key) || [];
2702
+ children.push(msg);
2703
+ childrenMap.set(key, children);
2704
+ } else {
2705
+ rootMessages.push(msg);
2706
+ }
2707
+ });
2708
+ return { rootMessages, childrenMap };
2709
+ }, []);
2710
+ const [viewerOpen, setViewerOpen] = (0, import_react21.useState)(false);
2711
+ const [viewerLoading, setViewerLoading] = (0, import_react21.useState)(false);
2712
+ const [viewerContent, setViewerContent] = (0, import_react21.useState)(null);
2713
+ const [viewerError, setViewerError] = (0, import_react21.useState)(null);
2714
+ const [viewerArtifact, setViewerArtifact] = (0, import_react21.useState)(null);
2715
+ const handleArtifactClick = (0, import_react21.useCallback)((artifact) => {
2716
+ onArtifactClick?.(artifact);
2717
+ if (!currentSessionId) return;
2718
+ setViewerArtifact(artifact);
2719
+ setViewerOpen(true);
2720
+ setViewerLoading(true);
2721
+ setViewerContent(null);
2722
+ setViewerError(null);
2723
+ adapter.getResourceContent(currentSessionId, artifact.id).then((data) => {
2724
+ setViewerContent(data);
2725
+ setViewerLoading(false);
2726
+ }).catch((err) => {
2727
+ console.error("[ChatWidget] Failed to fetch resource content:", err);
2728
+ setViewerError(err?.message || "\u65E0\u6CD5\u83B7\u53D6\u6587\u4EF6\u5185\u5BB9");
2729
+ setViewerLoading(false);
2730
+ });
2731
+ }, [onArtifactClick, currentSessionId]);
2732
+ const renderRoundMessages = (0, import_react21.useCallback)((round) => {
2733
+ const { rootMessages: rawRootMessages, childrenMap } = organizeMessagesByLevel(round.messages);
2734
+ const rootMessages = mergeConsecutiveThinkMessages(rawRootMessages);
2735
+ const elements = [];
2736
+ const renderedChildKeys = /* @__PURE__ */ new Set();
2737
+ const isCallAgentToolCard = (msg) => {
2738
+ if (!msg.toolPhase) return false;
2739
+ const name = (msg.toolName || "").toLowerCase();
2740
+ return name.includes("call_agent") || name.includes("callagent");
2741
+ };
2742
+ rootMessages.forEach((msg) => {
2743
+ if (isCallAgentToolCard(msg)) {
2744
+ const childKey = msg.toolCallId && childrenMap.has(msg.toolCallId) ? msg.toolCallId : childrenMap.has(String(msg.callBatchId)) ? String(msg.callBatchId) : null;
2745
+ if (childKey && !renderedChildKeys.has(childKey)) {
2746
+ renderedChildKeys.add(childKey);
2747
+ const children = childrenMap.get(childKey);
2748
+ const childGroups = /* @__PURE__ */ new Map();
2749
+ children.forEach((child) => {
2750
+ const group = childGroups.get(child.agentInstanceId) || [];
2751
+ group.push(child);
2752
+ childGroups.set(child.agentInstanceId, group);
2753
+ });
2754
+ childGroups.forEach((groupChildren, agentInstanceId) => {
2755
+ const firstChild = groupChildren[0];
2756
+ elements.push(
2757
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2758
+ ChildAgentCard_default,
2759
+ {
2760
+ message: firstChild,
2761
+ children: groupChildren.slice(1),
2762
+ maxHeight: config.childAgentMaxHeight,
2763
+ config,
2764
+ onArtifactClick: handleArtifactClick,
2765
+ defaultExpanded: true,
2766
+ parentTaskPurpose: msg.taskPurpose
2767
+ },
2768
+ `child-${childKey}-${agentInstanceId}`
2769
+ )
2770
+ );
2771
+ });
2772
+ return;
2773
+ }
2774
+ }
2775
+ elements.push(
2776
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2777
+ MessageContent_default,
2778
+ {
2779
+ message: msg,
2780
+ enableTypewriter: config.enableTypewriter,
2781
+ typewriterSpeed: config.typewriterSpeed,
2782
+ onArtifactClick: handleArtifactClick
2783
+ },
2784
+ msg.id
2785
+ )
2786
+ );
2787
+ });
2788
+ return elements;
2789
+ }, [organizeMessagesByLevel, config, handleArtifactClick]);
2790
+ (0, import_react21.useEffect)(() => {
2791
+ if (rounds.length > 0 && !pinnedInteractionId) {
2792
+ setPinnedInteractionId(rounds[rounds.length - 1].interactionId);
2793
+ }
2794
+ }, [rounds, pinnedInteractionId]);
2795
+ const [containerHeight, setContainerHeight] = (0, import_react21.useState)(0);
2796
+ const [roundHeights, setRoundHeights] = (0, import_react21.useState)(/* @__PURE__ */ new Map());
2797
+ (0, import_react21.useEffect)(() => {
2798
+ if (!messageListRef.current) return;
2799
+ const updateHeight = () => {
2800
+ if (messageListRef.current) {
2801
+ setContainerHeight(messageListRef.current.clientHeight);
2802
+ }
2803
+ };
2804
+ updateHeight();
2805
+ window.addEventListener("resize", updateHeight);
2806
+ return () => window.removeEventListener("resize", updateHeight);
2807
+ }, []);
2808
+ (0, import_react21.useEffect)(() => {
2809
+ if (!messageListRef.current) return;
2810
+ const roundContainers = messageListRef.current.querySelectorAll(".round-container");
2811
+ if (roundContainers.length === 0) return;
2812
+ const resizeObserver = new ResizeObserver((entries) => {
2813
+ const newHeights = new Map(roundHeights);
2814
+ let changed = false;
2815
+ entries.forEach((entry) => {
2816
+ const interactionId = entry.target.dataset.interactionId;
2817
+ if (interactionId) {
2818
+ const height2 = entry.contentRect.height;
2819
+ if (newHeights.get(interactionId) !== height2) {
2820
+ newHeights.set(interactionId, height2);
2821
+ changed = true;
2822
+ }
2823
+ }
2824
+ });
2825
+ if (changed) {
2826
+ setRoundHeights(newHeights);
2827
+ }
2828
+ });
2829
+ roundContainers.forEach((container) => {
2830
+ resizeObserver.observe(container);
2831
+ });
2832
+ return () => resizeObserver.disconnect();
2833
+ }, [rounds.length]);
2834
+ const getSpacerHeight = (0, import_react21.useCallback)((interactionId) => {
2835
+ const roundHeight = roundHeights.get(interactionId) || 0;
2836
+ return Math.max(0, containerHeight - roundHeight);
2837
+ }, [containerHeight, roundHeights]);
2838
+ const processedScrollRef = (0, import_react21.useRef)(null);
2839
+ (0, import_react21.useEffect)(() => {
2840
+ if (!pendingScrollInteractionId || !messageListRef.current) return;
2841
+ if (processedScrollRef.current === pendingScrollInteractionId) return;
2842
+ const roundExists = rounds.some((r) => r.interactionId === pendingScrollInteractionId);
2843
+ if (!roundExists) return;
2844
+ const targetInteractionId = pendingScrollInteractionId;
2845
+ processedScrollRef.current = targetInteractionId;
2846
+ setPendingScrollInteractionId(null);
2847
+ isProgrammaticScrollRef.current = true;
2848
+ clearScreenScrollTopRef.current = 0;
2849
+ const scrollToRoundTop = () => {
2850
+ if (!messageListRef.current) return;
2851
+ const container = messageListRef.current;
2852
+ const roundElement = container.querySelector(
2853
+ `[data-interaction-id="${targetInteractionId}"]`
2854
+ );
2855
+ if (roundElement) {
2856
+ const targetScrollTop = roundElement.offsetTop - 16 + 20;
2857
+ container.scrollTop = Math.max(0, targetScrollTop);
2858
+ clearScreenScrollTopRef.current = container.scrollTop;
2859
+ }
2860
+ };
2861
+ requestAnimationFrame(scrollToRoundTop);
2862
+ setTimeout(() => {
2863
+ isProgrammaticScrollRef.current = false;
2864
+ }, 200);
2865
+ }, [rounds, pendingScrollInteractionId]);
2866
+ (0, import_react21.useEffect)(() => {
2867
+ if (isProgrammaticScrollRef.current) return;
2868
+ if (!messageListRef.current || executionStatus !== "running" && executionStatus !== "compressing") return;
2869
+ requestAnimationFrame(() => {
2870
+ if (!messageListRef.current || isProgrammaticScrollRef.current) return;
2871
+ const container = messageListRef.current;
2872
+ const maxScrollTop = container.scrollHeight - container.clientHeight;
2873
+ const currentScrollTop = container.scrollTop;
2874
+ if (clearScreenScrollTopRef.current !== null) {
2875
+ const roundContainers = container.querySelectorAll(".round-container");
2876
+ const currentRoundElement = roundContainers[roundContainers.length - 1];
2877
+ if (currentRoundElement) {
2878
+ const roundBottom = currentRoundElement.offsetTop + currentRoundElement.offsetHeight;
2879
+ const visibleBottom = currentScrollTop + container.clientHeight;
2880
+ if (roundBottom > visibleBottom) {
2881
+ const targetScrollTop = roundBottom - container.clientHeight;
2882
+ const finalScrollTop = Math.max(targetScrollTop, clearScreenScrollTopRef.current);
2883
+ container.scrollTop = finalScrollTop;
2884
+ }
2885
+ }
2886
+ return;
2887
+ }
2888
+ if (isUserAtBottomRef.current) {
2889
+ container.scrollTop = maxScrollTop;
2890
+ }
2891
+ });
2892
+ }, [aggregatorState, executionStatus]);
2893
+ const scrollToBottom = (0, import_react21.useCallback)(() => {
2894
+ if (!messageListRef.current) return;
2895
+ messageListRef.current.scrollTop = messageListRef.current.scrollHeight;
2896
+ }, []);
2897
+ const getStatus = (0, import_react21.useCallback)(() => executionStatus, [executionStatus]);
2898
+ const widgetAPI = (0, import_react21.useMemo)(() => ({
2899
+ sendMessage,
2900
+ getCurrentRound,
2901
+ getAllRounds: getRoundsList,
2902
+ scrollToBottom,
2903
+ getStatus
2904
+ }), [sendMessage, getCurrentRound, getRoundsList, scrollToBottom, getStatus]);
2905
+ (0, import_react21.useEffect)(() => {
2906
+ onReady?.(widgetAPI);
2907
+ }, [onReady, widgetAPI]);
2908
+ (0, import_react21.useEffect)(() => {
2909
+ onStatusChange?.(executionStatus);
2910
+ }, [executionStatus, onStatusChange]);
2911
+ (0, import_react21.useEffect)(() => {
2912
+ if (!sessionTitle) {
2913
+ setDisplayedTitle("");
2914
+ setIsTitleAnimating(false);
2915
+ return;
2916
+ }
2917
+ if (sessionTitle !== displayedTitle) {
2918
+ setIsTitleAnimating(true);
2919
+ let currentIndex = 0;
2920
+ const animateTitle = () => {
2921
+ if (currentIndex <= sessionTitle.length) {
2922
+ setDisplayedTitle(sessionTitle.slice(0, currentIndex));
2923
+ currentIndex++;
2924
+ requestAnimationFrame(() => {
2925
+ setTimeout(animateTitle, 50);
2926
+ });
2927
+ } else {
2928
+ setIsTitleAnimating(false);
2929
+ }
2930
+ };
2931
+ setDisplayedTitle("");
2932
+ requestAnimationFrame(animateTitle);
2933
+ }
2934
+ }, [sessionTitle]);
2935
+ const fetchSessionTitle = (0, import_react21.useCallback)(async (sessionId) => {
2936
+ if (sessionTitleFetchedRef.current) return;
2937
+ try {
2938
+ console.log("[ChatWidget] Fetching session title for session:", sessionId);
2939
+ const detail = await adapter.getSessionDetail(sessionId);
2940
+ if (detail.session_title) {
2941
+ console.log("[ChatWidget] Fetched session title:", detail.session_title);
2942
+ setSessionTitle(detail.session_title);
2943
+ sessionTitleFetchedRef.current = true;
2944
+ }
2945
+ } catch (error) {
2946
+ console.warn("[ChatWidget] Failed to fetch session title:", error);
2947
+ }
2948
+ }, []);
2949
+ (0, import_react21.useEffect)(() => {
2950
+ if (executionStatus === "completed" && currentSessionId && !sessionTitle && !sessionTitleFetchedRef.current) {
2951
+ const timer = setTimeout(() => {
2952
+ fetchSessionTitle(currentSessionId);
2953
+ }, 500);
2954
+ return () => clearTimeout(timer);
2955
+ }
2956
+ }, [executionStatus, currentSessionId, sessionTitle, fetchSessionTitle]);
2957
+ (0, import_react21.useEffect)(() => {
2958
+ if (connectionStatus === "connected") {
2959
+ setExecutionStatus("running");
2960
+ } else if (connectionStatus === "disconnected") {
2961
+ setExecutionStatus("completed");
2962
+ } else if (connectionStatus === "error") {
2963
+ setExecutionStatus("error");
2964
+ }
2965
+ }, [connectionStatus]);
2966
+ (0, import_react21.useEffect)(() => {
2967
+ return () => {
2968
+ if (scrollDebounceTimer.current) {
2969
+ clearTimeout(scrollDebounceTimer.current);
2970
+ }
2971
+ };
2972
+ }, []);
2973
+ const getStatusDisplay = () => {
2974
+ if (executionStatus === "compressing") {
2975
+ return { icon: "\u{1F5DC}\uFE0F", text: "\u4E0A\u4E0B\u6587\u538B\u7F29\u4E2D..." };
2976
+ }
2977
+ switch (connectionStatus) {
2978
+ case "connecting":
2979
+ return { icon: "\u23F3", text: "\u8FDE\u63A5\u4E2D..." };
2980
+ case "connected":
2981
+ return { icon: "\u23F3", text: "\u6267\u884C\u4E2D..." };
2982
+ case "error":
2983
+ return { icon: "\u2717", text: "\u8FDE\u63A5\u9519\u8BEF" };
2984
+ default:
2985
+ return { icon: "\u25CB", text: "\u5F85\u547D" };
2986
+ }
2987
+ };
2988
+ const statusDisplay = getStatusDisplay();
2989
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
2990
+ "div",
2991
+ {
2992
+ className: `chat-widget ${config.theme} ${className || ""}`,
2993
+ style: { ...style, height },
2994
+ children: [
2995
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "chat-header", children: [
2996
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "chat-header-title", children: [
2997
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "header-icon", children: "\u{1F916}" }),
2998
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { children: "AI \u52A9\u624B" })
2999
+ ] }),
3000
+ displayedTitle && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: `session-title ${isTitleAnimating ? "animating" : ""}`, children: [
3001
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "session-title-text", children: displayedTitle }),
3002
+ isTitleAnimating && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "title-cursor", children: "|" })
3003
+ ] }),
3004
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
3005
+ currentSessionId != null && renderHeaderExtra?.(currentSessionId),
3006
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: `status-badge status-${connectionStatus}`, children: [
3007
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "status-icon", children: statusDisplay.icon }),
3008
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { children: statusDisplay.text })
3009
+ ] })
3010
+ ] })
3011
+ ] }),
3012
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "message-list-container", children: [
3013
+ config.showPinnedArea && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3014
+ PinnedArea_default,
3015
+ {
3016
+ round: pinnedRound,
3017
+ isTransitioning,
3018
+ config,
3019
+ todoMap
3020
+ }
3021
+ ),
3022
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
3023
+ "div",
3024
+ {
3025
+ className: "message-list",
3026
+ ref: messageListRef,
3027
+ onScroll: handleScroll,
3028
+ children: [
3029
+ rounds.map((round, index) => /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_react21.default.Fragment, { children: [
3030
+ index > 0 && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "round-separator", children: "\u2191 \u4E0A\u4E00\u8F6E\u5BF9\u8BDD \u2191" }),
3031
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
3032
+ "div",
3033
+ {
3034
+ className: "round-container",
3035
+ "data-round": round.index,
3036
+ "data-interaction-id": round.interactionId,
3037
+ children: [
3038
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3039
+ RoundHeader_default,
3040
+ {
3041
+ round,
3042
+ isPinned: round.interactionId === pinnedInteractionId,
3043
+ config,
3044
+ todoMap
3045
+ }
3046
+ ),
3047
+ renderRoundMessages(round)
3048
+ ]
3049
+ }
3050
+ ),
3051
+ index > 0 && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3052
+ "div",
3053
+ {
3054
+ className: `clear-screen-spacer ${index < rounds.length - 1 || getSpacerHeight(round.interactionId) <= 0 ? "spacer-collapsed" : ""}`,
3055
+ style: { height: index < rounds.length - 1 ? 0 : Math.max(0, getSpacerHeight(round.interactionId)) },
3056
+ "data-spacer-for": round.interactionId
3057
+ }
3058
+ )
3059
+ ] }, round.interactionId)),
3060
+ rounds.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "empty-state", children: [
3061
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "empty-icon", children: "\u{1F4AC}" }),
3062
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "empty-text", children: "\u7B49\u5F85\u5BF9\u8BDD\u5F00\u59CB..." })
3063
+ ] })
3064
+ ]
3065
+ }
3066
+ )
3067
+ ] }),
3068
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "chat-footer", children: renderFooter ? renderFooter(widgetAPI) : "\u7531 JetAgents \u9A71\u52A8" }),
3069
+ viewerOpen && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "artifact-viewer-overlay", onClick: () => setViewerOpen(false), children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "artifact-viewer-panel", onClick: (e) => e.stopPropagation(), children: [
3070
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "artifact-viewer-header", children: [
3071
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "artifact-viewer-title", children: [
3072
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "artifact-viewer-icon", children: "\u{1F4C4}" }),
3073
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { children: viewerArtifact?.name || "\u6587\u4EF6\u5185\u5BB9" })
3074
+ ] }),
3075
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { className: "artifact-viewer-close", onClick: () => setViewerOpen(false), children: "\u2715" })
3076
+ ] }),
3077
+ viewerContent && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "artifact-viewer-meta", children: [
3078
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { children: viewerContent.mime_type || "\u672A\u77E5\u7C7B\u578B" }),
3079
+ viewerContent.file_size != null && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { children: viewerContent.file_size > 1024 ? `${(viewerContent.file_size / 1024).toFixed(1)} KB` : `${viewerContent.file_size} B` }),
3080
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "artifact-viewer-path", children: viewerContent.git_path })
3081
+ ] }),
3082
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "artifact-viewer-body", children: [
3083
+ viewerLoading && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "artifact-viewer-loading", children: [
3084
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "loading-spinner" }),
3085
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { children: "\u52A0\u8F7D\u4E2D..." })
3086
+ ] }),
3087
+ viewerError && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "artifact-viewer-error", children: viewerError }),
3088
+ viewerContent && !viewerContent.is_binary && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("pre", { className: "artifact-viewer-content", children: viewerContent.content }),
3089
+ viewerContent?.is_binary && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "artifact-viewer-binary", children: "\u4E8C\u8FDB\u5236\u6587\u4EF6\uFF0C\u65E0\u6CD5\u76F4\u63A5\u9884\u89C8" })
3090
+ ] })
3091
+ ] }) })
3092
+ ]
3093
+ }
3094
+ );
3095
+ };
3096
+ var ChatWidget_default = ChatWidget;
3097
+
3098
+ // src/errors.ts
3099
+ var ChatWidgetAuthError = class extends Error {
3100
+ name = "ChatWidgetAuthError";
3101
+ constructor(message = "Authentication failed") {
3102
+ super(message);
3103
+ }
3104
+ };
3105
+ var ChatWidgetNetworkError = class extends Error {
3106
+ constructor(message, statusCode) {
3107
+ super(message);
3108
+ this.statusCode = statusCode;
3109
+ }
3110
+ name = "ChatWidgetNetworkError";
3111
+ };
3112
+
3113
+ // src/adapters/DefaultJetAgentsAdapter.ts
3114
+ var DefaultJetAgentsAdapter = class {
3115
+ baseUrl;
3116
+ options;
3117
+ refreshPromise = null;
3118
+ constructor(options) {
3119
+ this.baseUrl = options.baseUrl.replace(/\/+$/, "");
3120
+ this.options = options;
3121
+ }
3122
+ async createSession(agentId) {
3123
+ const res = await this.fetchJSON(
3124
+ "/api/session/create",
3125
+ {
3126
+ method: "POST",
3127
+ body: JSON.stringify({ agent_id: agentId, type: "user_input", contents: [{ type: "text", content: "" }] })
3128
+ }
3129
+ );
3130
+ return { sessionId: res.session_id, instanceId: res.user_caller_instance };
3131
+ }
3132
+ async getSessionDetail(sessionId) {
3133
+ const res = await this.fetchJSON(`/api/session/detail/${sessionId}`);
3134
+ return res;
3135
+ }
3136
+ async getSessionHistory(sessionId) {
3137
+ const res = await this.fetchJSON(`/api/session/messages/${sessionId}`);
3138
+ return res.turns ?? (Array.isArray(res) ? res : []);
3139
+ }
3140
+ async getResourceContent(sessionId, resourceId) {
3141
+ const res = await this.fetchJSON(`/api/workspace/resource/${sessionId}/${resourceId}/content`);
3142
+ return res.data ?? res;
3143
+ }
3144
+ async submitHITLResponse(response) {
3145
+ await this.fetchJSON("/api/human-response", {
3146
+ method: "POST",
3147
+ body: JSON.stringify(response)
3148
+ });
3149
+ }
3150
+ getStreamUrl() {
3151
+ return `${this.baseUrl}/api/session/send_message`;
3152
+ }
3153
+ getAuthHeaders() {
3154
+ const headers = {};
3155
+ const token = this.options.getToken();
3156
+ if (token) {
3157
+ headers["Authorization"] = `Bearer ${token}`;
3158
+ }
3159
+ const orgId = this.options.getOrgId?.();
3160
+ if (orgId) {
3161
+ headers["X-Org-Id"] = orgId;
3162
+ }
3163
+ return headers;
3164
+ }
3165
+ onAuthFailure() {
3166
+ this.options.onAuthFailure?.();
3167
+ }
3168
+ async refreshAuth() {
3169
+ if (!this.options.refreshToken) return false;
3170
+ if (this.refreshPromise) {
3171
+ return this.refreshPromise;
3172
+ }
3173
+ this.refreshPromise = this.options.refreshToken().then((token) => token !== null).finally(() => {
3174
+ this.refreshPromise = null;
3175
+ });
3176
+ return this.refreshPromise;
3177
+ }
3178
+ async fetchJSON(path, init) {
3179
+ const url = `${this.baseUrl}${path}`;
3180
+ const headers = {
3181
+ "Content-Type": "application/json",
3182
+ ...this.getAuthHeaders()
3183
+ };
3184
+ const response = await fetch(url, {
3185
+ ...init,
3186
+ headers: { ...headers, ...init?.headers }
3187
+ });
3188
+ if (response.status === 401) {
3189
+ const refreshed = await this.refreshAuth();
3190
+ if (refreshed) {
3191
+ const retryHeaders = {
3192
+ "Content-Type": "application/json",
3193
+ ...this.getAuthHeaders()
3194
+ };
3195
+ const retryResponse = await fetch(url, {
3196
+ ...init,
3197
+ headers: { ...retryHeaders, ...init?.headers }
3198
+ });
3199
+ if (retryResponse.ok) {
3200
+ return retryResponse.json();
3201
+ }
3202
+ }
3203
+ this.onAuthFailure();
3204
+ throw new ChatWidgetAuthError("Authentication failed");
3205
+ }
3206
+ if (!response.ok) {
3207
+ throw new ChatWidgetNetworkError(
3208
+ `Request failed: ${response.status} ${response.statusText}`,
3209
+ response.status
3210
+ );
3211
+ }
3212
+ return response.json();
3213
+ }
3214
+ };
3215
+ // Annotate the CommonJS export names for ESM import in node:
3216
+ 0 && (module.exports = {
3217
+ ChatWidget,
3218
+ ChatWidgetAuthError,
3219
+ ChatWidgetNetworkError,
3220
+ ChildAgentCard,
3221
+ DEFAULT_CONFIG,
3222
+ DefaultJetAgentsAdapter,
3223
+ MessageContent,
3224
+ PinnedArea,
3225
+ PlanCard,
3226
+ RoundHeader,
3227
+ SchemaFormRenderer,
3228
+ TodoCard,
3229
+ ToolCardBuffering,
3230
+ isTodoNotificationContent,
3231
+ mergeConsecutiveThinkMessages,
3232
+ useMessageAggregator,
3233
+ useSSE
3234
+ });
3235
+ //# sourceMappingURL=index.cjs.map