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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -99,7 +99,452 @@ function useAgentSession(sessionId, options = {}) {
99
99
  return { session, state, error };
100
100
  }
101
101
 
102
- // src/components/ChatView.tsx
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
103
548
  import { BladeApiError } from "@blade-hq/agent-client";
104
549
 
105
550
  // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/createLucideIcon.js
@@ -173,6 +618,13 @@ var createLucideIcon = (iconName, iconNode) => {
173
618
  return Component2;
174
619
  };
175
620
 
621
+ // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/archive.js
622
+ var Archive = createLucideIcon("Archive", [
623
+ ["rect", { width: "20", height: "5", x: "2", y: "3", rx: "1", key: "1wp1u1" }],
624
+ ["path", { d: "M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8", key: "1s80jp" }],
625
+ ["path", { d: "M10 12h4", key: "a56b0p" }]
626
+ ]);
627
+
176
628
  // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/arrow-up.js
177
629
  var ArrowUp = createLucideIcon("ArrowUp", [
178
630
  ["path", { d: "m5 12 7-7 7 7", key: "hav0vg" }],
@@ -240,6 +692,31 @@ var Copy = createLucideIcon("Copy", [
240
692
  ["path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2", key: "zix9uf" }]
241
693
  ]);
242
694
 
695
+ // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/download.js
696
+ var Download = createLucideIcon("Download", [
697
+ ["path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4", key: "ih7n3h" }],
698
+ ["polyline", { points: "7 10 12 15 17 10", key: "2ggqvy" }],
699
+ ["line", { x1: "12", x2: "12", y1: "15", y2: "3", key: "1vk2je" }]
700
+ ]);
701
+
702
+ // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/file-code-2.js
703
+ var FileCode2 = createLucideIcon("FileCode2", [
704
+ ["path", { d: "M4 22h14a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v4", key: "1pf5j1" }],
705
+ ["path", { d: "M14 2v4a2 2 0 0 0 2 2h4", key: "tnqrlb" }],
706
+ ["path", { d: "m5 12-3 3 3 3", key: "oke12k" }],
707
+ ["path", { d: "m9 18 3-3-3-3", key: "112psh" }]
708
+ ]);
709
+
710
+ // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/file-spreadsheet.js
711
+ var FileSpreadsheet = createLucideIcon("FileSpreadsheet", [
712
+ ["path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z", key: "1rqfz7" }],
713
+ ["path", { d: "M14 2v4a2 2 0 0 0 2 2h4", key: "tnqrlb" }],
714
+ ["path", { d: "M8 13h2", key: "yr2amv" }],
715
+ ["path", { d: "M14 13h2", key: "un5t4a" }],
716
+ ["path", { d: "M8 17h2", key: "2yhykz" }],
717
+ ["path", { d: "M14 17h2", key: "10kma7" }]
718
+ ]);
719
+
243
720
  // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/file-text.js
244
721
  var FileText = createLucideIcon("FileText", [
245
722
  ["path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z", key: "1rqfz7" }],
@@ -249,6 +726,31 @@ var FileText = createLucideIcon("FileText", [
249
726
  ["path", { d: "M16 17H8", key: "z1uh3a" }]
250
727
  ]);
251
728
 
729
+ // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/file.js
730
+ var File = createLucideIcon("File", [
731
+ ["path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z", key: "1rqfz7" }],
732
+ ["path", { d: "M14 2v4a2 2 0 0 0 2 2h4", key: "tnqrlb" }]
733
+ ]);
734
+
735
+ // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/film.js
736
+ var Film = createLucideIcon("Film", [
737
+ ["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }],
738
+ ["path", { d: "M7 3v18", key: "bbkbws" }],
739
+ ["path", { d: "M3 7.5h4", key: "zfgn84" }],
740
+ ["path", { d: "M3 12h18", key: "1i2n21" }],
741
+ ["path", { d: "M3 16.5h4", key: "1230mu" }],
742
+ ["path", { d: "M17 3v18", key: "in4fa5" }],
743
+ ["path", { d: "M17 7.5h4", key: "myr1c1" }],
744
+ ["path", { d: "M17 16.5h4", key: "go4c1d" }]
745
+ ]);
746
+
747
+ // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/image.js
748
+ var Image = createLucideIcon("Image", [
749
+ ["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2", key: "1m3agn" }],
750
+ ["circle", { cx: "9", cy: "9", r: "2", key: "af1f0g" }],
751
+ ["path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21", key: "1xmnt7" }]
752
+ ]);
753
+
252
754
  // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/layers.js
253
755
  var Layers = createLucideIcon("Layers", [
254
756
  [
@@ -312,6 +814,26 @@ var MessageSquare = createLucideIcon("MessageSquare", [
312
814
  ["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" }]
313
815
  ]);
314
816
 
817
+ // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/music.js
818
+ var Music = createLucideIcon("Music", [
819
+ ["path", { d: "M9 18V5l12-2v13", key: "1jmyc2" }],
820
+ ["circle", { cx: "6", cy: "18", r: "3", key: "fqmcym" }],
821
+ ["circle", { cx: "18", cy: "16", r: "3", key: "1hluhg" }]
822
+ ]);
823
+
824
+ // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/play.js
825
+ var Play = createLucideIcon("Play", [
826
+ ["polygon", { points: "6 3 20 12 6 21 6 3", key: "1oa8hb" }]
827
+ ]);
828
+
829
+ // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/settings-2.js
830
+ var Settings2 = createLucideIcon("Settings2", [
831
+ ["path", { d: "M20 7h-9", key: "3s1dr2" }],
832
+ ["path", { d: "M14 17H5", key: "gfn3mx" }],
833
+ ["circle", { cx: "17", cy: "17", r: "3", key: "18b49y" }],
834
+ ["circle", { cx: "7", cy: "7", r: "3", key: "dfmy0x" }]
835
+ ]);
836
+
315
837
  // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/square.js
316
838
  var Square = createLucideIcon("Square", [
317
839
  ["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }]
@@ -336,8 +858,8 @@ var X = createLucideIcon("X", [
336
858
  ["path", { d: "m6 6 12 12", key: "d8bk6v" }]
337
859
  ]);
338
860
 
339
- // src/components/ChatView.tsx
340
- import { useEffect as useEffect7, useState as useState11 } from "react";
861
+ // src/components/AgentChat.tsx
862
+ import { useEffect as useEffect7, useState as useState12 } from "react";
341
863
 
342
864
  // src/lib/utils.ts
343
865
  function cn(...inputs) {
@@ -352,8 +874,120 @@ async function copyToClipboard(text) {
352
874
  }
353
875
  }
354
876
 
355
- // src/components/ChatInput.tsx
877
+ // src/components/ReplayBar.tsx
356
878
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
879
+ var SPEED_OPTIONS = [1, 2, 5];
880
+ function ReplayBar({
881
+ isReplay,
882
+ speed,
883
+ onSpeedChange,
884
+ onExit,
885
+ canControl = true,
886
+ className
887
+ }) {
888
+ if (!isReplay) return null;
889
+ return /* @__PURE__ */ jsxs(
890
+ "div",
891
+ {
892
+ className: cn(
893
+ "flex flex-wrap items-center gap-2 border-b border-[hsl(var(--border))] bg-[hsl(var(--muted))]/40 px-4 py-2 text-xs",
894
+ className
895
+ ),
896
+ children: [
897
+ /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5 font-medium text-[hsl(var(--foreground))]", children: [
898
+ /* @__PURE__ */ jsx2(Play, { size: 13 }),
899
+ "\u56DE\u653E\u6A21\u5F0F"
900
+ ] }),
901
+ /* @__PURE__ */ jsx2("span", { className: "text-[hsl(var(--muted-foreground))]", children: "\u6B63\u5728\u91CD\u73B0\u4E4B\u524D\u7684\u5BF9\u8BDD" }),
902
+ /* @__PURE__ */ jsxs("div", { className: "ml-auto flex items-center gap-2", children: [
903
+ /* @__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(
904
+ "button",
905
+ {
906
+ type: "button",
907
+ onClick: () => onSpeedChange(option),
908
+ "aria-pressed": speed === option,
909
+ disabled: !canControl,
910
+ className: cn(
911
+ "h-6 rounded px-2 font-medium transition-colors",
912
+ 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))]",
913
+ !canControl && "cursor-not-allowed opacity-50"
914
+ ),
915
+ children: [
916
+ option,
917
+ "x"
918
+ ]
919
+ },
920
+ option
921
+ )) }),
922
+ /* @__PURE__ */ jsx2(
923
+ "button",
924
+ {
925
+ type: "button",
926
+ onClick: onExit,
927
+ disabled: !canControl,
928
+ className: cn(
929
+ "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))]",
930
+ !canControl && "cursor-not-allowed opacity-50 hover:bg-transparent"
931
+ ),
932
+ children: "\u9000\u51FA\u56DE\u653E"
933
+ }
934
+ )
935
+ ] })
936
+ ]
937
+ }
938
+ );
939
+ }
940
+
941
+ // src/components/ReplayMismatchPrompt.tsx
942
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
943
+ function ReplayMismatchPrompt({ mismatch, className }) {
944
+ if (!mismatch) return null;
945
+ return /* @__PURE__ */ jsxs2(
946
+ "div",
947
+ {
948
+ className: cn(
949
+ "mx-auto my-3 max-w-3xl rounded-2xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-4 py-3 text-sm",
950
+ className
951
+ ),
952
+ children: [
953
+ /* @__PURE__ */ jsx3("div", { className: "font-medium text-[hsl(var(--foreground))]", children: "\u8FD9\u53E5\u8BDD\u548C\u4E4B\u524D\u5F55\u5236\u7684\u4E0D\u4E00\u6837" }),
954
+ /* @__PURE__ */ jsxs2("dl", { className: "mt-2 space-y-1 text-xs text-[hsl(var(--muted-foreground))]", children: [
955
+ /* @__PURE__ */ jsxs2("div", { className: "flex gap-2", children: [
956
+ /* @__PURE__ */ jsx3("dt", { className: "shrink-0", children: "\u5F55\u5236\u7684\u662F" }),
957
+ /* @__PURE__ */ jsx3("dd", { className: "min-w-0 break-words text-[hsl(var(--foreground))]", children: mismatch.expectedMessage || "\uFF08\u7A7A\uFF09" })
958
+ ] }),
959
+ /* @__PURE__ */ jsxs2("div", { className: "flex gap-2", children: [
960
+ /* @__PURE__ */ jsx3("dt", { className: "shrink-0", children: "\u4F60\u8F93\u5165\u7684" }),
961
+ /* @__PURE__ */ jsx3("dd", { className: "min-w-0 break-words text-[hsl(var(--foreground))]", children: mismatch.actualMessage || "\uFF08\u7A7A\uFF09" })
962
+ ] })
963
+ ] }),
964
+ /* @__PURE__ */ jsxs2("div", { className: "mt-3 flex flex-wrap gap-2", children: [
965
+ /* @__PURE__ */ jsx3(
966
+ "button",
967
+ {
968
+ type: "button",
969
+ onClick: () => mismatch.resolve("keep_replay"),
970
+ 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",
971
+ children: "\u6309\u5F55\u5236\u5185\u5BB9\u7EE7\u7EED"
972
+ }
973
+ ),
974
+ /* @__PURE__ */ jsx3(
975
+ "button",
976
+ {
977
+ type: "button",
978
+ onClick: () => mismatch.resolve("continue_replay"),
979
+ 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))]",
980
+ children: "\u4ECE\u8FD9\u91CC\u5F00\u59CB\u771F\u7684\u8FD0\u884C"
981
+ }
982
+ )
983
+ ] })
984
+ ]
985
+ }
986
+ );
987
+ }
988
+
989
+ // src/components/ChatInput.tsx
990
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
357
991
  function ChatInput({
358
992
  value,
359
993
  onValueChange,
@@ -378,8 +1012,8 @@ function ChatInput({
378
1012
  void handleSend();
379
1013
  }
380
1014
  };
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(
1015
+ 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: [
1016
+ /* @__PURE__ */ jsx4(
383
1017
  "textarea",
384
1018
  {
385
1019
  value,
@@ -396,7 +1030,7 @@ function ChatInput({
396
1030
  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)]"
397
1031
  }
398
1032
  ),
399
- isStreaming ? /* @__PURE__ */ jsx2(
1033
+ isStreaming ? /* @__PURE__ */ jsx4(
400
1034
  "button",
401
1035
  {
402
1036
  type: "button",
@@ -405,9 +1039,9 @@ function ChatInput({
405
1039
  "aria-label": isStopping ? "\u6B63\u5728\u505C\u6B62" : "\u505C\u6B62\u56DE\u590D",
406
1040
  title: isStopping ? "\u6B63\u5728\u505C\u6B62" : "\u505C\u6B62\u56DE\u590D",
407
1041
  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",
408
- children: isStopping ? /* @__PURE__ */ jsx2(LoaderCircle, { size: 14, className: "animate-spin" }) : /* @__PURE__ */ jsx2(Square, { size: 12, fill: "currentColor" })
1042
+ children: isStopping ? /* @__PURE__ */ jsx4(LoaderCircle, { size: 14, className: "animate-spin" }) : /* @__PURE__ */ jsx4(Square, { size: 12, fill: "currentColor" })
409
1043
  }
410
- ) : /* @__PURE__ */ jsx2(
1044
+ ) : /* @__PURE__ */ jsx4(
411
1045
  "button",
412
1046
  {
413
1047
  type: "button",
@@ -416,81 +1050,31 @@ function ChatInput({
416
1050
  "aria-label": "\u53D1\u9001\u6D88\u606F",
417
1051
  title: "\u53D1\u9001\u6D88\u606F",
418
1052
  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",
419
- children: /* @__PURE__ */ jsx2(ArrowUp, { size: 15 })
1053
+ children: /* @__PURE__ */ jsx4(ArrowUp, { size: 15 })
420
1054
  }
421
1055
  )
422
1056
  ] }) });
423
1057
  }
424
1058
 
425
1059
  // src/components/ConnectionBanner.tsx
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";
428
- var CONNECTION_NOTICE_DELAY_MS = 3e3;
429
- var CONNECTION_ERROR_DELAY_MS = 15e3;
430
- function useConnectionNoticePhase(connected) {
431
- const [phase, setPhase] = useState2("hidden");
432
- const connectedRef = useRef2(connected);
433
- const timersRef = useRef2([]);
434
- connectedRef.current = connected;
435
- useEffect2(() => {
436
- const clearTimers = () => {
437
- for (const timer of timersRef.current) clearTimeout(timer);
438
- timersRef.current = [];
439
- };
440
- const startGracePeriod = () => {
441
- clearTimers();
442
- setPhase("hidden");
443
- timersRef.current = [
444
- setTimeout(() => setPhase("recovering"), CONNECTION_NOTICE_DELAY_MS),
445
- setTimeout(() => setPhase("failed"), CONNECTION_ERROR_DELAY_MS)
446
- ];
447
- };
448
- if (connected) {
449
- clearTimers();
450
- setPhase("hidden");
451
- } else {
452
- startGracePeriod();
453
- }
454
- const handleForeground = () => {
455
- if (!connectedRef.current) startGracePeriod();
456
- };
457
- const handleVisibilityChange = () => {
458
- if (document.visibilityState === "visible") handleForeground();
459
- };
460
- window.addEventListener("blade:app-active", handleForeground);
461
- window.addEventListener("focus", handleForeground);
462
- window.addEventListener("pageshow", handleForeground);
463
- document.addEventListener("visibilitychange", handleVisibilityChange);
464
- return () => {
465
- clearTimers();
466
- window.removeEventListener("blade:app-active", handleForeground);
467
- window.removeEventListener("focus", handleForeground);
468
- window.removeEventListener("pageshow", handleForeground);
469
- document.removeEventListener("visibilitychange", handleVisibilityChange);
470
- };
471
- }, [connected]);
472
- return phase;
473
- }
1060
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
474
1061
  function ConnectionBanner({ connection, className }) {
475
- const hasConnectedRef = useRef2(connection === "connected" || connection === "reconnecting");
476
- if (connection === "connected") hasConnectedRef.current = true;
477
- const connected = connection === "connected";
478
- const phase = useConnectionNoticePhase(connected);
479
- if (connected || phase === "hidden") return null;
480
- const recovering = phase === "recovering";
481
- const firstConnection = !hasConnectedRef.current;
482
- return /* @__PURE__ */ jsx3("div", { className: cn("blade-chat-banner bg-[hsl(var(--background))] px-5 pt-3", className), children: /* @__PURE__ */ jsxs2(
1062
+ if (connection === "connected" || connection === "connecting") {
1063
+ return null;
1064
+ }
1065
+ const reconnecting = connection === "reconnecting";
1066
+ return /* @__PURE__ */ jsx5("div", { className: cn("blade-chat-banner bg-[hsl(var(--background))] px-5 pt-3", className), children: /* @__PURE__ */ jsxs4(
483
1067
  "div",
484
1068
  {
485
1069
  className: cn(
486
1070
  "mx-auto flex max-w-3xl items-start gap-3 rounded-2xl border px-4 py-3",
487
- recovering ? "border-amber-500/25 bg-amber-500/10 text-amber-100" : "border-rose-500/25 bg-rose-500/10 text-rose-100"
1071
+ reconnecting ? "border-amber-500/25 bg-amber-500/10 text-amber-100" : "border-rose-500/25 bg-rose-500/10 text-rose-100"
488
1072
  ),
489
1073
  children: [
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" })
1074
+ /* @__PURE__ */ jsx5("span", { className: "mt-0.5 shrink-0", children: reconnecting ? /* @__PURE__ */ jsx5(LoaderCircle, { size: 14, className: "animate-spin" }) : /* @__PURE__ */ jsx5(TriangleAlert, { size: 14 }) }),
1075
+ /* @__PURE__ */ jsxs4("div", { className: "min-w-0", children: [
1076
+ /* @__PURE__ */ jsx5("div", { className: "text-sm font-medium", children: reconnecting ? "\u8FDE\u63A5\u5DF2\u65AD\u5F00\uFF0C\u6B63\u5728\u91CD\u8FDE\u2026" : "\u8FDE\u63A5\u5DF2\u65AD\u5F00" }),
1077
+ /* @__PURE__ */ jsx5("div", { className: "text-xs opacity-80", children: "\u6D88\u606F\u540C\u6B65\u53EF\u80FD\u4F1A\u5EF6\u8FDF\uFF0C\u7CFB\u7EDF\u4F1A\u7EE7\u7EED\u81EA\u52A8\u91CD\u8BD5" })
494
1078
  ] })
495
1079
  ]
496
1080
  }
@@ -499,10 +1083,10 @@ function ConnectionBanner({ connection, className }) {
499
1083
 
500
1084
  // src/components/MessageList.tsx
501
1085
  import { isHiddenInternalMessage } from "@blade-hq/agent-client";
502
- import { useCallback as useCallback3, useEffect as useEffect6, useMemo as useMemo6, useRef as useRef6, useState as useState10 } from "react";
1086
+ import { useCallback as useCallback5, useEffect as useEffect6, useMemo as useMemo7, useRef as useRef6, useState as useState11 } from "react";
503
1087
 
504
1088
  // ../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/useStickToBottom.js
505
- import { useCallback, useMemo as useMemo2, useRef as useRef3, useState as useState3 } from "react";
1089
+ import { useCallback as useCallback3, useMemo as useMemo3, useRef as useRef3, useState as useState4 } from "react";
506
1090
  var DEFAULT_SPRING_ANIMATION = {
507
1091
  /**
508
1092
  * A value from 0 to 1, on how much to damp the animation.
@@ -539,12 +1123,12 @@ globalThis.document?.addEventListener("click", () => {
539
1123
  mouseDown = false;
540
1124
  });
541
1125
  var useStickToBottom = (options = {}) => {
542
- const [escapedFromLock, updateEscapedFromLock] = useState3(false);
543
- const [isAtBottom, updateIsAtBottom] = useState3(options.initial !== false);
544
- const [isNearBottom, setIsNearBottom] = useState3(false);
1126
+ const [escapedFromLock, updateEscapedFromLock] = useState4(false);
1127
+ const [isAtBottom, updateIsAtBottom] = useState4(options.initial !== false);
1128
+ const [isNearBottom, setIsNearBottom] = useState4(false);
545
1129
  const optionsRef = useRef3(null);
546
1130
  optionsRef.current = options;
547
- const isSelecting = useCallback(() => {
1131
+ const isSelecting = useCallback3(() => {
548
1132
  if (!mouseDown) {
549
1133
  return false;
550
1134
  }
@@ -555,15 +1139,15 @@ var useStickToBottom = (options = {}) => {
555
1139
  const range = selection.getRangeAt(0);
556
1140
  return range.commonAncestorContainer.contains(scrollRef.current) || scrollRef.current?.contains(range.commonAncestorContainer);
557
1141
  }, []);
558
- const setIsAtBottom = useCallback((isAtBottom2) => {
1142
+ const setIsAtBottom = useCallback3((isAtBottom2) => {
559
1143
  state.isAtBottom = isAtBottom2;
560
1144
  updateIsAtBottom(isAtBottom2);
561
1145
  }, []);
562
- const setEscapedFromLock = useCallback((escapedFromLock2) => {
1146
+ const setEscapedFromLock = useCallback3((escapedFromLock2) => {
563
1147
  state.escapedFromLock = escapedFromLock2;
564
1148
  updateEscapedFromLock(escapedFromLock2);
565
1149
  }, []);
566
- const state = useMemo2(() => {
1150
+ const state = useMemo3(() => {
567
1151
  let lastCalculation;
568
1152
  return {
569
1153
  escapedFromLock,
@@ -616,7 +1200,7 @@ var useStickToBottom = (options = {}) => {
616
1200
  }
617
1201
  };
618
1202
  }, []);
619
- const scrollToBottom = useCallback((scrollOptions = {}) => {
1203
+ const scrollToBottom = useCallback3((scrollOptions = {}) => {
620
1204
  if (typeof scrollOptions === "string") {
621
1205
  scrollOptions = { animation: scrollOptions };
622
1206
  }
@@ -701,11 +1285,11 @@ var useStickToBottom = (options = {}) => {
701
1285
  }
702
1286
  return next();
703
1287
  }, [setIsAtBottom, isSelecting, state]);
704
- const stopScroll = useCallback(() => {
1288
+ const stopScroll = useCallback3(() => {
705
1289
  setEscapedFromLock(true);
706
1290
  setIsAtBottom(false);
707
1291
  }, [setEscapedFromLock, setIsAtBottom]);
708
- const handleScroll = useCallback(({ target }) => {
1292
+ const handleScroll = useCallback3(({ target }) => {
709
1293
  if (target !== scrollRef.current) {
710
1294
  return;
711
1295
  }
@@ -744,7 +1328,7 @@ var useStickToBottom = (options = {}) => {
744
1328
  }
745
1329
  }, 1);
746
1330
  }, [setEscapedFromLock, setIsAtBottom, isSelecting, state]);
747
- const handleWheel = useCallback(({ target, deltaY }) => {
1331
+ const handleWheel = useCallback3(({ target, deltaY }) => {
748
1332
  let element = target;
749
1333
  while (!["scroll", "auto"].includes(getComputedStyle(element).overflow)) {
750
1334
  if (!element.parentElement) {
@@ -814,7 +1398,7 @@ var useStickToBottom = (options = {}) => {
814
1398
  };
815
1399
  };
816
1400
  function useRefCallback(callback, deps) {
817
- const result = useCallback((ref) => {
1401
+ const result = useCallback3((ref) => {
818
1402
  result.current = ref;
819
1403
  return callback(ref);
820
1404
  }, deps);
@@ -846,7 +1430,7 @@ function mergeAnimations(...animations) {
846
1430
 
847
1431
  // ../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/StickToBottom.js
848
1432
  import * as React from "react";
849
- import { createContext as createContext2, useContext as useContext2, useEffect as useEffect3, useImperativeHandle, useLayoutEffect, useMemo as useMemo3, useRef as useRef4 } from "react";
1433
+ import { createContext as createContext2, useContext as useContext2, useEffect as useEffect3, useImperativeHandle, useLayoutEffect, useMemo as useMemo4, useRef as useRef4 } from "react";
850
1434
  var StickToBottomContext = createContext2(null);
851
1435
  var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect3;
852
1436
  function StickToBottom({ instance, children, resize, initial, mass, damping, stiffness, targetScrollTop: currentTargetScrollTop, contextRef, ...props }) {
@@ -864,7 +1448,7 @@ function StickToBottom({ instance, children, resize, initial, mass, damping, sti
864
1448
  targetScrollTop
865
1449
  });
866
1450
  const { scrollRef, contentRef, scrollToBottom, stopScroll, isAtBottom, escapedFromLock, state } = instance ?? defaultInstance;
867
- const context = useMemo3(() => ({
1451
+ const context = useMemo4(() => ({
868
1452
  scrollToBottom,
869
1453
  stopScroll,
870
1454
  scrollRef,
@@ -927,10 +1511,10 @@ function useStickToBottomContext() {
927
1511
 
928
1512
  // src/components/AssistantTurnBlock.tsx
929
1513
  import { getTextContent, normalizeMessageContent } from "@blade-hq/agent-client";
930
- import { useState as useState9 } from "react";
1514
+ import { useState as useState10 } from "react";
931
1515
 
932
1516
  // src/components/AgentLoopBlock.tsx
933
- import { useState as useState4 } from "react";
1517
+ import { useState as useState5 } from "react";
934
1518
 
935
1519
  // src/components/display-utils.ts
936
1520
  var TOOL_NAME_ALIASES = {
@@ -1052,7 +1636,7 @@ function formatToolResult(result) {
1052
1636
  }
1053
1637
 
1054
1638
  // src/components/AgentLoopBlock.tsx
1055
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1639
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1056
1640
  function parseAgentDescription(argumentsJson) {
1057
1641
  try {
1058
1642
  const parsed = JSON.parse(argumentsJson);
@@ -1062,12 +1646,12 @@ function parseAgentDescription(argumentsJson) {
1062
1646
  }
1063
1647
  }
1064
1648
  function AgentLoopBlock({ toolCall }) {
1065
- const [expanded, setExpanded] = useState4(false);
1649
+ const [expanded, setExpanded] = useState5(false);
1066
1650
  const description = parseAgentDescription(toolCall.arguments);
1067
1651
  const running = toolCall.status === "pending" || toolCall.status === "awaiting_answer";
1068
1652
  const failed = toolCall.status === "error" || toolCall.status === "cancelled";
1069
- return /* @__PURE__ */ jsxs3("div", { className: "blade-chat-agent-loop ml-4 text-xs", children: [
1070
- /* @__PURE__ */ jsxs3(
1653
+ return /* @__PURE__ */ jsxs5("div", { className: "blade-chat-agent-loop ml-4 text-xs", children: [
1654
+ /* @__PURE__ */ jsxs5(
1071
1655
  "div",
1072
1656
  {
1073
1657
  className: cn(
@@ -1075,7 +1659,7 @@ function AgentLoopBlock({ toolCall }) {
1075
1659
  failed ? "border-l-[hsl(var(--muted-foreground)/0.5)]" : running ? "border-l-blue-500" : "border-l-[hsl(var(--primary))]"
1076
1660
  ),
1077
1661
  children: [
1078
- /* @__PURE__ */ jsxs3(
1662
+ /* @__PURE__ */ jsxs5(
1079
1663
  "button",
1080
1664
  {
1081
1665
  type: "button",
@@ -1083,7 +1667,7 @@ function AgentLoopBlock({ toolCall }) {
1083
1667
  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",
1084
1668
  "aria-expanded": expanded,
1085
1669
  children: [
1086
- /* @__PURE__ */ jsx4(
1670
+ /* @__PURE__ */ jsx6(
1087
1671
  ChevronRight,
1088
1672
  {
1089
1673
  size: 11,
@@ -1093,8 +1677,8 @@ function AgentLoopBlock({ toolCall }) {
1093
1677
  )
1094
1678
  }
1095
1679
  ),
1096
- /* @__PURE__ */ jsx4(Bot, { size: 12, className: "shrink-0 text-[hsl(var(--muted-foreground))]" }),
1097
- /* @__PURE__ */ jsxs3(
1680
+ /* @__PURE__ */ jsx6(Bot, { size: 12, className: "shrink-0 text-[hsl(var(--muted-foreground))]" }),
1681
+ /* @__PURE__ */ jsxs5(
1098
1682
  "span",
1099
1683
  {
1100
1684
  className: cn(
@@ -1102,25 +1686,25 @@ function AgentLoopBlock({ toolCall }) {
1102
1686
  failed ? "text-[hsl(var(--muted-foreground))]" : running ? "text-blue-300" : "text-[hsl(var(--primary))]"
1103
1687
  ),
1104
1688
  children: [
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" })
1689
+ running ? /* @__PURE__ */ jsx6(LoaderCircle, { size: 11, className: "animate-spin" }) : failed ? /* @__PURE__ */ jsx6(X, { size: 11 }) : /* @__PURE__ */ jsx6(Check, { size: 11 }),
1690
+ /* @__PURE__ */ jsx6("span", { children: running ? "\u6267\u884C\u4E2D" : failed ? "\u5DF2\u7EC8\u6B62" : "\u5B8C\u6210" })
1107
1691
  ]
1108
1692
  }
1109
1693
  ),
1110
- /* @__PURE__ */ jsxs3("span", { className: "min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]", children: [
1694
+ /* @__PURE__ */ jsxs5("span", { className: "min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]", children: [
1111
1695
  "\u5B50\u667A\u80FD\u4F53\uFF1A",
1112
1696
  description
1113
1697
  ] })
1114
1698
  ]
1115
1699
  }
1116
1700
  ),
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) })
1701
+ 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) })
1118
1702
  ]
1119
1703
  }
1120
1704
  ),
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) })
1705
+ expanded && toolCall.result != null && /* @__PURE__ */ jsxs5("div", { className: "ml-4 mt-1 rounded-xl bg-[hsl(var(--card))] px-3 py-3", children: [
1706
+ /* @__PURE__ */ jsx6("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u7ED3\u679C" }),
1707
+ /* @__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) })
1124
1708
  ] })
1125
1709
  ] });
1126
1710
  }
@@ -1128,9 +1712,9 @@ function AgentLoopBlock({ toolCall }) {
1128
1712
  // src/components/MarkdownContent.tsx
1129
1713
  import {
1130
1714
  useEffect as useEffect4,
1131
- useMemo as useMemo4,
1715
+ useMemo as useMemo5,
1132
1716
  useRef as useRef5,
1133
- useState as useState6
1717
+ useState as useState7
1134
1718
  } from "react";
1135
1719
 
1136
1720
  // src/lib/media-tags.ts
@@ -1177,8 +1761,26 @@ function collapseMediaTags(text, options) {
1177
1761
  }
1178
1762
 
1179
1763
  // src/components/FileCard.tsx
1180
- import { useState as useState5 } from "react";
1181
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1764
+ import { useState as useState6 } from "react";
1765
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1766
+ var IMAGE_EXTS = /* @__PURE__ */ new Set(["png", "jpg", "jpeg", "gif", "svg", "webp", "ico", "bmp"]);
1767
+ var SHEET_EXTS = /* @__PURE__ */ new Set(["xlsx", "xls", "xlsm", "xlsb", "csv"]);
1768
+ var TEXT_EXTS = /* @__PURE__ */ new Set(["txt", "md", "markdown", "pdf", "doc", "docx", "rtf"]);
1769
+ var CODE_EXTS = /* @__PURE__ */ new Set(["js", "jsx", "ts", "tsx", "py", "go", "json", "yaml", "yml", "html", "css"]);
1770
+ var VIDEO_EXTS = /* @__PURE__ */ new Set(["mp4", "webm", "mov", "mkv", "avi"]);
1771
+ var AUDIO_EXTS = /* @__PURE__ */ new Set(["mp3", "wav", "ogg", "m4a", "flac"]);
1772
+ var ARCHIVE_EXTS = /* @__PURE__ */ new Set(["zip", "tar", "gz", "tgz", "rar", "7z"]);
1773
+ function getFileIcon(name) {
1774
+ const ext = name.split(".").pop()?.toLowerCase() ?? "";
1775
+ if (IMAGE_EXTS.has(ext)) return Image;
1776
+ if (SHEET_EXTS.has(ext)) return FileSpreadsheet;
1777
+ if (TEXT_EXTS.has(ext)) return FileText;
1778
+ if (CODE_EXTS.has(ext)) return FileCode2;
1779
+ if (VIDEO_EXTS.has(ext)) return Film;
1780
+ if (AUDIO_EXTS.has(ext)) return Music;
1781
+ if (ARCHIVE_EXTS.has(ext)) return Archive;
1782
+ return File;
1783
+ }
1182
1784
  function stringValue(value) {
1183
1785
  return typeof value === "string" ? value : void 0;
1184
1786
  }
@@ -1202,18 +1804,14 @@ function FileCard({
1202
1804
  ...props
1203
1805
  }) {
1204
1806
  const client = useBladeClient();
1205
- const [downloading, setDownloading] = useState5(false);
1206
- const [failed, setFailed] = useState5(false);
1807
+ const [downloading, setDownloading] = useState6(false);
1808
+ const [failed, setFailed] = useState6(false);
1207
1809
  const nodeProperties = node && typeof node === "object" && "properties" in node ? node.properties : void 0;
1208
1810
  const path = pathAttribute ?? dataPath ?? stringValue(nodeProperties?.["data-path"]) ?? stringValue(nodeProperties?.dataPath) ?? "";
1209
1811
  const name = nameAttribute ?? dataName ?? stringValue(nodeProperties?.["data-name"]) ?? stringValue(nodeProperties?.dataName) ?? extractChildrenText(children) ?? "";
1210
1812
  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();
1813
+ const Icon2 = getFileIcon(name);
1814
+ const handleDownload = async () => {
1217
1815
  if (disabled || downloading || !sessionId) return;
1218
1816
  setDownloading(true);
1219
1817
  setFailed(false);
@@ -1225,32 +1823,33 @@ function FileCard({
1225
1823
  setDownloading(false);
1226
1824
  }
1227
1825
  };
1228
- return /* @__PURE__ */ jsxs4("span", { ...props, className: cn("blade-chat-file-card", className), children: [
1229
- /* @__PURE__ */ jsx5(
1230
- "a",
1826
+ return /* @__PURE__ */ jsxs6("span", { ...props, className: cn("blade-chat-file-card", className), children: [
1827
+ /* @__PURE__ */ jsxs6(
1828
+ "button",
1231
1829
  {
1232
- href: downloadUrl,
1233
- download: name,
1830
+ type: "button",
1234
1831
  onClick: handleDownload,
1235
- "aria-disabled": disabled || void 0,
1236
- "aria-busy": downloading || void 0,
1832
+ disabled: disabled || downloading,
1237
1833
  "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
1834
+ title: failed ? "\u4E0B\u8F7D\u5931\u8D25\uFF0C\u70B9\u51FB\u91CD\u8BD5" : `\u4E0B\u8F7D ${name}`,
1835
+ children: [
1836
+ /* @__PURE__ */ jsx7("span", { className: "blade-chat-file-card-icon", children: downloading ? /* @__PURE__ */ jsx7(LoaderCircle, { size: 15, className: "animate-spin" }) : /* @__PURE__ */ jsx7(Icon2, { size: 15 }) }),
1837
+ /* @__PURE__ */ jsx7("span", { className: "blade-chat-file-card-name", children: name }),
1838
+ /* @__PURE__ */ jsx7(Download, { size: 13, className: "blade-chat-file-card-action" })
1839
+ ]
1241
1840
  }
1242
1841
  ),
1243
- failed && /* @__PURE__ */ jsx5("span", { className: "blade-chat-file-card-error", children: "\u4E0B\u8F7D\u5931\u8D25" })
1842
+ failed && /* @__PURE__ */ jsx7("span", { className: "blade-chat-file-card-error", children: "\u4E0B\u8F7D\u5931\u8D25" })
1244
1843
  ] });
1245
1844
  }
1246
1845
 
1247
1846
  // src/components/MarkdownContent.tsx
1248
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1847
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1249
1848
  var SYSTEM_REMINDER_RE = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
1250
1849
  function CodeBlockPre({ children, node: _node, ...props }) {
1251
1850
  const preRef = useRef5(null);
1252
- const [copied, setCopied] = useState6(false);
1253
- const [language, setLanguage] = useState6("");
1851
+ const [copied, setCopied] = useState7(false);
1852
+ const [language, setLanguage] = useState7("");
1254
1853
  useEffect4(() => {
1255
1854
  const codeEl = preRef.current?.querySelector("code");
1256
1855
  setLanguage(codeEl?.className.match(/language-(\S+)/)?.[1] ?? "");
@@ -1262,10 +1861,10 @@ function CodeBlockPre({ children, node: _node, ...props }) {
1262
1861
  setTimeout(() => setCopied(false), 2e3);
1263
1862
  }
1264
1863
  };
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(
1864
+ return /* @__PURE__ */ jsxs7("div", { className: "blade-chat-codeblock not-prose my-3 overflow-hidden rounded-xl border border-[hsl(var(--border))]", children: [
1865
+ /* @__PURE__ */ jsxs7("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: [
1866
+ /* @__PURE__ */ jsx8("span", { className: "font-mono text-[12px] text-[hsl(var(--muted-foreground))]", children: language || "code" }),
1867
+ /* @__PURE__ */ jsxs7(
1269
1868
  "button",
1270
1869
  {
1271
1870
  type: "button",
@@ -1275,13 +1874,13 @@ function CodeBlockPre({ children, node: _node, ...props }) {
1275
1874
  copied ? "text-[hsl(var(--primary))]" : "text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]"
1276
1875
  ),
1277
1876
  children: [
1278
- copied ? /* @__PURE__ */ jsx6(Check, { size: 12 }) : /* @__PURE__ */ jsx6(Copy, { size: 12 }),
1279
- /* @__PURE__ */ jsx6("span", { children: copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236" })
1877
+ copied ? /* @__PURE__ */ jsx8(Check, { size: 12 }) : /* @__PURE__ */ jsx8(Copy, { size: 12 }),
1878
+ /* @__PURE__ */ jsx8("span", { children: copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236" })
1280
1879
  ]
1281
1880
  }
1282
1881
  )
1283
1882
  ] }),
1284
- /* @__PURE__ */ jsx6(
1883
+ /* @__PURE__ */ jsx8(
1285
1884
  "pre",
1286
1885
  {
1287
1886
  ref: preRef,
@@ -1293,7 +1892,7 @@ function CodeBlockPre({ children, node: _node, ...props }) {
1293
1892
  ] });
1294
1893
  }
1295
1894
  function ExternalAnchor({ node: _node, children, ...props }) {
1296
- return /* @__PURE__ */ jsx6("a", { ...props, target: "_blank", rel: "noopener noreferrer", children });
1895
+ return /* @__PURE__ */ jsx8("a", { ...props, target: "_blank", rel: "noopener noreferrer", children });
1297
1896
  }
1298
1897
  var MARKDOWN_COMPONENTS = {
1299
1898
  pre: CodeBlockPre,
@@ -1304,19 +1903,19 @@ var CUSTOM_ALLOWED_TAGS = {
1304
1903
  };
1305
1904
  function MarkdownContent({ children, className, mode, sessionId }) {
1306
1905
  const streaming = mode === "streaming";
1307
- const resolvedChildren = useMemo4(() => {
1906
+ const resolvedChildren = useMemo5(() => {
1308
1907
  const withoutReminders = children.replace(SYSTEM_REMINDER_RE, "");
1309
1908
  if (!sessionId) return withoutReminders;
1310
1909
  return collapseMediaTags(withoutReminders, { streaming });
1311
1910
  }, [children, sessionId, streaming]);
1312
- const components = useMemo4(
1911
+ const components = useMemo5(
1313
1912
  () => ({
1314
1913
  ...MARKDOWN_COMPONENTS,
1315
- [FILE_CARD_TAG]: (fileCardProps) => /* @__PURE__ */ jsx6(FileCard, { ...fileCardProps, sessionId })
1914
+ [FILE_CARD_TAG]: (fileCardProps) => /* @__PURE__ */ jsx8(FileCard, { ...fileCardProps, sessionId })
1316
1915
  }),
1317
1916
  [sessionId]
1318
1917
  );
1319
- return /* @__PURE__ */ jsx6(
1918
+ return /* @__PURE__ */ jsx8(
1320
1919
  _r,
1321
1920
  {
1322
1921
  className: cn("blade-chat-markdown break-words", className),
@@ -1330,17 +1929,17 @@ function MarkdownContent({ children, className, mode, sessionId }) {
1330
1929
  }
1331
1930
 
1332
1931
  // src/components/Shimmer.tsx
1333
- import { jsx as jsx7 } from "react/jsx-runtime";
1932
+ import { jsx as jsx9 } from "react/jsx-runtime";
1334
1933
  function Shimmer({ children = "\u6B63\u5728\u601D\u8003...", className }) {
1335
- return /* @__PURE__ */ jsx7("span", { className: cn("blade-shimmer-text text-sm font-medium", className), children });
1934
+ return /* @__PURE__ */ jsx9("span", { className: cn("blade-shimmer-text text-sm font-medium", className), children });
1336
1935
  }
1337
1936
 
1338
1937
  // src/components/ToolCallBlock.tsx
1339
- import { useState as useState8 } from "react";
1938
+ import { useState as useState9 } from "react";
1340
1939
 
1341
1940
  // src/components/AskUserQuestionBlock.tsx
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";
1941
+ import { useEffect as useEffect5, useMemo as useMemo6, useState as useState8 } from "react";
1942
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
1344
1943
  function AskUserQuestionBlock({
1345
1944
  data,
1346
1945
  answered,
@@ -1349,16 +1948,16 @@ function AskUserQuestionBlock({
1349
1948
  answerData,
1350
1949
  onAnswer
1351
1950
  }) {
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);
1951
+ const [selections, setSelections] = useState8(/* @__PURE__ */ new Map());
1952
+ const [customTexts, setCustomTexts] = useState8(/* @__PURE__ */ new Map());
1953
+ const [usingCustom, setUsingCustom] = useState8(/* @__PURE__ */ new Set());
1954
+ const [submitted, setSubmitted] = useState8(false);
1356
1955
  useEffect5(() => {
1357
1956
  if (sessionStatus === "failed" || sessionStatus === "interrupted") {
1358
1957
  setSubmitted(false);
1359
1958
  }
1360
1959
  }, [sessionStatus]);
1361
- const displayAnswerState = useMemo5(() => {
1960
+ const displayAnswerState = useMemo6(() => {
1362
1961
  if (!(answered && answerData)) {
1363
1962
  return { selections, customTexts, usingCustom };
1364
1963
  }
@@ -1443,7 +2042,7 @@ ${parts.join("\n")}`;
1443
2042
  setSubmitted(true);
1444
2043
  onAnswer(text, toolCallId, nextAnswerData);
1445
2044
  };
1446
- return /* @__PURE__ */ jsxs6(
2045
+ return /* @__PURE__ */ jsxs8(
1447
2046
  "div",
1448
2047
  {
1449
2048
  className: cn(
@@ -1451,12 +2050,12 @@ ${parts.join("\n")}`;
1451
2050
  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"
1452
2051
  ),
1453
2052
  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: [
2053
+ data.source_loop?.description && /* @__PURE__ */ jsxs8("div", { className: "rounded-lg bg-[hsl(var(--muted)/0.35)] px-3 py-2 text-xs text-[hsl(var(--muted-foreground))]", children: [
1455
2054
  "\u5B50\u667A\u80FD\u4F53\u300C",
1456
2055
  data.source_loop.description,
1457
2056
  "\u300D\u5728\u7B49\u5F85\u4F60\u7684\u56DE\u7B54"
1458
2057
  ] }),
1459
- data.questions.map((q, qIdx) => /* @__PURE__ */ jsx8(
2058
+ data.questions.map((q, qIdx) => /* @__PURE__ */ jsx10(
1460
2059
  QuestionCard,
1461
2060
  {
1462
2061
  question: q,
@@ -1471,7 +2070,7 @@ ${parts.join("\n")}`;
1471
2070
  },
1472
2071
  q.question
1473
2072
  )),
1474
- !answered && !submitted && onAnswer && /* @__PURE__ */ jsx8(
2073
+ !answered && !submitted && onAnswer && /* @__PURE__ */ jsx10(
1475
2074
  "button",
1476
2075
  {
1477
2076
  type: "button",
@@ -1481,14 +2080,14 @@ ${parts.join("\n")}`;
1481
2080
  children: allAnswered ? "\u786E\u8BA4" : "\u8BF7\u5148\u9009\u62E9\u4E00\u4E2A\u9009\u9879"
1482
2081
  }
1483
2082
  ),
1484
- submitted && !answered && /* @__PURE__ */ jsxs6(
2083
+ submitted && !answered && /* @__PURE__ */ jsxs8(
1485
2084
  "button",
1486
2085
  {
1487
2086
  type: "button",
1488
2087
  disabled: true,
1489
2088
  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",
1490
2089
  children: [
1491
- /* @__PURE__ */ jsx8(LoaderCircle, { size: 14, className: "animate-spin" }),
2090
+ /* @__PURE__ */ jsx10(LoaderCircle, { size: 14, className: "animate-spin" }),
1492
2091
  "\u786E\u8BA4\u4E2D"
1493
2092
  ]
1494
2093
  }
@@ -1509,30 +2108,30 @@ function QuestionCard({
1509
2108
  onCustomChange
1510
2109
  }) {
1511
2110
  const multi = question.multiSelect ?? false;
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(
2111
+ return /* @__PURE__ */ jsxs8("div", { children: [
2112
+ /* @__PURE__ */ jsxs8("div", { className: cn("flex items-start gap-2", answered ? "mb-2" : "mb-3"), children: [
2113
+ /* @__PURE__ */ jsx10(
1515
2114
  MessageSquareMore,
1516
2115
  {
1517
2116
  size: answered ? 12 : 13,
1518
2117
  className: "mt-0.5 shrink-0 text-[hsl(var(--primary))]"
1519
2118
  }
1520
2119
  ),
1521
- /* @__PURE__ */ jsx8(
2120
+ /* @__PURE__ */ jsx10(
1522
2121
  "div",
1523
2122
  {
1524
2123
  className: cn(
1525
2124
  "min-w-0 flex-1 font-medium text-[hsl(var(--foreground))]",
1526
2125
  answered ? "text-xs" : "text-sm"
1527
2126
  ),
1528
- children: /* @__PURE__ */ jsx8(MarkdownContent, { className: "blade-chat-prose", children: question.question })
2127
+ children: /* @__PURE__ */ jsx10(MarkdownContent, { className: "blade-chat-prose", children: question.question })
1529
2128
  }
1530
2129
  )
1531
2130
  ] }),
1532
- /* @__PURE__ */ jsxs6("div", { className: cn("flex flex-col pl-5", answered ? "gap-1" : "gap-1.5"), children: [
2131
+ /* @__PURE__ */ jsxs8("div", { className: cn("flex flex-col pl-5", answered ? "gap-1" : "gap-1.5"), children: [
1533
2132
  question.options.map((opt, optIdx) => {
1534
2133
  const isSel = selected.has(optIdx);
1535
- return /* @__PURE__ */ jsxs6(
2134
+ return /* @__PURE__ */ jsxs8(
1536
2135
  "button",
1537
2136
  {
1538
2137
  type: "button",
@@ -1546,14 +2145,14 @@ function QuestionCard({
1546
2145
  answered && "cursor-default opacity-70"
1547
2146
  ),
1548
2147
  children: [
1549
- multi && /* @__PURE__ */ jsx8(
2148
+ multi && /* @__PURE__ */ jsx10(
1550
2149
  "div",
1551
2150
  {
1552
2151
  className: cn(
1553
2152
  "mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border transition-colors",
1554
2153
  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))]"
1555
2154
  ),
1556
- children: isSel && /* @__PURE__ */ jsx8(
2155
+ children: isSel && /* @__PURE__ */ jsx10(
1557
2156
  Check,
1558
2157
  {
1559
2158
  size: 9,
@@ -1562,9 +2161,9 @@ function QuestionCard({
1562
2161
  )
1563
2162
  }
1564
2163
  ),
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(
2164
+ /* @__PURE__ */ jsxs8("div", { className: "min-w-0", children: [
2165
+ /* @__PURE__ */ jsx10("div", { className: cn("font-medium", answered ? "text-xs" : "text-[13px]"), children: opt.label }),
2166
+ opt.description && /* @__PURE__ */ jsx10(
1568
2167
  "div",
1569
2168
  {
1570
2169
  className: cn(
@@ -1581,7 +2180,7 @@ function QuestionCard({
1581
2180
  opt.label
1582
2181
  );
1583
2182
  }),
1584
- answered && !isCustom ? null : /* @__PURE__ */ jsxs6(
2183
+ answered && !isCustom ? null : /* @__PURE__ */ jsxs8(
1585
2184
  "div",
1586
2185
  {
1587
2186
  className: cn(
@@ -1591,8 +2190,8 @@ function QuestionCard({
1591
2190
  answered && "cursor-default opacity-70"
1592
2191
  ),
1593
2192
  children: [
1594
- /* @__PURE__ */ jsx8("span", { className: "shrink-0 text-xs text-[hsl(var(--muted-foreground))]", children: "\u5176\u4ED6\uFF1A" }),
1595
- /* @__PURE__ */ jsx8(
2193
+ /* @__PURE__ */ jsx10("span", { className: "shrink-0 text-xs text-[hsl(var(--muted-foreground))]", children: "\u5176\u4ED6\uFF1A" }),
2194
+ /* @__PURE__ */ jsx10(
1596
2195
  "input",
1597
2196
  {
1598
2197
  type: "text",
@@ -1660,7 +2259,7 @@ function normalizeOptionItem(value) {
1660
2259
  }
1661
2260
 
1662
2261
  // src/components/ToolCallBlock.tsx
1663
- import { Fragment, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
2262
+ import { Fragment, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
1664
2263
  function resolveAskQuestionState({
1665
2264
  toolStatus,
1666
2265
  hasAnswerData,
@@ -1680,12 +2279,12 @@ function ToolCallBlock({
1680
2279
  sessionStatus,
1681
2280
  renderer
1682
2281
  }) {
1683
- const [expanded, setExpanded] = useState8(false);
2282
+ const [expanded, setExpanded] = useState9(false);
1684
2283
  const normalizedName = formatToolName(toolCall.name);
1685
2284
  if (renderer) {
1686
2285
  const custom = renderer(toolCall);
1687
2286
  if (custom !== null && custom !== void 0) {
1688
- return /* @__PURE__ */ jsx9(Fragment, { children: custom });
2287
+ return /* @__PURE__ */ jsx11(Fragment, { children: custom });
1689
2288
  }
1690
2289
  }
1691
2290
  if (normalizedName === "AskUserQuestion") {
@@ -1697,7 +2296,7 @@ function ToolCallBlock({
1697
2296
  });
1698
2297
  const canAnswer = questionState.awaitingAnswer && Boolean(onAnswer);
1699
2298
  if (askData) {
1700
- return /* @__PURE__ */ jsx9(
2299
+ return /* @__PURE__ */ jsx11(
1701
2300
  AskUserQuestionBlock,
1702
2301
  {
1703
2302
  data: askData,
@@ -1710,24 +2309,24 @@ function ToolCallBlock({
1710
2309
  );
1711
2310
  }
1712
2311
  if (toolCall.status === "pending") {
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" })
2312
+ return /* @__PURE__ */ jsxs9("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: [
2313
+ /* @__PURE__ */ jsx11(LoaderCircle, { size: 14, className: "animate-spin" }),
2314
+ /* @__PURE__ */ jsx11("span", { children: "\u6B63\u5728\u51C6\u5907\u95EE\u9898\u2026" })
1716
2315
  ] });
1717
2316
  }
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" })
2317
+ return /* @__PURE__ */ jsxs9("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: [
2318
+ /* @__PURE__ */ jsx11("div", { className: "font-semibold", children: "\u9009\u62E9\u9898\u5185\u5BB9\u6682\u65F6\u65E0\u6CD5\u663E\u793A" }),
2319
+ /* @__PURE__ */ jsx11("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" })
1721
2320
  ] });
1722
2321
  }
1723
2322
  const tone = getToolTone(toolCall.status);
1724
2323
  const displayName = getToolDisplayLabel(toolCall);
1725
2324
  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))]";
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 });
2325
+ const statusIcon = toolCall.status === "pending" ? /* @__PURE__ */ jsx11(LoaderCircle, { size: 11, className: "animate-spin" }) : toolCall.status === "awaiting_answer" ? /* @__PURE__ */ jsx11(MessageSquareMore, { size: 11 }) : toolCall.status === "cancelled" || toolCall.status === "error" ? /* @__PURE__ */ jsx11(X, { size: 11 }) : /* @__PURE__ */ jsx11(Check, { size: 11 });
1727
2326
  const statusTextClass = tone === "red" ? "text-[hsl(var(--muted-foreground))]" : tone === "amber" ? "text-amber-300" : tone === "blue" ? "text-blue-300" : "text-[hsl(var(--primary))]";
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(
2327
+ return /* @__PURE__ */ jsxs9("div", { className: "blade-chat-tool ml-4 text-xs", children: [
2328
+ /* @__PURE__ */ jsxs9("div", { className: cn("border-l-[3px] flex items-center gap-2 px-3 py-2", toneClass), children: [
2329
+ /* @__PURE__ */ jsxs9(
1731
2330
  "button",
1732
2331
  {
1733
2332
  type: "button",
@@ -1735,7 +2334,7 @@ function ToolCallBlock({
1735
2334
  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",
1736
2335
  "aria-expanded": expanded,
1737
2336
  children: [
1738
- /* @__PURE__ */ jsx9(
2337
+ /* @__PURE__ */ jsx11(
1739
2338
  ChevronRight,
1740
2339
  {
1741
2340
  size: 11,
@@ -1745,24 +2344,24 @@ function ToolCallBlock({
1745
2344
  )
1746
2345
  }
1747
2346
  ),
1748
- /* @__PURE__ */ jsxs7("span", { className: cn("flex shrink-0 items-center gap-1 text-[10px]", statusTextClass), children: [
2347
+ /* @__PURE__ */ jsxs9("span", { className: cn("flex shrink-0 items-center gap-1 text-[10px]", statusTextClass), children: [
1749
2348
  statusIcon,
1750
- /* @__PURE__ */ jsx9("span", { children: getToolStatusLabel(toolCall.status) })
2349
+ /* @__PURE__ */ jsx11("span", { children: getToolStatusLabel(toolCall.status) })
1751
2350
  ] }),
1752
- /* @__PURE__ */ jsx9("span", { className: "min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]", children: displayName })
2351
+ /* @__PURE__ */ jsx11("span", { className: "min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]", children: displayName })
1753
2352
  ]
1754
2353
  }
1755
2354
  ),
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) })
2355
+ typeof toolCall.duration_ms === "number" && toolCall.duration_ms > 0 && /* @__PURE__ */ jsx11("span", { className: "shrink-0 font-mono text-[10px] text-[hsl(var(--muted-foreground))]", children: formatToolDuration(toolCall.duration_ms) })
1757
2356
  ] }),
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) })
2357
+ expanded && /* @__PURE__ */ jsxs9("div", { className: "blade-chat-tool-detail ml-4 mt-1 rounded-xl bg-[hsl(var(--card))] px-3 py-3", children: [
2358
+ /* @__PURE__ */ jsx11("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u5DE5\u5177" }),
2359
+ /* @__PURE__ */ jsx11("div", { className: "mb-3 font-mono text-[11px] text-[hsl(var(--foreground))]", children: normalizedName }),
2360
+ /* @__PURE__ */ jsx11("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u53C2\u6570" }),
2361
+ /* @__PURE__ */ jsx11("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) }),
2362
+ toolCall.result != null && /* @__PURE__ */ jsxs9(Fragment, { children: [
2363
+ /* @__PURE__ */ jsx11("div", { className: "mb-1 mt-3 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u7ED3\u679C" }),
2364
+ /* @__PURE__ */ jsx11("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) })
1766
2365
  ] })
1767
2366
  ] })
1768
2367
  ] });
@@ -1780,11 +2379,11 @@ function buildAskUserPayload(argumentsJson) {
1780
2379
  }
1781
2380
 
1782
2381
  // src/components/AssistantTurnBlock.tsx
1783
- import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
2382
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1784
2383
  function ThinkingBlock({ reasoning, isStreaming }) {
1785
- const [open, setOpen] = useState9(false);
1786
- return /* @__PURE__ */ jsxs8("div", { className: "blade-chat-thinking ml-4 text-sm", children: [
1787
- /* @__PURE__ */ jsxs8(
2384
+ const [open, setOpen] = useState10(false);
2385
+ return /* @__PURE__ */ jsxs10("div", { className: "blade-chat-thinking ml-4 text-sm", children: [
2386
+ /* @__PURE__ */ jsxs10(
1788
2387
  "button",
1789
2388
  {
1790
2389
  type: "button",
@@ -1792,14 +2391,14 @@ function ThinkingBlock({ reasoning, isStreaming }) {
1792
2391
  "aria-expanded": open,
1793
2392
  className: "inline-flex items-center gap-1.5 text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))]",
1794
2393
  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: [
2394
+ /* @__PURE__ */ jsx12(Brain, { size: 12, className: "shrink-0" }),
2395
+ isStreaming ? /* @__PURE__ */ jsx12(Shimmer, { className: "text-xs", children: "\u6B63\u5728\u601D\u8003" }) : /* @__PURE__ */ jsx12("span", { children: "\u601D\u8003\u8FC7\u7A0B" }),
2396
+ /* @__PURE__ */ jsxs10("span", { className: "text-[hsl(var(--muted-foreground))]/70", children: [
1798
2397
  "\xB7 ",
1799
2398
  new Intl.NumberFormat("zh-CN").format(reasoning.length),
1800
2399
  " \u5B57"
1801
2400
  ] }),
1802
- /* @__PURE__ */ jsx10(
2401
+ /* @__PURE__ */ jsx12(
1803
2402
  ChevronDown,
1804
2403
  {
1805
2404
  size: 12,
@@ -1809,7 +2408,7 @@ function ThinkingBlock({ reasoning, isStreaming }) {
1809
2408
  ]
1810
2409
  }
1811
2410
  ),
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 })
2411
+ open && /* @__PURE__ */ jsx12("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 })
1813
2412
  ] });
1814
2413
  }
1815
2414
  function getMessageText(message) {
@@ -1835,21 +2434,21 @@ function AssistantTurnBlock({
1835
2434
  (message) => getMessageText(message) || message.reasoning || (message.tool_calls?.length ?? 0) > 0
1836
2435
  );
1837
2436
  const latestReasoningIndex = isStreaming ? findLatestReasoningMessageIndex(messages) : -1;
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" }),
2437
+ return /* @__PURE__ */ jsxs10("div", { className: "blade-chat-assistant-turn flex flex-col gap-3", children: [
2438
+ hasInterrupted && /* @__PURE__ */ jsx12("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" }),
1840
2439
  messages.map((message, index) => {
1841
2440
  const isLast = index === messages.length - 1;
1842
2441
  const streamingThis = isStreaming && isLast;
1843
2442
  const text = getMessageText(message);
1844
2443
  const toolCalls = message.tool_calls ?? [];
1845
2444
  const showReasoning = !!message.reasoning && (!isStreaming || index === latestReasoningIndex);
1846
- return /* @__PURE__ */ jsxs8(
2445
+ return /* @__PURE__ */ jsxs10(
1847
2446
  "div",
1848
2447
  {
1849
2448
  className: "flex flex-col gap-3",
1850
2449
  children: [
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(
2450
+ showReasoning && message.reasoning && /* @__PURE__ */ jsx12(ThinkingBlock, { reasoning: message.reasoning, isStreaming: streamingThis && !text }),
2451
+ text && /* @__PURE__ */ jsx12("div", { className: "blade-chat-assistant-text text-[15px] leading-8 text-[hsl(var(--foreground))]", children: /* @__PURE__ */ jsx12(
1853
2452
  MarkdownContent,
1854
2453
  {
1855
2454
  mode: streamingThis ? "streaming" : "static",
@@ -1858,8 +2457,8 @@ function AssistantTurnBlock({
1858
2457
  children: text
1859
2458
  }
1860
2459
  ) }),
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(
2460
+ toolCalls.length > 0 && /* @__PURE__ */ jsx12("div", { className: "flex flex-col gap-2", children: toolCalls.map(
2461
+ (toolCall) => formatToolName(toolCall.name) === "Agent" ? /* @__PURE__ */ jsx12(AgentLoopBlock, { toolCall }, toolCall.id) : /* @__PURE__ */ jsx12(
1863
2462
  ToolCallBlock,
1864
2463
  {
1865
2464
  toolCall,
@@ -1877,13 +2476,13 @@ function AssistantTurnBlock({
1877
2476
  message.entry_id ?? `${message.timestamp ?? "assistant"}-${index}`
1878
2477
  );
1879
2478
  }),
1880
- isStreaming && !hasAnyContent && /* @__PURE__ */ jsx10(Shimmer, { className: "ml-4", children: "\u6B63\u5728\u751F\u6210..." })
2479
+ isStreaming && !hasAnyContent && /* @__PURE__ */ jsx12(Shimmer, { className: "ml-4", children: "\u6B63\u5728\u751F\u6210..." })
1881
2480
  ] });
1882
2481
  }
1883
2482
 
1884
2483
  // src/components/RenderErrorBoundary.tsx
1885
2484
  import { Component } from "react";
1886
- import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
2485
+ import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
1887
2486
  function getFirstComponentName(componentStack) {
1888
2487
  const match = componentStack.match(/\n\s+at\s+([^\s(]+)/);
1889
2488
  return match?.[1] ?? null;
@@ -1916,18 +2515,18 @@ var RenderErrorBoundary = class extends Component {
1916
2515
  return children;
1917
2516
  }
1918
2517
  const componentName = getFirstComponentName(componentStack);
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: [
2518
+ return /* @__PURE__ */ jsx13("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__ */ jsxs11("div", { className: "flex items-start gap-2", children: [
2519
+ /* @__PURE__ */ jsx13(TriangleAlert, { className: "mt-0.5 h-4 w-4 shrink-0 text-amber-300" }),
2520
+ /* @__PURE__ */ jsxs11("div", { className: "min-w-0 flex-1", children: [
2521
+ /* @__PURE__ */ jsxs11("div", { className: "font-medium", children: [
1923
2522
  label,
1924
2523
  "\u6E32\u67D3\u5931\u8D25"
1925
2524
  ] }),
1926
- /* @__PURE__ */ jsxs9("div", { className: "mt-1 break-words text-xs leading-5 text-amber-100/75", children: [
2525
+ /* @__PURE__ */ jsxs11("div", { className: "mt-1 break-words text-xs leading-5 text-amber-100/75", children: [
1927
2526
  componentName ? `\u7EC4\u4EF6\uFF1A${componentName}\u3002` : null,
1928
2527
  error.message || "\u53D1\u751F\u4E86\u672A\u9884\u671F\u7684\u6E32\u67D3\u9519\u8BEF\u3002"
1929
2528
  ] }),
1930
- details ? /* @__PURE__ */ jsx11("div", { className: "mt-1 truncate text-xs text-amber-100/55", children: details }) : null
2529
+ details ? /* @__PURE__ */ jsx13("div", { className: "mt-1 truncate text-xs text-amber-100/55", children: details }) : null
1931
2530
  ] })
1932
2531
  ] }) });
1933
2532
  }
@@ -1935,7 +2534,7 @@ var RenderErrorBoundary = class extends Component {
1935
2534
 
1936
2535
  // src/components/UserMessageBubble.tsx
1937
2536
  import { getFileParts, getImageParts, getTextContent as getTextContent2 } from "@blade-hq/agent-client";
1938
- import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
2537
+ import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
1939
2538
  function isUserMessage(message) {
1940
2539
  return message.role === "user";
1941
2540
  }
@@ -1947,8 +2546,8 @@ function UserMessageBubble({ message, className }) {
1947
2546
  const text = getTextContent2(message.content).trim();
1948
2547
  const fileParts = getFileParts(message.content);
1949
2548
  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(
2549
+ 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: [
2550
+ imageParts.length > 0 && /* @__PURE__ */ jsx14("div", { className: "grid gap-2", children: imageParts.map((part) => /* @__PURE__ */ jsx14(
1952
2551
  "img",
1953
2552
  {
1954
2553
  src: part.image_url.url,
@@ -1957,21 +2556,21 @@ function UserMessageBubble({ message, className }) {
1957
2556
  },
1958
2557
  part.image_url.url
1959
2558
  )) }),
1960
- fileParts.length > 0 && /* @__PURE__ */ jsx12("div", { className: "flex flex-col items-end gap-1.5", children: fileParts.map((part) => /* @__PURE__ */ jsxs10(
2559
+ fileParts.length > 0 && /* @__PURE__ */ jsx14("div", { className: "flex flex-col items-end gap-1.5", children: fileParts.map((part) => /* @__PURE__ */ jsxs12(
1961
2560
  "div",
1962
2561
  {
1963
2562
  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))]",
1964
2563
  children: [
1965
- /* @__PURE__ */ jsx12(FileText, { size: 12, className: "shrink-0" }),
1966
- /* @__PURE__ */ jsx12("span", { className: "max-w-56 truncate", title: part.name, children: part.name })
2564
+ /* @__PURE__ */ jsx14(FileText, { size: 12, className: "shrink-0" }),
2565
+ /* @__PURE__ */ jsx14("span", { className: "max-w-56 truncate", title: part.name, children: part.name })
1967
2566
  ]
1968
2567
  },
1969
2568
  `${part.name}-${part.data.length}`
1970
2569
  )) }),
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" })
2570
+ 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 }) }),
2571
+ 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: [
2572
+ /* @__PURE__ */ jsx14(LoaderCircle, { size: 11, className: "animate-spin", "aria-hidden": "true" }),
2573
+ /* @__PURE__ */ jsx14("span", { children: "\u53D1\u9001\u4E2D" })
1975
2574
  ] })
1976
2575
  ] }) });
1977
2576
  }
@@ -1980,11 +2579,11 @@ function ErrorMessageBlock({
1980
2579
  className
1981
2580
  }) {
1982
2581
  const text = getTextContent2(message.content);
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 }) });
2582
+ 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 }) });
1984
2583
  }
1985
2584
 
1986
2585
  // src/components/MessageList.tsx
1987
- import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
2586
+ import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
1988
2587
  function parseModeChange(message) {
1989
2588
  if (message.kind !== "mode_change" || typeof message.content !== "string") {
1990
2589
  return null;
@@ -2027,7 +2626,7 @@ function MessageList({
2027
2626
  className,
2028
2627
  sessionId
2029
2628
  }) {
2030
- const renderBlocks = useMemo6(() => {
2629
+ const renderBlocks = useMemo7(() => {
2031
2630
  const visible = messages.filter((message) => {
2032
2631
  if ((message.loop_name ?? "root") !== "root") return false;
2033
2632
  if (isHiddenInternalMessage(message)) return false;
@@ -2100,24 +2699,24 @@ function MessageList({
2100
2699
  }
2101
2700
  return blocks;
2102
2701
  }, [messages, isStreaming]);
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" })
2702
+ 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: [
2703
+ /* @__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: [
2704
+ renderBlocks.length === 0 ? emptyState ?? /* @__PURE__ */ jsxs13("div", { className: "blade-chat-empty", children: [
2705
+ /* @__PURE__ */ jsx15(MessageSquare, { size: 40, strokeWidth: 1.5 }),
2706
+ /* @__PURE__ */ jsx15("span", { className: "text-base font-medium", children: "\u5F00\u59CB\u5BF9\u8BDD" }),
2707
+ /* @__PURE__ */ jsx15("span", { className: "text-sm opacity-60", children: "\u5728\u4E0B\u65B9\u8F93\u5165\u6D88\u606F\u5F00\u59CB\u804A\u5929" })
2109
2708
  ] }) : renderBlocks.map((block) => {
2110
2709
  if (block.type === "message") {
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);
2710
+ 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);
2112
2711
  }
2113
2712
  if (block.type === "assistant_turn") {
2114
- return /* @__PURE__ */ jsx13("div", { "data-entry-id": block.messages[0]?.entry_id, children: /* @__PURE__ */ jsx13(
2713
+ return /* @__PURE__ */ jsx15("div", { "data-entry-id": block.messages[0]?.entry_id, children: /* @__PURE__ */ jsx15(
2115
2714
  RenderErrorBoundary,
2116
2715
  {
2117
2716
  label: "\u52A9\u624B\u6D88\u606F",
2118
2717
  details: block.key,
2119
2718
  resetKey: getMessageResetSignature(block.messages),
2120
- children: /* @__PURE__ */ jsx13(
2719
+ children: /* @__PURE__ */ jsx15(
2121
2720
  AssistantTurnBlock,
2122
2721
  {
2123
2722
  messages: block.messages,
@@ -2133,24 +2732,24 @@ function MessageList({
2133
2732
  ) }, block.key);
2134
2733
  }
2135
2734
  if (block.type === "compaction") {
2136
- return /* @__PURE__ */ jsxs11(
2735
+ return /* @__PURE__ */ jsxs13(
2137
2736
  "div",
2138
2737
  {
2139
2738
  className: "flex items-center gap-2 text-xs text-[hsl(var(--muted-foreground))]",
2140
2739
  children: [
2141
- /* @__PURE__ */ jsx13(Layers, { size: 12 }),
2142
- /* @__PURE__ */ jsx13("span", { children: "\u4E0A\u4E0B\u6587\u5DF2\u538B\u7F29" })
2740
+ /* @__PURE__ */ jsx15(Layers, { size: 12 }),
2741
+ /* @__PURE__ */ jsx15("span", { children: "\u4E0A\u4E0B\u6587\u5DF2\u538B\u7F29" })
2143
2742
  ]
2144
2743
  },
2145
2744
  block.key
2146
2745
  );
2147
2746
  }
2148
- return /* @__PURE__ */ jsx13(PlanningDivider, { kind: block.kind }, block.key);
2747
+ return /* @__PURE__ */ jsx15(PlanningDivider, { kind: block.kind }, block.key);
2149
2748
  }),
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
2749
+ 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
2151
2750
  ] }) }) }),
2152
- /* @__PURE__ */ jsx13(AutoScrollOnUserSend, { userMessageCount: messages.filter((m) => isUserMessage(m)).length }),
2153
- /* @__PURE__ */ jsx13(ScrollToBottomButton, {})
2751
+ /* @__PURE__ */ jsx15(AutoScrollOnUserSend, { userMessageCount: messages.filter((m) => isUserMessage(m)).length }),
2752
+ /* @__PURE__ */ jsx15(ScrollToBottomButton, {})
2154
2753
  ] }) });
2155
2754
  }
2156
2755
  function AutoScrollOnUserSend({ userMessageCount }) {
@@ -2166,7 +2765,7 @@ function AutoScrollOnUserSend({ userMessageCount }) {
2166
2765
  }
2167
2766
  function ScrollToBottomButton() {
2168
2767
  const { isAtBottom, scrollToBottom } = useStickToBottomContext();
2169
- const [visible, setVisible] = useState10(false);
2768
+ const [visible, setVisible] = useState11(false);
2170
2769
  const hideTimerRef = useRef6(null);
2171
2770
  useEffect6(() => {
2172
2771
  if (isAtBottom) {
@@ -2190,7 +2789,7 @@ function ScrollToBottomButton() {
2190
2789
  }
2191
2790
  };
2192
2791
  }, [isAtBottom]);
2193
- const handleClick = useCallback3(() => {
2792
+ const handleClick = useCallback5(() => {
2194
2793
  if (hideTimerRef.current) {
2195
2794
  clearTimeout(hideTimerRef.current);
2196
2795
  hideTimerRef.current = null;
@@ -2199,7 +2798,7 @@ function ScrollToBottomButton() {
2199
2798
  scrollToBottom();
2200
2799
  }, [scrollToBottom]);
2201
2800
  if (!visible) return null;
2202
- return /* @__PURE__ */ jsxs11(
2801
+ return /* @__PURE__ */ jsxs13(
2203
2802
  "button",
2204
2803
  {
2205
2804
  type: "button",
@@ -2207,34 +2806,108 @@ function ScrollToBottomButton() {
2207
2806
  "aria-label": "\u6EDA\u52A8\u5230\u5E95\u90E8",
2208
2807
  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))]",
2209
2808
  children: [
2210
- /* @__PURE__ */ jsx13(ChevronDown, { size: 14 }),
2211
- /* @__PURE__ */ jsx13("span", { className: "blade-chat-scroll-bottom-label", children: "\u6EDA\u52A8\u5230\u5E95\u90E8" })
2809
+ /* @__PURE__ */ jsx15(ChevronDown, { size: 14 }),
2810
+ /* @__PURE__ */ jsx15("span", { className: "blade-chat-scroll-bottom-label", children: "\u6EDA\u52A8\u5230\u5E95\u90E8" })
2212
2811
  ]
2213
2812
  }
2214
2813
  );
2215
2814
  }
2216
2815
  function PlanningDivider({ kind }) {
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" })
2816
+ return /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-3 py-1", children: [
2817
+ /* @__PURE__ */ jsx15("div", { className: "h-px flex-1 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent" }),
2818
+ /* @__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: [
2819
+ /* @__PURE__ */ jsx15(Lightbulb, { size: 12 }),
2820
+ /* @__PURE__ */ jsx15("span", { children: kind === "enter" ? "\u8FDB\u5165\u89C4\u5212\u6A21\u5F0F" : "\u89C4\u5212\u5B8C\u6210" })
2222
2821
  ] }),
2223
- /* @__PURE__ */ jsx13("div", { className: "h-px flex-1 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent" })
2822
+ /* @__PURE__ */ jsx15("div", { className: "h-px flex-1 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent" })
2224
2823
  ] });
2225
2824
  }
2226
2825
 
2227
- // src/components/ChatView.tsx
2228
- import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
2826
+ // src/components/ChatSurface.tsx
2827
+ import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
2229
2828
  function themeAttr(theme) {
2230
2829
  return theme === "dark" ? "dark" : void 0;
2231
2830
  }
2831
+ function ChatSurface({
2832
+ theme,
2833
+ classNames,
2834
+ renderers,
2835
+ slots,
2836
+ placeholder,
2837
+ connection,
2838
+ errorMessage,
2839
+ messages,
2840
+ isStreaming,
2841
+ isStopping,
2842
+ inputText,
2843
+ onInputChange,
2844
+ onSend,
2845
+ onStop,
2846
+ sessionStatus,
2847
+ askAnswers,
2848
+ onAnswer,
2849
+ sessionId,
2850
+ beforeInput,
2851
+ banner
2852
+ }) {
2853
+ return /* @__PURE__ */ jsxs14(
2854
+ "div",
2855
+ {
2856
+ "data-theme": themeAttr(theme),
2857
+ className: cn(
2858
+ "blade-chat flex min-h-0 flex-1 flex-col overflow-hidden",
2859
+ classNames?.root
2860
+ ),
2861
+ children: [
2862
+ /* @__PURE__ */ jsx16(ConnectionBanner, { connection, className: classNames?.banner }),
2863
+ banner,
2864
+ errorMessage && /* @__PURE__ */ jsxs14("div", { className: "blade-chat-error-bar flex items-start gap-2 border-b px-4 py-3 text-sm", children: [
2865
+ /* @__PURE__ */ jsx16(CircleAlert, { size: 16, className: "mt-0.5 shrink-0" }),
2866
+ /* @__PURE__ */ jsx16("span", { children: errorMessage })
2867
+ ] }),
2868
+ slots?.header,
2869
+ /* @__PURE__ */ jsx16(
2870
+ MessageList,
2871
+ {
2872
+ messages,
2873
+ isStreaming,
2874
+ sessionStatus,
2875
+ askAnswers,
2876
+ onAnswer,
2877
+ toolCallRenderer: renderers?.toolCall,
2878
+ emptyState: slots?.emptyState,
2879
+ className: classNames?.messageList,
2880
+ sessionId
2881
+ }
2882
+ ),
2883
+ beforeInput,
2884
+ /* @__PURE__ */ jsx16(
2885
+ ChatInput,
2886
+ {
2887
+ value: inputText,
2888
+ onValueChange: onInputChange,
2889
+ onSend,
2890
+ onStop,
2891
+ isStreaming,
2892
+ isStopping,
2893
+ placeholder,
2894
+ className: classNames?.chatInput
2895
+ }
2896
+ ),
2897
+ slots?.footer
2898
+ ]
2899
+ }
2900
+ );
2901
+ }
2902
+
2903
+ // src/components/AgentChat.tsx
2904
+ import { Fragment as Fragment2, jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
2232
2905
  function isUnauthorizedError(error) {
2233
2906
  return error instanceof BladeApiError && error.status === 401;
2234
2907
  }
2235
2908
  function LoginCard({ client, onLoggedIn }) {
2236
- const [loggingIn, setLoggingIn] = useState11(false);
2237
- const [loginError, setLoginError] = useState11(null);
2909
+ const [loggingIn, setLoggingIn] = useState12(false);
2910
+ const [loginError, setLoginError] = useState12(null);
2238
2911
  const handleLogin = async () => {
2239
2912
  setLoggingIn(true);
2240
2913
  setLoginError(null);
@@ -2247,11 +2920,11 @@ function LoginCard({ client, onLoggedIn }) {
2247
2920
  setLoggingIn(false);
2248
2921
  }
2249
2922
  };
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(
2923
+ 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: [
2924
+ /* @__PURE__ */ jsx17(LockKeyhole, { size: 28, className: "text-[hsl(var(--muted-foreground))]" }),
2925
+ /* @__PURE__ */ jsx17("div", { className: "text-base font-medium text-[hsl(var(--foreground))]", children: "\u9700\u8981\u767B\u5F55\u540E\u4F7F\u7528" }),
2926
+ /* @__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" }),
2927
+ /* @__PURE__ */ jsx17(
2255
2928
  "button",
2256
2929
  {
2257
2930
  type: "button",
@@ -2261,20 +2934,20 @@ function LoginCard({ client, onLoggedIn }) {
2261
2934
  children: loggingIn ? "\u767B\u5F55\u4E2D\u2026" : "\u767B\u5F55"
2262
2935
  }
2263
2936
  ),
2264
- loginError && /* @__PURE__ */ jsx14("div", { className: "text-xs text-[hsl(var(--destructive))]", children: loginError })
2937
+ loginError && /* @__PURE__ */ jsx17("div", { className: "text-xs text-[hsl(var(--destructive))]", children: loginError })
2265
2938
  ] }) });
2266
2939
  }
2267
- function ChatView(props) {
2940
+ function AgentChat(props) {
2268
2941
  const client = useBladeClient();
2269
- const [attempt, setAttempt] = useState11(0);
2270
- const [needLogin, setNeedLogin] = useState11(() => !client.hasToken());
2942
+ const [attempt, setAttempt] = useState12(0);
2943
+ const [needLogin, setNeedLogin] = useState12(() => !client.hasToken());
2271
2944
  if (needLogin) {
2272
- return /* @__PURE__ */ jsx14(
2945
+ return /* @__PURE__ */ jsx17(
2273
2946
  "div",
2274
2947
  {
2275
2948
  "data-theme": themeAttr(props.theme),
2276
2949
  className: cn("blade-chat flex min-h-0 flex-1 flex-col", props.classNames?.root),
2277
- children: /* @__PURE__ */ jsx14(
2950
+ children: /* @__PURE__ */ jsx17(
2278
2951
  LoginCard,
2279
2952
  {
2280
2953
  client,
@@ -2287,7 +2960,7 @@ function ChatView(props) {
2287
2960
  }
2288
2961
  );
2289
2962
  }
2290
- return /* @__PURE__ */ jsx14(ChatSessionView, { ...props, onUnauthorized: () => setNeedLogin(true) }, attempt);
2963
+ return /* @__PURE__ */ jsx17(ChatSessionView, { ...props, onUnauthorized: () => setNeedLogin(true) }, attempt);
2291
2964
  }
2292
2965
  function ChatSessionView({
2293
2966
  sessionId,
@@ -2306,8 +2979,9 @@ function ChatSessionView({
2306
2979
  createOptions,
2307
2980
  onSessionCreated
2308
2981
  });
2309
- const [stopRequested, setStopRequested] = useState11(false);
2310
- const [inputText, setInputText] = useState11("");
2982
+ const replay = useReplay(session);
2983
+ const [stopRequested, setStopRequested] = useState12(false);
2984
+ const [inputText, setInputText] = useState12("");
2311
2985
  useEffect7(() => {
2312
2986
  if (session) {
2313
2987
  onSessionReady?.(session);
@@ -2346,7 +3020,7 @@ ${content}`);
2346
3020
  const isStreaming = state?.isStreaming ?? false;
2347
3021
  const isStopping = stopRequested && isStreaming;
2348
3022
  const connectError = error && !isUnauthorizedError(error) ? error.message || "\u8FDE\u63A5\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5" : null;
2349
- const errorMessage = connectError ?? state?.errorMessage ?? null;
3023
+ const errorMessage = connectError ?? state?.errorMessage ?? replay.error?.message ?? null;
2350
3024
  const handleSend = (text) => {
2351
3025
  setStopRequested(false);
2352
3026
  return session?.send(text, { mode: state?.mode ?? void 0 }) ?? Promise.resolve(false);
@@ -2355,73 +3029,281 @@ ${content}`);
2355
3029
  setStopRequested(true);
2356
3030
  void session?.stop();
2357
3031
  };
2358
- return /* @__PURE__ */ jsxs12(
2359
- "div",
3032
+ return /* @__PURE__ */ jsx17(
3033
+ ChatSurface,
2360
3034
  {
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,
3035
+ theme,
3036
+ classNames,
3037
+ renderers,
3038
+ slots,
3039
+ placeholder,
3040
+ connection: state?.connection ?? "connecting",
3041
+ banner: /* @__PURE__ */ jsxs15(Fragment2, { children: [
3042
+ /* @__PURE__ */ jsx17(
3043
+ ReplayBar,
2369
3044
  {
2370
- connection: state?.connection ?? "connecting",
2371
- className: classNames?.banner
3045
+ isReplay: replay.isReplay,
3046
+ speed: replay.speed,
3047
+ canControl: replay.canControl,
3048
+ onSpeedChange: (next) => void replay.setSpeed(next),
3049
+ onExit: () => void replay.exitToAutonomous()
2372
3050
  }
2373
3051
  ),
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,
3052
+ /* @__PURE__ */ jsx17(ReplayMismatchPrompt, { mismatch: replay.mismatch })
3053
+ ] }),
3054
+ errorMessage,
3055
+ messages: state?.messages ?? [],
3056
+ isStreaming,
3057
+ isStopping,
3058
+ inputText,
3059
+ onInputChange: setInputText,
3060
+ onSend: handleSend,
3061
+ onStop: handleStop,
3062
+ sessionStatus: state?.status ?? void 0,
3063
+ askAnswers: state?.askAnswers,
3064
+ onAnswer: (answer, toolCallId, answerData) => {
3065
+ void session?.send(answer, {
3066
+ mode: state?.mode ?? void 0,
3067
+ askUserAnswer: { tool_call_id: toolCallId, ...answerData }
3068
+ });
3069
+ },
3070
+ sessionId: session?.sessionId
3071
+ }
3072
+ );
3073
+ }
3074
+
3075
+ // src/components/LlmChat.tsx
3076
+ import { useEffect as useEffect8, useMemo as useMemo8, useState as useState14 } from "react";
3077
+
3078
+ // src/components/LlmAdvancedSettings.tsx
3079
+ import { useState as useState13 } from "react";
3080
+ import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
3081
+ var FIELDS = [
3082
+ { id: "baseURL", label: "\u6A21\u578B\u670D\u52A1\u5730\u5740", placeholder: "http://\u5185\u7F51\u5730\u5740/v1" },
3083
+ { id: "model", label: "\u6A21\u578B", placeholder: "\u6A21\u578B\u540D\u79F0" },
3084
+ { id: "apiKey", label: "\u5BC6\u94A5", placeholder: "\u7559\u7A7A\u8868\u793A\u4E0D\u6539", secret: true }
3085
+ ];
3086
+ function normalizeAdvanced(value) {
3087
+ if (!value) return null;
3088
+ const config = value === true ? {} : value;
3089
+ return {
3090
+ baseURL: config.baseURL ?? true,
3091
+ model: config.model ?? true,
3092
+ apiKey: config.apiKey ?? false,
3093
+ storage: config.storage ?? "local"
3094
+ };
3095
+ }
3096
+ function storageKeyFor(settings, baseURL) {
3097
+ const explicit = typeof settings === "object" ? settings.storageKey : void 0;
3098
+ return `blade-llm-override:${explicit ?? baseURL}`;
3099
+ }
3100
+ function readOverride(settings, baseURL) {
3101
+ const normalized = normalizeAdvanced(settings);
3102
+ if (!normalized || normalized.storage !== "local" || typeof localStorage === "undefined") return {};
3103
+ try {
3104
+ const raw = localStorage.getItem(storageKeyFor(settings, baseURL));
3105
+ if (!raw) return {};
3106
+ const stored = JSON.parse(raw);
3107
+ return Object.fromEntries(
3108
+ Object.entries(stored).filter(([key]) => normalized[key])
3109
+ );
3110
+ } catch {
3111
+ return {};
3112
+ }
3113
+ }
3114
+ function writeOverride(settings, baseURL, override) {
3115
+ const normalized = normalizeAdvanced(settings);
3116
+ if (!normalized || normalized.storage !== "local" || typeof localStorage === "undefined") return;
3117
+ try {
3118
+ const key = storageKeyFor(settings, baseURL);
3119
+ if (Object.keys(override).length === 0) localStorage.removeItem(key);
3120
+ else localStorage.setItem(key, JSON.stringify(override));
3121
+ } catch {
3122
+ }
3123
+ }
3124
+ function LlmAdvancedSettingsBar({ settings, defaults, override, onChange }) {
3125
+ const normalized = normalizeAdvanced(settings);
3126
+ const [open, setOpen] = useState13(false);
3127
+ const [draft, setDraft] = useState13(override);
3128
+ if (!normalized) return null;
3129
+ const fields = FIELDS.filter((field) => normalized[field.id]);
3130
+ const dirty = Object.keys(override).length > 0;
3131
+ return /* @__PURE__ */ jsxs16("div", { className: "blade-chat-advanced border-t border-[hsl(var(--border))] px-4 py-2 text-xs", children: [
3132
+ /* @__PURE__ */ jsxs16(
3133
+ "button",
3134
+ {
3135
+ type: "button",
3136
+ onClick: () => {
3137
+ setDraft(override);
3138
+ setOpen((value) => !value);
3139
+ },
3140
+ className: "flex items-center gap-1.5 text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))]",
3141
+ children: [
3142
+ /* @__PURE__ */ jsx18(Settings2, { size: 13 }),
3143
+ "\u9AD8\u7EA7\u8BBE\u7F6E",
3144
+ dirty && /* @__PURE__ */ jsx18("span", { className: "text-[hsl(var(--primary))]", children: "\uFF08\u5DF2\u81EA\u5B9A\u4E49\uFF09" })
3145
+ ]
3146
+ }
3147
+ ),
3148
+ open && /* @__PURE__ */ jsxs16("div", { className: "mt-2 flex flex-col gap-2", children: [
3149
+ fields.map((field) => /* @__PURE__ */ jsxs16("label", { className: "flex flex-col gap-1", children: [
3150
+ /* @__PURE__ */ jsx18("span", { className: "text-[hsl(var(--muted-foreground))]", children: field.label }),
3151
+ /* @__PURE__ */ jsx18(
3152
+ "input",
2381
3153
  {
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
- });
3154
+ type: field.secret ? "password" : "text",
3155
+ value: draft[field.id] ?? "",
3156
+ placeholder: field.id === "apiKey" ? field.placeholder : defaults[field.id] || field.placeholder,
3157
+ onChange: (event) => setDraft({ ...draft, [field.id]: event.target.value }),
3158
+ className: "rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--background))] px-2 py-1 text-xs text-[hsl(var(--foreground))] outline-none"
3159
+ }
3160
+ )
3161
+ ] }, field.id)),
3162
+ 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" }),
3163
+ /* @__PURE__ */ jsxs16("div", { className: "flex gap-2", children: [
3164
+ /* @__PURE__ */ jsx18(
3165
+ "button",
3166
+ {
3167
+ type: "button",
3168
+ onClick: () => {
3169
+ const next = Object.fromEntries(
3170
+ Object.entries(draft).filter(([, value]) => String(value ?? "").trim() !== "")
3171
+ );
3172
+ onChange(next);
3173
+ setOpen(false);
2391
3174
  },
2392
- toolCallRenderer: renderers?.toolCall,
2393
- emptyState: slots?.emptyState,
2394
- className: classNames?.messageList,
2395
- sessionId: session?.sessionId
3175
+ className: "rounded-md bg-[hsl(var(--primary))] px-2.5 py-1 font-medium text-[hsl(var(--primary-foreground))]",
3176
+ children: "\u4FDD\u5B58"
2396
3177
  }
2397
3178
  ),
2398
- /* @__PURE__ */ jsx14(
2399
- ChatInput,
3179
+ /* @__PURE__ */ jsx18(
3180
+ "button",
2400
3181
  {
2401
- value: inputText,
2402
- onValueChange: setInputText,
2403
- onSend: handleSend,
2404
- onStop: handleStop,
2405
- isStreaming,
2406
- isStopping,
2407
- placeholder,
2408
- className: classNames?.chatInput
3182
+ type: "button",
3183
+ onClick: () => {
3184
+ setDraft({});
3185
+ onChange({});
3186
+ setOpen(false);
3187
+ },
3188
+ className: "rounded-md border border-[hsl(var(--border))] px-2.5 py-1 text-[hsl(var(--foreground))]",
3189
+ children: "\u6062\u590D\u9ED8\u8BA4"
2409
3190
  }
2410
- ),
2411
- slots?.footer
2412
- ]
3191
+ )
3192
+ ] })
3193
+ ] })
3194
+ ] });
3195
+ }
3196
+
3197
+ // src/components/LlmChat.tsx
3198
+ import { jsx as jsx19 } from "react/jsx-runtime";
3199
+ function LlmChat({
3200
+ classNames,
3201
+ renderers,
3202
+ slots,
3203
+ placeholder,
3204
+ theme,
3205
+ onReady,
3206
+ advanced,
3207
+ onOverrideChange,
3208
+ ...options
3209
+ }) {
3210
+ const [override, setOverride] = useState14(() => readOverride(advanced, options.baseURL));
3211
+ const effective = { ...options, ...override };
3212
+ const { messages, isStreaming, error, send, stop, reset } = useLlmChat(effective);
3213
+ const [inputText, setInputText] = useState14("");
3214
+ const [stopRequested, setStopRequested] = useState14(false);
3215
+ const handle = useMemo8(
3216
+ () => ({
3217
+ insertText: (text) => setInputText((prev) => prev ? `${prev}
3218
+ ${text}` : text),
3219
+ send,
3220
+ reset
3221
+ }),
3222
+ [send, reset]
3223
+ );
3224
+ useEffect8(() => {
3225
+ onReady?.(handle);
3226
+ }, [handle, onReady]);
3227
+ return /* @__PURE__ */ jsx19(
3228
+ ChatSurface,
3229
+ {
3230
+ theme,
3231
+ classNames,
3232
+ renderers,
3233
+ slots,
3234
+ placeholder,
3235
+ connection: "connected",
3236
+ errorMessage: error,
3237
+ messages,
3238
+ isStreaming,
3239
+ isStopping: stopRequested && isStreaming,
3240
+ inputText,
3241
+ onInputChange: setInputText,
3242
+ onSend: async (text) => {
3243
+ setStopRequested(false);
3244
+ if (!text.trim() || isStreaming) return false;
3245
+ void send(text);
3246
+ return true;
3247
+ },
3248
+ onStop: () => {
3249
+ setStopRequested(true);
3250
+ stop();
3251
+ },
3252
+ beforeInput: advanced ? /* @__PURE__ */ jsx19(
3253
+ LlmAdvancedSettingsBar,
3254
+ {
3255
+ settings: advanced,
3256
+ defaults: { baseURL: options.baseURL, model: options.model },
3257
+ override,
3258
+ onChange: (next) => {
3259
+ setOverride(next);
3260
+ writeOverride(advanced, options.baseURL, next);
3261
+ onOverrideChange?.(next);
3262
+ }
3263
+ }
3264
+ ) : void 0
2413
3265
  }
2414
3266
  );
2415
3267
  }
2416
3268
 
3269
+ // src/components/ChatView.tsx
3270
+ import { jsx as jsx20 } from "react/jsx-runtime";
3271
+ function ChatView(props) {
3272
+ const { mode, llm, onLlmReady, ...rest } = props;
3273
+ if (mode === "llm") {
3274
+ if (!llm) {
3275
+ throw new Error('ChatView: mode="llm" \u9700\u8981\u540C\u65F6\u4F20 llm={{ baseURL, model }}');
3276
+ }
3277
+ return /* @__PURE__ */ jsx20(
3278
+ LlmChat,
3279
+ {
3280
+ ...llm,
3281
+ classNames: rest.classNames,
3282
+ renderers: rest.renderers,
3283
+ slots: rest.slots,
3284
+ placeholder: rest.placeholder,
3285
+ theme: rest.theme,
3286
+ onReady: onLlmReady
3287
+ }
3288
+ );
3289
+ }
3290
+ return /* @__PURE__ */ jsx20(AgentChat, { ...rest });
3291
+ }
3292
+
2417
3293
  // src/index.ts
2418
3294
  export * from "@blade-hq/agent-client";
2419
3295
  export {
3296
+ AgentChat,
2420
3297
  BladeProvider,
2421
3298
  ChatView,
3299
+ LlmChat,
2422
3300
  MarkdownContent,
3301
+ ReplayBar,
3302
+ ReplayMismatchPrompt,
2423
3303
  useAgentSession,
2424
- useBladeClient
3304
+ useBladeClient,
3305
+ useLlmChat,
3306
+ useReplay
2425
3307
  };
2426
3308
  /*! Bundled license information:
2427
3309
 
@@ -2429,6 +3311,7 @@ lucide-react/dist/esm/shared/src/utils.js:
2429
3311
  lucide-react/dist/esm/defaultAttributes.js:
2430
3312
  lucide-react/dist/esm/Icon.js:
2431
3313
  lucide-react/dist/esm/createLucideIcon.js:
3314
+ lucide-react/dist/esm/icons/archive.js:
2432
3315
  lucide-react/dist/esm/icons/arrow-up.js:
2433
3316
  lucide-react/dist/esm/icons/bot.js:
2434
3317
  lucide-react/dist/esm/icons/brain.js:
@@ -2437,13 +3320,22 @@ lucide-react/dist/esm/icons/chevron-down.js:
2437
3320
  lucide-react/dist/esm/icons/chevron-right.js:
2438
3321
  lucide-react/dist/esm/icons/circle-alert.js:
2439
3322
  lucide-react/dist/esm/icons/copy.js:
3323
+ lucide-react/dist/esm/icons/download.js:
3324
+ lucide-react/dist/esm/icons/file-code-2.js:
3325
+ lucide-react/dist/esm/icons/file-spreadsheet.js:
2440
3326
  lucide-react/dist/esm/icons/file-text.js:
3327
+ lucide-react/dist/esm/icons/file.js:
3328
+ lucide-react/dist/esm/icons/film.js:
3329
+ lucide-react/dist/esm/icons/image.js:
2441
3330
  lucide-react/dist/esm/icons/layers.js:
2442
3331
  lucide-react/dist/esm/icons/lightbulb.js:
2443
3332
  lucide-react/dist/esm/icons/loader-circle.js:
2444
3333
  lucide-react/dist/esm/icons/lock-keyhole.js:
2445
3334
  lucide-react/dist/esm/icons/message-square-more.js:
2446
3335
  lucide-react/dist/esm/icons/message-square.js:
3336
+ lucide-react/dist/esm/icons/music.js:
3337
+ lucide-react/dist/esm/icons/play.js:
3338
+ lucide-react/dist/esm/icons/settings-2.js:
2447
3339
  lucide-react/dist/esm/icons/square.js:
2448
3340
  lucide-react/dist/esm/icons/triangle-alert.js:
2449
3341
  lucide-react/dist/esm/icons/x.js: