@blade-hq/agent-react 2608.0.7-beta.0 → 2608.0.7-beta.1

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.js CHANGED
@@ -99,453 +99,8 @@ function useAgentSession(sessionId, options = {}) {
99
99
  return { session, state, error };
100
100
  }
101
101
 
102
- // src/hooks/use-replay.ts
103
- import { DEFAULT_REPLAY_SPEED } from "@blade-hq/agent-client";
104
- import { useCallback, useEffect as useEffect2, useState as useState2 } from "react";
105
- var replayCreating = /* @__PURE__ */ new Set();
106
- function useReplay(session) {
107
- const client = useBladeClient();
108
- const sessionId = session?.sessionId ?? null;
109
- const [replay, setReplay] = useState2(null);
110
- const [viewerRole, setViewerRole] = useState2(null);
111
- const [sourceRunning, setSourceRunning] = useState2(false);
112
- const [mismatch, setMismatch] = useState2(null);
113
- const [preview, setPreview] = useState2({ status: "loading" });
114
- const [isStarting, setIsStarting] = useState2(false);
115
- const [error, setError] = useState2(null);
116
- const [draftSpeed, setDraftSpeed] = useState2(DEFAULT_REPLAY_SPEED);
117
- useEffect2(() => {
118
- if (!session) {
119
- setReplay(null);
120
- setViewerRole(null);
121
- setSourceRunning(false);
122
- return;
123
- }
124
- const pull = () => {
125
- const state = session.getState();
126
- setReplay(state.replay);
127
- setViewerRole(state.viewerRole);
128
- setSourceRunning(state.isStreaming || state.status === "running");
129
- };
130
- pull();
131
- return session.subscribe(pull);
132
- }, [session]);
133
- const [lastSessionId, setLastSessionId] = useState2(sessionId);
134
- if (sessionId !== lastSessionId) {
135
- setLastSessionId(sessionId);
136
- setMismatch(null);
137
- setError(null);
138
- setDraftSpeed(DEFAULT_REPLAY_SPEED);
139
- }
140
- useEffect2(() => {
141
- setPreview({ status: "loading" });
142
- if (!sessionId) return;
143
- let alive = true;
144
- client.sessions.getReplayPreview(sessionId).then((result) => {
145
- if (!alive) return;
146
- setPreview({
147
- status: "ready",
148
- supported: result.supported,
149
- reason: result.reason ?? null
150
- });
151
- }).catch(() => {
152
- if (alive) setPreview({ status: "ready", supported: true, reason: null });
153
- });
154
- return () => {
155
- alive = false;
156
- };
157
- }, [client, sessionId, sourceRunning]);
158
- useEffect2(() => {
159
- if (!session) return;
160
- return session.on("replayMismatch", ({ actualMessage, expectedMessage, respond }) => {
161
- setMismatch({
162
- actualMessage: actualMessage ?? "",
163
- expectedMessage: expectedMessage ?? "",
164
- resolve: (decision) => {
165
- setMismatch(null);
166
- respond(decision);
167
- }
168
- });
169
- });
170
- }, [session]);
171
- const isReplay = replay?.isReplay ?? false;
172
- const setSpeed = useCallback(
173
- async (next) => {
174
- setDraftSpeed(next);
175
- if (!session) return;
176
- setError(null);
177
- try {
178
- await session.setReplaySpeed(next);
179
- } catch (err) {
180
- setError(err instanceof Error ? err : new Error(String(err)));
181
- }
182
- },
183
- [session]
184
- );
185
- const exitToAutonomous = useCallback(async () => {
186
- if (!session) return;
187
- setError(null);
188
- const pending = mismatch;
189
- if (pending) {
190
- pending.resolve("continue_replay");
191
- return;
192
- }
193
- try {
194
- await session.exitReplay();
195
- } catch (err) {
196
- setError(err instanceof Error ? err : new Error(String(err)));
197
- }
198
- }, [session, mismatch]);
199
- const startReplay = useCallback(
200
- async (speed) => {
201
- if (!sessionId) throw new Error("\u5F53\u524D\u6CA1\u6709\u53EF\u56DE\u653E\u7684\u4F1A\u8BDD");
202
- if (replayCreating.has(sessionId)) throw new Error("\u6B63\u5728\u521B\u5EFA\u56DE\u653E\u4F1A\u8BDD\uFF0C\u8BF7\u7A0D\u5019");
203
- replayCreating.add(sessionId);
204
- setIsStarting(true);
205
- setError(null);
206
- try {
207
- const result = await client.sessions.startReplaySession(sessionId, speed ?? draftSpeed);
208
- return result.session_id;
209
- } catch (err) {
210
- const failure = err instanceof Error ? err : new Error(String(err));
211
- setError(failure);
212
- throw failure;
213
- } finally {
214
- replayCreating.delete(sessionId);
215
- setIsStarting(false);
216
- }
217
- },
218
- [client, sessionId, draftSpeed]
219
- );
220
- return {
221
- isReplay,
222
- speed: isReplay ? replay?.speed ?? 1 : draftSpeed,
223
- setSpeed,
224
- exitToAutonomous,
225
- mismatch,
226
- canControl: viewerRole !== "viewer",
227
- canReplay: Boolean(sessionId) && preview.status === "ready" && preview.supported && viewerRole !== "viewer",
228
- unsupportedReason: preview.status === "ready" && !preview.supported ? preview.reason : null,
229
- startReplay,
230
- isStarting,
231
- error
232
- };
233
- }
234
-
235
- // src/hooks/use-llm-chat.ts
236
- import { useCallback as useCallback2, useMemo as useMemo2, useRef as useRef2, useState as useState3 } from "react";
237
- var DEFAULT_HISTORY_TURNS = 12;
238
- var DEFAULT_MAX_TOOL_ROUNDS = 4;
239
- var syntheticIdSeq = 0;
240
- function useLlmChat(options) {
241
- const [history, setHistory] = useState3([]);
242
- const [streamingText, setStreamingText] = useState3(null);
243
- const [streamingCalls, setStreamingCalls] = useState3([]);
244
- const [failedToolIds, setFailedToolIds] = useState3([]);
245
- const [error, setError] = useState3(null);
246
- const [isStreaming, setIsStreaming] = useState3(false);
247
- const abortRef = useRef2(null);
248
- const generationRef = useRef2(0);
249
- const historyRef = useRef2([]);
250
- const optionsRef = useRef2(options);
251
- optionsRef.current = options;
252
- historyRef.current = history;
253
- const stop = useCallback2(() => {
254
- abortRef.current?.abort();
255
- abortRef.current = null;
256
- }, []);
257
- const reset = useCallback2(() => {
258
- generationRef.current += 1;
259
- stop();
260
- setHistory([]);
261
- setStreamingText(null);
262
- setStreamingCalls([]);
263
- setFailedToolIds([]);
264
- setError(null);
265
- setIsStreaming(false);
266
- }, [stop]);
267
- const send = useCallback2(async (text) => {
268
- const content = text.trim();
269
- if (!content || abortRef.current) return false;
270
- const opts = optionsRef.current;
271
- const maxRounds = opts.maxToolRounds ?? DEFAULT_MAX_TOOL_ROUNDS;
272
- const commit = (message) => {
273
- historyRef.current = [...historyRef.current, message];
274
- setHistory(historyRef.current);
275
- };
276
- setError(null);
277
- setIsStreaming(true);
278
- setStreamingText("");
279
- setStreamingCalls([]);
280
- commit({ role: "user", content });
281
- let partial = "";
282
- const controller = new AbortController();
283
- abortRef.current = controller;
284
- const generation = generationRef.current;
285
- try {
286
- for (let round = 0; ; round += 1) {
287
- const result = await streamOnce(opts, historyRef.current, controller.signal, {
288
- onText: (chunk) => {
289
- partial += chunk;
290
- setStreamingText((prev) => `${prev ?? ""}${chunk}`);
291
- },
292
- onToolCalls: (calls) => setStreamingCalls(calls)
293
- });
294
- const assistant = {
295
- role: "assistant",
296
- content: result.content,
297
- ...result.toolCalls.length ? { tool_calls: result.toolCalls } : {}
298
- };
299
- setStreamingText(null);
300
- setStreamingCalls([]);
301
- commit(assistant);
302
- if (!result.toolCalls.length) return true;
303
- const bail = (reason) => {
304
- for (const call of result.toolCalls) {
305
- commit({ role: "tool", tool_call_id: call.id, content: JSON.stringify({ error: reason }) });
306
- }
307
- setFailedToolIds((prev) => [...prev, ...result.toolCalls.map((call) => call.id)]);
308
- };
309
- if (!opts.onToolCall) {
310
- bail("\u8FD9\u4E2A\u5E94\u7528\u6CA1\u6709\u63D0\u4F9B\u5DE5\u5177\u6267\u884C\u5165\u53E3");
311
- return true;
312
- }
313
- if (round >= maxRounds) {
314
- bail(`\u5DE5\u5177\u8C03\u7528\u5DF2\u8FBE\u4E0A\u9650 ${maxRounds} \u8F6E\uFF0C\u6CA1\u6709\u6267\u884C`);
315
- setError(`\u5DE5\u5177\u8C03\u7528\u8D85\u8FC7 ${maxRounds} \u8F6E\u4ECD\u672A\u7ED9\u51FA\u7ED3\u8BBA\uFF0C\u5DF2\u505C\u4E0B\u3002`);
316
- return false;
317
- }
318
- for (const call of result.toolCalls) {
319
- let output;
320
- try {
321
- const value = await opts.onToolCall({
322
- id: call.id,
323
- name: call.function.name,
324
- arguments: call.function.arguments
325
- });
326
- output = typeof value === "string" ? value : JSON.stringify(value ?? null);
327
- } catch (err) {
328
- output = JSON.stringify({ error: err instanceof Error ? err.message : String(err) });
329
- setFailedToolIds((prev) => [...prev, call.id]);
330
- }
331
- commit({ role: "tool", tool_call_id: call.id, content: output });
332
- }
333
- setStreamingText("");
334
- }
335
- } catch (err) {
336
- setStreamingText(null);
337
- setStreamingCalls([]);
338
- if (controller.signal.aborted) {
339
- if (generation !== generationRef.current) return false;
340
- commit({ role: "assistant", content: partial ? `${partial}\uFF08\u5DF2\u505C\u6B62\uFF09` : "\uFF08\u5DF2\u505C\u6B62\uFF09" });
341
- return false;
342
- }
343
- if (partial && generation === generationRef.current) {
344
- commit({ role: "assistant", content: partial });
345
- }
346
- setError(err instanceof Error && err.message ? err.message : "\u5BF9\u8BDD\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
347
- return false;
348
- } finally {
349
- if (abortRef.current === controller) abortRef.current = null;
350
- setIsStreaming(false);
351
- }
352
- }, []);
353
- const messages = useMemo2(
354
- () => toChatMessages(history, streamingText, streamingCalls, failedToolIds),
355
- [history, streamingText, streamingCalls, failedToolIds]
356
- );
357
- return { messages, isStreaming, error, send, stop, reset };
358
- }
359
- async function streamOnce(options, history, signal, handlers) {
360
- const {
361
- baseURL,
362
- model,
363
- apiKey,
364
- headers,
365
- system,
366
- historyTurns = DEFAULT_HISTORY_TURNS,
367
- temperature,
368
- extraBody,
369
- tools,
370
- fetchImpl
371
- } = options;
372
- const trimmed = trimHistory(history, historyTurns);
373
- const payload = {
374
- model,
375
- stream: true,
376
- messages: system ? [{ role: "system", content: system }, ...trimmed] : trimmed,
377
- ...tools?.length ? { tools } : {},
378
- ...temperature === void 0 ? {} : { temperature },
379
- ...extraBody
380
- };
381
- const doFetch = fetchImpl ?? globalThis.fetch;
382
- const url = `${baseURL.replace(/\/$/, "")}/chat/completions`;
383
- let response;
384
- try {
385
- response = await doFetch(url, {
386
- method: "POST",
387
- headers: {
388
- "Content-Type": "application/json",
389
- ...apiKey ? { Authorization: `Bearer ${apiKey}` } : {},
390
- ...headers
391
- },
392
- body: JSON.stringify(payload),
393
- signal
394
- });
395
- } catch (err) {
396
- if (signal.aborted) throw err;
397
- throw new Error(`\u8FDE\u4E0D\u4E0A\u6A21\u578B\u670D\u52A1 ${url}\uFF1A${err instanceof Error ? err.message : String(err)}`);
398
- }
399
- if (!response.ok) {
400
- throw new Error(await describeHttpError(response));
401
- }
402
- if (!response.body) {
403
- throw new Error("\u6A21\u578B\u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6D41\u5F0F\u54CD\u5E94");
404
- }
405
- let content = "";
406
- const calls = [];
407
- for await (const chunk of readSse(response.body, signal)) {
408
- if (chunk.error) {
409
- throw new Error(chunk.error);
410
- }
411
- if (chunk.text) {
412
- content += chunk.text;
413
- handlers.onText(chunk.text);
414
- }
415
- if (chunk.toolCallDeltas) {
416
- for (const delta of chunk.toolCallDeltas) {
417
- const index = delta.index ?? 0;
418
- const existing = calls[index] ?? { id: "", type: "function", function: { name: "", arguments: "" } };
419
- calls[index] = {
420
- id: delta.id ?? existing.id,
421
- type: "function",
422
- function: {
423
- name: delta.function?.name ?? existing.function.name,
424
- arguments: `${existing.function.arguments}${delta.function?.arguments ?? ""}`
425
- }
426
- };
427
- }
428
- handlers.onToolCalls(calls.filter(Boolean).map((call) => ({ ...call })));
429
- }
430
- }
431
- const normalized = calls.filter(Boolean).filter((call) => call.function.name).map((call) => {
432
- if (call.id) return call;
433
- syntheticIdSeq += 1;
434
- return { ...call, id: `call_auto_${syntheticIdSeq}` };
435
- });
436
- return { content, toolCalls: normalized };
437
- }
438
- function trimHistory(history, historyTurns) {
439
- const userIndexes = history.reduce((acc, msg, i) => {
440
- if (msg.role === "user") acc.push(i);
441
- return acc;
442
- }, []);
443
- if (userIndexes.length <= historyTurns) return history;
444
- return history.slice(userIndexes[userIndexes.length - historyTurns]);
445
- }
446
- async function describeHttpError(response) {
447
- const raw = await response.text().catch(() => "");
448
- try {
449
- const parsed = JSON.parse(raw);
450
- const message = typeof parsed.error === "string" ? parsed.error : parsed.error?.message ?? (typeof parsed.detail === "string" ? parsed.detail : void 0);
451
- if (message) return `\u6A21\u578B\u670D\u52A1\u8FD4\u56DE ${response.status}\uFF1A${message}`;
452
- } catch {
453
- }
454
- const snippet = raw.trim().slice(0, 120);
455
- return snippet ? `\u6A21\u578B\u670D\u52A1\u8FD4\u56DE ${response.status}\uFF1A${snippet}` : `\u6A21\u578B\u670D\u52A1\u8FD4\u56DE ${response.status}`;
456
- }
457
- async function* readSse(stream, signal) {
458
- const reader = stream.getReader();
459
- const decoder = new TextDecoder();
460
- let buffer = "";
461
- try {
462
- while (!signal.aborted) {
463
- const { done, value } = await reader.read();
464
- if (done) {
465
- const tail = parseSse(buffer);
466
- if (tail && tail !== "done") yield tail;
467
- break;
468
- }
469
- buffer = `${buffer}${decoder.decode(value, { stream: true })}`.replace(/\r\n/g, "\n");
470
- let boundary = buffer.indexOf("\n\n");
471
- while (boundary !== -1) {
472
- const chunk = parseSse(buffer.slice(0, boundary));
473
- buffer = buffer.slice(boundary + 2);
474
- if (chunk === "done") return;
475
- if (chunk) yield chunk;
476
- boundary = buffer.indexOf("\n\n");
477
- }
478
- }
479
- } finally {
480
- reader.cancel().catch(() => {
481
- });
482
- }
483
- }
484
- function parseSse(raw) {
485
- const dataLines = [];
486
- for (const line of raw.split("\n")) {
487
- if (line.startsWith("data:")) dataLines.push(line.slice(5).trim());
488
- }
489
- if (!dataLines.length) return null;
490
- const data = dataLines.join("\n");
491
- if (data === "[DONE]") return "done";
492
- let parsed;
493
- try {
494
- parsed = JSON.parse(data);
495
- } catch {
496
- return null;
497
- }
498
- if (parsed.error) {
499
- const message = typeof parsed.error === "string" ? parsed.error : parsed.error.message;
500
- return { error: message || "\u6A21\u578B\u670D\u52A1\u8FD4\u56DE\u4E86\u9519\u8BEF" };
501
- }
502
- const delta = parsed.choices?.[0]?.delta;
503
- if (!delta) return null;
504
- return { text: delta.content, toolCallDeltas: delta.tool_calls };
505
- }
506
- function toChatMessages(history, streamingText, streamingCalls, failedToolIds) {
507
- const results = /* @__PURE__ */ new Map();
508
- for (const msg of history) {
509
- if (msg.role === "tool") results.set(msg.tool_call_id, msg.content);
510
- }
511
- const messages = [];
512
- for (const msg of history) {
513
- if (msg.role === "tool") continue;
514
- if (msg.role === "user") {
515
- messages.push({ role: "user", content: msg.content, status: "completed" });
516
- continue;
517
- }
518
- messages.push({
519
- role: "assistant",
520
- content: msg.content,
521
- status: "completed",
522
- ...msg.tool_calls?.length ? { tool_calls: msg.tool_calls.map((call) => toToolCallInfo(call, results, failedToolIds)) } : {}
523
- });
524
- }
525
- if (streamingText !== null) {
526
- messages.push({
527
- role: "assistant",
528
- content: streamingText,
529
- status: "streaming",
530
- ...streamingCalls.length ? { tool_calls: streamingCalls.map((call) => toToolCallInfo(call, results, failedToolIds)) } : {}
531
- });
532
- }
533
- return messages;
534
- }
535
- function toToolCallInfo(call, results, failedToolIds) {
536
- const result = results.get(call.id);
537
- const failed = failedToolIds.includes(call.id);
538
- return {
539
- id: call.id,
540
- name: call.function.name,
541
- arguments: call.function.arguments,
542
- ...result === void 0 ? {} : { result },
543
- status: result === void 0 ? "pending" : failed ? "error" : "done"
544
- };
545
- }
546
-
547
- // src/components/AgentChat.tsx
548
- import { BladeApiError, latestPostChatFollowup } from "@blade-hq/agent-client";
102
+ // src/components/ChatView.tsx
103
+ import { BladeApiError } from "@blade-hq/agent-client";
549
104
 
550
105
  // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/createLucideIcon.js
551
106
  import { forwardRef as forwardRef2, createElement as createElement2 } from "react";
@@ -618,18 +173,6 @@ var createLucideIcon = (iconName, iconNode) => {
618
173
  return Component2;
619
174
  };
620
175
 
621
- // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/arrow-right.js
622
- var ArrowRight = createLucideIcon("ArrowRight", [
623
- ["path", { d: "M5 12h14", key: "1ays0h" }],
624
- ["path", { d: "m12 5 7 7-7 7", key: "xquz4c" }]
625
- ]);
626
-
627
- // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/arrow-up-right.js
628
- var ArrowUpRight = createLucideIcon("ArrowUpRight", [
629
- ["path", { d: "M7 7h10v10", key: "1tivn9" }],
630
- ["path", { d: "M7 17 17 7", key: "1vkiza" }]
631
- ]);
632
-
633
176
  // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/arrow-up.js
634
177
  var ArrowUp = createLucideIcon("ArrowUp", [
635
178
  ["path", { d: "m5 12 7-7 7 7", key: "hav0vg" }],
@@ -706,13 +249,6 @@ var FileText = createLucideIcon("FileText", [
706
249
  ["path", { d: "M16 17H8", key: "z1uh3a" }]
707
250
  ]);
708
251
 
709
- // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/globe.js
710
- var Globe = createLucideIcon("Globe", [
711
- ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
712
- ["path", { d: "M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20", key: "13o1zl" }],
713
- ["path", { d: "M2 12h20", key: "9i4pu4" }]
714
- ]);
715
-
716
252
  // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/layers.js
717
253
  var Layers = createLucideIcon("Layers", [
718
254
  [
@@ -776,34 +312,6 @@ var MessageSquare = createLucideIcon("MessageSquare", [
776
312
  ["path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z", key: "1lielz" }]
777
313
  ]);
778
314
 
779
- // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/play.js
780
- var Play = createLucideIcon("Play", [
781
- ["polygon", { points: "6 3 20 12 6 21 6 3", key: "1oa8hb" }]
782
- ]);
783
-
784
- // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/settings-2.js
785
- var Settings2 = createLucideIcon("Settings2", [
786
- ["path", { d: "M20 7h-9", key: "3s1dr2" }],
787
- ["path", { d: "M14 17H5", key: "gfn3mx" }],
788
- ["circle", { cx: "17", cy: "17", r: "3", key: "18b49y" }],
789
- ["circle", { cx: "7", cy: "7", r: "3", key: "dfmy0x" }]
790
- ]);
791
-
792
- // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/sparkles.js
793
- var Sparkles = createLucideIcon("Sparkles", [
794
- [
795
- "path",
796
- {
797
- d: "M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z",
798
- key: "4pj2yx"
799
- }
800
- ],
801
- ["path", { d: "M20 3v4", key: "1olli1" }],
802
- ["path", { d: "M22 5h-4", key: "1gvqau" }],
803
- ["path", { d: "M4 17v2", key: "vumght" }],
804
- ["path", { d: "M5 18H3", key: "zchphs" }]
805
- ]);
806
-
807
315
  // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/square.js
808
316
  var Square = createLucideIcon("Square", [
809
317
  ["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }]
@@ -828,8 +336,8 @@ var X = createLucideIcon("X", [
828
336
  ["path", { d: "m6 6 12 12", key: "d8bk6v" }]
829
337
  ]);
830
338
 
831
- // src/components/AgentChat.tsx
832
- import { useCallback as useCallback7, useEffect as useEffect9, useMemo as useMemo8, useState as useState13 } from "react";
339
+ // src/components/ChatView.tsx
340
+ import { useEffect as useEffect7, useState as useState11 } from "react";
833
341
 
834
342
  // src/lib/utils.ts
835
343
  function cn(...inputs) {
@@ -844,120 +352,8 @@ async function copyToClipboard(text) {
844
352
  }
845
353
  }
846
354
 
847
- // src/components/ReplayBar.tsx
848
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
849
- var SPEED_OPTIONS = [1, 2, 5];
850
- function ReplayBar({
851
- isReplay,
852
- speed,
853
- onSpeedChange,
854
- onExit,
855
- canControl = true,
856
- className
857
- }) {
858
- if (!isReplay) return null;
859
- return /* @__PURE__ */ jsxs(
860
- "div",
861
- {
862
- className: cn(
863
- "flex flex-wrap items-center gap-2 border-b border-[hsl(var(--border))] bg-[hsl(var(--muted))]/40 px-4 py-2 text-xs",
864
- className
865
- ),
866
- children: [
867
- /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5 font-medium text-[hsl(var(--foreground))]", children: [
868
- /* @__PURE__ */ jsx2(Play, { size: 13 }),
869
- "\u56DE\u653E\u6A21\u5F0F"
870
- ] }),
871
- /* @__PURE__ */ jsx2("span", { className: "text-[hsl(var(--muted-foreground))]", children: "\u6B63\u5728\u91CD\u73B0\u4E4B\u524D\u7684\u5BF9\u8BDD" }),
872
- /* @__PURE__ */ jsxs("div", { className: "ml-auto flex items-center gap-2", children: [
873
- /* @__PURE__ */ jsx2("div", { className: "flex items-center gap-0.5 rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-0.5", children: SPEED_OPTIONS.map((option) => /* @__PURE__ */ jsxs(
874
- "button",
875
- {
876
- type: "button",
877
- onClick: () => onSpeedChange(option),
878
- "aria-pressed": speed === option,
879
- disabled: !canControl,
880
- className: cn(
881
- "h-6 rounded px-2 font-medium transition-colors",
882
- speed === option ? "bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))]" : "text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]",
883
- !canControl && "cursor-not-allowed opacity-50"
884
- ),
885
- children: [
886
- option,
887
- "x"
888
- ]
889
- },
890
- option
891
- )) }),
892
- /* @__PURE__ */ jsx2(
893
- "button",
894
- {
895
- type: "button",
896
- onClick: onExit,
897
- disabled: !canControl,
898
- className: cn(
899
- "h-7 rounded-md border border-[hsl(var(--border))] px-2.5 text-[hsl(var(--muted-foreground))] transition-colors hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]",
900
- !canControl && "cursor-not-allowed opacity-50 hover:bg-transparent"
901
- ),
902
- children: "\u9000\u51FA\u56DE\u653E"
903
- }
904
- )
905
- ] })
906
- ]
907
- }
908
- );
909
- }
910
-
911
- // src/components/ReplayMismatchPrompt.tsx
912
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
913
- function ReplayMismatchPrompt({ mismatch, className }) {
914
- if (!mismatch) return null;
915
- return /* @__PURE__ */ jsxs2(
916
- "div",
917
- {
918
- className: cn(
919
- "mx-auto my-3 max-w-3xl rounded-2xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-4 py-3 text-sm",
920
- className
921
- ),
922
- children: [
923
- /* @__PURE__ */ jsx3("div", { className: "font-medium text-[hsl(var(--foreground))]", children: "\u8FD9\u53E5\u8BDD\u548C\u4E4B\u524D\u5F55\u5236\u7684\u4E0D\u4E00\u6837" }),
924
- /* @__PURE__ */ jsxs2("dl", { className: "mt-2 space-y-1 text-xs text-[hsl(var(--muted-foreground))]", children: [
925
- /* @__PURE__ */ jsxs2("div", { className: "flex gap-2", children: [
926
- /* @__PURE__ */ jsx3("dt", { className: "shrink-0", children: "\u5F55\u5236\u7684\u662F" }),
927
- /* @__PURE__ */ jsx3("dd", { className: "min-w-0 break-words text-[hsl(var(--foreground))]", children: mismatch.expectedMessage || "\uFF08\u7A7A\uFF09" })
928
- ] }),
929
- /* @__PURE__ */ jsxs2("div", { className: "flex gap-2", children: [
930
- /* @__PURE__ */ jsx3("dt", { className: "shrink-0", children: "\u4F60\u8F93\u5165\u7684" }),
931
- /* @__PURE__ */ jsx3("dd", { className: "min-w-0 break-words text-[hsl(var(--foreground))]", children: mismatch.actualMessage || "\uFF08\u7A7A\uFF09" })
932
- ] })
933
- ] }),
934
- /* @__PURE__ */ jsxs2("div", { className: "mt-3 flex flex-wrap gap-2", children: [
935
- /* @__PURE__ */ jsx3(
936
- "button",
937
- {
938
- type: "button",
939
- onClick: () => mismatch.resolve("keep_replay"),
940
- className: "h-8 rounded-md bg-[hsl(var(--primary))] px-3 text-xs font-medium text-[hsl(var(--primary-foreground))] transition-opacity hover:opacity-90",
941
- children: "\u6309\u5F55\u5236\u5185\u5BB9\u7EE7\u7EED"
942
- }
943
- ),
944
- /* @__PURE__ */ jsx3(
945
- "button",
946
- {
947
- type: "button",
948
- onClick: () => mismatch.resolve("continue_replay"),
949
- className: "h-8 rounded-md border border-[hsl(var(--border))] px-3 text-xs text-[hsl(var(--muted-foreground))] transition-colors hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]",
950
- children: "\u4ECE\u8FD9\u91CC\u5F00\u59CB\u771F\u7684\u8FD0\u884C"
951
- }
952
- )
953
- ] })
954
- ]
955
- }
956
- );
957
- }
958
-
959
355
  // src/components/ChatInput.tsx
960
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
356
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
961
357
  function ChatInput({
962
358
  value,
963
359
  onValueChange,
@@ -982,8 +378,8 @@ function ChatInput({
982
378
  void handleSend();
983
379
  }
984
380
  };
985
- return /* @__PURE__ */ jsx4("div", { className: cn("blade-chat-input border-t border-[hsl(var(--border))] py-3", className), children: /* @__PURE__ */ jsxs3("div", { className: "blade-chat-input-inner mx-auto flex max-w-[748px] items-end gap-2 rounded-2xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-3 py-2", children: [
986
- /* @__PURE__ */ jsx4(
381
+ return /* @__PURE__ */ jsx2("div", { className: cn("blade-chat-input border-t border-[hsl(var(--border))] py-3", className), children: /* @__PURE__ */ jsxs("div", { className: "blade-chat-input-inner mx-auto flex max-w-[748px] items-end gap-2 rounded-2xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-3 py-2", children: [
382
+ /* @__PURE__ */ jsx2(
987
383
  "textarea",
988
384
  {
989
385
  value,
@@ -1000,7 +396,7 @@ function ChatInput({
1000
396
  className: "blade-chat-textarea max-h-48 min-h-[28px] flex-1 resize-none bg-transparent py-1 text-sm leading-6 text-[hsl(var(--foreground))] outline-none placeholder:text-[hsl(var(--muted-foreground)/0.6)]"
1001
397
  }
1002
398
  ),
1003
- isStreaming ? /* @__PURE__ */ jsx4(
399
+ isStreaming ? /* @__PURE__ */ jsx2(
1004
400
  "button",
1005
401
  {
1006
402
  type: "button",
@@ -1009,9 +405,9 @@ function ChatInput({
1009
405
  "aria-label": isStopping ? "\u6B63\u5728\u505C\u6B62" : "\u505C\u6B62\u56DE\u590D",
1010
406
  title: isStopping ? "\u6B63\u5728\u505C\u6B62" : "\u505C\u6B62\u56DE\u590D",
1011
407
  className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[hsl(var(--muted))] text-[hsl(var(--foreground))] transition-opacity hover:opacity-90 disabled:opacity-60",
1012
- children: isStopping ? /* @__PURE__ */ jsx4(LoaderCircle, { size: 14, className: "animate-spin" }) : /* @__PURE__ */ jsx4(Square, { size: 12, fill: "currentColor" })
408
+ children: isStopping ? /* @__PURE__ */ jsx2(LoaderCircle, { size: 14, className: "animate-spin" }) : /* @__PURE__ */ jsx2(Square, { size: 12, fill: "currentColor" })
1013
409
  }
1014
- ) : /* @__PURE__ */ jsx4(
410
+ ) : /* @__PURE__ */ jsx2(
1015
411
  "button",
1016
412
  {
1017
413
  type: "button",
@@ -1020,23 +416,23 @@ function ChatInput({
1020
416
  "aria-label": "\u53D1\u9001\u6D88\u606F",
1021
417
  title: "\u53D1\u9001\u6D88\u606F",
1022
418
  className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))] transition-opacity hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-40",
1023
- children: /* @__PURE__ */ jsx4(ArrowUp, { size: 15 })
419
+ children: /* @__PURE__ */ jsx2(ArrowUp, { size: 15 })
1024
420
  }
1025
421
  )
1026
422
  ] }) });
1027
423
  }
1028
424
 
1029
425
  // src/components/ConnectionBanner.tsx
1030
- import { useEffect as useEffect3, useRef as useRef3, useState as useState4 } from "react";
1031
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
426
+ import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
427
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
1032
428
  var CONNECTION_NOTICE_DELAY_MS = 3e3;
1033
429
  var CONNECTION_ERROR_DELAY_MS = 15e3;
1034
430
  function useConnectionNoticePhase(connected) {
1035
- const [phase, setPhase] = useState4("hidden");
1036
- const connectedRef = useRef3(connected);
1037
- const timersRef = useRef3([]);
431
+ const [phase, setPhase] = useState2("hidden");
432
+ const connectedRef = useRef2(connected);
433
+ const timersRef = useRef2([]);
1038
434
  connectedRef.current = connected;
1039
- useEffect3(() => {
435
+ useEffect2(() => {
1040
436
  const clearTimers = () => {
1041
437
  for (const timer of timersRef.current) clearTimeout(timer);
1042
438
  timersRef.current = [];
@@ -1076,14 +472,14 @@ function useConnectionNoticePhase(connected) {
1076
472
  return phase;
1077
473
  }
1078
474
  function ConnectionBanner({ connection, className }) {
1079
- const hasConnectedRef = useRef3(connection === "connected" || connection === "reconnecting");
475
+ const hasConnectedRef = useRef2(connection === "connected" || connection === "reconnecting");
1080
476
  if (connection === "connected") hasConnectedRef.current = true;
1081
477
  const connected = connection === "connected";
1082
478
  const phase = useConnectionNoticePhase(connected);
1083
479
  if (connected || phase === "hidden") return null;
1084
480
  const recovering = phase === "recovering";
1085
481
  const firstConnection = !hasConnectedRef.current;
1086
- return /* @__PURE__ */ jsx5("div", { className: cn("blade-chat-banner bg-[hsl(var(--background))] px-5 pt-3", className), children: /* @__PURE__ */ jsxs4(
482
+ return /* @__PURE__ */ jsx3("div", { className: cn("blade-chat-banner bg-[hsl(var(--background))] px-5 pt-3", className), children: /* @__PURE__ */ jsxs2(
1087
483
  "div",
1088
484
  {
1089
485
  className: cn(
@@ -1091,10 +487,10 @@ function ConnectionBanner({ connection, className }) {
1091
487
  recovering ? "border-amber-500/25 bg-amber-500/10 text-amber-100" : "border-rose-500/25 bg-rose-500/10 text-rose-100"
1092
488
  ),
1093
489
  children: [
1094
- /* @__PURE__ */ jsx5("span", { className: "mt-0.5 shrink-0", children: recovering ? /* @__PURE__ */ jsx5(LoaderCircle, { size: 14, className: "animate-spin" }) : /* @__PURE__ */ jsx5(TriangleAlert, { size: 14 }) }),
1095
- /* @__PURE__ */ jsxs4("div", { className: "min-w-0", children: [
1096
- /* @__PURE__ */ jsx5("div", { className: "text-sm font-medium", children: recovering ? firstConnection ? "\u6B63\u5728\u8FDE\u63A5\u2026" : "\u6B63\u5728\u6062\u590D\u8FDE\u63A5\u2026" : "\u6682\u65F6\u65E0\u6CD5\u8FDE\u63A5" }),
1097
- /* @__PURE__ */ jsx5("div", { className: "text-xs opacity-80", children: recovering ? "\u6062\u590D\u540E\u4F1A\u81EA\u52A8\u540C\u6B65\u6700\u65B0\u6D88\u606F\uFF0C\u8BF7\u7A0D\u5019" : "\u8BF7\u68C0\u67E5\u7F51\u7EDC\u6216\u670D\u52A1\u72B6\u6001\uFF0C\u7CFB\u7EDF\u4F1A\u7EE7\u7EED\u81EA\u52A8\u91CD\u8BD5" })
490
+ /* @__PURE__ */ jsx3("span", { className: "mt-0.5 shrink-0", children: recovering ? /* @__PURE__ */ jsx3(LoaderCircle, { size: 14, className: "animate-spin" }) : /* @__PURE__ */ jsx3(TriangleAlert, { size: 14 }) }),
491
+ /* @__PURE__ */ jsxs2("div", { className: "min-w-0", children: [
492
+ /* @__PURE__ */ jsx3("div", { className: "text-sm font-medium", children: recovering ? firstConnection ? "\u6B63\u5728\u8FDE\u63A5\u2026" : "\u6B63\u5728\u6062\u590D\u8FDE\u63A5\u2026" : "\u6682\u65F6\u65E0\u6CD5\u8FDE\u63A5" }),
493
+ /* @__PURE__ */ jsx3("div", { className: "text-xs opacity-80", children: recovering ? "\u6062\u590D\u540E\u4F1A\u81EA\u52A8\u540C\u6B65\u6700\u65B0\u6D88\u606F\uFF0C\u8BF7\u7A0D\u5019" : "\u8BF7\u68C0\u67E5\u7F51\u7EDC\u6216\u670D\u52A1\u72B6\u6001\uFF0C\u7CFB\u7EDF\u4F1A\u7EE7\u7EED\u81EA\u52A8\u91CD\u8BD5" })
1098
494
  ] })
1099
495
  ]
1100
496
  }
@@ -1103,10 +499,10 @@ function ConnectionBanner({ connection, className }) {
1103
499
 
1104
500
  // src/components/MessageList.tsx
1105
501
  import { isHiddenInternalMessage } from "@blade-hq/agent-client";
1106
- import { useCallback as useCallback6, useEffect as useEffect8, useMemo as useMemo7, useRef as useRef8, useState as useState12 } from "react";
502
+ import { useCallback as useCallback3, useEffect as useEffect6, useMemo as useMemo6, useRef as useRef6, useState as useState10 } from "react";
1107
503
 
1108
504
  // ../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/useStickToBottom.js
1109
- import { useCallback as useCallback3, useMemo as useMemo3, useRef as useRef4, useState as useState5 } from "react";
505
+ import { useCallback, useMemo as useMemo2, useRef as useRef3, useState as useState3 } from "react";
1110
506
  var DEFAULT_SPRING_ANIMATION = {
1111
507
  /**
1112
508
  * A value from 0 to 1, on how much to damp the animation.
@@ -1143,12 +539,12 @@ globalThis.document?.addEventListener("click", () => {
1143
539
  mouseDown = false;
1144
540
  });
1145
541
  var useStickToBottom = (options = {}) => {
1146
- const [escapedFromLock, updateEscapedFromLock] = useState5(false);
1147
- const [isAtBottom, updateIsAtBottom] = useState5(options.initial !== false);
1148
- const [isNearBottom, setIsNearBottom] = useState5(false);
1149
- const optionsRef = useRef4(null);
542
+ const [escapedFromLock, updateEscapedFromLock] = useState3(false);
543
+ const [isAtBottom, updateIsAtBottom] = useState3(options.initial !== false);
544
+ const [isNearBottom, setIsNearBottom] = useState3(false);
545
+ const optionsRef = useRef3(null);
1150
546
  optionsRef.current = options;
1151
- const isSelecting = useCallback3(() => {
547
+ const isSelecting = useCallback(() => {
1152
548
  if (!mouseDown) {
1153
549
  return false;
1154
550
  }
@@ -1159,15 +555,15 @@ var useStickToBottom = (options = {}) => {
1159
555
  const range = selection.getRangeAt(0);
1160
556
  return range.commonAncestorContainer.contains(scrollRef.current) || scrollRef.current?.contains(range.commonAncestorContainer);
1161
557
  }, []);
1162
- const setIsAtBottom = useCallback3((isAtBottom2) => {
558
+ const setIsAtBottom = useCallback((isAtBottom2) => {
1163
559
  state.isAtBottom = isAtBottom2;
1164
560
  updateIsAtBottom(isAtBottom2);
1165
561
  }, []);
1166
- const setEscapedFromLock = useCallback3((escapedFromLock2) => {
562
+ const setEscapedFromLock = useCallback((escapedFromLock2) => {
1167
563
  state.escapedFromLock = escapedFromLock2;
1168
564
  updateEscapedFromLock(escapedFromLock2);
1169
565
  }, []);
1170
- const state = useMemo3(() => {
566
+ const state = useMemo2(() => {
1171
567
  let lastCalculation;
1172
568
  return {
1173
569
  escapedFromLock,
@@ -1220,7 +616,7 @@ var useStickToBottom = (options = {}) => {
1220
616
  }
1221
617
  };
1222
618
  }, []);
1223
- const scrollToBottom = useCallback3((scrollOptions = {}) => {
619
+ const scrollToBottom = useCallback((scrollOptions = {}) => {
1224
620
  if (typeof scrollOptions === "string") {
1225
621
  scrollOptions = { animation: scrollOptions };
1226
622
  }
@@ -1305,11 +701,11 @@ var useStickToBottom = (options = {}) => {
1305
701
  }
1306
702
  return next();
1307
703
  }, [setIsAtBottom, isSelecting, state]);
1308
- const stopScroll = useCallback3(() => {
704
+ const stopScroll = useCallback(() => {
1309
705
  setEscapedFromLock(true);
1310
706
  setIsAtBottom(false);
1311
707
  }, [setEscapedFromLock, setIsAtBottom]);
1312
- const handleScroll = useCallback3(({ target }) => {
708
+ const handleScroll = useCallback(({ target }) => {
1313
709
  if (target !== scrollRef.current) {
1314
710
  return;
1315
711
  }
@@ -1348,7 +744,7 @@ var useStickToBottom = (options = {}) => {
1348
744
  }
1349
745
  }, 1);
1350
746
  }, [setEscapedFromLock, setIsAtBottom, isSelecting, state]);
1351
- const handleWheel = useCallback3(({ target, deltaY }) => {
747
+ const handleWheel = useCallback(({ target, deltaY }) => {
1352
748
  let element = target;
1353
749
  while (!["scroll", "auto"].includes(getComputedStyle(element).overflow)) {
1354
750
  if (!element.parentElement) {
@@ -1418,7 +814,7 @@ var useStickToBottom = (options = {}) => {
1418
814
  };
1419
815
  };
1420
816
  function useRefCallback(callback, deps) {
1421
- const result = useCallback3((ref) => {
817
+ const result = useCallback((ref) => {
1422
818
  result.current = ref;
1423
819
  return callback(ref);
1424
820
  }, deps);
@@ -1450,11 +846,11 @@ function mergeAnimations(...animations) {
1450
846
 
1451
847
  // ../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/StickToBottom.js
1452
848
  import * as React from "react";
1453
- import { createContext as createContext2, useContext as useContext2, useEffect as useEffect4, useImperativeHandle, useLayoutEffect, useMemo as useMemo4, useRef as useRef5 } from "react";
849
+ import { createContext as createContext2, useContext as useContext2, useEffect as useEffect3, useImperativeHandle, useLayoutEffect, useMemo as useMemo3, useRef as useRef4 } from "react";
1454
850
  var StickToBottomContext = createContext2(null);
1455
- var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect4;
851
+ var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect3;
1456
852
  function StickToBottom({ instance, children, resize, initial, mass, damping, stiffness, targetScrollTop: currentTargetScrollTop, contextRef, ...props }) {
1457
- const customTargetScrollTop = useRef5(null);
853
+ const customTargetScrollTop = useRef4(null);
1458
854
  const targetScrollTop = React.useCallback((target, elements) => {
1459
855
  const get = context?.targetScrollTop ?? currentTargetScrollTop;
1460
856
  return get?.(target, elements) ?? target;
@@ -1468,7 +864,7 @@ function StickToBottom({ instance, children, resize, initial, mass, damping, sti
1468
864
  targetScrollTop
1469
865
  });
1470
866
  const { scrollRef, contentRef, scrollToBottom, stopScroll, isAtBottom, escapedFromLock, state } = instance ?? defaultInstance;
1471
- const context = useMemo4(() => ({
867
+ const context = useMemo3(() => ({
1472
868
  scrollToBottom,
1473
869
  stopScroll,
1474
870
  scrollRef,
@@ -1531,10 +927,10 @@ function useStickToBottomContext() {
1531
927
 
1532
928
  // src/components/AssistantTurnBlock.tsx
1533
929
  import { getTextContent, normalizeMessageContent } from "@blade-hq/agent-client";
1534
- import { useState as useState10 } from "react";
930
+ import { useState as useState9 } from "react";
1535
931
 
1536
932
  // src/components/AgentLoopBlock.tsx
1537
- import { useState as useState6 } from "react";
933
+ import { useState as useState4 } from "react";
1538
934
 
1539
935
  // src/components/display-utils.ts
1540
936
  var TOOL_NAME_ALIASES = {
@@ -1656,7 +1052,7 @@ function formatToolResult(result) {
1656
1052
  }
1657
1053
 
1658
1054
  // src/components/AgentLoopBlock.tsx
1659
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1055
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1660
1056
  function parseAgentDescription(argumentsJson) {
1661
1057
  try {
1662
1058
  const parsed = JSON.parse(argumentsJson);
@@ -1666,12 +1062,12 @@ function parseAgentDescription(argumentsJson) {
1666
1062
  }
1667
1063
  }
1668
1064
  function AgentLoopBlock({ toolCall }) {
1669
- const [expanded, setExpanded] = useState6(false);
1065
+ const [expanded, setExpanded] = useState4(false);
1670
1066
  const description = parseAgentDescription(toolCall.arguments);
1671
1067
  const running = toolCall.status === "pending" || toolCall.status === "awaiting_answer";
1672
1068
  const failed = toolCall.status === "error" || toolCall.status === "cancelled";
1673
- return /* @__PURE__ */ jsxs5("div", { className: "blade-chat-agent-loop ml-4 text-xs", children: [
1674
- /* @__PURE__ */ jsxs5(
1069
+ return /* @__PURE__ */ jsxs3("div", { className: "blade-chat-agent-loop ml-4 text-xs", children: [
1070
+ /* @__PURE__ */ jsxs3(
1675
1071
  "div",
1676
1072
  {
1677
1073
  className: cn(
@@ -1679,7 +1075,7 @@ function AgentLoopBlock({ toolCall }) {
1679
1075
  failed ? "border-l-[hsl(var(--muted-foreground)/0.5)]" : running ? "border-l-blue-500" : "border-l-[hsl(var(--primary))]"
1680
1076
  ),
1681
1077
  children: [
1682
- /* @__PURE__ */ jsxs5(
1078
+ /* @__PURE__ */ jsxs3(
1683
1079
  "button",
1684
1080
  {
1685
1081
  type: "button",
@@ -1687,7 +1083,7 @@ function AgentLoopBlock({ toolCall }) {
1687
1083
  className: "flex min-w-0 flex-1 items-center gap-2 text-left transition-colors hover:bg-white/3 focus-visible:ring-1 focus-visible:ring-[hsl(var(--ring))] focus:outline-none",
1688
1084
  "aria-expanded": expanded,
1689
1085
  children: [
1690
- /* @__PURE__ */ jsx6(
1086
+ /* @__PURE__ */ jsx4(
1691
1087
  ChevronRight,
1692
1088
  {
1693
1089
  size: 11,
@@ -1697,8 +1093,8 @@ function AgentLoopBlock({ toolCall }) {
1697
1093
  )
1698
1094
  }
1699
1095
  ),
1700
- /* @__PURE__ */ jsx6(Bot, { size: 12, className: "shrink-0 text-[hsl(var(--muted-foreground))]" }),
1701
- /* @__PURE__ */ jsxs5(
1096
+ /* @__PURE__ */ jsx4(Bot, { size: 12, className: "shrink-0 text-[hsl(var(--muted-foreground))]" }),
1097
+ /* @__PURE__ */ jsxs3(
1702
1098
  "span",
1703
1099
  {
1704
1100
  className: cn(
@@ -1706,43 +1102,156 @@ function AgentLoopBlock({ toolCall }) {
1706
1102
  failed ? "text-[hsl(var(--muted-foreground))]" : running ? "text-blue-300" : "text-[hsl(var(--primary))]"
1707
1103
  ),
1708
1104
  children: [
1709
- running ? /* @__PURE__ */ jsx6(LoaderCircle, { size: 11, className: "animate-spin" }) : failed ? /* @__PURE__ */ jsx6(X, { size: 11 }) : /* @__PURE__ */ jsx6(Check, { size: 11 }),
1710
- /* @__PURE__ */ jsx6("span", { children: running ? "\u6267\u884C\u4E2D" : failed ? "\u5DF2\u7EC8\u6B62" : "\u5B8C\u6210" })
1105
+ running ? /* @__PURE__ */ jsx4(LoaderCircle, { size: 11, className: "animate-spin" }) : failed ? /* @__PURE__ */ jsx4(X, { size: 11 }) : /* @__PURE__ */ jsx4(Check, { size: 11 }),
1106
+ /* @__PURE__ */ jsx4("span", { children: running ? "\u6267\u884C\u4E2D" : failed ? "\u5DF2\u7EC8\u6B62" : "\u5B8C\u6210" })
1711
1107
  ]
1712
1108
  }
1713
1109
  ),
1714
- /* @__PURE__ */ jsxs5("span", { className: "min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]", children: [
1110
+ /* @__PURE__ */ jsxs3("span", { className: "min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]", children: [
1715
1111
  "\u5B50\u667A\u80FD\u4F53\uFF1A",
1716
1112
  description
1717
1113
  ] })
1718
1114
  ]
1719
1115
  }
1720
1116
  ),
1721
- typeof toolCall.duration_ms === "number" && toolCall.duration_ms > 0 && /* @__PURE__ */ jsx6("span", { className: "shrink-0 font-mono text-[10px] text-[hsl(var(--muted-foreground))]", children: formatToolDuration(toolCall.duration_ms) })
1117
+ typeof toolCall.duration_ms === "number" && toolCall.duration_ms > 0 && /* @__PURE__ */ jsx4("span", { className: "shrink-0 font-mono text-[10px] text-[hsl(var(--muted-foreground))]", children: formatToolDuration(toolCall.duration_ms) })
1722
1118
  ]
1723
1119
  }
1724
1120
  ),
1725
- expanded && toolCall.result != null && /* @__PURE__ */ jsxs5("div", { className: "ml-4 mt-1 rounded-xl bg-[hsl(var(--card))] px-3 py-3", children: [
1726
- /* @__PURE__ */ jsx6("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u7ED3\u679C" }),
1727
- /* @__PURE__ */ jsx6("pre", { className: "max-h-[400px] overflow-auto whitespace-pre-wrap rounded-md bg-[hsl(var(--muted))] p-2 font-mono text-[11px] text-[hsl(var(--foreground))]", children: formatToolResult(toolCall.result) })
1121
+ expanded && toolCall.result != null && /* @__PURE__ */ jsxs3("div", { className: "ml-4 mt-1 rounded-xl bg-[hsl(var(--card))] px-3 py-3", children: [
1122
+ /* @__PURE__ */ jsx4("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u7ED3\u679C" }),
1123
+ /* @__PURE__ */ jsx4("pre", { className: "max-h-[400px] overflow-auto whitespace-pre-wrap rounded-md bg-[hsl(var(--muted))] p-2 font-mono text-[11px] text-[hsl(var(--foreground))]", children: formatToolResult(toolCall.result) })
1728
1124
  ] })
1729
1125
  ] });
1730
1126
  }
1731
1127
 
1732
1128
  // src/components/MarkdownContent.tsx
1733
1129
  import {
1734
- useEffect as useEffect5,
1735
- useMemo as useMemo5,
1736
- useRef as useRef6,
1737
- useState as useState7
1130
+ useEffect as useEffect4,
1131
+ useMemo as useMemo4,
1132
+ useRef as useRef5,
1133
+ useState as useState6
1738
1134
  } from "react";
1739
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1135
+
1136
+ // src/lib/media-tags.ts
1137
+ var FILE_CARD_TAG = "blade-file-card";
1138
+ var MEDIA_LINE_RE = /^[ \t]*(?:[-*+][ \t]+|\d+\.[ \t]+)?(?<wrap>[`"'*_]*)MEDIA:[ \t]*(?<path>.+?)\k<wrap>[ \t]*[。,、;;,]?[ \t]*\r?$/gm;
1139
+ var MEDIA_LINE_START_RE = /^[ \t]*(?:[-*+][ \t]+|\d+\.[ \t]+)?[`"'*_]*MEDIA:/;
1140
+ function basename(path) {
1141
+ return path.split(/[/\\]/).filter(Boolean).pop() || path;
1142
+ }
1143
+ function unwrapMediaPath(path) {
1144
+ if (!path.startsWith("<")) return path;
1145
+ const closingBracket = path.indexOf(">");
1146
+ if (closingBracket < 0) return null;
1147
+ return `${path.slice(1, closingBracket)}${path.slice(closingBracket + 1)}`;
1148
+ }
1149
+ function isSafeMediaPath(path) {
1150
+ if (!path) return false;
1151
+ return !path.split(/[/\\]/).includes("..");
1152
+ }
1153
+ function escapeAttribute(value) {
1154
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1155
+ }
1156
+ function foldMediaLines(text) {
1157
+ return text.replace(MEDIA_LINE_RE, (match, _wrap, rawPath) => {
1158
+ const path = unwrapMediaPath(rawPath);
1159
+ if (path === null) return match;
1160
+ const name = basename(path);
1161
+ if (!isSafeMediaPath(path)) {
1162
+ return `\`${name}\``;
1163
+ }
1164
+ return `<${FILE_CARD_TAG} data-path="${escapeAttribute(path)}" data-name="${escapeAttribute(name)}">${escapeAttribute(name)}</${FILE_CARD_TAG}>`;
1165
+ });
1166
+ }
1167
+ function collapseMediaTags(text, options) {
1168
+ if (!text.includes("MEDIA:")) return text;
1169
+ if (!options?.streaming) return foldMediaLines(text);
1170
+ const newlineIndex = text.lastIndexOf("\n");
1171
+ const head = newlineIndex < 0 ? "" : text.slice(0, newlineIndex + 1);
1172
+ const lastLine = newlineIndex < 0 ? text : text.slice(newlineIndex + 1);
1173
+ if (MEDIA_LINE_START_RE.test(lastLine)) {
1174
+ return foldMediaLines(head);
1175
+ }
1176
+ return foldMediaLines(head) + lastLine;
1177
+ }
1178
+
1179
+ // src/components/FileCard.tsx
1180
+ import { useState as useState5 } from "react";
1181
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1182
+ function stringValue(value) {
1183
+ return typeof value === "string" ? value : void 0;
1184
+ }
1185
+ function extractChildrenText(children) {
1186
+ if (typeof children === "string") return children;
1187
+ if (Array.isArray(children)) return children.map(extractChildrenText).join("");
1188
+ if (children && typeof children === "object" && "props" in children) {
1189
+ return extractChildrenText(children.props?.children);
1190
+ }
1191
+ return "";
1192
+ }
1193
+ function FileCard({
1194
+ node,
1195
+ "data-path": pathAttribute,
1196
+ "data-name": nameAttribute,
1197
+ dataPath,
1198
+ dataName,
1199
+ sessionId,
1200
+ children,
1201
+ className,
1202
+ ...props
1203
+ }) {
1204
+ const client = useBladeClient();
1205
+ const [downloading, setDownloading] = useState5(false);
1206
+ const [failed, setFailed] = useState5(false);
1207
+ const nodeProperties = node && typeof node === "object" && "properties" in node ? node.properties : void 0;
1208
+ const path = pathAttribute ?? dataPath ?? stringValue(nodeProperties?.["data-path"]) ?? stringValue(nodeProperties?.dataPath) ?? "";
1209
+ const name = nameAttribute ?? dataName ?? stringValue(nodeProperties?.["data-name"]) ?? stringValue(nodeProperties?.dataName) ?? extractChildrenText(children) ?? "";
1210
+ const disabled = !sessionId || !isSafeMediaPath(path);
1211
+ const downloadUrl = disabled ? void 0 : client.buildAuthedUrl(
1212
+ `/api/sessions/${encodeURIComponent(sessionId)}/files/${encodeURIComponent(path)}`
1213
+ );
1214
+ const handleDownload = async (event) => {
1215
+ if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
1216
+ event.preventDefault();
1217
+ if (disabled || downloading || !sessionId) return;
1218
+ setDownloading(true);
1219
+ setFailed(false);
1220
+ try {
1221
+ await client.sessions.downloadFile(sessionId, path, name);
1222
+ } catch {
1223
+ setFailed(true);
1224
+ } finally {
1225
+ setDownloading(false);
1226
+ }
1227
+ };
1228
+ return /* @__PURE__ */ jsxs4("span", { ...props, className: cn("blade-chat-file-card", className), children: [
1229
+ /* @__PURE__ */ jsx5(
1230
+ "a",
1231
+ {
1232
+ href: downloadUrl,
1233
+ download: name,
1234
+ onClick: handleDownload,
1235
+ "aria-disabled": disabled || void 0,
1236
+ "aria-busy": downloading || void 0,
1237
+ "aria-label": `\u4E0B\u8F7D\u6587\u4EF6\uFF1A${name}`,
1238
+ title: failed ? "\u4E0B\u8F7D\u5931\u8D25\uFF0C\u70B9\u51FB\u91CD\u8BD5" : name,
1239
+ className: "blade-chat-file-link",
1240
+ children: name
1241
+ }
1242
+ ),
1243
+ failed && /* @__PURE__ */ jsx5("span", { className: "blade-chat-file-card-error", children: "\u4E0B\u8F7D\u5931\u8D25" })
1244
+ ] });
1245
+ }
1246
+
1247
+ // src/components/MarkdownContent.tsx
1248
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1740
1249
  var SYSTEM_REMINDER_RE = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
1741
1250
  function CodeBlockPre({ children, node: _node, ...props }) {
1742
- const preRef = useRef6(null);
1743
- const [copied, setCopied] = useState7(false);
1744
- const [language, setLanguage] = useState7("");
1745
- useEffect5(() => {
1251
+ const preRef = useRef5(null);
1252
+ const [copied, setCopied] = useState6(false);
1253
+ const [language, setLanguage] = useState6("");
1254
+ useEffect4(() => {
1746
1255
  const codeEl = preRef.current?.querySelector("code");
1747
1256
  setLanguage(codeEl?.className.match(/language-(\S+)/)?.[1] ?? "");
1748
1257
  }, []);
@@ -1753,10 +1262,10 @@ function CodeBlockPre({ children, node: _node, ...props }) {
1753
1262
  setTimeout(() => setCopied(false), 2e3);
1754
1263
  }
1755
1264
  };
1756
- return /* @__PURE__ */ jsxs6("div", { className: "blade-chat-codeblock not-prose my-3 overflow-hidden rounded-xl border border-[hsl(var(--border))]", children: [
1757
- /* @__PURE__ */ jsxs6("div", { className: "blade-chat-codeblock-header flex h-[34px] items-center justify-between border-b border-[hsl(var(--border))] bg-[hsl(var(--muted))/0.5] pl-3.5 pr-1.5", children: [
1758
- /* @__PURE__ */ jsx7("span", { className: "font-mono text-[12px] text-[hsl(var(--muted-foreground))]", children: language || "code" }),
1759
- /* @__PURE__ */ jsxs6(
1265
+ return /* @__PURE__ */ jsxs5("div", { className: "blade-chat-codeblock not-prose my-3 overflow-hidden rounded-xl border border-[hsl(var(--border))]", children: [
1266
+ /* @__PURE__ */ jsxs5("div", { className: "blade-chat-codeblock-header flex h-[34px] items-center justify-between border-b border-[hsl(var(--border))] bg-[hsl(var(--muted))/0.5] pl-3.5 pr-1.5", children: [
1267
+ /* @__PURE__ */ jsx6("span", { className: "font-mono text-[12px] text-[hsl(var(--muted-foreground))]", children: language || "code" }),
1268
+ /* @__PURE__ */ jsxs5(
1760
1269
  "button",
1761
1270
  {
1762
1271
  type: "button",
@@ -1766,13 +1275,13 @@ function CodeBlockPre({ children, node: _node, ...props }) {
1766
1275
  copied ? "text-[hsl(var(--primary))]" : "text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]"
1767
1276
  ),
1768
1277
  children: [
1769
- copied ? /* @__PURE__ */ jsx7(Check, { size: 12 }) : /* @__PURE__ */ jsx7(Copy, { size: 12 }),
1770
- /* @__PURE__ */ jsx7("span", { children: copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236" })
1278
+ copied ? /* @__PURE__ */ jsx6(Check, { size: 12 }) : /* @__PURE__ */ jsx6(Copy, { size: 12 }),
1279
+ /* @__PURE__ */ jsx6("span", { children: copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236" })
1771
1280
  ]
1772
1281
  }
1773
1282
  )
1774
1283
  ] }),
1775
- /* @__PURE__ */ jsx7(
1284
+ /* @__PURE__ */ jsx6(
1776
1285
  "pre",
1777
1286
  {
1778
1287
  ref: preRef,
@@ -1784,22 +1293,36 @@ function CodeBlockPre({ children, node: _node, ...props }) {
1784
1293
  ] });
1785
1294
  }
1786
1295
  function ExternalAnchor({ node: _node, children, ...props }) {
1787
- return /* @__PURE__ */ jsx7("a", { ...props, target: "_blank", rel: "noopener noreferrer", children });
1296
+ return /* @__PURE__ */ jsx6("a", { ...props, target: "_blank", rel: "noopener noreferrer", children });
1788
1297
  }
1789
1298
  var MARKDOWN_COMPONENTS = {
1790
1299
  pre: CodeBlockPre,
1791
1300
  a: ExternalAnchor
1792
1301
  };
1302
+ var CUSTOM_ALLOWED_TAGS = {
1303
+ [FILE_CARD_TAG]: ["dataPath", "dataName", "data-path", "data-name"]
1304
+ };
1793
1305
  function MarkdownContent({ children, className, mode, sessionId }) {
1794
- const resolvedChildren = useMemo5(() => {
1795
- return children.replace(SYSTEM_REMINDER_RE, "");
1796
- }, [children]);
1797
- return /* @__PURE__ */ jsx7(
1306
+ const streaming = mode === "streaming";
1307
+ const resolvedChildren = useMemo4(() => {
1308
+ const withoutReminders = children.replace(SYSTEM_REMINDER_RE, "");
1309
+ if (!sessionId) return withoutReminders;
1310
+ return collapseMediaTags(withoutReminders, { streaming });
1311
+ }, [children, sessionId, streaming]);
1312
+ const components = useMemo4(
1313
+ () => ({
1314
+ ...MARKDOWN_COMPONENTS,
1315
+ [FILE_CARD_TAG]: (fileCardProps) => /* @__PURE__ */ jsx6(FileCard, { ...fileCardProps, sessionId })
1316
+ }),
1317
+ [sessionId]
1318
+ );
1319
+ return /* @__PURE__ */ jsx6(
1798
1320
  _r,
1799
1321
  {
1800
1322
  className: cn("blade-chat-markdown break-words", className),
1801
1323
  mode: mode ?? "static",
1802
- components: MARKDOWN_COMPONENTS,
1324
+ components,
1325
+ allowedTags: CUSTOM_ALLOWED_TAGS,
1803
1326
  children: resolvedChildren
1804
1327
  },
1805
1328
  sessionId ?? "no-session"
@@ -1807,17 +1330,17 @@ function MarkdownContent({ children, className, mode, sessionId }) {
1807
1330
  }
1808
1331
 
1809
1332
  // src/components/Shimmer.tsx
1810
- import { jsx as jsx8 } from "react/jsx-runtime";
1333
+ import { jsx as jsx7 } from "react/jsx-runtime";
1811
1334
  function Shimmer({ children = "\u6B63\u5728\u601D\u8003...", className }) {
1812
- return /* @__PURE__ */ jsx8("span", { className: cn("blade-shimmer-text text-sm font-medium", className), children });
1335
+ return /* @__PURE__ */ jsx7("span", { className: cn("blade-shimmer-text text-sm font-medium", className), children });
1813
1336
  }
1814
1337
 
1815
1338
  // src/components/ToolCallBlock.tsx
1816
- import { useState as useState9 } from "react";
1339
+ import { useState as useState8 } from "react";
1817
1340
 
1818
1341
  // src/components/AskUserQuestionBlock.tsx
1819
- import { useEffect as useEffect6, useMemo as useMemo6, useState as useState8 } from "react";
1820
- import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
1342
+ import { useEffect as useEffect5, useMemo as useMemo5, useState as useState7 } from "react";
1343
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
1821
1344
  function AskUserQuestionBlock({
1822
1345
  data,
1823
1346
  answered,
@@ -1826,16 +1349,16 @@ function AskUserQuestionBlock({
1826
1349
  answerData,
1827
1350
  onAnswer
1828
1351
  }) {
1829
- const [selections, setSelections] = useState8(/* @__PURE__ */ new Map());
1830
- const [customTexts, setCustomTexts] = useState8(/* @__PURE__ */ new Map());
1831
- const [usingCustom, setUsingCustom] = useState8(/* @__PURE__ */ new Set());
1832
- const [submitted, setSubmitted] = useState8(false);
1833
- useEffect6(() => {
1352
+ const [selections, setSelections] = useState7(/* @__PURE__ */ new Map());
1353
+ const [customTexts, setCustomTexts] = useState7(/* @__PURE__ */ new Map());
1354
+ const [usingCustom, setUsingCustom] = useState7(/* @__PURE__ */ new Set());
1355
+ const [submitted, setSubmitted] = useState7(false);
1356
+ useEffect5(() => {
1834
1357
  if (sessionStatus === "failed" || sessionStatus === "interrupted") {
1835
1358
  setSubmitted(false);
1836
1359
  }
1837
1360
  }, [sessionStatus]);
1838
- const displayAnswerState = useMemo6(() => {
1361
+ const displayAnswerState = useMemo5(() => {
1839
1362
  if (!(answered && answerData)) {
1840
1363
  return { selections, customTexts, usingCustom };
1841
1364
  }
@@ -1920,7 +1443,7 @@ ${parts.join("\n")}`;
1920
1443
  setSubmitted(true);
1921
1444
  onAnswer(text, toolCallId, nextAnswerData);
1922
1445
  };
1923
- return /* @__PURE__ */ jsxs7(
1446
+ return /* @__PURE__ */ jsxs6(
1924
1447
  "div",
1925
1448
  {
1926
1449
  className: cn(
@@ -1928,12 +1451,12 @@ ${parts.join("\n")}`;
1928
1451
  answered ? "max-w-2xl space-y-3 p-3 text-xs text-[hsl(var(--muted-foreground))] opacity-80" : "max-w-lg space-y-5 p-4 text-sm"
1929
1452
  ),
1930
1453
  children: [
1931
- data.source_loop?.description && /* @__PURE__ */ jsxs7("div", { className: "rounded-lg bg-[hsl(var(--muted)/0.35)] px-3 py-2 text-xs text-[hsl(var(--muted-foreground))]", children: [
1454
+ data.source_loop?.description && /* @__PURE__ */ jsxs6("div", { className: "rounded-lg bg-[hsl(var(--muted)/0.35)] px-3 py-2 text-xs text-[hsl(var(--muted-foreground))]", children: [
1932
1455
  "\u5B50\u667A\u80FD\u4F53\u300C",
1933
1456
  data.source_loop.description,
1934
1457
  "\u300D\u5728\u7B49\u5F85\u4F60\u7684\u56DE\u7B54"
1935
1458
  ] }),
1936
- data.questions.map((q, qIdx) => /* @__PURE__ */ jsx9(
1459
+ data.questions.map((q, qIdx) => /* @__PURE__ */ jsx8(
1937
1460
  QuestionCard,
1938
1461
  {
1939
1462
  question: q,
@@ -1948,7 +1471,7 @@ ${parts.join("\n")}`;
1948
1471
  },
1949
1472
  q.question
1950
1473
  )),
1951
- !answered && !submitted && onAnswer && /* @__PURE__ */ jsx9(
1474
+ !answered && !submitted && onAnswer && /* @__PURE__ */ jsx8(
1952
1475
  "button",
1953
1476
  {
1954
1477
  type: "button",
@@ -1958,14 +1481,14 @@ ${parts.join("\n")}`;
1958
1481
  children: allAnswered ? "\u786E\u8BA4" : "\u8BF7\u5148\u9009\u62E9\u4E00\u4E2A\u9009\u9879"
1959
1482
  }
1960
1483
  ),
1961
- submitted && !answered && /* @__PURE__ */ jsxs7(
1484
+ submitted && !answered && /* @__PURE__ */ jsxs6(
1962
1485
  "button",
1963
1486
  {
1964
1487
  type: "button",
1965
1488
  disabled: true,
1966
1489
  className: "flex w-full items-center justify-center gap-2 rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-xs font-semibold text-[hsl(var(--primary-foreground))] opacity-80",
1967
1490
  children: [
1968
- /* @__PURE__ */ jsx9(LoaderCircle, { size: 14, className: "animate-spin" }),
1491
+ /* @__PURE__ */ jsx8(LoaderCircle, { size: 14, className: "animate-spin" }),
1969
1492
  "\u786E\u8BA4\u4E2D"
1970
1493
  ]
1971
1494
  }
@@ -1986,30 +1509,30 @@ function QuestionCard({
1986
1509
  onCustomChange
1987
1510
  }) {
1988
1511
  const multi = question.multiSelect ?? false;
1989
- return /* @__PURE__ */ jsxs7("div", { children: [
1990
- /* @__PURE__ */ jsxs7("div", { className: cn("flex items-start gap-2", answered ? "mb-2" : "mb-3"), children: [
1991
- /* @__PURE__ */ jsx9(
1512
+ return /* @__PURE__ */ jsxs6("div", { children: [
1513
+ /* @__PURE__ */ jsxs6("div", { className: cn("flex items-start gap-2", answered ? "mb-2" : "mb-3"), children: [
1514
+ /* @__PURE__ */ jsx8(
1992
1515
  MessageSquareMore,
1993
1516
  {
1994
1517
  size: answered ? 12 : 13,
1995
1518
  className: "mt-0.5 shrink-0 text-[hsl(var(--primary))]"
1996
1519
  }
1997
1520
  ),
1998
- /* @__PURE__ */ jsx9(
1521
+ /* @__PURE__ */ jsx8(
1999
1522
  "div",
2000
1523
  {
2001
1524
  className: cn(
2002
1525
  "min-w-0 flex-1 font-medium text-[hsl(var(--foreground))]",
2003
1526
  answered ? "text-xs" : "text-sm"
2004
1527
  ),
2005
- children: /* @__PURE__ */ jsx9(MarkdownContent, { className: "blade-chat-prose", children: question.question })
1528
+ children: /* @__PURE__ */ jsx8(MarkdownContent, { className: "blade-chat-prose", children: question.question })
2006
1529
  }
2007
1530
  )
2008
1531
  ] }),
2009
- /* @__PURE__ */ jsxs7("div", { className: cn("flex flex-col pl-5", answered ? "gap-1" : "gap-1.5"), children: [
1532
+ /* @__PURE__ */ jsxs6("div", { className: cn("flex flex-col pl-5", answered ? "gap-1" : "gap-1.5"), children: [
2010
1533
  question.options.map((opt, optIdx) => {
2011
1534
  const isSel = selected.has(optIdx);
2012
- return /* @__PURE__ */ jsxs7(
1535
+ return /* @__PURE__ */ jsxs6(
2013
1536
  "button",
2014
1537
  {
2015
1538
  type: "button",
@@ -2023,14 +1546,14 @@ function QuestionCard({
2023
1546
  answered && "cursor-default opacity-70"
2024
1547
  ),
2025
1548
  children: [
2026
- multi && /* @__PURE__ */ jsx9(
1549
+ multi && /* @__PURE__ */ jsx8(
2027
1550
  "div",
2028
1551
  {
2029
1552
  className: cn(
2030
1553
  "mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border transition-colors",
2031
1554
  isSel && !answered ? "border-[hsl(var(--primary-foreground)/0.6)] bg-[hsl(var(--primary-foreground)/0.2)]" : isSel ? "border-[hsl(var(--primary)/0.45)] bg-[hsl(var(--primary)/0.12)]" : "border-[hsl(var(--border))]"
2032
1555
  ),
2033
- children: isSel && /* @__PURE__ */ jsx9(
1556
+ children: isSel && /* @__PURE__ */ jsx8(
2034
1557
  Check,
2035
1558
  {
2036
1559
  size: 9,
@@ -2039,9 +1562,9 @@ function QuestionCard({
2039
1562
  )
2040
1563
  }
2041
1564
  ),
2042
- /* @__PURE__ */ jsxs7("div", { className: "min-w-0", children: [
2043
- /* @__PURE__ */ jsx9("div", { className: cn("font-medium", answered ? "text-xs" : "text-[13px]"), children: opt.label }),
2044
- opt.description && /* @__PURE__ */ jsx9(
1565
+ /* @__PURE__ */ jsxs6("div", { className: "min-w-0", children: [
1566
+ /* @__PURE__ */ jsx8("div", { className: cn("font-medium", answered ? "text-xs" : "text-[13px]"), children: opt.label }),
1567
+ opt.description && /* @__PURE__ */ jsx8(
2045
1568
  "div",
2046
1569
  {
2047
1570
  className: cn(
@@ -2058,7 +1581,7 @@ function QuestionCard({
2058
1581
  opt.label
2059
1582
  );
2060
1583
  }),
2061
- answered && !isCustom ? null : /* @__PURE__ */ jsxs7(
1584
+ answered && !isCustom ? null : /* @__PURE__ */ jsxs6(
2062
1585
  "div",
2063
1586
  {
2064
1587
  className: cn(
@@ -2068,8 +1591,8 @@ function QuestionCard({
2068
1591
  answered && "cursor-default opacity-70"
2069
1592
  ),
2070
1593
  children: [
2071
- /* @__PURE__ */ jsx9("span", { className: "shrink-0 text-xs text-[hsl(var(--muted-foreground))]", children: "\u5176\u4ED6\uFF1A" }),
2072
- /* @__PURE__ */ jsx9(
1594
+ /* @__PURE__ */ jsx8("span", { className: "shrink-0 text-xs text-[hsl(var(--muted-foreground))]", children: "\u5176\u4ED6\uFF1A" }),
1595
+ /* @__PURE__ */ jsx8(
2073
1596
  "input",
2074
1597
  {
2075
1598
  type: "text",
@@ -2137,7 +1660,7 @@ function normalizeOptionItem(value) {
2137
1660
  }
2138
1661
 
2139
1662
  // src/components/ToolCallBlock.tsx
2140
- import { Fragment, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
1663
+ import { Fragment, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
2141
1664
  function resolveAskQuestionState({
2142
1665
  toolStatus,
2143
1666
  hasAnswerData,
@@ -2157,12 +1680,12 @@ function ToolCallBlock({
2157
1680
  sessionStatus,
2158
1681
  renderer
2159
1682
  }) {
2160
- const [expanded, setExpanded] = useState9(false);
1683
+ const [expanded, setExpanded] = useState8(false);
2161
1684
  const normalizedName = formatToolName(toolCall.name);
2162
1685
  if (renderer) {
2163
1686
  const custom = renderer(toolCall);
2164
1687
  if (custom !== null && custom !== void 0) {
2165
- return /* @__PURE__ */ jsx10(Fragment, { children: custom });
1688
+ return /* @__PURE__ */ jsx9(Fragment, { children: custom });
2166
1689
  }
2167
1690
  }
2168
1691
  if (normalizedName === "AskUserQuestion") {
@@ -2174,7 +1697,7 @@ function ToolCallBlock({
2174
1697
  });
2175
1698
  const canAnswer = questionState.awaitingAnswer && Boolean(onAnswer);
2176
1699
  if (askData) {
2177
- return /* @__PURE__ */ jsx10(
1700
+ return /* @__PURE__ */ jsx9(
2178
1701
  AskUserQuestionBlock,
2179
1702
  {
2180
1703
  data: askData,
@@ -2187,24 +1710,24 @@ function ToolCallBlock({
2187
1710
  );
2188
1711
  }
2189
1712
  if (toolCall.status === "pending") {
2190
- return /* @__PURE__ */ jsxs8("div", { className: "ml-4 flex max-w-lg items-center gap-2 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4 text-sm text-[hsl(var(--muted-foreground))]", children: [
2191
- /* @__PURE__ */ jsx10(LoaderCircle, { size: 14, className: "animate-spin" }),
2192
- /* @__PURE__ */ jsx10("span", { children: "\u6B63\u5728\u51C6\u5907\u95EE\u9898\u2026" })
1713
+ return /* @__PURE__ */ jsxs7("div", { className: "ml-4 flex max-w-lg items-center gap-2 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4 text-sm text-[hsl(var(--muted-foreground))]", children: [
1714
+ /* @__PURE__ */ jsx9(LoaderCircle, { size: 14, className: "animate-spin" }),
1715
+ /* @__PURE__ */ jsx9("span", { children: "\u6B63\u5728\u51C6\u5907\u95EE\u9898\u2026" })
2193
1716
  ] });
2194
1717
  }
2195
- return /* @__PURE__ */ jsxs8("div", { className: "ml-4 max-w-lg rounded-xl border border-amber-500/35 bg-amber-500/10 p-4 text-sm text-[hsl(var(--foreground))]", children: [
2196
- /* @__PURE__ */ jsx10("div", { className: "font-semibold", children: "\u9009\u62E9\u9898\u5185\u5BB9\u6682\u65F6\u65E0\u6CD5\u663E\u793A" }),
2197
- /* @__PURE__ */ jsx10("div", { className: "mt-1 text-xs leading-5 text-[hsl(var(--muted-foreground))]", children: "\u6536\u5230\u7684\u4EA4\u4E92\u6570\u636E\u4E0D\u5B8C\u6574\u3002\u8BF7\u8BA9\u667A\u80FD\u4F53\u91CD\u65B0\u63D0\u95EE\u3002" })
1718
+ return /* @__PURE__ */ jsxs7("div", { className: "ml-4 max-w-lg rounded-xl border border-amber-500/35 bg-amber-500/10 p-4 text-sm text-[hsl(var(--foreground))]", children: [
1719
+ /* @__PURE__ */ jsx9("div", { className: "font-semibold", children: "\u9009\u62E9\u9898\u5185\u5BB9\u6682\u65F6\u65E0\u6CD5\u663E\u793A" }),
1720
+ /* @__PURE__ */ jsx9("div", { className: "mt-1 text-xs leading-5 text-[hsl(var(--muted-foreground))]", children: "\u6536\u5230\u7684\u4EA4\u4E92\u6570\u636E\u4E0D\u5B8C\u6574\u3002\u8BF7\u8BA9\u667A\u80FD\u4F53\u91CD\u65B0\u63D0\u95EE\u3002" })
2198
1721
  ] });
2199
1722
  }
2200
1723
  const tone = getToolTone(toolCall.status);
2201
1724
  const displayName = getToolDisplayLabel(toolCall);
2202
1725
  const toneClass = tone === "red" ? "border-l-[hsl(var(--muted-foreground)/0.5)]" : tone === "amber" ? "border-l-amber-400" : tone === "blue" ? "border-l-blue-500" : "border-l-[hsl(var(--primary))]";
2203
- const statusIcon = toolCall.status === "pending" ? /* @__PURE__ */ jsx10(LoaderCircle, { size: 11, className: "animate-spin" }) : toolCall.status === "awaiting_answer" ? /* @__PURE__ */ jsx10(MessageSquareMore, { size: 11 }) : toolCall.status === "cancelled" || toolCall.status === "error" ? /* @__PURE__ */ jsx10(X, { size: 11 }) : /* @__PURE__ */ jsx10(Check, { size: 11 });
1726
+ const statusIcon = toolCall.status === "pending" ? /* @__PURE__ */ jsx9(LoaderCircle, { size: 11, className: "animate-spin" }) : toolCall.status === "awaiting_answer" ? /* @__PURE__ */ jsx9(MessageSquareMore, { size: 11 }) : toolCall.status === "cancelled" || toolCall.status === "error" ? /* @__PURE__ */ jsx9(X, { size: 11 }) : /* @__PURE__ */ jsx9(Check, { size: 11 });
2204
1727
  const statusTextClass = tone === "red" ? "text-[hsl(var(--muted-foreground))]" : tone === "amber" ? "text-amber-300" : tone === "blue" ? "text-blue-300" : "text-[hsl(var(--primary))]";
2205
- return /* @__PURE__ */ jsxs8("div", { className: "blade-chat-tool ml-4 text-xs", children: [
2206
- /* @__PURE__ */ jsxs8("div", { className: cn("border-l-[3px] flex items-center gap-2 px-3 py-2", toneClass), children: [
2207
- /* @__PURE__ */ jsxs8(
1728
+ return /* @__PURE__ */ jsxs7("div", { className: "blade-chat-tool ml-4 text-xs", children: [
1729
+ /* @__PURE__ */ jsxs7("div", { className: cn("border-l-[3px] flex items-center gap-2 px-3 py-2", toneClass), children: [
1730
+ /* @__PURE__ */ jsxs7(
2208
1731
  "button",
2209
1732
  {
2210
1733
  type: "button",
@@ -2212,7 +1735,7 @@ function ToolCallBlock({
2212
1735
  className: "flex min-w-0 flex-1 items-center gap-2 text-left transition-colors hover:bg-white/3 focus-visible:ring-1 focus-visible:ring-[hsl(var(--ring))] focus:outline-none",
2213
1736
  "aria-expanded": expanded,
2214
1737
  children: [
2215
- /* @__PURE__ */ jsx10(
1738
+ /* @__PURE__ */ jsx9(
2216
1739
  ChevronRight,
2217
1740
  {
2218
1741
  size: 11,
@@ -2222,24 +1745,24 @@ function ToolCallBlock({
2222
1745
  )
2223
1746
  }
2224
1747
  ),
2225
- /* @__PURE__ */ jsxs8("span", { className: cn("flex shrink-0 items-center gap-1 text-[10px]", statusTextClass), children: [
1748
+ /* @__PURE__ */ jsxs7("span", { className: cn("flex shrink-0 items-center gap-1 text-[10px]", statusTextClass), children: [
2226
1749
  statusIcon,
2227
- /* @__PURE__ */ jsx10("span", { children: getToolStatusLabel(toolCall.status) })
1750
+ /* @__PURE__ */ jsx9("span", { children: getToolStatusLabel(toolCall.status) })
2228
1751
  ] }),
2229
- /* @__PURE__ */ jsx10("span", { className: "min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]", children: displayName })
1752
+ /* @__PURE__ */ jsx9("span", { className: "min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]", children: displayName })
2230
1753
  ]
2231
1754
  }
2232
1755
  ),
2233
- typeof toolCall.duration_ms === "number" && toolCall.duration_ms > 0 && /* @__PURE__ */ jsx10("span", { className: "shrink-0 font-mono text-[10px] text-[hsl(var(--muted-foreground))]", children: formatToolDuration(toolCall.duration_ms) })
1756
+ typeof toolCall.duration_ms === "number" && toolCall.duration_ms > 0 && /* @__PURE__ */ jsx9("span", { className: "shrink-0 font-mono text-[10px] text-[hsl(var(--muted-foreground))]", children: formatToolDuration(toolCall.duration_ms) })
2234
1757
  ] }),
2235
- expanded && /* @__PURE__ */ jsxs8("div", { className: "blade-chat-tool-detail ml-4 mt-1 rounded-xl bg-[hsl(var(--card))] px-3 py-3", children: [
2236
- /* @__PURE__ */ jsx10("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u5DE5\u5177" }),
2237
- /* @__PURE__ */ jsx10("div", { className: "mb-3 font-mono text-[11px] text-[hsl(var(--foreground))]", children: normalizedName }),
2238
- /* @__PURE__ */ jsx10("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u53C2\u6570" }),
2239
- /* @__PURE__ */ jsx10("pre", { className: "overflow-x-auto whitespace-pre-wrap rounded-md bg-[hsl(var(--muted))] p-2 font-mono text-[11px] text-[hsl(var(--foreground))]", children: formatToolArgs(toolCall.arguments) }),
2240
- toolCall.result != null && /* @__PURE__ */ jsxs8(Fragment, { children: [
2241
- /* @__PURE__ */ jsx10("div", { className: "mb-1 mt-3 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u7ED3\u679C" }),
2242
- /* @__PURE__ */ jsx10("pre", { className: "max-h-[400px] overflow-auto whitespace-pre-wrap rounded-md bg-[hsl(var(--muted))] p-2 font-mono text-[11px] text-[hsl(var(--foreground))]", children: formatToolResult(toolCall.result) })
1758
+ expanded && /* @__PURE__ */ jsxs7("div", { className: "blade-chat-tool-detail ml-4 mt-1 rounded-xl bg-[hsl(var(--card))] px-3 py-3", children: [
1759
+ /* @__PURE__ */ jsx9("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u5DE5\u5177" }),
1760
+ /* @__PURE__ */ jsx9("div", { className: "mb-3 font-mono text-[11px] text-[hsl(var(--foreground))]", children: normalizedName }),
1761
+ /* @__PURE__ */ jsx9("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u53C2\u6570" }),
1762
+ /* @__PURE__ */ jsx9("pre", { className: "overflow-x-auto whitespace-pre-wrap rounded-md bg-[hsl(var(--muted))] p-2 font-mono text-[11px] text-[hsl(var(--foreground))]", children: formatToolArgs(toolCall.arguments) }),
1763
+ toolCall.result != null && /* @__PURE__ */ jsxs7(Fragment, { children: [
1764
+ /* @__PURE__ */ jsx9("div", { className: "mb-1 mt-3 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u7ED3\u679C" }),
1765
+ /* @__PURE__ */ jsx9("pre", { className: "max-h-[400px] overflow-auto whitespace-pre-wrap rounded-md bg-[hsl(var(--muted))] p-2 font-mono text-[11px] text-[hsl(var(--foreground))]", children: formatToolResult(toolCall.result) })
2243
1766
  ] })
2244
1767
  ] })
2245
1768
  ] });
@@ -2257,11 +1780,11 @@ function buildAskUserPayload(argumentsJson) {
2257
1780
  }
2258
1781
 
2259
1782
  // src/components/AssistantTurnBlock.tsx
2260
- import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
1783
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
2261
1784
  function ThinkingBlock({ reasoning, isStreaming }) {
2262
- const [open, setOpen] = useState10(false);
2263
- return /* @__PURE__ */ jsxs9("div", { className: "blade-chat-thinking ml-4 text-sm", children: [
2264
- /* @__PURE__ */ jsxs9(
1785
+ const [open, setOpen] = useState9(false);
1786
+ return /* @__PURE__ */ jsxs8("div", { className: "blade-chat-thinking ml-4 text-sm", children: [
1787
+ /* @__PURE__ */ jsxs8(
2265
1788
  "button",
2266
1789
  {
2267
1790
  type: "button",
@@ -2269,14 +1792,14 @@ function ThinkingBlock({ reasoning, isStreaming }) {
2269
1792
  "aria-expanded": open,
2270
1793
  className: "inline-flex items-center gap-1.5 text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))]",
2271
1794
  children: [
2272
- /* @__PURE__ */ jsx11(Brain, { size: 12, className: "shrink-0" }),
2273
- isStreaming ? /* @__PURE__ */ jsx11(Shimmer, { className: "text-xs", children: "\u6B63\u5728\u601D\u8003" }) : /* @__PURE__ */ jsx11("span", { children: "\u601D\u8003\u8FC7\u7A0B" }),
2274
- /* @__PURE__ */ jsxs9("span", { className: "text-[hsl(var(--muted-foreground))]/70", children: [
1795
+ /* @__PURE__ */ jsx10(Brain, { size: 12, className: "shrink-0" }),
1796
+ isStreaming ? /* @__PURE__ */ jsx10(Shimmer, { className: "text-xs", children: "\u6B63\u5728\u601D\u8003" }) : /* @__PURE__ */ jsx10("span", { children: "\u601D\u8003\u8FC7\u7A0B" }),
1797
+ /* @__PURE__ */ jsxs8("span", { className: "text-[hsl(var(--muted-foreground))]/70", children: [
2275
1798
  "\xB7 ",
2276
1799
  new Intl.NumberFormat("zh-CN").format(reasoning.length),
2277
1800
  " \u5B57"
2278
1801
  ] }),
2279
- /* @__PURE__ */ jsx11(
1802
+ /* @__PURE__ */ jsx10(
2280
1803
  ChevronDown,
2281
1804
  {
2282
1805
  size: 12,
@@ -2286,7 +1809,7 @@ function ThinkingBlock({ reasoning, isStreaming }) {
2286
1809
  ]
2287
1810
  }
2288
1811
  ),
2289
- open && /* @__PURE__ */ jsx11("div", { className: "mt-1.5 whitespace-pre-wrap border-l-2 border-[hsl(var(--border))] pl-3 text-[11px] leading-5 text-[hsl(var(--muted-foreground))]", children: reasoning })
1812
+ open && /* @__PURE__ */ jsx10("div", { className: "mt-1.5 whitespace-pre-wrap border-l-2 border-[hsl(var(--border))] pl-3 text-[11px] leading-5 text-[hsl(var(--muted-foreground))]", children: reasoning })
2290
1813
  ] });
2291
1814
  }
2292
1815
  function getMessageText(message) {
@@ -2312,21 +1835,21 @@ function AssistantTurnBlock({
2312
1835
  (message) => getMessageText(message) || message.reasoning || (message.tool_calls?.length ?? 0) > 0
2313
1836
  );
2314
1837
  const latestReasoningIndex = isStreaming ? findLatestReasoningMessageIndex(messages) : -1;
2315
- return /* @__PURE__ */ jsxs9("div", { className: "blade-chat-assistant-turn flex flex-col gap-3", children: [
2316
- hasInterrupted && /* @__PURE__ */ jsx11("div", { className: "ml-4 w-fit rounded-full border border-amber-500/30 bg-amber-500/10 px-2.5 py-1 text-[10px] font-medium uppercase tracking-[0.12em] text-amber-300", children: "\u5DF2\u4E2D\u65AD" }),
1838
+ return /* @__PURE__ */ jsxs8("div", { className: "blade-chat-assistant-turn flex flex-col gap-3", children: [
1839
+ hasInterrupted && /* @__PURE__ */ jsx10("div", { className: "ml-4 w-fit rounded-full border border-amber-500/30 bg-amber-500/10 px-2.5 py-1 text-[10px] font-medium uppercase tracking-[0.12em] text-amber-300", children: "\u5DF2\u4E2D\u65AD" }),
2317
1840
  messages.map((message, index) => {
2318
1841
  const isLast = index === messages.length - 1;
2319
1842
  const streamingThis = isStreaming && isLast;
2320
1843
  const text = getMessageText(message);
2321
1844
  const toolCalls = message.tool_calls ?? [];
2322
1845
  const showReasoning = !!message.reasoning && (!isStreaming || index === latestReasoningIndex);
2323
- return /* @__PURE__ */ jsxs9(
1846
+ return /* @__PURE__ */ jsxs8(
2324
1847
  "div",
2325
1848
  {
2326
1849
  className: "flex flex-col gap-3",
2327
1850
  children: [
2328
- showReasoning && message.reasoning && /* @__PURE__ */ jsx11(ThinkingBlock, { reasoning: message.reasoning, isStreaming: streamingThis && !text }),
2329
- text && /* @__PURE__ */ jsx11("div", { className: "blade-chat-assistant-text text-[15px] leading-8 text-[hsl(var(--foreground))]", children: /* @__PURE__ */ jsx11(
1851
+ showReasoning && message.reasoning && /* @__PURE__ */ jsx10(ThinkingBlock, { reasoning: message.reasoning, isStreaming: streamingThis && !text }),
1852
+ text && /* @__PURE__ */ jsx10("div", { className: "blade-chat-assistant-text text-[15px] leading-8 text-[hsl(var(--foreground))]", children: /* @__PURE__ */ jsx10(
2330
1853
  MarkdownContent,
2331
1854
  {
2332
1855
  mode: streamingThis ? "streaming" : "static",
@@ -2335,8 +1858,8 @@ function AssistantTurnBlock({
2335
1858
  children: text
2336
1859
  }
2337
1860
  ) }),
2338
- toolCalls.length > 0 && /* @__PURE__ */ jsx11("div", { className: "flex flex-col gap-2", children: toolCalls.map(
2339
- (toolCall) => formatToolName(toolCall.name) === "Agent" ? /* @__PURE__ */ jsx11(AgentLoopBlock, { toolCall }, toolCall.id) : /* @__PURE__ */ jsx11(
1861
+ toolCalls.length > 0 && /* @__PURE__ */ jsx10("div", { className: "flex flex-col gap-2", children: toolCalls.map(
1862
+ (toolCall) => formatToolName(toolCall.name) === "Agent" ? /* @__PURE__ */ jsx10(AgentLoopBlock, { toolCall }, toolCall.id) : /* @__PURE__ */ jsx10(
2340
1863
  ToolCallBlock,
2341
1864
  {
2342
1865
  toolCall,
@@ -2354,13 +1877,13 @@ function AssistantTurnBlock({
2354
1877
  message.entry_id ?? `${message.timestamp ?? "assistant"}-${index}`
2355
1878
  );
2356
1879
  }),
2357
- isStreaming && !hasAnyContent && /* @__PURE__ */ jsx11(Shimmer, { className: "ml-4", children: "\u6B63\u5728\u751F\u6210..." })
1880
+ isStreaming && !hasAnyContent && /* @__PURE__ */ jsx10(Shimmer, { className: "ml-4", children: "\u6B63\u5728\u751F\u6210..." })
2358
1881
  ] });
2359
1882
  }
2360
1883
 
2361
1884
  // src/components/RenderErrorBoundary.tsx
2362
1885
  import { Component } from "react";
2363
- import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1886
+ import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
2364
1887
  function getFirstComponentName(componentStack) {
2365
1888
  const match = componentStack.match(/\n\s+at\s+([^\s(]+)/);
2366
1889
  return match?.[1] ?? null;
@@ -2393,426 +1916,40 @@ var RenderErrorBoundary = class extends Component {
2393
1916
  return children;
2394
1917
  }
2395
1918
  const componentName = getFirstComponentName(componentStack);
2396
- return /* @__PURE__ */ jsx12("div", { className: "blade-chat-render-error rounded-xl border border-amber-500/30 bg-amber-500/8 px-4 py-3 text-sm text-amber-100", children: /* @__PURE__ */ jsxs10("div", { className: "flex items-start gap-2", children: [
2397
- /* @__PURE__ */ jsx12(TriangleAlert, { className: "mt-0.5 h-4 w-4 shrink-0 text-amber-300" }),
2398
- /* @__PURE__ */ jsxs10("div", { className: "min-w-0 flex-1", children: [
2399
- /* @__PURE__ */ jsxs10("div", { className: "font-medium", children: [
1919
+ return /* @__PURE__ */ jsx11("div", { className: "blade-chat-render-error rounded-xl border border-amber-500/30 bg-amber-500/8 px-4 py-3 text-sm text-amber-100", children: /* @__PURE__ */ jsxs9("div", { className: "flex items-start gap-2", children: [
1920
+ /* @__PURE__ */ jsx11(TriangleAlert, { className: "mt-0.5 h-4 w-4 shrink-0 text-amber-300" }),
1921
+ /* @__PURE__ */ jsxs9("div", { className: "min-w-0 flex-1", children: [
1922
+ /* @__PURE__ */ jsxs9("div", { className: "font-medium", children: [
2400
1923
  label,
2401
1924
  "\u6E32\u67D3\u5931\u8D25"
2402
1925
  ] }),
2403
- /* @__PURE__ */ jsxs10("div", { className: "mt-1 break-words text-xs leading-5 text-amber-100/75", children: [
1926
+ /* @__PURE__ */ jsxs9("div", { className: "mt-1 break-words text-xs leading-5 text-amber-100/75", children: [
2404
1927
  componentName ? `\u7EC4\u4EF6\uFF1A${componentName}\u3002` : null,
2405
1928
  error.message || "\u53D1\u751F\u4E86\u672A\u9884\u671F\u7684\u6E32\u67D3\u9519\u8BEF\u3002"
2406
1929
  ] }),
2407
- details ? /* @__PURE__ */ jsx12("div", { className: "mt-1 truncate text-xs text-amber-100/55", children: details }) : null
1930
+ details ? /* @__PURE__ */ jsx11("div", { className: "mt-1 truncate text-xs text-amber-100/55", children: details }) : null
2408
1931
  ] })
2409
1932
  ] }) });
2410
1933
  }
2411
1934
  };
2412
1935
 
2413
- // src/components/PostChatFollowupBlock.tsx
2414
- import { useCallback as useCallback5, useEffect as useEffect7, useRef as useRef7, useState as useState11 } from "react";
2415
- import { Fragment as Fragment2, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
2416
- function emitInteraction(callback, event) {
2417
- try {
2418
- callback?.(event);
2419
- } catch {
2420
- }
1936
+ // src/components/UserMessageBubble.tsx
1937
+ import { getFileParts, getImageParts, getTextContent as getTextContent2 } from "@blade-hq/agent-client";
1938
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1939
+ function isUserMessage(message) {
1940
+ return message.role === "user";
2421
1941
  }
2422
- function basename(path) {
2423
- return path.split(/[\\/]/).filter(Boolean).pop() || path;
1942
+ function isErrorMessage(message) {
1943
+ return message.role === "error";
2424
1944
  }
2425
- function ArtifactCard({
2426
- artifact,
2427
- sessionId,
2428
- assistantEntryId,
2429
- artifactIndex,
2430
- onInteraction,
2431
- onArtifactOpened
2432
- }) {
2433
- const client = useBladeClient();
2434
- const [downloading, setDownloading] = useState11(false);
2435
- const name = artifact.label || basename(artifact.target);
2436
- if (artifact.kind === "link") {
2437
- return /* @__PURE__ */ jsxs11(
2438
- "a",
2439
- {
2440
- href: artifact.target,
2441
- target: "_blank",
2442
- rel: "noopener noreferrer",
2443
- onClick: () => onArtifactOpened(artifactIndex, "link"),
2444
- title: `${name}
2445
- ${artifact.target}`,
2446
- className: "group relative flex min-w-0 items-center gap-1.5 rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-2 py-1.5 text-xs text-[hsl(var(--card-foreground))] hover:bg-[hsl(var(--accent))]",
2447
- children: [
2448
- /* @__PURE__ */ jsx13(Globe, { size: 15, className: "shrink-0 text-[hsl(var(--primary))]" }),
2449
- /* @__PURE__ */ jsx13("span", { className: "min-w-0 flex-1 truncate font-medium", children: name }),
2450
- /* @__PURE__ */ jsx13(ArrowUpRight, { size: 13, className: "absolute right-2 opacity-0 group-hover:opacity-100 group-focus-visible:opacity-100" })
2451
- ]
2452
- }
2453
- );
2454
- }
2455
- const fileName = basename(artifact.target);
2456
- const downloadUrl = sessionId ? client.buildAuthedUrl(
2457
- `/api/sessions/${encodeURIComponent(sessionId)}/files/${encodeURIComponent(artifact.target)}`
2458
- ) : void 0;
2459
- const handleDownload = async (event) => {
2460
- if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
2461
- event.preventDefault();
2462
- if (!sessionId || downloading) return;
2463
- setDownloading(true);
2464
- emitInteraction(onInteraction, {
2465
- type: "artifact_download_started",
2466
- sessionId,
2467
- assistantEntryId,
2468
- artifactIndex,
2469
- artifactKind: "file"
2470
- });
2471
- try {
2472
- await client.sessions.downloadFile(sessionId, artifact.target, fileName);
2473
- emitInteraction(onInteraction, {
2474
- type: "artifact_download_succeeded",
2475
- sessionId,
2476
- assistantEntryId,
2477
- artifactIndex,
2478
- artifactKind: "file"
2479
- });
2480
- } catch {
2481
- } finally {
2482
- setDownloading(false);
2483
- }
2484
- };
2485
- return /* @__PURE__ */ jsx13(
2486
- "a",
2487
- {
2488
- href: downloadUrl,
2489
- download: fileName,
2490
- onClick: handleDownload,
2491
- title: fileName,
2492
- "aria-label": `\u4E0B\u8F7D\u6587\u4EF6\uFF1A${fileName}`,
2493
- "aria-disabled": !sessionId || void 0,
2494
- "aria-busy": downloading || void 0,
2495
- className: "min-w-0 cursor-pointer break-all text-xs text-[hsl(var(--primary))] underline aria-disabled:cursor-not-allowed aria-disabled:opacity-60 aria-busy:cursor-wait",
2496
- children: fileName
2497
- }
2498
- );
2499
- }
2500
- var FEEDBACK_REASONS = [
2501
- { value: "not_solved", label: "\u6CA1\u89E3\u51B3\u95EE\u9898" },
2502
- { value: "inaccurate", label: "\u5185\u5BB9\u4E0D\u51C6\u786E" },
2503
- { value: "incomplete", label: "\u5185\u5BB9\u4E0D\u5B8C\u6574" },
2504
- { value: "needs_major_changes", label: "\u9700\u8981\u5927\u91CF\u4FEE\u6539" },
2505
- { value: "too_slow", label: "\u592A\u6162" },
2506
- { value: "other", label: "\u5176\u4ED6" }
2507
- ];
2508
- function feedbackReasonLabel(reason) {
2509
- return FEEDBACK_REASONS.find((item) => item.value === reason)?.label ?? null;
2510
- }
2511
- function HistoricalResultFeedback({ feedback }) {
2512
- const label = feedbackReasonLabel(feedback.reason);
2513
- return /* @__PURE__ */ jsxs11(
2514
- "section",
2515
- {
2516
- "aria-label": "\u5386\u53F2\u7ED3\u679C\u53CD\u9988",
2517
- className: "mt-3 w-fit max-w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.2)] px-3 py-2 text-xs text-[hsl(var(--muted-foreground))]",
2518
- children: [
2519
- /* @__PURE__ */ jsxs11("span", { children: [
2520
- "\u4F60\u5BF9\u6B64\u8F6E\u7ED3\u679C\u7684\u8BC4\u4EF7\uFF1A",
2521
- feedback.helpful ? "\u6709\u5E2E\u52A9" : "\u6CA1\u5E2E\u52A9"
2522
- ] }),
2523
- label ? /* @__PURE__ */ jsxs11("span", { children: [
2524
- " \xB7 ",
2525
- label
2526
- ] }) : null
2527
- ]
2528
- }
2529
- );
2530
- }
2531
- function ResultFeedback({
2532
- followup,
2533
- sessionId,
2534
- isViewer,
2535
- onInteraction,
2536
- savedFeedback,
2537
- onFeedbackSaved
2538
- }) {
2539
- const client = useBladeClient();
2540
- const [saved, setSaved] = useState11(savedFeedback ?? null);
2541
- const [helpful, setHelpful] = useState11(savedFeedback?.helpful ?? null);
2542
- const [reason, setReason] = useState11(savedFeedback?.reason ?? null);
2543
- const [saving, setSaving] = useState11(false);
2544
- const [saveError, setSaveError] = useState11(false);
2545
- const reportedShown = useRef7(false);
2546
- const latestChoice = useRef7(null);
2547
- const eligible = followup.feedback_eligible === true && Boolean(sessionId) && !isViewer;
2548
- useEffect7(() => {
2549
- if (!eligible || reportedShown.current) return;
2550
- reportedShown.current = true;
2551
- emitInteraction(onInteraction, {
2552
- type: "result_feedback_shown",
2553
- sessionId,
2554
- assistantEntryId: followup.assistant_entry_id
2555
- });
2556
- }, [eligible, followup.assistant_entry_id, onInteraction, sessionId]);
2557
- useEffect7(() => {
2558
- if (!savedFeedback || latestChoice.current) return;
2559
- setSaved(savedFeedback);
2560
- setHelpful(savedFeedback.helpful);
2561
- setReason(savedFeedback.reason);
2562
- }, [savedFeedback]);
2563
- const submit = useCallback5(
2564
- async (nextHelpful, nextReason) => {
2565
- if (!sessionId) return;
2566
- const choice = { helpful: nextHelpful, reason: nextReason };
2567
- latestChoice.current = choice;
2568
- setHelpful(nextHelpful);
2569
- setReason(nextReason);
2570
- setSaving(true);
2571
- setSaveError(false);
2572
- try {
2573
- const result = await client.sessions.putResultFeedback(
2574
- sessionId,
2575
- followup.assistant_entry_id,
2576
- choice
2577
- );
2578
- if (latestChoice.current !== choice) return;
2579
- setSaved(result);
2580
- onFeedbackSaved?.(result);
2581
- emitInteraction(onInteraction, {
2582
- type: "result_feedback_submitted",
2583
- sessionId,
2584
- assistantEntryId: followup.assistant_entry_id,
2585
- helpful: result.helpful,
2586
- reason: result.reason,
2587
- updatedAt: result.updated_at
2588
- });
2589
- } catch {
2590
- if (latestChoice.current === choice) setSaveError(true);
2591
- } finally {
2592
- if (latestChoice.current === choice) setSaving(false);
2593
- }
2594
- },
2595
- [client, followup.assistant_entry_id, onFeedbackSaved, onInteraction, sessionId]
2596
- );
2597
- if (!eligible) return null;
2598
- return /* @__PURE__ */ jsxs11(
2599
- "section",
2600
- {
2601
- "aria-label": "\u7ED3\u679C\u53CD\u9988",
2602
- className: "flex flex-col gap-2 border-t border-[hsl(var(--border))] pt-3",
2603
- children: [
2604
- /* @__PURE__ */ jsx13("div", { className: "text-xs font-medium text-[hsl(var(--muted-foreground))]", children: "\u76EE\u524D\u7684\u6574\u4F53\u7ED3\u679C\u6709\u5E2E\u52A9\u5417\uFF1F" }),
2605
- /* @__PURE__ */ jsxs11("div", { className: "flex flex-wrap gap-1.5", children: [
2606
- /* @__PURE__ */ jsx13(
2607
- "button",
2608
- {
2609
- type: "button",
2610
- "aria-pressed": helpful === true,
2611
- disabled: saving,
2612
- onClick: () => void submit(true, null),
2613
- className: "rounded-lg border border-[hsl(var(--border))] px-2.5 py-1.5 text-xs aria-pressed:bg-[hsl(var(--primary)/0.1)] disabled:opacity-60",
2614
- children: "\u6709\u5E2E\u52A9"
2615
- }
2616
- ),
2617
- /* @__PURE__ */ jsx13(
2618
- "button",
2619
- {
2620
- type: "button",
2621
- "aria-pressed": helpful === false,
2622
- disabled: saving,
2623
- onClick: () => void submit(false, reason),
2624
- className: "rounded-lg border border-[hsl(var(--border))] px-2.5 py-1.5 text-xs aria-pressed:bg-[hsl(var(--primary)/0.1)] disabled:opacity-60",
2625
- children: "\u6CA1\u5E2E\u52A9"
2626
- }
2627
- )
2628
- ] }),
2629
- helpful === false ? /* @__PURE__ */ jsx13("div", { className: "flex flex-wrap gap-1.5", "aria-label": "\u6CA1\u5E2E\u52A9\u7684\u4E3B\u8981\u539F\u56E0", children: FEEDBACK_REASONS.map((item) => /* @__PURE__ */ jsx13(
2630
- "button",
2631
- {
2632
- type: "button",
2633
- "aria-pressed": reason === item.value,
2634
- disabled: saving,
2635
- onClick: () => void submit(false, item.value),
2636
- className: "rounded-full bg-[hsl(var(--muted)/0.62)] px-2.5 py-1 text-[11px] aria-pressed:bg-[hsl(var(--primary)/0.14)] disabled:opacity-60",
2637
- children: item.label
2638
- },
2639
- item.value
2640
- )) }) : null,
2641
- saveError ? /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2 text-xs text-[hsl(var(--destructive))]", children: [
2642
- /* @__PURE__ */ jsx13("span", { children: "\u53CD\u9988\u6682\u672A\u4FDD\u5B58\uFF0C\u53EF\u91CD\u8BD5" }),
2643
- /* @__PURE__ */ jsx13(
2644
- "button",
2645
- {
2646
- type: "button",
2647
- className: "underline",
2648
- onClick: () => {
2649
- const choice = latestChoice.current;
2650
- if (choice) void submit(choice.helpful, choice.reason);
2651
- },
2652
- children: "\u91CD\u8BD5"
2653
- }
2654
- )
2655
- ] }) : saved ? /* @__PURE__ */ jsx13("div", { className: "text-[11px] text-[hsl(var(--muted-foreground))]", children: "\u5DF2\u4FDD\u5B58\uFF0C\u53EF\u968F\u65F6\u4FEE\u6539" }) : null
2656
- ]
2657
- }
2658
- );
2659
- }
2660
- function PostChatFollowupBlock({
2661
- followup,
2662
- sessionId,
2663
- onSuggestion,
2664
- isViewer = false,
2665
- onInteraction,
2666
- savedFeedback,
2667
- onFeedbackSaved
2668
- }) {
2669
- const [expanded, setExpanded] = useState11(false);
2670
- const adopted = useRef7(/* @__PURE__ */ new Set());
2671
- const reportedSuggestions = useRef7(false);
2672
- const reportedArtifacts = useRef7(/* @__PURE__ */ new Set());
2673
- const openedArtifacts = useRef7(/* @__PURE__ */ new Set());
2674
- const artifacts = followup.final_artifacts ?? [];
2675
- const visibleArtifacts = expanded ? artifacts : artifacts.slice(0, 3);
2676
- useEffect7(() => {
2677
- if (!reportedSuggestions.current && followup.suggestions.length > 0) {
2678
- reportedSuggestions.current = true;
2679
- emitInteraction(onInteraction, {
2680
- type: "suggestions_shown",
2681
- sessionId,
2682
- assistantEntryId: followup.assistant_entry_id,
2683
- count: followup.suggestions.length
2684
- });
2685
- }
2686
- for (let artifactIndex = 0; artifactIndex < visibleArtifacts.length; artifactIndex += 1) {
2687
- if (reportedArtifacts.current.has(artifactIndex)) continue;
2688
- const artifact = visibleArtifacts[artifactIndex];
2689
- if (!artifact) continue;
2690
- reportedArtifacts.current.add(artifactIndex);
2691
- emitInteraction(onInteraction, {
2692
- type: "artifact_shown",
2693
- sessionId,
2694
- assistantEntryId: followup.assistant_entry_id,
2695
- artifactIndex,
2696
- artifactKind: artifact.kind
2697
- });
2698
- }
2699
- }, [
2700
- followup.assistant_entry_id,
2701
- followup.suggestions.length,
2702
- onInteraction,
2703
- sessionId,
2704
- visibleArtifacts
2705
- ]);
2706
- const reportArtifactOpened = useCallback5(
2707
- (artifactIndex, artifactKind) => {
2708
- if (openedArtifacts.current.has(artifactIndex)) return;
2709
- openedArtifacts.current.add(artifactIndex);
2710
- emitInteraction(onInteraction, {
2711
- type: "artifact_opened",
2712
- sessionId,
2713
- assistantEntryId: followup.assistant_entry_id,
2714
- artifactIndex,
2715
- artifactKind
2716
- });
2717
- },
2718
- [followup.assistant_entry_id, onInteraction, sessionId]
2719
- );
2720
- if (!followup.recaption && artifacts.length === 0 && followup.suggestions.length === 0 && !followup.feedback_eligible)
2721
- return null;
2722
- return /* @__PURE__ */ jsxs11("div", { className: "mt-3 flex w-fit max-w-full flex-col gap-3 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.28)] p-3 sm:max-w-[680px]", children: [
2723
- followup.recaption || artifacts.length > 0 ? /* @__PURE__ */ jsxs11("section", { "aria-label": "\u672C\u8F6E\u5C0F\u7ED3", className: "flex flex-col gap-2", children: [
2724
- /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1.5 text-xs font-medium text-[hsl(var(--muted-foreground))]", children: [
2725
- /* @__PURE__ */ jsx13(Sparkles, { size: 14 }),
2726
- "\u672C\u8F6E\u5C0F\u7ED3"
2727
- ] }),
2728
- followup.recaption ? /* @__PURE__ */ jsx13("p", { className: "text-[13px] leading-5", children: followup.recaption }) : null,
2729
- artifacts.length > 0 ? /* @__PURE__ */ jsxs11(Fragment2, { children: [
2730
- /* @__PURE__ */ jsx13("div", { className: "grid max-w-full grid-cols-3 gap-1.5", children: visibleArtifacts.map((artifact, artifactIndex) => /* @__PURE__ */ jsx13(
2731
- ArtifactCard,
2732
- {
2733
- artifact,
2734
- sessionId,
2735
- assistantEntryId: followup.assistant_entry_id,
2736
- artifactIndex,
2737
- onInteraction,
2738
- onArtifactOpened: reportArtifactOpened
2739
- },
2740
- `${artifact.kind}:${artifactIndex}`
2741
- )) }),
2742
- artifacts.length > 3 ? /* @__PURE__ */ jsxs11(
2743
- "button",
2744
- {
2745
- type: "button",
2746
- onClick: () => setExpanded((value) => !value),
2747
- "aria-expanded": expanded,
2748
- className: "flex w-fit items-center gap-0.5 text-[11px] text-[hsl(var(--muted-foreground))]",
2749
- children: [
2750
- expanded ? "\u6536\u8D77" : `\u5C55\u5F00 ${artifacts.length - 3} \u4E2A`,
2751
- /* @__PURE__ */ jsx13(ChevronDown, { size: 13, className: expanded ? "rotate-180" : void 0 })
2752
- ]
2753
- }
2754
- ) : null
2755
- ] }) : null
2756
- ] }) : null,
2757
- followup.suggestions.length > 0 ? /* @__PURE__ */ jsxs11("section", { "aria-label": "\u4E0B\u4E00\u6B65\u5EFA\u8BAE", className: "flex flex-col gap-1.5", children: [
2758
- /* @__PURE__ */ jsx13("div", { className: "text-xs font-medium text-[hsl(var(--muted-foreground))]", children: "\u4E0B\u4E00\u6B65\u53EF\u4EE5" }),
2759
- followup.suggestions.map((suggestion, suggestionIndex) => /* @__PURE__ */ jsxs11(
2760
- "button",
2761
- {
2762
- type: "button",
2763
- disabled: isViewer,
2764
- onClick: () => {
2765
- if (!adopted.current.has(suggestionIndex)) {
2766
- adopted.current.add(suggestionIndex);
2767
- emitInteraction(onInteraction, {
2768
- type: "suggestion_adopted",
2769
- sessionId,
2770
- assistantEntryId: followup.assistant_entry_id,
2771
- suggestionIndex
2772
- });
2773
- }
2774
- onSuggestion?.(suggestion);
2775
- },
2776
- className: "group flex items-center gap-2 rounded-xl bg-[hsl(var(--muted)/0.62)] px-3 py-2 text-left text-[13px] disabled:cursor-default disabled:opacity-60",
2777
- children: [
2778
- /* @__PURE__ */ jsx13("span", { children: suggestion }),
2779
- /* @__PURE__ */ jsx13(ArrowRight, { size: 14, className: "ml-auto shrink-0" })
2780
- ]
2781
- },
2782
- suggestion
2783
- ))
2784
- ] }) : null,
2785
- /* @__PURE__ */ jsx13(
2786
- ResultFeedback,
2787
- {
2788
- followup,
2789
- sessionId,
2790
- isViewer,
2791
- onInteraction,
2792
- savedFeedback,
2793
- onFeedbackSaved
2794
- }
2795
- )
2796
- ] });
2797
- }
2798
-
2799
- // src/components/UserMessageBubble.tsx
2800
- import { getFileParts, getImageParts, getTextContent as getTextContent2 } from "@blade-hq/agent-client";
2801
- import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
2802
- function isUserMessage(message) {
2803
- return message.role === "user";
2804
- }
2805
- function isErrorMessage(message) {
2806
- return message.role === "error";
2807
- }
2808
- var isSending = (message) => message.status === "streaming";
2809
- function UserMessageBubble({ message, className }) {
2810
- const text = getTextContent2(message.content).trim();
2811
- const fileParts = getFileParts(message.content);
2812
- const imageParts = getImageParts(message.content);
2813
- return /* @__PURE__ */ jsx14("div", { className: cn("blade-chat-user-row flex justify-end", className), children: /* @__PURE__ */ jsxs12("div", { className: "blade-chat-user-col flex max-w-[72%] flex-col items-end gap-3", children: [
2814
- imageParts.length > 0 && /* @__PURE__ */ jsx14("div", { className: "grid gap-2", children: imageParts.map((part) => /* @__PURE__ */ jsx14(
2815
- "img",
1945
+ var isSending = (message) => message.status === "streaming";
1946
+ function UserMessageBubble({ message, className }) {
1947
+ const text = getTextContent2(message.content).trim();
1948
+ const fileParts = getFileParts(message.content);
1949
+ const imageParts = getImageParts(message.content);
1950
+ return /* @__PURE__ */ jsx12("div", { className: cn("blade-chat-user-row flex justify-end", className), children: /* @__PURE__ */ jsxs10("div", { className: "blade-chat-user-col flex max-w-[72%] flex-col items-end gap-3", children: [
1951
+ imageParts.length > 0 && /* @__PURE__ */ jsx12("div", { className: "grid gap-2", children: imageParts.map((part) => /* @__PURE__ */ jsx12(
1952
+ "img",
2816
1953
  {
2817
1954
  src: part.image_url.url,
2818
1955
  alt: "\u7528\u6237\u4E0A\u4F20\u7684\u56FE\u7247",
@@ -2820,21 +1957,21 @@ function UserMessageBubble({ message, className }) {
2820
1957
  },
2821
1958
  part.image_url.url
2822
1959
  )) }),
2823
- fileParts.length > 0 && /* @__PURE__ */ jsx14("div", { className: "flex flex-col items-end gap-1.5", children: fileParts.map((part) => /* @__PURE__ */ jsxs12(
1960
+ fileParts.length > 0 && /* @__PURE__ */ jsx12("div", { className: "flex flex-col items-end gap-1.5", children: fileParts.map((part) => /* @__PURE__ */ jsxs10(
2824
1961
  "div",
2825
1962
  {
2826
1963
  className: "flex items-center gap-1.5 rounded-lg border border-[hsl(var(--user-msg-border))] bg-[hsl(var(--muted)/0.3)] px-2.5 py-1.5 text-xs text-[hsl(var(--muted-foreground))]",
2827
1964
  children: [
2828
- /* @__PURE__ */ jsx14(FileText, { size: 12, className: "shrink-0" }),
2829
- /* @__PURE__ */ jsx14("span", { className: "max-w-56 truncate", title: part.name, children: part.name })
1965
+ /* @__PURE__ */ jsx12(FileText, { size: 12, className: "shrink-0" }),
1966
+ /* @__PURE__ */ jsx12("span", { className: "max-w-56 truncate", title: part.name, children: part.name })
2830
1967
  ]
2831
1968
  },
2832
1969
  `${part.name}-${part.data.length}`
2833
1970
  )) }),
2834
- text && /* @__PURE__ */ jsx14("div", { className: "blade-chat-user-bubble max-w-full rounded-[20px] rounded-br-[6px] border border-[hsl(var(--user-msg-border))] bg-[hsl(var(--user-msg-bg))] px-[18px] py-[13px] text-sm leading-[1.65] text-[hsl(var(--user-msg-fg))]", children: /* @__PURE__ */ jsx14(MarkdownContent, { className: "blade-chat-prose", children: text }) }),
2835
- text && isSending(message) && /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1 pr-1 text-[11px] font-medium text-[hsl(var(--muted-foreground))/0.85]", children: [
2836
- /* @__PURE__ */ jsx14(LoaderCircle, { size: 11, className: "animate-spin", "aria-hidden": "true" }),
2837
- /* @__PURE__ */ jsx14("span", { children: "\u53D1\u9001\u4E2D" })
1971
+ text && /* @__PURE__ */ jsx12("div", { className: "blade-chat-user-bubble max-w-full rounded-[20px] rounded-br-[6px] border border-[hsl(var(--user-msg-border))] bg-[hsl(var(--user-msg-bg))] px-[18px] py-[13px] text-sm leading-[1.65] text-[hsl(var(--user-msg-fg))]", children: /* @__PURE__ */ jsx12(MarkdownContent, { className: "blade-chat-prose", children: text }) }),
1972
+ text && isSending(message) && /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1 pr-1 text-[11px] font-medium text-[hsl(var(--muted-foreground))/0.85]", children: [
1973
+ /* @__PURE__ */ jsx12(LoaderCircle, { size: 11, className: "animate-spin", "aria-hidden": "true" }),
1974
+ /* @__PURE__ */ jsx12("span", { children: "\u53D1\u9001\u4E2D" })
2838
1975
  ] })
2839
1976
  ] }) });
2840
1977
  }
@@ -2843,11 +1980,11 @@ function ErrorMessageBlock({
2843
1980
  className
2844
1981
  }) {
2845
1982
  const text = getTextContent2(message.content);
2846
- return /* @__PURE__ */ jsx14("div", { className: cn("blade-chat-error-row flex justify-center", className), children: /* @__PURE__ */ jsx14("div", { className: "blade-chat-error-block max-w-[85%] border-l-[3px] border-[hsl(var(--border))] px-4 py-1 text-sm leading-7 text-[hsl(var(--muted-foreground))]", children: text }) });
1983
+ return /* @__PURE__ */ jsx12("div", { className: cn("blade-chat-error-row flex justify-center", className), children: /* @__PURE__ */ jsx12("div", { className: "blade-chat-error-block max-w-[85%] border-l-[3px] border-[hsl(var(--border))] px-4 py-1 text-sm leading-7 text-[hsl(var(--muted-foreground))]", children: text }) });
2847
1984
  }
2848
1985
 
2849
1986
  // src/components/MessageList.tsx
2850
- import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
1987
+ import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
2851
1988
  function parseModeChange(message) {
2852
1989
  if (message.kind !== "mode_change" || typeof message.content !== "string") {
2853
1990
  return null;
@@ -2881,8 +2018,6 @@ function getMessageResetSignature(messages) {
2881
2018
  }
2882
2019
  function MessageList({
2883
2020
  messages,
2884
- postChatFollowup,
2885
- onSuggestion,
2886
2021
  isStreaming,
2887
2022
  sessionStatus,
2888
2023
  askAnswers,
@@ -2890,13 +2025,9 @@ function MessageList({
2890
2025
  toolCallRenderer,
2891
2026
  emptyState,
2892
2027
  className,
2893
- sessionId,
2894
- isViewer = false,
2895
- onFollowupInteraction,
2896
- resultFeedbackByEntry = /* @__PURE__ */ new Map(),
2897
- onResultFeedbackSaved
2028
+ sessionId
2898
2029
  }) {
2899
- const renderBlocks = useMemo7(() => {
2030
+ const renderBlocks = useMemo6(() => {
2900
2031
  const visible = messages.filter((message) => {
2901
2032
  if ((message.loop_name ?? "root") !== "root") return false;
2902
2033
  if (isHiddenInternalMessage(message)) return false;
@@ -2942,7 +2073,7 @@ function MessageList({
2942
2073
  blocks.push({
2943
2074
  type: "message",
2944
2075
  message,
2945
- key: message.render_id ?? message.entry_id ?? `${message.role}-${blocks.length}`
2076
+ key: message.entry_id ?? `${message.role}-${blocks.length}`
2946
2077
  });
2947
2078
  }
2948
2079
  flushAssistant();
@@ -2969,86 +2100,63 @@ function MessageList({
2969
2100
  }
2970
2101
  return blocks;
2971
2102
  }, [messages, isStreaming]);
2972
- return /* @__PURE__ */ jsx15("div", { className: cn("blade-chat-messages relative min-h-0 flex-1", className), children: /* @__PURE__ */ jsxs13(StickToBottom, { className: "h-full overflow-y-hidden", initial: "instant", resize: "instant", children: [
2973
- /* @__PURE__ */ jsx15(StickToBottom.Content, { className: "blade-chat-messages-scroll", children: /* @__PURE__ */ jsx15("div", { className: "blade-chat-messages-content mx-auto max-w-[748px]", children: /* @__PURE__ */ jsxs13("div", { className: "flex min-w-0 flex-col", children: [
2974
- renderBlocks.length === 0 ? emptyState ?? /* @__PURE__ */ jsxs13("div", { className: "blade-chat-empty", children: [
2975
- /* @__PURE__ */ jsx15(MessageSquare, { size: 40, strokeWidth: 1.5 }),
2976
- /* @__PURE__ */ jsx15("span", { className: "text-base font-medium", children: "\u5F00\u59CB\u5BF9\u8BDD" }),
2977
- /* @__PURE__ */ jsx15("span", { className: "text-sm opacity-60", children: "\u5728\u4E0B\u65B9\u8F93\u5165\u6D88\u606F\u5F00\u59CB\u804A\u5929" })
2103
+ return /* @__PURE__ */ jsx13("div", { className: cn("blade-chat-messages relative min-h-0 flex-1", className), children: /* @__PURE__ */ jsxs11(StickToBottom, { className: "h-full overflow-y-hidden", initial: "instant", resize: "instant", children: [
2104
+ /* @__PURE__ */ jsx13(StickToBottom.Content, { className: "blade-chat-messages-scroll", children: /* @__PURE__ */ jsx13("div", { className: "blade-chat-messages-content mx-auto max-w-[748px]", children: /* @__PURE__ */ jsxs11("div", { className: "flex min-w-0 flex-col", children: [
2105
+ renderBlocks.length === 0 ? emptyState ?? /* @__PURE__ */ jsxs11("div", { className: "blade-chat-empty", children: [
2106
+ /* @__PURE__ */ jsx13(MessageSquare, { size: 40, strokeWidth: 1.5 }),
2107
+ /* @__PURE__ */ jsx13("span", { className: "text-base font-medium", children: "\u5F00\u59CB\u5BF9\u8BDD" }),
2108
+ /* @__PURE__ */ jsx13("span", { className: "text-sm opacity-60", children: "\u5728\u4E0B\u65B9\u8F93\u5165\u6D88\u606F\u5F00\u59CB\u804A\u5929" })
2978
2109
  ] }) : renderBlocks.map((block) => {
2979
2110
  if (block.type === "message") {
2980
- return /* @__PURE__ */ jsx15("div", { "data-entry-id": block.message.entry_id, children: isUserMessage(block.message) ? /* @__PURE__ */ jsx15(UserMessageBubble, { message: block.message }) : isErrorMessage(block.message) ? /* @__PURE__ */ jsx15(ErrorMessageBlock, { message: block.message }) : null }, block.key);
2111
+ return /* @__PURE__ */ jsx13("div", { "data-entry-id": block.message.entry_id, children: isUserMessage(block.message) ? /* @__PURE__ */ jsx13(UserMessageBubble, { message: block.message }) : isErrorMessage(block.message) ? /* @__PURE__ */ jsx13(ErrorMessageBlock, { message: block.message }) : null }, block.key);
2981
2112
  }
2982
2113
  if (block.type === "assistant_turn") {
2983
- const blockFeedback = block.messages.map(
2984
- (message) => message.entry_id ? resultFeedbackByEntry.get(message.entry_id) : void 0
2985
- ).find((feedback) => feedback != null);
2986
- const hasActiveFollowup = Boolean(
2987
- postChatFollowup && block.messages.some(
2988
- (message) => message.entry_id === postChatFollowup.assistant_entry_id
2989
- )
2990
- );
2991
- return /* @__PURE__ */ jsx15("div", { "data-entry-id": block.messages[0]?.entry_id, children: /* @__PURE__ */ jsxs13(
2114
+ return /* @__PURE__ */ jsx13("div", { "data-entry-id": block.messages[0]?.entry_id, children: /* @__PURE__ */ jsx13(
2992
2115
  RenderErrorBoundary,
2993
2116
  {
2994
2117
  label: "\u52A9\u624B\u6D88\u606F",
2995
2118
  details: block.key,
2996
2119
  resetKey: getMessageResetSignature(block.messages),
2997
- children: [
2998
- /* @__PURE__ */ jsx15(
2999
- AssistantTurnBlock,
3000
- {
3001
- messages: block.messages,
3002
- isStreaming: block.isStreaming,
3003
- askAnswers,
3004
- onAnswer,
3005
- sessionStatus,
3006
- toolCallRenderer,
3007
- sessionId
3008
- }
3009
- ),
3010
- blockFeedback && !hasActiveFollowup ? /* @__PURE__ */ jsx15(HistoricalResultFeedback, { feedback: blockFeedback }) : null,
3011
- hasActiveFollowup && postChatFollowup ? /* @__PURE__ */ jsx15(
3012
- PostChatFollowupBlock,
3013
- {
3014
- followup: postChatFollowup,
3015
- sessionId,
3016
- onSuggestion,
3017
- isViewer,
3018
- onInteraction: onFollowupInteraction,
3019
- savedFeedback: blockFeedback,
3020
- onFeedbackSaved: onResultFeedbackSaved
3021
- }
3022
- ) : null
3023
- ]
2120
+ children: /* @__PURE__ */ jsx13(
2121
+ AssistantTurnBlock,
2122
+ {
2123
+ messages: block.messages,
2124
+ isStreaming: block.isStreaming,
2125
+ askAnswers,
2126
+ onAnswer,
2127
+ sessionStatus,
2128
+ toolCallRenderer,
2129
+ sessionId
2130
+ }
2131
+ )
3024
2132
  }
3025
2133
  ) }, block.key);
3026
2134
  }
3027
2135
  if (block.type === "compaction") {
3028
- return /* @__PURE__ */ jsxs13(
2136
+ return /* @__PURE__ */ jsxs11(
3029
2137
  "div",
3030
2138
  {
3031
2139
  className: "flex items-center gap-2 text-xs text-[hsl(var(--muted-foreground))]",
3032
2140
  children: [
3033
- /* @__PURE__ */ jsx15(Layers, { size: 12 }),
3034
- /* @__PURE__ */ jsx15("span", { children: "\u4E0A\u4E0B\u6587\u5DF2\u538B\u7F29" })
2141
+ /* @__PURE__ */ jsx13(Layers, { size: 12 }),
2142
+ /* @__PURE__ */ jsx13("span", { children: "\u4E0A\u4E0B\u6587\u5DF2\u538B\u7F29" })
3035
2143
  ]
3036
2144
  },
3037
2145
  block.key
3038
2146
  );
3039
2147
  }
3040
- return /* @__PURE__ */ jsx15(PlanningDivider, { kind: block.kind }, block.key);
2148
+ return /* @__PURE__ */ jsx13(PlanningDivider, { kind: block.kind }, block.key);
3041
2149
  }),
3042
- sessionStatus === "interrupted" && !isStreaming ? /* @__PURE__ */ jsx15("div", { className: "flex", children: /* @__PURE__ */ jsx15("div", { className: "rounded-full border border-amber-500/30 bg-amber-500/10 px-2.5 py-1 text-[10px] font-medium uppercase tracking-[0.12em] text-amber-300", children: "\u5DF2\u4E2D\u65AD" }) }) : null
2150
+ sessionStatus === "interrupted" && !isStreaming ? /* @__PURE__ */ jsx13("div", { className: "flex", children: /* @__PURE__ */ jsx13("div", { className: "rounded-full border border-amber-500/30 bg-amber-500/10 px-2.5 py-1 text-[10px] font-medium uppercase tracking-[0.12em] text-amber-300", children: "\u5DF2\u4E2D\u65AD" }) }) : null
3043
2151
  ] }) }) }),
3044
- /* @__PURE__ */ jsx15(AutoScrollOnUserSend, { userMessageCount: messages.filter((m) => isUserMessage(m)).length }),
3045
- /* @__PURE__ */ jsx15(ScrollToBottomButton, {})
2152
+ /* @__PURE__ */ jsx13(AutoScrollOnUserSend, { userMessageCount: messages.filter((m) => isUserMessage(m)).length }),
2153
+ /* @__PURE__ */ jsx13(ScrollToBottomButton, {})
3046
2154
  ] }) });
3047
2155
  }
3048
2156
  function AutoScrollOnUserSend({ userMessageCount }) {
3049
2157
  const { scrollToBottom } = useStickToBottomContext();
3050
- const previousCountRef = useRef8(userMessageCount);
3051
- useEffect8(() => {
2158
+ const previousCountRef = useRef6(userMessageCount);
2159
+ useEffect6(() => {
3052
2160
  if (userMessageCount > previousCountRef.current) {
3053
2161
  scrollToBottom("instant");
3054
2162
  }
@@ -3058,9 +2166,9 @@ function AutoScrollOnUserSend({ userMessageCount }) {
3058
2166
  }
3059
2167
  function ScrollToBottomButton() {
3060
2168
  const { isAtBottom, scrollToBottom } = useStickToBottomContext();
3061
- const [visible, setVisible] = useState12(false);
3062
- const hideTimerRef = useRef8(null);
3063
- useEffect8(() => {
2169
+ const [visible, setVisible] = useState10(false);
2170
+ const hideTimerRef = useRef6(null);
2171
+ useEffect6(() => {
3064
2172
  if (isAtBottom) {
3065
2173
  if (!hideTimerRef.current) {
3066
2174
  hideTimerRef.current = setTimeout(() => {
@@ -3082,7 +2190,7 @@ function ScrollToBottomButton() {
3082
2190
  }
3083
2191
  };
3084
2192
  }, [isAtBottom]);
3085
- const handleClick = useCallback6(() => {
2193
+ const handleClick = useCallback3(() => {
3086
2194
  if (hideTimerRef.current) {
3087
2195
  clearTimeout(hideTimerRef.current);
3088
2196
  hideTimerRef.current = null;
@@ -3091,7 +2199,7 @@ function ScrollToBottomButton() {
3091
2199
  scrollToBottom();
3092
2200
  }, [scrollToBottom]);
3093
2201
  if (!visible) return null;
3094
- return /* @__PURE__ */ jsxs13(
2202
+ return /* @__PURE__ */ jsxs11(
3095
2203
  "button",
3096
2204
  {
3097
2205
  type: "button",
@@ -3099,120 +2207,34 @@ function ScrollToBottomButton() {
3099
2207
  "aria-label": "\u6EDA\u52A8\u5230\u5E95\u90E8",
3100
2208
  className: "blade-chat-scroll-bottom absolute bottom-4 right-4 flex items-center gap-1 rounded-full border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-3 py-1.5 text-xs text-[hsl(var(--muted-foreground))] shadow-lg transition-colors hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]",
3101
2209
  children: [
3102
- /* @__PURE__ */ jsx15(ChevronDown, { size: 14 }),
3103
- /* @__PURE__ */ jsx15("span", { className: "blade-chat-scroll-bottom-label", children: "\u6EDA\u52A8\u5230\u5E95\u90E8" })
2210
+ /* @__PURE__ */ jsx13(ChevronDown, { size: 14 }),
2211
+ /* @__PURE__ */ jsx13("span", { className: "blade-chat-scroll-bottom-label", children: "\u6EDA\u52A8\u5230\u5E95\u90E8" })
3104
2212
  ]
3105
2213
  }
3106
2214
  );
3107
2215
  }
3108
2216
  function PlanningDivider({ kind }) {
3109
- return /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-3 py-1", children: [
3110
- /* @__PURE__ */ jsx15("div", { className: "h-px flex-1 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent" }),
3111
- /* @__PURE__ */ jsxs13("div", { className: "inline-flex items-center gap-1.5 rounded-full border border-amber-500/30 bg-amber-500/10 px-3 py-1 text-[11px] text-amber-300", children: [
3112
- /* @__PURE__ */ jsx15(Lightbulb, { size: 12 }),
3113
- /* @__PURE__ */ jsx15("span", { children: kind === "enter" ? "\u8FDB\u5165\u89C4\u5212\u6A21\u5F0F" : "\u89C4\u5212\u5B8C\u6210" })
2217
+ return /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-3 py-1", children: [
2218
+ /* @__PURE__ */ jsx13("div", { className: "h-px flex-1 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent" }),
2219
+ /* @__PURE__ */ jsxs11("div", { className: "inline-flex items-center gap-1.5 rounded-full border border-amber-500/30 bg-amber-500/10 px-3 py-1 text-[11px] text-amber-300", children: [
2220
+ /* @__PURE__ */ jsx13(Lightbulb, { size: 12 }),
2221
+ /* @__PURE__ */ jsx13("span", { children: kind === "enter" ? "\u8FDB\u5165\u89C4\u5212\u6A21\u5F0F" : "\u89C4\u5212\u5B8C\u6210" })
3114
2222
  ] }),
3115
- /* @__PURE__ */ jsx15("div", { className: "h-px flex-1 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent" })
2223
+ /* @__PURE__ */ jsx13("div", { className: "h-px flex-1 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent" })
3116
2224
  ] });
3117
2225
  }
3118
2226
 
3119
- // src/components/ChatSurface.tsx
3120
- import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
2227
+ // src/components/ChatView.tsx
2228
+ import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
3121
2229
  function themeAttr(theme) {
3122
2230
  return theme === "dark" ? "dark" : void 0;
3123
2231
  }
3124
- function ChatSurface({
3125
- theme,
3126
- classNames,
3127
- renderers,
3128
- slots,
3129
- placeholder,
3130
- connection,
3131
- errorMessage,
3132
- messages,
3133
- postChatFollowup,
3134
- isStreaming,
3135
- isStopping,
3136
- inputText,
3137
- onInputChange,
3138
- onSuggestion,
3139
- onSend,
3140
- onStop,
3141
- sessionStatus,
3142
- askAnswers,
3143
- onAnswer,
3144
- sessionId,
3145
- isViewer,
3146
- resultFeedbackByEntry,
3147
- onResultFeedbackSaved,
3148
- onFollowupInteraction,
3149
- beforeInput,
3150
- banner
3151
- }) {
3152
- return /* @__PURE__ */ jsxs14(
3153
- "div",
3154
- {
3155
- "data-theme": themeAttr(theme),
3156
- className: cn(
3157
- "blade-chat flex min-h-0 flex-1 flex-col overflow-hidden",
3158
- classNames?.root
3159
- ),
3160
- children: [
3161
- /* @__PURE__ */ jsx16(ConnectionBanner, { connection, className: classNames?.banner }),
3162
- banner,
3163
- errorMessage && /* @__PURE__ */ jsxs14("div", { className: "blade-chat-error-bar flex items-start gap-2 border-b px-4 py-3 text-sm", children: [
3164
- /* @__PURE__ */ jsx16(CircleAlert, { size: 16, className: "mt-0.5 shrink-0" }),
3165
- /* @__PURE__ */ jsx16("span", { children: errorMessage })
3166
- ] }),
3167
- slots?.header,
3168
- /* @__PURE__ */ jsx16(
3169
- MessageList,
3170
- {
3171
- messages,
3172
- postChatFollowup,
3173
- onSuggestion,
3174
- isStreaming,
3175
- sessionStatus,
3176
- askAnswers,
3177
- onAnswer,
3178
- toolCallRenderer: renderers?.toolCall,
3179
- emptyState: slots?.emptyState,
3180
- className: classNames?.messageList,
3181
- sessionId,
3182
- isViewer,
3183
- resultFeedbackByEntry,
3184
- onResultFeedbackSaved,
3185
- onFollowupInteraction
3186
- }
3187
- ),
3188
- beforeInput,
3189
- /* @__PURE__ */ jsx16(
3190
- ChatInput,
3191
- {
3192
- value: inputText,
3193
- onValueChange: onInputChange,
3194
- onSend,
3195
- onStop,
3196
- isStreaming,
3197
- isStopping,
3198
- placeholder,
3199
- className: classNames?.chatInput
3200
- }
3201
- ),
3202
- slots?.footer
3203
- ]
3204
- }
3205
- );
3206
- }
3207
-
3208
- // src/components/AgentChat.tsx
3209
- import { Fragment as Fragment3, jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
3210
2232
  function isUnauthorizedError(error) {
3211
2233
  return error instanceof BladeApiError && error.status === 401;
3212
2234
  }
3213
2235
  function LoginCard({ client, onLoggedIn }) {
3214
- const [loggingIn, setLoggingIn] = useState13(false);
3215
- const [loginError, setLoginError] = useState13(null);
2236
+ const [loggingIn, setLoggingIn] = useState11(false);
2237
+ const [loginError, setLoginError] = useState11(null);
3216
2238
  const handleLogin = async () => {
3217
2239
  setLoggingIn(true);
3218
2240
  setLoginError(null);
@@ -3225,11 +2247,11 @@ function LoginCard({ client, onLoggedIn }) {
3225
2247
  setLoggingIn(false);
3226
2248
  }
3227
2249
  };
3228
- return /* @__PURE__ */ jsx17("div", { className: "blade-chat-login flex flex-1 items-center justify-center p-6", children: /* @__PURE__ */ jsxs15("div", { className: "flex w-full max-w-sm flex-col items-center gap-4 rounded-2xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-6 py-8 text-center", children: [
3229
- /* @__PURE__ */ jsx17(LockKeyhole, { size: 28, className: "text-[hsl(var(--muted-foreground))]" }),
3230
- /* @__PURE__ */ jsx17("div", { className: "text-base font-medium text-[hsl(var(--foreground))]", children: "\u9700\u8981\u767B\u5F55\u540E\u4F7F\u7528" }),
3231
- /* @__PURE__ */ jsx17("div", { className: "text-sm text-[hsl(var(--muted-foreground))]", children: "\u767B\u5F55\u540E\u5373\u53EF\u4E0E\u667A\u80FD\u4F53\u5BF9\u8BDD\uFF0C\u4F60\u7684\u4F1A\u8BDD\u5185\u5BB9\u4EC5\u81EA\u5DF1\u53EF\u89C1\u3002" }),
3232
- /* @__PURE__ */ jsx17(
2250
+ return /* @__PURE__ */ jsx14("div", { className: "blade-chat-login flex flex-1 items-center justify-center p-6", children: /* @__PURE__ */ jsxs12("div", { className: "flex w-full max-w-sm flex-col items-center gap-4 rounded-2xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-6 py-8 text-center", children: [
2251
+ /* @__PURE__ */ jsx14(LockKeyhole, { size: 28, className: "text-[hsl(var(--muted-foreground))]" }),
2252
+ /* @__PURE__ */ jsx14("div", { className: "text-base font-medium text-[hsl(var(--foreground))]", children: "\u9700\u8981\u767B\u5F55\u540E\u4F7F\u7528" }),
2253
+ /* @__PURE__ */ jsx14("div", { className: "text-sm text-[hsl(var(--muted-foreground))]", children: "\u767B\u5F55\u540E\u5373\u53EF\u4E0E\u667A\u80FD\u4F53\u5BF9\u8BDD\uFF0C\u4F60\u7684\u4F1A\u8BDD\u5185\u5BB9\u4EC5\u81EA\u5DF1\u53EF\u89C1\u3002" }),
2254
+ /* @__PURE__ */ jsx14(
3233
2255
  "button",
3234
2256
  {
3235
2257
  type: "button",
@@ -3239,20 +2261,20 @@ function LoginCard({ client, onLoggedIn }) {
3239
2261
  children: loggingIn ? "\u767B\u5F55\u4E2D\u2026" : "\u767B\u5F55"
3240
2262
  }
3241
2263
  ),
3242
- loginError && /* @__PURE__ */ jsx17("div", { className: "text-xs text-[hsl(var(--destructive))]", children: loginError })
2264
+ loginError && /* @__PURE__ */ jsx14("div", { className: "text-xs text-[hsl(var(--destructive))]", children: loginError })
3243
2265
  ] }) });
3244
2266
  }
3245
- function AgentChat(props) {
2267
+ function ChatView(props) {
3246
2268
  const client = useBladeClient();
3247
- const [attempt, setAttempt] = useState13(0);
3248
- const [needLogin, setNeedLogin] = useState13(() => !client.hasToken());
2269
+ const [attempt, setAttempt] = useState11(0);
2270
+ const [needLogin, setNeedLogin] = useState11(() => !client.hasToken());
3249
2271
  if (needLogin) {
3250
- return /* @__PURE__ */ jsx17(
2272
+ return /* @__PURE__ */ jsx14(
3251
2273
  "div",
3252
2274
  {
3253
2275
  "data-theme": themeAttr(props.theme),
3254
2276
  className: cn("blade-chat flex min-h-0 flex-1 flex-col", props.classNames?.root),
3255
- children: /* @__PURE__ */ jsx17(
2277
+ children: /* @__PURE__ */ jsx14(
3256
2278
  LoginCard,
3257
2279
  {
3258
2280
  client,
@@ -3265,7 +2287,7 @@ function AgentChat(props) {
3265
2287
  }
3266
2288
  );
3267
2289
  }
3268
- return /* @__PURE__ */ jsx17(ChatSessionView, { ...props, onUnauthorized: () => setNeedLogin(true) }, attempt);
2290
+ return /* @__PURE__ */ jsx14(ChatSessionView, { ...props, onUnauthorized: () => setNeedLogin(true) }, attempt);
3269
2291
  }
3270
2292
  function ChatSessionView({
3271
2293
  sessionId,
@@ -3278,61 +2300,20 @@ function ChatSessionView({
3278
2300
  slots,
3279
2301
  placeholder,
3280
2302
  theme,
3281
- onFollowupInteraction,
3282
2303
  onUnauthorized
3283
2304
  }) {
3284
- const client = useBladeClient();
3285
2305
  const { session, state, error } = useAgentSession(sessionId, {
3286
2306
  createOptions,
3287
2307
  onSessionCreated
3288
2308
  });
3289
- const replay = useReplay(session);
3290
- const [stopRequested, setStopRequested] = useState13(false);
3291
- const [inputText, setInputText] = useState13("");
3292
- const [resultFeedback, setResultFeedback] = useState13([]);
3293
- const resolvedSessionId = session?.sessionId;
3294
- const isViewer = state?.viewerRole === "viewer";
3295
- useEffect9(() => {
3296
- setResultFeedback([]);
3297
- if (!resolvedSessionId || isViewer) return;
3298
- let cancelled = false;
3299
- void client.sessions.listResultFeedback(resolvedSessionId).then(
3300
- (items) => {
3301
- if (cancelled) return;
3302
- setResultFeedback((current) => {
3303
- const merged = new Map(items.map((item) => [item.assistant_entry_id, item]));
3304
- for (const item of current) {
3305
- const fetched = merged.get(item.assistant_entry_id);
3306
- if (!fetched || fetched.updated_at < item.updated_at) {
3307
- merged.set(item.assistant_entry_id, item);
3308
- }
3309
- }
3310
- return [...merged.values()];
3311
- });
3312
- },
3313
- () => {
3314
- }
3315
- );
3316
- return () => {
3317
- cancelled = true;
3318
- };
3319
- }, [client, isViewer, resolvedSessionId]);
3320
- const resultFeedbackByEntry = useMemo8(
3321
- () => new Map(resultFeedback.map((item) => [item.assistant_entry_id, item])),
3322
- [resultFeedback]
3323
- );
3324
- const handleResultFeedbackSaved = useCallback7((saved) => {
3325
- setResultFeedback((current) => [
3326
- ...current.filter((item) => item.assistant_entry_id !== saved.assistant_entry_id),
3327
- saved
3328
- ]);
3329
- }, []);
3330
- useEffect9(() => {
2309
+ const [stopRequested, setStopRequested] = useState11(false);
2310
+ const [inputText, setInputText] = useState11("");
2311
+ useEffect7(() => {
3331
2312
  if (session) {
3332
2313
  onSessionReady?.(session);
3333
2314
  }
3334
2315
  }, [session, onSessionReady]);
3335
- useEffect9(() => {
2316
+ useEffect7(() => {
3336
2317
  if (!session) return;
3337
2318
  const offAttach = session.on("attachRequested", ({ label, content }) => {
3338
2319
  setInputText((prev) => `${prev ? `${prev}
@@ -3348,12 +2329,12 @@ ${content}`);
3348
2329
  offInsert();
3349
2330
  };
3350
2331
  }, [session]);
3351
- useEffect9(() => {
2332
+ useEffect7(() => {
3352
2333
  if (isUnauthorizedError(error)) {
3353
2334
  onUnauthorized();
3354
2335
  }
3355
2336
  }, [error, onUnauthorized]);
3356
- useEffect9(() => {
2337
+ useEffect7(() => {
3357
2338
  if (!session || !commands) return;
3358
2339
  const unsubscribes = Object.entries(commands).map(
3359
2340
  ([action, handler]) => session.onCommand(action, (payload) => handler(payload))
@@ -3365,7 +2346,7 @@ ${content}`);
3365
2346
  const isStreaming = state?.isStreaming ?? false;
3366
2347
  const isStopping = stopRequested && isStreaming;
3367
2348
  const connectError = error && !isUnauthorizedError(error) ? error.message || "\u8FDE\u63A5\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5" : null;
3368
- const errorMessage = connectError ?? state?.errorMessage ?? replay.error?.message ?? null;
2349
+ const errorMessage = connectError ?? state?.errorMessage ?? null;
3369
2350
  const handleSend = (text) => {
3370
2351
  setStopRequested(false);
3371
2352
  return session?.send(text, { mode: state?.mode ?? void 0 }) ?? Promise.resolve(false);
@@ -3374,287 +2355,73 @@ ${content}`);
3374
2355
  setStopRequested(true);
3375
2356
  void session?.stop();
3376
2357
  };
3377
- return /* @__PURE__ */ jsx17(
3378
- ChatSurface,
2358
+ return /* @__PURE__ */ jsxs12(
2359
+ "div",
3379
2360
  {
3380
- theme,
3381
- onFollowupInteraction,
3382
- classNames,
3383
- renderers,
3384
- slots,
3385
- placeholder,
3386
- connection: state?.connection ?? "connecting",
3387
- banner: /* @__PURE__ */ jsxs15(Fragment3, { children: [
3388
- /* @__PURE__ */ jsx17(
3389
- ReplayBar,
2361
+ "data-theme": themeAttr(theme),
2362
+ className: cn(
2363
+ "blade-chat flex min-h-0 flex-1 flex-col overflow-hidden",
2364
+ classNames?.root
2365
+ ),
2366
+ children: [
2367
+ /* @__PURE__ */ jsx14(
2368
+ ConnectionBanner,
3390
2369
  {
3391
- isReplay: replay.isReplay,
3392
- speed: replay.speed,
3393
- canControl: replay.canControl,
3394
- onSpeedChange: (next) => void replay.setSpeed(next),
3395
- onExit: () => void replay.exitToAutonomous()
2370
+ connection: state?.connection ?? "connecting",
2371
+ className: classNames?.banner
3396
2372
  }
3397
2373
  ),
3398
- /* @__PURE__ */ jsx17(ReplayMismatchPrompt, { mismatch: replay.mismatch })
3399
- ] }),
3400
- errorMessage,
3401
- messages: state?.messages ?? [],
3402
- postChatFollowup: latestPostChatFollowup(state?.turns ?? []),
3403
- resultFeedbackByEntry,
3404
- onResultFeedbackSaved: handleResultFeedbackSaved,
3405
- isStreaming,
3406
- isStopping,
3407
- inputText,
3408
- onInputChange: setInputText,
3409
- onSuggestion: setInputText,
3410
- onSend: handleSend,
3411
- onStop: handleStop,
3412
- sessionStatus: state?.status ?? void 0,
3413
- askAnswers: state?.askAnswers,
3414
- onAnswer: (answer, toolCallId, answerData) => {
3415
- void session?.send(answer, {
3416
- mode: state?.mode ?? void 0,
3417
- askUserAnswer: { tool_call_id: toolCallId, ...answerData }
3418
- });
3419
- },
3420
- sessionId: resolvedSessionId,
3421
- isViewer
3422
- }
3423
- );
3424
- }
3425
-
3426
- // src/components/LlmChat.tsx
3427
- import { useEffect as useEffect10, useMemo as useMemo9, useState as useState15 } from "react";
3428
-
3429
- // src/components/LlmAdvancedSettings.tsx
3430
- import { useState as useState14 } from "react";
3431
- import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
3432
- var FIELDS = [
3433
- { id: "baseURL", label: "\u6A21\u578B\u670D\u52A1\u5730\u5740", placeholder: "http://\u5185\u7F51\u5730\u5740/v1" },
3434
- { id: "model", label: "\u6A21\u578B", placeholder: "\u6A21\u578B\u540D\u79F0" },
3435
- { id: "apiKey", label: "\u5BC6\u94A5", placeholder: "\u7559\u7A7A\u8868\u793A\u4E0D\u6539", secret: true }
3436
- ];
3437
- function normalizeAdvanced(value) {
3438
- if (!value) return null;
3439
- const config = value === true ? {} : value;
3440
- return {
3441
- baseURL: config.baseURL ?? true,
3442
- model: config.model ?? true,
3443
- apiKey: config.apiKey ?? false,
3444
- storage: config.storage ?? "local"
3445
- };
3446
- }
3447
- function storageKeyFor(settings, baseURL) {
3448
- const explicit = typeof settings === "object" ? settings.storageKey : void 0;
3449
- return `blade-llm-override:${explicit ?? baseURL}`;
3450
- }
3451
- function readOverride(settings, baseURL) {
3452
- const normalized = normalizeAdvanced(settings);
3453
- if (!normalized || normalized.storage !== "local" || typeof localStorage === "undefined") return {};
3454
- try {
3455
- const raw = localStorage.getItem(storageKeyFor(settings, baseURL));
3456
- if (!raw) return {};
3457
- const stored = JSON.parse(raw);
3458
- return Object.fromEntries(
3459
- Object.entries(stored).filter(([key]) => normalized[key])
3460
- );
3461
- } catch {
3462
- return {};
3463
- }
3464
- }
3465
- function writeOverride(settings, baseURL, override) {
3466
- const normalized = normalizeAdvanced(settings);
3467
- if (!normalized || normalized.storage !== "local" || typeof localStorage === "undefined") return;
3468
- try {
3469
- const key = storageKeyFor(settings, baseURL);
3470
- if (Object.keys(override).length === 0) localStorage.removeItem(key);
3471
- else localStorage.setItem(key, JSON.stringify(override));
3472
- } catch {
3473
- }
3474
- }
3475
- function LlmAdvancedSettingsBar({ settings, defaults, override, onChange }) {
3476
- const normalized = normalizeAdvanced(settings);
3477
- const [open, setOpen] = useState14(false);
3478
- const [draft, setDraft] = useState14(override);
3479
- if (!normalized) return null;
3480
- const fields = FIELDS.filter((field) => normalized[field.id]);
3481
- const dirty = Object.keys(override).length > 0;
3482
- return /* @__PURE__ */ jsxs16("div", { className: "blade-chat-advanced border-t border-[hsl(var(--border))] px-4 py-2 text-xs", children: [
3483
- /* @__PURE__ */ jsxs16(
3484
- "button",
3485
- {
3486
- type: "button",
3487
- onClick: () => {
3488
- setDraft(override);
3489
- setOpen((value) => !value);
3490
- },
3491
- className: "flex items-center gap-1.5 text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))]",
3492
- children: [
3493
- /* @__PURE__ */ jsx18(Settings2, { size: 13 }),
3494
- "\u9AD8\u7EA7\u8BBE\u7F6E",
3495
- dirty && /* @__PURE__ */ jsx18("span", { className: "text-[hsl(var(--primary))]", children: "\uFF08\u5DF2\u81EA\u5B9A\u4E49\uFF09" })
3496
- ]
3497
- }
3498
- ),
3499
- open && /* @__PURE__ */ jsxs16("div", { className: "mt-2 flex flex-col gap-2", children: [
3500
- fields.map((field) => /* @__PURE__ */ jsxs16("label", { className: "flex flex-col gap-1", children: [
3501
- /* @__PURE__ */ jsx18("span", { className: "text-[hsl(var(--muted-foreground))]", children: field.label }),
3502
- /* @__PURE__ */ jsx18(
3503
- "input",
3504
- {
3505
- type: field.secret ? "password" : "text",
3506
- value: draft[field.id] ?? "",
3507
- placeholder: field.id === "apiKey" ? field.placeholder : defaults[field.id] || field.placeholder,
3508
- onChange: (event) => setDraft({ ...draft, [field.id]: event.target.value }),
3509
- className: "rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--background))] px-2 py-1 text-xs text-[hsl(var(--foreground))] outline-none"
3510
- }
3511
- )
3512
- ] }, field.id)),
3513
- normalized.apiKey && /* @__PURE__ */ jsx18("p", { className: "text-[hsl(var(--muted-foreground))]", children: "\u5BC6\u94A5\u4F1A\u5B58\u5728\u8FD9\u53F0\u6D4F\u89C8\u5668\u91CC\u3002\u53EA\u5728\u4F60\u4FE1\u5F97\u8FC7\u8FD9\u53F0\u673A\u5668\u65F6\u586B\u3002" }),
3514
- /* @__PURE__ */ jsxs16("div", { className: "flex gap-2", children: [
3515
- /* @__PURE__ */ jsx18(
3516
- "button",
2374
+ errorMessage && /* @__PURE__ */ jsxs12("div", { className: "blade-chat-error-bar flex items-start gap-2 border-b px-4 py-3 text-sm", children: [
2375
+ /* @__PURE__ */ jsx14(CircleAlert, { size: 16, className: "mt-0.5 shrink-0" }),
2376
+ /* @__PURE__ */ jsx14("span", { children: errorMessage })
2377
+ ] }),
2378
+ slots?.header,
2379
+ /* @__PURE__ */ jsx14(
2380
+ MessageList,
3517
2381
  {
3518
- type: "button",
3519
- onClick: () => {
3520
- const next = Object.fromEntries(
3521
- Object.entries(draft).filter(([, value]) => String(value ?? "").trim() !== "")
3522
- );
3523
- onChange(next);
3524
- setOpen(false);
2382
+ messages: state?.messages ?? [],
2383
+ isStreaming,
2384
+ sessionStatus: state?.status ?? void 0,
2385
+ askAnswers: state?.askAnswers,
2386
+ onAnswer: (answer, toolCallId, answerData) => {
2387
+ void session?.send(answer, {
2388
+ mode: state?.mode ?? void 0,
2389
+ askUserAnswer: { tool_call_id: toolCallId, ...answerData }
2390
+ });
3525
2391
  },
3526
- className: "rounded-md bg-[hsl(var(--primary))] px-2.5 py-1 font-medium text-[hsl(var(--primary-foreground))]",
3527
- children: "\u4FDD\u5B58"
2392
+ toolCallRenderer: renderers?.toolCall,
2393
+ emptyState: slots?.emptyState,
2394
+ className: classNames?.messageList,
2395
+ sessionId: session?.sessionId
3528
2396
  }
3529
2397
  ),
3530
- /* @__PURE__ */ jsx18(
3531
- "button",
2398
+ /* @__PURE__ */ jsx14(
2399
+ ChatInput,
3532
2400
  {
3533
- type: "button",
3534
- onClick: () => {
3535
- setDraft({});
3536
- onChange({});
3537
- setOpen(false);
3538
- },
3539
- className: "rounded-md border border-[hsl(var(--border))] px-2.5 py-1 text-[hsl(var(--foreground))]",
3540
- children: "\u6062\u590D\u9ED8\u8BA4"
3541
- }
3542
- )
3543
- ] })
3544
- ] })
3545
- ] });
3546
- }
3547
-
3548
- // src/components/LlmChat.tsx
3549
- import { jsx as jsx19 } from "react/jsx-runtime";
3550
- function LlmChat({
3551
- classNames,
3552
- renderers,
3553
- slots,
3554
- placeholder,
3555
- theme,
3556
- onReady,
3557
- advanced,
3558
- onOverrideChange,
3559
- ...options
3560
- }) {
3561
- const [override, setOverride] = useState15(() => readOverride(advanced, options.baseURL));
3562
- const effective = { ...options, ...override };
3563
- const { messages, isStreaming, error, send, stop, reset } = useLlmChat(effective);
3564
- const [inputText, setInputText] = useState15("");
3565
- const [stopRequested, setStopRequested] = useState15(false);
3566
- const handle = useMemo9(
3567
- () => ({
3568
- insertText: (text) => setInputText((prev) => prev ? `${prev}
3569
- ${text}` : text),
3570
- send,
3571
- reset
3572
- }),
3573
- [send, reset]
3574
- );
3575
- useEffect10(() => {
3576
- onReady?.(handle);
3577
- }, [handle, onReady]);
3578
- return /* @__PURE__ */ jsx19(
3579
- ChatSurface,
3580
- {
3581
- theme,
3582
- classNames,
3583
- renderers,
3584
- slots,
3585
- placeholder,
3586
- connection: "connected",
3587
- errorMessage: error,
3588
- messages,
3589
- isStreaming,
3590
- isStopping: stopRequested && isStreaming,
3591
- inputText,
3592
- onInputChange: setInputText,
3593
- onSend: async (text) => {
3594
- setStopRequested(false);
3595
- if (!text.trim() || isStreaming) return false;
3596
- void send(text);
3597
- return true;
3598
- },
3599
- onStop: () => {
3600
- setStopRequested(true);
3601
- stop();
3602
- },
3603
- beforeInput: advanced ? /* @__PURE__ */ jsx19(
3604
- LlmAdvancedSettingsBar,
3605
- {
3606
- settings: advanced,
3607
- defaults: { baseURL: options.baseURL, model: options.model },
3608
- override,
3609
- onChange: (next) => {
3610
- setOverride(next);
3611
- writeOverride(advanced, options.baseURL, next);
3612
- onOverrideChange?.(next);
2401
+ value: inputText,
2402
+ onValueChange: setInputText,
2403
+ onSend: handleSend,
2404
+ onStop: handleStop,
2405
+ isStreaming,
2406
+ isStopping,
2407
+ placeholder,
2408
+ className: classNames?.chatInput
3613
2409
  }
3614
- }
3615
- ) : void 0
2410
+ ),
2411
+ slots?.footer
2412
+ ]
3616
2413
  }
3617
2414
  );
3618
2415
  }
3619
2416
 
3620
- // src/components/ChatView.tsx
3621
- import { jsx as jsx20 } from "react/jsx-runtime";
3622
- function ChatView(props) {
3623
- const { mode, llm, onLlmReady, ...rest } = props;
3624
- if (mode === "llm") {
3625
- if (!llm) {
3626
- throw new Error('ChatView: mode="llm" \u9700\u8981\u540C\u65F6\u4F20 llm={{ baseURL, model }}');
3627
- }
3628
- return /* @__PURE__ */ jsx20(
3629
- LlmChat,
3630
- {
3631
- ...llm,
3632
- classNames: rest.classNames,
3633
- renderers: rest.renderers,
3634
- slots: rest.slots,
3635
- placeholder: rest.placeholder,
3636
- theme: rest.theme,
3637
- onReady: onLlmReady
3638
- }
3639
- );
3640
- }
3641
- return /* @__PURE__ */ jsx20(AgentChat, { ...rest });
3642
- }
3643
-
3644
2417
  // src/index.ts
3645
2418
  export * from "@blade-hq/agent-client";
3646
2419
  export {
3647
- AgentChat,
3648
2420
  BladeProvider,
3649
2421
  ChatView,
3650
- LlmChat,
3651
2422
  MarkdownContent,
3652
- ReplayBar,
3653
- ReplayMismatchPrompt,
3654
2423
  useAgentSession,
3655
- useBladeClient,
3656
- useLlmChat,
3657
- useReplay
2424
+ useBladeClient
3658
2425
  };
3659
2426
  /*! Bundled license information:
3660
2427
 
@@ -3662,8 +2429,6 @@ lucide-react/dist/esm/shared/src/utils.js:
3662
2429
  lucide-react/dist/esm/defaultAttributes.js:
3663
2430
  lucide-react/dist/esm/Icon.js:
3664
2431
  lucide-react/dist/esm/createLucideIcon.js:
3665
- lucide-react/dist/esm/icons/arrow-right.js:
3666
- lucide-react/dist/esm/icons/arrow-up-right.js:
3667
2432
  lucide-react/dist/esm/icons/arrow-up.js:
3668
2433
  lucide-react/dist/esm/icons/bot.js:
3669
2434
  lucide-react/dist/esm/icons/brain.js:
@@ -3673,16 +2438,12 @@ lucide-react/dist/esm/icons/chevron-right.js:
3673
2438
  lucide-react/dist/esm/icons/circle-alert.js:
3674
2439
  lucide-react/dist/esm/icons/copy.js:
3675
2440
  lucide-react/dist/esm/icons/file-text.js:
3676
- lucide-react/dist/esm/icons/globe.js:
3677
2441
  lucide-react/dist/esm/icons/layers.js:
3678
2442
  lucide-react/dist/esm/icons/lightbulb.js:
3679
2443
  lucide-react/dist/esm/icons/loader-circle.js:
3680
2444
  lucide-react/dist/esm/icons/lock-keyhole.js:
3681
2445
  lucide-react/dist/esm/icons/message-square-more.js:
3682
2446
  lucide-react/dist/esm/icons/message-square.js:
3683
- lucide-react/dist/esm/icons/play.js:
3684
- lucide-react/dist/esm/icons/settings-2.js:
3685
- lucide-react/dist/esm/icons/sparkles.js:
3686
2447
  lucide-react/dist/esm/icons/square.js:
3687
2448
  lucide-react/dist/esm/icons/triangle-alert.js:
3688
2449
  lucide-react/dist/esm/icons/x.js: