@blade-hq/agent-react 2610.0.0-beta.31 → 2610.0.0-beta.33

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
@@ -17,6 +17,9 @@ function useBladeClient() {
17
17
  }
18
18
  return client;
19
19
  }
20
+ function useOptionalBladeClient() {
21
+ return useContext(BladeClientContext);
22
+ }
20
23
 
21
24
  // src/hooks/use-agent-session.ts
22
25
  import { useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
@@ -30,11 +33,14 @@ function useAgentSession(sessionId, options = {}) {
30
33
  const connRef = useRef({
31
34
  id: null,
32
35
  session: null,
36
+ cleanup: null,
33
37
  gen: 0
34
38
  });
35
39
  const createdIdPromiseRef = useRef(null);
36
40
  const onCreatedRef = useRef(options.onSessionCreated);
37
41
  onCreatedRef.current = options.onSessionCreated;
42
+ const onConnectedRef = useRef(options.onSessionConnected);
43
+ onConnectedRef.current = options.onSessionConnected;
38
44
  const createOptionsRef = useRef(options.createOptions);
39
45
  createOptionsRef.current = options.createOptions;
40
46
  const sessionIdRef = useRef(sessionId);
@@ -42,6 +48,7 @@ function useAgentSession(sessionId, options = {}) {
42
48
  const connect = useMemo(() => {
43
49
  return (targetId) => {
44
50
  const gen = ++connRef.current.gen;
51
+ let pendingCleanup = null;
45
52
  const idPromise = targetId ? Promise.resolve(targetId) : (
46
53
  // biome-ignore lint/suspicious/noAssignInExpressions: ??= 挂 ref 是 StrictMode 下"只创建一次"的关键
47
54
  createdIdPromiseRef.current ??= client.sessions.create(createOptionsRef.current ?? {}).then((created) => {
@@ -51,16 +58,25 @@ function useAgentSession(sessionId, options = {}) {
51
58
  return id;
52
59
  })
53
60
  );
54
- idPromise.then((id) => client.hub.connect(id)).then((next) => {
61
+ idPromise.then(
62
+ (id) => client.hub.connect(id, {
63
+ setup: (next) => {
64
+ pendingCleanup = onConnectedRef.current?.(next) ?? null;
65
+ }
66
+ })
67
+ ).then((next) => {
55
68
  if (connRef.current.gen !== gen) {
69
+ pendingCleanup?.();
56
70
  next.dispose();
57
71
  return;
58
72
  }
59
73
  connRef.current.id = next.sessionId;
60
74
  connRef.current.session = next;
75
+ connRef.current.cleanup = pendingCleanup;
61
76
  setSession(next);
62
77
  setError(null);
63
78
  }).catch((err) => {
79
+ pendingCleanup?.();
64
80
  if (connRef.current.gen !== gen) return;
65
81
  createdIdPromiseRef.current = null;
66
82
  setError(err instanceof Error ? err : new Error(String(err)));
@@ -72,8 +88,11 @@ function useAgentSession(sessionId, options = {}) {
72
88
  return () => {
73
89
  connRef.current.gen++;
74
90
  const toRelease = connRef.current.session;
91
+ const cleanup = connRef.current.cleanup;
75
92
  connRef.current.id = null;
76
93
  connRef.current.session = null;
94
+ connRef.current.cleanup = null;
95
+ cleanup?.();
77
96
  setSession(null);
78
97
  if (toRelease) setTimeout(() => toRelease.dispose(), DISPOSE_DELAY_MS);
79
98
  };
@@ -83,8 +102,11 @@ function useAgentSession(sessionId, options = {}) {
83
102
  if (connRef.current.id === null) return;
84
103
  if (sessionId === connRef.current.id) return;
85
104
  const previous = connRef.current.session;
105
+ const cleanup = connRef.current.cleanup;
86
106
  connRef.current.id = null;
87
107
  connRef.current.session = null;
108
+ connRef.current.cleanup = null;
109
+ cleanup?.();
88
110
  if (previous) setTimeout(() => previous.dispose(), DISPOSE_DELAY_MS);
89
111
  connect(sessionId);
90
112
  }, [sessionId, connect]);
@@ -900,6 +922,17 @@ var CircleAlert = createLucideIcon("CircleAlert", [
900
922
  ["line", { x1: "12", x2: "12.01", y1: "16", y2: "16", key: "4dfq90" }]
901
923
  ]);
902
924
 
925
+ // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/circle-dot.js
926
+ var CircleDot = createLucideIcon("CircleDot", [
927
+ ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
928
+ ["circle", { cx: "12", cy: "12", r: "1", key: "41hilf" }]
929
+ ]);
930
+
931
+ // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/circle.js
932
+ var Circle = createLucideIcon("Circle", [
933
+ ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }]
934
+ ]);
935
+
903
936
  // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/copy.js
904
937
  var Copy = createLucideIcon("Copy", [
905
938
  ["rect", { width: "14", height: "14", x: "8", y: "8", rx: "2", ry: "2", key: "17jyea" }],
@@ -993,6 +1026,15 @@ var Lightbulb = createLucideIcon("Lightbulb", [
993
1026
  ["path", { d: "M10 22h4", key: "ceow96" }]
994
1027
  ]);
995
1028
 
1029
+ // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/list-checks.js
1030
+ var ListChecks = createLucideIcon("ListChecks", [
1031
+ ["path", { d: "m3 17 2 2 4-4", key: "1jhpwq" }],
1032
+ ["path", { d: "m3 7 2 2 4-4", key: "1obspn" }],
1033
+ ["path", { d: "M13 6h8", key: "15sg57" }],
1034
+ ["path", { d: "M13 12h8", key: "h98zly" }],
1035
+ ["path", { d: "M13 18h8", key: "oe0vm4" }]
1036
+ ]);
1037
+
996
1038
  // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/loader-circle.js
997
1039
  var LoaderCircle = createLucideIcon("LoaderCircle", [
998
1040
  ["path", { d: "M21 12a9 9 0 1 1-6.219-8.56", key: "13zald" }]
@@ -1102,7 +1144,7 @@ var X = createLucideIcon("X", [
1102
1144
  ]);
1103
1145
 
1104
1146
  // src/components/AgentChat.tsx
1105
- import { useCallback as useCallback8, useEffect as useEffect11, useMemo as useMemo8, useState as useState13 } from "react";
1147
+ import { useCallback as useCallback8, useEffect as useEffect12, useMemo as useMemo8, useState as useState14 } from "react";
1106
1148
 
1107
1149
  // src/lib/utils.ts
1108
1150
  function cn(...inputs) {
@@ -1229,11 +1271,349 @@ function ReplayMismatchPrompt({ mismatch, className }) {
1229
1271
  );
1230
1272
  }
1231
1273
 
1274
+ // src/components/PlanUpdateBlock.tsx
1275
+ import { useEffect as useEffect4, useRef as useRef4, useState as useState4 } from "react";
1276
+
1277
+ // src/components/display-utils.ts
1278
+ var TOOL_NAME_ALIASES = {
1279
+ agent: "Agent",
1280
+ ask_user_question: "AskUserQuestion",
1281
+ bash: "Bash",
1282
+ bg_bash: "BgBash",
1283
+ edit: "Edit",
1284
+ exit_plan_mode: "ExitPlanMode",
1285
+ file_edit: "Edit",
1286
+ file_read: "Read",
1287
+ file_write: "Write",
1288
+ finish_task: "FinishTask",
1289
+ glob: "Glob",
1290
+ grep: "Grep",
1291
+ kb_search: "KbSearch",
1292
+ ls: "Ls",
1293
+ multi_edit: "MultiEdit",
1294
+ read: "Read",
1295
+ read_skill: "ReadSkill",
1296
+ update_plan: "UpdatePlan",
1297
+ web_fetch: "WebFetch",
1298
+ web_search: "WebSearch",
1299
+ write: "Write"
1300
+ };
1301
+ var TOOL_DISPLAY_LABELS = {
1302
+ Bash: "\u6267\u884C\u547D\u4EE4",
1303
+ BgBash: "\u540E\u53F0\u6267\u884C\u547D\u4EE4",
1304
+ Read: "\u8BFB\u53D6\u6587\u4EF6",
1305
+ Write: "\u5199\u5165\u6587\u4EF6",
1306
+ Edit: "\u7F16\u8F91\u6587\u4EF6",
1307
+ MultiEdit: "\u7F16\u8F91\u6587\u4EF6",
1308
+ Ls: "\u5217\u51FA\u76EE\u5F55",
1309
+ Glob: "\u5339\u914D\u6587\u4EF6",
1310
+ Grep: "\u641C\u7D22\u6587\u672C",
1311
+ KbSearch: "\u68C0\u7D22\u77E5\u8BC6\u5E93",
1312
+ WebSearch: "\u641C\u7D22\u7F51\u9875",
1313
+ WebFetch: "\u6574\u7406\u7F51\u9875\u5185\u5BB9",
1314
+ Agent: "\u6D3E\u751F\u5B50\u667A\u80FD\u4F53",
1315
+ AskUserQuestion: "\u5411\u7528\u6237\u63D0\u95EE",
1316
+ ReadSkill: "\u8BFB\u53D6\u6280\u80FD",
1317
+ FinishTask: "\u4EFB\u52A1\u5B8C\u6210",
1318
+ ExitPlanMode: "\u63D0\u4EA4\u8BA1\u5212",
1319
+ ListSessions: "\u5217\u51FA\u5386\u53F2\u4F1A\u8BDD",
1320
+ GetSessionHistory: "\u8BFB\u53D6\u4F1A\u8BDD\u5386\u53F2"
1321
+ };
1322
+ function safeParseJson(value) {
1323
+ if (!value) return null;
1324
+ try {
1325
+ return JSON.parse(value);
1326
+ } catch {
1327
+ return null;
1328
+ }
1329
+ }
1330
+ function getStringArgValue(args, key) {
1331
+ const value = args?.[key];
1332
+ return typeof value === "string" ? value.trim() : "";
1333
+ }
1334
+ var SKILL_ENTRY_FILE_NAMES = /* @__PURE__ */ new Set(["skill.md", "command.md"]);
1335
+ var NON_SKILL_DIR_NAMES = /* @__PURE__ */ new Set([".", "..", ".agent", ".agents", ".claude", "skill_data", "skills"]);
1336
+ function getSkillNameFromFilePath(filePath) {
1337
+ if (!filePath) return null;
1338
+ const segments = filePath.split(/[\\/]+/).filter(Boolean);
1339
+ const fileName = segments.pop();
1340
+ if (!fileName || !SKILL_ENTRY_FILE_NAMES.has(fileName.toLowerCase())) return null;
1341
+ const dirName = segments.pop();
1342
+ if (!dirName || NON_SKILL_DIR_NAMES.has(dirName.toLowerCase())) return null;
1343
+ return dirName;
1344
+ }
1345
+ function formatToolName(name) {
1346
+ const trimmed = name.trim();
1347
+ if (!trimmed) return name;
1348
+ const stripped = trimmed.split(":").pop()?.split("/").pop()?.split(".").pop()?.trim() || trimmed;
1349
+ const normalized = stripped.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
1350
+ return TOOL_NAME_ALIASES[normalized] ?? stripped;
1351
+ }
1352
+ function getToolDisplayLabel(toolCall) {
1353
+ const normalized = formatToolName(toolCall.name);
1354
+ const args = safeParseJson(toolCall.arguments);
1355
+ const displayName = toolCall.display_name?.trim() ?? "";
1356
+ const baseLabel = displayName || TOOL_DISPLAY_LABELS[normalized] || normalized;
1357
+ const metaDisplayName = getStringArgValue(args, "_meta_display_name");
1358
+ if (metaDisplayName) {
1359
+ return metaDisplayName;
1360
+ }
1361
+ const description = getStringArgValue(args, "description");
1362
+ if (normalized === "BgBash") {
1363
+ return description ? `\u540E\u53F0\u6267\u884C\uFF1A${description}` : "\u540E\u53F0\u6267\u884C\u547D\u4EE4";
1364
+ }
1365
+ if (normalized === "ReadSkill") {
1366
+ const skillName = getStringArgValue(args, "skill") || getStringArgValue(args, "skill_name");
1367
+ return skillName ? `${baseLabel}\u300C${skillName}\u300D` : baseLabel;
1368
+ }
1369
+ if (normalized === "Read") {
1370
+ const skillName = getSkillNameFromFilePath(
1371
+ getStringArgValue(args, "file_path") || getStringArgValue(args, "path")
1372
+ );
1373
+ if (skillName) return `\u8BFB\u53D6\u6280\u80FD\u300C${skillName}\u300D`;
1374
+ }
1375
+ if (normalized === "FinishTask") {
1376
+ const title = getStringArgValue(args, "title");
1377
+ return title ? `${baseLabel}\uFF1A${title}` : baseLabel;
1378
+ }
1379
+ return description || baseLabel;
1380
+ }
1381
+ function getToolTone(status) {
1382
+ if (status === "error" || status === "cancelled") return "red";
1383
+ if (status === "awaiting_answer") return "amber";
1384
+ if (status === "pending") return "blue";
1385
+ return "emerald";
1386
+ }
1387
+ function getToolStatusLabel(status) {
1388
+ if (status === "pending") return "\u8FD0\u884C\u4E2D";
1389
+ if (status === "awaiting_answer") return "\u7B49\u5F85\u56DE\u7B54";
1390
+ if (status === "error") return "\u9519\u8BEF";
1391
+ if (status === "cancelled") return "\u5DF2\u53D6\u6D88";
1392
+ return "\u5B8C\u6210";
1393
+ }
1394
+ function formatToolDuration(ms) {
1395
+ if (ms < 1e3) return `${Math.round(ms)}ms`;
1396
+ const seconds = ms / 1e3;
1397
+ if (seconds < 60) return `${seconds.toFixed(1)}s`;
1398
+ const minutes = Math.floor(seconds / 60);
1399
+ const remainingSeconds = Math.round(seconds % 60);
1400
+ return remainingSeconds > 0 ? `${minutes}m${remainingSeconds}s` : `${minutes}m`;
1401
+ }
1402
+ function formatToolArgs(args) {
1403
+ try {
1404
+ return JSON.stringify(JSON.parse(args), null, 2);
1405
+ } catch {
1406
+ return args;
1407
+ }
1408
+ }
1409
+ var RESULT_PREVIEW_LIMIT = 4e3;
1410
+ function formatToolResult(result) {
1411
+ const text = typeof result === "string" ? result : JSON.stringify(result, null, 2);
1412
+ if (text == null) return "";
1413
+ if (text.length <= RESULT_PREVIEW_LIMIT) return text;
1414
+ return `${text.slice(0, RESULT_PREVIEW_LIMIT)}
1415
+ \u2026\uFF08\u7ED3\u679C\u8FC7\u957F\uFF0C\u5DF2\u622A\u65AD\uFF09`;
1416
+ }
1417
+
1418
+ // src/components/PlanUpdateBlock.tsx
1419
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1420
+ var PLAN_STEP_STATUSES = /* @__PURE__ */ new Set(["pending", "in_progress", "completed"]);
1421
+ var PLAN_AUTO_COLLAPSE_MS = 5e3;
1422
+ function isPlanUpdateTool(toolCall) {
1423
+ return formatToolName(toolCall.name) === "UpdatePlan";
1424
+ }
1425
+ function getPlanUpdateDisplayState(messages) {
1426
+ let current = null;
1427
+ let latestAttempt = null;
1428
+ let latestAttemptStreaming = false;
1429
+ for (const message of messages) {
1430
+ if ((message.loop_name ?? "root") !== "root") continue;
1431
+ for (const toolCall of message.tool_calls ?? []) {
1432
+ if (!isPlanUpdateTool(toolCall)) continue;
1433
+ latestAttempt = toolCall;
1434
+ latestAttemptStreaming = message.status === "streaming";
1435
+ if (toolCall.status === "done" && parsePlanUpdate(toolCall.arguments)) current = toolCall;
1436
+ }
1437
+ }
1438
+ return {
1439
+ current,
1440
+ updating: latestAttempt?.status === "pending" && latestAttemptStreaming
1441
+ };
1442
+ }
1443
+ function parsePlanUpdate(argumentsJson) {
1444
+ try {
1445
+ const raw = JSON.parse(argumentsJson);
1446
+ if (!raw || typeof raw !== "object") return null;
1447
+ const candidate = raw;
1448
+ if (!Array.isArray(candidate.plan)) return null;
1449
+ const plan = candidate.plan.map((item) => {
1450
+ if (!item || typeof item !== "object") return null;
1451
+ const step = item.step;
1452
+ const status = item.status;
1453
+ if (typeof step !== "string" || !step.trim() || typeof status !== "string" || !PLAN_STEP_STATUSES.has(status)) {
1454
+ return null;
1455
+ }
1456
+ return { step: step.trim(), status };
1457
+ });
1458
+ if (plan.some((item) => item === null)) return null;
1459
+ if (plan.filter((item) => item?.status === "in_progress").length > 1) return null;
1460
+ return { plan };
1461
+ } catch {
1462
+ return null;
1463
+ }
1464
+ }
1465
+ function pickCurrentPlanStep(plan) {
1466
+ return plan.find((item) => item.status === "in_progress") ?? plan.find((item) => item.status === "pending") ?? plan[plan.length - 1] ?? null;
1467
+ }
1468
+ function PlanStepIcon({
1469
+ status,
1470
+ size = 17,
1471
+ running = false
1472
+ }) {
1473
+ if (status === "completed") {
1474
+ return /* @__PURE__ */ jsx4(Check, { size, strokeWidth: 2, className: "shrink-0 text-emerald-500" });
1475
+ }
1476
+ if (status === "in_progress") {
1477
+ return running ? /* @__PURE__ */ jsx4(LoaderCircle, { size, className: "shrink-0 animate-spin text-[hsl(var(--muted-foreground))]" }) : /* @__PURE__ */ jsx4(CircleDot, { size, className: "shrink-0 text-amber-500" });
1478
+ }
1479
+ return /* @__PURE__ */ jsx4(Circle, { size, className: "shrink-0 text-[hsl(var(--muted-foreground))]/60" });
1480
+ }
1481
+ function PlanUpdateBlock({
1482
+ toolCall,
1483
+ running = false,
1484
+ autoReveal = false
1485
+ }) {
1486
+ const updateKey = `${toolCall.id}:${toolCall.arguments}`;
1487
+ const revealKey = autoReveal ? updateKey : null;
1488
+ const [collapsed, setCollapsed] = useState4(!autoReveal);
1489
+ const collapseTimerRef = useRef4(null);
1490
+ const data = parsePlanUpdate(toolCall.arguments);
1491
+ useEffect4(() => {
1492
+ if (!revealKey) return;
1493
+ if (collapseTimerRef.current) clearTimeout(collapseTimerRef.current);
1494
+ setCollapsed(false);
1495
+ collapseTimerRef.current = setTimeout(() => {
1496
+ setCollapsed(true);
1497
+ collapseTimerRef.current = null;
1498
+ }, PLAN_AUTO_COLLAPSE_MS);
1499
+ }, [revealKey]);
1500
+ useEffect4(
1501
+ () => () => {
1502
+ if (collapseTimerRef.current) clearTimeout(collapseTimerRef.current);
1503
+ },
1504
+ []
1505
+ );
1506
+ if (!data) return null;
1507
+ const completed = data.plan.filter((item) => item.status === "completed").length;
1508
+ const currentStep = pickCurrentPlanStep(data.plan);
1509
+ const pausedAtCurrentStep = !running && currentStep?.status === "in_progress";
1510
+ return /* @__PURE__ */ jsxs3("section", { className: "overflow-hidden", children: [
1511
+ /* @__PURE__ */ jsxs3(
1512
+ "button",
1513
+ {
1514
+ type: "button",
1515
+ "aria-expanded": !collapsed,
1516
+ onClick: () => {
1517
+ if (collapseTimerRef.current) {
1518
+ clearTimeout(collapseTimerRef.current);
1519
+ collapseTimerRef.current = null;
1520
+ }
1521
+ setCollapsed((value) => !value);
1522
+ },
1523
+ className: cn(
1524
+ "flex w-full items-center gap-2 px-3 py-2 text-left transition-colors hover:bg-[hsl(var(--muted)/0.3)]",
1525
+ !collapsed && "border-b border-[hsl(var(--border))]"
1526
+ ),
1527
+ children: [
1528
+ collapsed && currentStep ? /* @__PURE__ */ jsxs3("span", { className: "flex min-w-0 flex-1 items-center gap-1.5 text-xs text-[hsl(var(--foreground))]", children: [
1529
+ /* @__PURE__ */ jsx4(PlanStepIcon, { status: currentStep.status, size: 14, running }),
1530
+ /* @__PURE__ */ jsx4("span", { className: "truncate", children: currentStep.step }),
1531
+ pausedAtCurrentStep ? /* @__PURE__ */ jsx4("span", { className: "shrink-0 text-[11px] text-amber-500", children: "\u5DF2\u6682\u505C" }) : null
1532
+ ] }) : /* @__PURE__ */ jsxs3("span", { className: "flex min-w-0 flex-1 items-center gap-1.5 text-[11px] text-[hsl(var(--muted-foreground))]", children: [
1533
+ /* @__PURE__ */ jsx4(ListChecks, { size: 14, className: "shrink-0", "aria-hidden": "true" }),
1534
+ /* @__PURE__ */ jsx4("span", { className: "truncate", children: "\u4EFB\u52A1\u8FDB\u5EA6" }),
1535
+ pausedAtCurrentStep ? /* @__PURE__ */ jsx4("span", { className: "shrink-0 text-amber-500", children: "\u5DF2\u6682\u505C" }) : null
1536
+ ] }),
1537
+ /* @__PURE__ */ jsxs3("span", { className: "shrink-0 text-[11px] tabular-nums text-[hsl(var(--muted-foreground))]", children: [
1538
+ completed,
1539
+ "/",
1540
+ data.plan.length
1541
+ ] }),
1542
+ /* @__PURE__ */ jsx4(
1543
+ ChevronDown,
1544
+ {
1545
+ size: 14,
1546
+ className: cn(
1547
+ "shrink-0 text-[hsl(var(--muted-foreground))] transition-transform duration-300 ease-out motion-reduce:transition-none",
1548
+ !collapsed && "rotate-180"
1549
+ )
1550
+ }
1551
+ )
1552
+ ]
1553
+ }
1554
+ ),
1555
+ /* @__PURE__ */ jsx4(
1556
+ "div",
1557
+ {
1558
+ "aria-hidden": collapsed,
1559
+ className: cn(
1560
+ "grid transition-[grid-template-rows,opacity] duration-300 ease-out motion-reduce:transition-none",
1561
+ collapsed ? "grid-rows-[0fr] opacity-0" : "grid-rows-[1fr] opacity-100"
1562
+ ),
1563
+ children: /* @__PURE__ */ jsx4("div", { className: "min-h-0 overflow-hidden", children: /* @__PURE__ */ jsx4("div", { className: "flex max-h-40 flex-col gap-0.5 overflow-y-auto px-3 py-2", children: data.plan.length === 0 ? /* @__PURE__ */ jsx4("span", { className: "text-xs text-[hsl(var(--muted-foreground))]", children: "\u6682\u65E0\u4EFB\u52A1\u6B65\u9AA4" }) : data.plan.map((item, index) => /* @__PURE__ */ jsxs3("div", { className: "flex items-start gap-2 py-0.5", children: [
1564
+ /* @__PURE__ */ jsx4("span", { className: "mt-[3px] flex shrink-0", children: /* @__PURE__ */ jsx4(PlanStepIcon, { status: item.status, size: 14, running }) }),
1565
+ /* @__PURE__ */ jsx4(
1566
+ "span",
1567
+ {
1568
+ className: cn(
1569
+ "min-w-0 flex-1 break-words text-[13px] leading-5",
1570
+ item.status === "completed" ? "text-[hsl(var(--muted-foreground))]" : item.status === "in_progress" ? "font-medium text-[hsl(var(--foreground))]" : "text-[hsl(var(--muted-foreground))]"
1571
+ ),
1572
+ children: item.step
1573
+ }
1574
+ )
1575
+ ] }, `${index}-${item.step}`)) }) })
1576
+ }
1577
+ )
1578
+ ] });
1579
+ }
1580
+ function CurrentPlanPanel({
1581
+ messages,
1582
+ running = false,
1583
+ revealRevision = 0,
1584
+ sessionId,
1585
+ className
1586
+ }) {
1587
+ const { current, updating } = getPlanUpdateDisplayState(messages);
1588
+ const revealBaselinesRef = useRef4(/* @__PURE__ */ new Map([[sessionId, revealRevision]]));
1589
+ const autoReveal = (revealBaselinesRef.current.get(sessionId) ?? 0) !== revealRevision;
1590
+ useEffect4(() => {
1591
+ if (!current) return;
1592
+ revealBaselinesRef.current.set(sessionId, revealRevision);
1593
+ }, [current, revealRevision, sessionId]);
1594
+ if (!current && !updating) return null;
1595
+ return /* @__PURE__ */ jsxs3("div", { className: cn("blade-chat-plan mx-auto w-full max-w-[748px] px-4", className), children: [
1596
+ updating ? /* @__PURE__ */ jsxs3("div", { className: "mb-2 flex items-center gap-2 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-3 py-2 text-xs text-[hsl(var(--muted-foreground))]", children: [
1597
+ /* @__PURE__ */ jsx4(LoaderCircle, { size: 14, className: "shrink-0 animate-spin" }),
1598
+ /* @__PURE__ */ jsx4("span", { children: "\u6B63\u5728\u66F4\u65B0\u4EFB\u52A1\u8FDB\u5EA6\u2026" })
1599
+ ] }) : null,
1600
+ current ? /* @__PURE__ */ jsx4(
1601
+ PlanUpdateBlock,
1602
+ {
1603
+ toolCall: current,
1604
+ running,
1605
+ autoReveal
1606
+ },
1607
+ sessionId ?? "current-session"
1608
+ ) : null
1609
+ ] });
1610
+ }
1611
+
1232
1612
  // src/components/ChatSurface.tsx
1233
1613
  import { chatErrorForDisplay as chatErrorForDisplay2 } from "@blade-hq/agent-client";
1234
1614
 
1235
1615
  // src/components/ChatInput.tsx
1236
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1616
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1237
1617
  function isImeCompositionKey(event) {
1238
1618
  return event.isComposing || event.keyCode === 229;
1239
1619
  }
@@ -1269,8 +1649,8 @@ function ChatInput({
1269
1649
  void handleSend();
1270
1650
  }
1271
1651
  };
1272
- 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: [
1273
- /* @__PURE__ */ jsx4(
1652
+ return /* @__PURE__ */ jsx5("div", { className: cn("blade-chat-input border-t border-[hsl(var(--border))] py-3", className), children: /* @__PURE__ */ jsxs4("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: [
1653
+ /* @__PURE__ */ jsx5(
1274
1654
  "textarea",
1275
1655
  {
1276
1656
  value,
@@ -1287,7 +1667,7 @@ function ChatInput({
1287
1667
  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)]"
1288
1668
  }
1289
1669
  ),
1290
- isStreaming ? /* @__PURE__ */ jsx4(
1670
+ isStreaming ? /* @__PURE__ */ jsx5(
1291
1671
  "button",
1292
1672
  {
1293
1673
  type: "button",
@@ -1296,9 +1676,9 @@ function ChatInput({
1296
1676
  "aria-label": isStopping ? "\u6B63\u5728\u505C\u6B62" : "\u505C\u6B62\u56DE\u590D",
1297
1677
  title: isStopping ? "\u6B63\u5728\u505C\u6B62" : "\u505C\u6B62\u56DE\u590D",
1298
1678
  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",
1299
- children: isStopping ? /* @__PURE__ */ jsx4(LoaderCircle, { size: 14, className: "animate-spin" }) : /* @__PURE__ */ jsx4(Square, { size: 12, fill: "currentColor" })
1679
+ children: isStopping ? /* @__PURE__ */ jsx5(LoaderCircle, { size: 14, className: "animate-spin" }) : /* @__PURE__ */ jsx5(Square, { size: 12, fill: "currentColor" })
1300
1680
  }
1301
- ) : /* @__PURE__ */ jsx4(
1681
+ ) : /* @__PURE__ */ jsx5(
1302
1682
  "button",
1303
1683
  {
1304
1684
  type: "button",
@@ -1307,23 +1687,23 @@ function ChatInput({
1307
1687
  "aria-label": "\u53D1\u9001\u6D88\u606F",
1308
1688
  title: "\u53D1\u9001\u6D88\u606F",
1309
1689
  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",
1310
- children: /* @__PURE__ */ jsx4(ArrowUp, { size: 15 })
1690
+ children: /* @__PURE__ */ jsx5(ArrowUp, { size: 15 })
1311
1691
  }
1312
1692
  )
1313
1693
  ] }) });
1314
1694
  }
1315
1695
 
1316
1696
  // src/components/ConnectionBanner.tsx
1317
- import { useEffect as useEffect4, useRef as useRef4, useState as useState4 } from "react";
1318
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1697
+ import { useEffect as useEffect5, useRef as useRef5, useState as useState5 } from "react";
1698
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1319
1699
  var CONNECTION_NOTICE_DELAY_MS = 3e3;
1320
1700
  var CONNECTION_ERROR_DELAY_MS = 15e3;
1321
1701
  function useConnectionNoticePhase(connected) {
1322
- const [phase, setPhase] = useState4("hidden");
1323
- const connectedRef = useRef4(connected);
1324
- const timersRef = useRef4([]);
1702
+ const [phase, setPhase] = useState5("hidden");
1703
+ const connectedRef = useRef5(connected);
1704
+ const timersRef = useRef5([]);
1325
1705
  connectedRef.current = connected;
1326
- useEffect4(() => {
1706
+ useEffect5(() => {
1327
1707
  const clearTimers = () => {
1328
1708
  for (const timer of timersRef.current) clearTimeout(timer);
1329
1709
  timersRef.current = [];
@@ -1363,14 +1743,14 @@ function useConnectionNoticePhase(connected) {
1363
1743
  return phase;
1364
1744
  }
1365
1745
  function ConnectionBanner({ connection, className }) {
1366
- const hasConnectedRef = useRef4(connection === "connected" || connection === "reconnecting");
1746
+ const hasConnectedRef = useRef5(connection === "connected" || connection === "reconnecting");
1367
1747
  if (connection === "connected") hasConnectedRef.current = true;
1368
1748
  const connected = connection === "connected";
1369
1749
  const phase = useConnectionNoticePhase(connected);
1370
1750
  if (connected || phase === "hidden") return null;
1371
1751
  const recovering = phase === "recovering";
1372
1752
  const firstConnection = !hasConnectedRef.current;
1373
- return /* @__PURE__ */ jsx5("div", { className: cn("blade-chat-banner bg-[hsl(var(--background))] px-5 pt-3", className), children: /* @__PURE__ */ jsxs4(
1753
+ return /* @__PURE__ */ jsx6("div", { className: cn("blade-chat-banner bg-[hsl(var(--background))] px-5 pt-3", className), children: /* @__PURE__ */ jsxs5(
1374
1754
  "div",
1375
1755
  {
1376
1756
  className: cn(
@@ -1378,10 +1758,10 @@ function ConnectionBanner({ connection, className }) {
1378
1758
  recovering ? "border-amber-500/25 bg-amber-500/10 text-amber-100" : "border-rose-500/25 bg-rose-500/10 text-rose-100"
1379
1759
  ),
1380
1760
  children: [
1381
- /* @__PURE__ */ jsx5("span", { className: "mt-0.5 shrink-0", children: recovering ? /* @__PURE__ */ jsx5(LoaderCircle, { size: 14, className: "animate-spin" }) : /* @__PURE__ */ jsx5(TriangleAlert, { size: 14 }) }),
1382
- /* @__PURE__ */ jsxs4("div", { className: "min-w-0", children: [
1383
- /* @__PURE__ */ jsx5("div", { className: "text-sm font-medium", children: recovering ? firstConnection ? "\u6B63\u5728\u8FDE\u63A5\u2026" : "\u6B63\u5728\u6062\u590D\u8FDE\u63A5\u2026" : "\u6682\u65F6\u65E0\u6CD5\u8FDE\u63A5" }),
1384
- /* @__PURE__ */ jsx5("div", { className: "text-xs opacity-80", children: recovering ? "\u6062\u590D\u540E\u4F1A\u81EA\u52A8\u540C\u6B65\u6700\u65B0\u6D88\u606F\uFF0C\u8BF7\u7A0D\u5019" : "\u8BF7\u68C0\u67E5\u7F51\u7EDC\u6216\u670D\u52A1\u72B6\u6001\uFF0C\u7CFB\u7EDF\u4F1A\u7EE7\u7EED\u81EA\u52A8\u91CD\u8BD5" })
1761
+ /* @__PURE__ */ jsx6("span", { className: "mt-0.5 shrink-0", children: recovering ? /* @__PURE__ */ jsx6(LoaderCircle, { size: 14, className: "animate-spin" }) : /* @__PURE__ */ jsx6(TriangleAlert, { size: 14 }) }),
1762
+ /* @__PURE__ */ jsxs5("div", { className: "min-w-0", children: [
1763
+ /* @__PURE__ */ jsx6("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" }),
1764
+ /* @__PURE__ */ jsx6("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" })
1385
1765
  ] })
1386
1766
  ]
1387
1767
  }
@@ -1390,10 +1770,10 @@ function ConnectionBanner({ connection, className }) {
1390
1770
 
1391
1771
  // src/components/MessageList.tsx
1392
1772
  import { isHiddenInternalMessage } from "@blade-hq/agent-client";
1393
- import { useCallback as useCallback7, useEffect as useEffect10, useMemo as useMemo7, useRef as useRef11, useState as useState12 } from "react";
1773
+ import { useCallback as useCallback7, useEffect as useEffect11, useMemo as useMemo7, useRef as useRef12, useState as useState13 } from "react";
1394
1774
 
1395
1775
  // ../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/useStickToBottom.js
1396
- import { useCallback as useCallback4, useMemo as useMemo3, useRef as useRef5, useState as useState5 } from "react";
1776
+ import { useCallback as useCallback4, useMemo as useMemo3, useRef as useRef6, useState as useState6 } from "react";
1397
1777
  var DEFAULT_SPRING_ANIMATION = {
1398
1778
  /**
1399
1779
  * A value from 0 to 1, on how much to damp the animation.
@@ -1430,10 +1810,10 @@ globalThis.document?.addEventListener("click", () => {
1430
1810
  mouseDown = false;
1431
1811
  });
1432
1812
  var useStickToBottom = (options = {}) => {
1433
- const [escapedFromLock, updateEscapedFromLock] = useState5(false);
1434
- const [isAtBottom, updateIsAtBottom] = useState5(options.initial !== false);
1435
- const [isNearBottom, setIsNearBottom] = useState5(false);
1436
- const optionsRef = useRef5(null);
1813
+ const [escapedFromLock, updateEscapedFromLock] = useState6(false);
1814
+ const [isAtBottom, updateIsAtBottom] = useState6(options.initial !== false);
1815
+ const [isNearBottom, setIsNearBottom] = useState6(false);
1816
+ const optionsRef = useRef6(null);
1437
1817
  optionsRef.current = options;
1438
1818
  const isSelecting = useCallback4(() => {
1439
1819
  if (!mouseDown) {
@@ -1737,11 +2117,11 @@ function mergeAnimations(...animations) {
1737
2117
 
1738
2118
  // ../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/StickToBottom.js
1739
2119
  import * as React from "react";
1740
- import { createContext as createContext2, useContext as useContext2, useEffect as useEffect5, useImperativeHandle, useLayoutEffect, useMemo as useMemo4, useRef as useRef6 } from "react";
2120
+ import { createContext as createContext2, useContext as useContext2, useEffect as useEffect6, useImperativeHandle, useLayoutEffect, useMemo as useMemo4, useRef as useRef7 } from "react";
1741
2121
  var StickToBottomContext = createContext2(null);
1742
- var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect5;
2122
+ var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect6;
1743
2123
  function StickToBottom({ instance, children, resize, initial, mass, damping, stiffness, targetScrollTop: currentTargetScrollTop, contextRef, ...props }) {
1744
- const customTargetScrollTop = useRef6(null);
2124
+ const customTargetScrollTop = useRef7(null);
1745
2125
  const targetScrollTop = React.useCallback((target, elements) => {
1746
2126
  const get = context?.targetScrollTop ?? currentTargetScrollTop;
1747
2127
  return get?.(target, elements) ?? target;
@@ -1823,153 +2203,11 @@ import {
1823
2203
  getTextContent,
1824
2204
  normalizeMessageContent
1825
2205
  } from "@blade-hq/agent-client";
1826
- import { useEffect as useEffect8, useRef as useRef9, useState as useState10 } from "react";
2206
+ import { useEffect as useEffect9, useRef as useRef10, useState as useState11 } from "react";
1827
2207
 
1828
2208
  // src/components/AgentLoopBlock.tsx
1829
- import { useState as useState6 } from "react";
1830
-
1831
- // src/components/display-utils.ts
1832
- var TOOL_NAME_ALIASES = {
1833
- agent: "Agent",
1834
- ask_user_question: "AskUserQuestion",
1835
- bash: "Bash",
1836
- bg_bash: "BgBash",
1837
- edit: "Edit",
1838
- exit_plan_mode: "ExitPlanMode",
1839
- file_edit: "Edit",
1840
- file_read: "Read",
1841
- file_write: "Write",
1842
- finish_task: "FinishTask",
1843
- glob: "Glob",
1844
- grep: "Grep",
1845
- kb_search: "KbSearch",
1846
- ls: "Ls",
1847
- multi_edit: "MultiEdit",
1848
- read: "Read",
1849
- read_skill: "ReadSkill",
1850
- web_fetch: "WebFetch",
1851
- web_search: "WebSearch",
1852
- write: "Write"
1853
- };
1854
- var TOOL_DISPLAY_LABELS = {
1855
- Bash: "\u6267\u884C\u547D\u4EE4",
1856
- BgBash: "\u540E\u53F0\u6267\u884C\u547D\u4EE4",
1857
- Read: "\u8BFB\u53D6\u6587\u4EF6",
1858
- Write: "\u5199\u5165\u6587\u4EF6",
1859
- Edit: "\u7F16\u8F91\u6587\u4EF6",
1860
- MultiEdit: "\u7F16\u8F91\u6587\u4EF6",
1861
- Ls: "\u5217\u51FA\u76EE\u5F55",
1862
- Glob: "\u5339\u914D\u6587\u4EF6",
1863
- Grep: "\u641C\u7D22\u6587\u672C",
1864
- KbSearch: "\u68C0\u7D22\u77E5\u8BC6\u5E93",
1865
- WebSearch: "\u641C\u7D22\u7F51\u9875",
1866
- WebFetch: "\u6574\u7406\u7F51\u9875\u5185\u5BB9",
1867
- Agent: "\u6D3E\u751F\u5B50\u667A\u80FD\u4F53",
1868
- AskUserQuestion: "\u5411\u7528\u6237\u63D0\u95EE",
1869
- ReadSkill: "\u8BFB\u53D6\u6280\u80FD",
1870
- FinishTask: "\u4EFB\u52A1\u5B8C\u6210",
1871
- ExitPlanMode: "\u63D0\u4EA4\u8BA1\u5212",
1872
- ListSessions: "\u5217\u51FA\u5386\u53F2\u4F1A\u8BDD",
1873
- GetSessionHistory: "\u8BFB\u53D6\u4F1A\u8BDD\u5386\u53F2"
1874
- };
1875
- function safeParseJson(value) {
1876
- if (!value) return null;
1877
- try {
1878
- return JSON.parse(value);
1879
- } catch {
1880
- return null;
1881
- }
1882
- }
1883
- function getStringArgValue(args, key) {
1884
- const value = args?.[key];
1885
- return typeof value === "string" ? value.trim() : "";
1886
- }
1887
- var SKILL_ENTRY_FILE_NAMES = /* @__PURE__ */ new Set(["skill.md", "command.md"]);
1888
- var NON_SKILL_DIR_NAMES = /* @__PURE__ */ new Set([".", "..", ".agent", ".agents", ".claude", "skill_data", "skills"]);
1889
- function getSkillNameFromFilePath(filePath) {
1890
- if (!filePath) return null;
1891
- const segments = filePath.split(/[\\/]+/).filter(Boolean);
1892
- const fileName = segments.pop();
1893
- if (!fileName || !SKILL_ENTRY_FILE_NAMES.has(fileName.toLowerCase())) return null;
1894
- const dirName = segments.pop();
1895
- if (!dirName || NON_SKILL_DIR_NAMES.has(dirName.toLowerCase())) return null;
1896
- return dirName;
1897
- }
1898
- function formatToolName(name) {
1899
- const trimmed = name.trim();
1900
- if (!trimmed) return name;
1901
- const stripped = trimmed.split(":").pop()?.split("/").pop()?.split(".").pop()?.trim() || trimmed;
1902
- const normalized = stripped.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
1903
- return TOOL_NAME_ALIASES[normalized] ?? stripped;
1904
- }
1905
- function getToolDisplayLabel(toolCall) {
1906
- const normalized = formatToolName(toolCall.name);
1907
- const args = safeParseJson(toolCall.arguments);
1908
- const displayName = toolCall.display_name?.trim() ?? "";
1909
- const baseLabel = displayName || TOOL_DISPLAY_LABELS[normalized] || normalized;
1910
- const metaDisplayName = getStringArgValue(args, "_meta_display_name");
1911
- if (metaDisplayName) {
1912
- return metaDisplayName;
1913
- }
1914
- const description = getStringArgValue(args, "description");
1915
- if (normalized === "BgBash") {
1916
- return description ? `\u540E\u53F0\u6267\u884C\uFF1A${description}` : "\u540E\u53F0\u6267\u884C\u547D\u4EE4";
1917
- }
1918
- if (normalized === "ReadSkill") {
1919
- const skillName = getStringArgValue(args, "skill") || getStringArgValue(args, "skill_name");
1920
- return skillName ? `${baseLabel}\u300C${skillName}\u300D` : baseLabel;
1921
- }
1922
- if (normalized === "Read") {
1923
- const skillName = getSkillNameFromFilePath(
1924
- getStringArgValue(args, "file_path") || getStringArgValue(args, "path")
1925
- );
1926
- if (skillName) return `\u8BFB\u53D6\u6280\u80FD\u300C${skillName}\u300D`;
1927
- }
1928
- if (normalized === "FinishTask") {
1929
- const title = getStringArgValue(args, "title");
1930
- return title ? `${baseLabel}\uFF1A${title}` : baseLabel;
1931
- }
1932
- return description || baseLabel;
1933
- }
1934
- function getToolTone(status) {
1935
- if (status === "error" || status === "cancelled") return "red";
1936
- if (status === "awaiting_answer") return "amber";
1937
- if (status === "pending") return "blue";
1938
- return "emerald";
1939
- }
1940
- function getToolStatusLabel(status) {
1941
- if (status === "pending") return "\u8FD0\u884C\u4E2D";
1942
- if (status === "awaiting_answer") return "\u7B49\u5F85\u56DE\u7B54";
1943
- if (status === "error") return "\u9519\u8BEF";
1944
- if (status === "cancelled") return "\u5DF2\u53D6\u6D88";
1945
- return "\u5B8C\u6210";
1946
- }
1947
- function formatToolDuration(ms) {
1948
- if (ms < 1e3) return `${Math.round(ms)}ms`;
1949
- const seconds = ms / 1e3;
1950
- if (seconds < 60) return `${seconds.toFixed(1)}s`;
1951
- const minutes = Math.floor(seconds / 60);
1952
- const remainingSeconds = Math.round(seconds % 60);
1953
- return remainingSeconds > 0 ? `${minutes}m${remainingSeconds}s` : `${minutes}m`;
1954
- }
1955
- function formatToolArgs(args) {
1956
- try {
1957
- return JSON.stringify(JSON.parse(args), null, 2);
1958
- } catch {
1959
- return args;
1960
- }
1961
- }
1962
- var RESULT_PREVIEW_LIMIT = 4e3;
1963
- function formatToolResult(result) {
1964
- const text = typeof result === "string" ? result : JSON.stringify(result, null, 2);
1965
- if (text == null) return "";
1966
- if (text.length <= RESULT_PREVIEW_LIMIT) return text;
1967
- return `${text.slice(0, RESULT_PREVIEW_LIMIT)}
1968
- \u2026\uFF08\u7ED3\u679C\u8FC7\u957F\uFF0C\u5DF2\u622A\u65AD\uFF09`;
1969
- }
1970
-
1971
- // src/components/AgentLoopBlock.tsx
1972
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
2209
+ import { useState as useState7 } from "react";
2210
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1973
2211
  function parseAgentDescription(argumentsJson) {
1974
2212
  try {
1975
2213
  const parsed = JSON.parse(argumentsJson);
@@ -1979,7 +2217,7 @@ function parseAgentDescription(argumentsJson) {
1979
2217
  }
1980
2218
  }
1981
2219
  function AgentLoopBlock({ toolCall }) {
1982
- const [expanded, setExpanded] = useState6(false);
2220
+ const [expanded, setExpanded] = useState7(false);
1983
2221
  const description = parseAgentDescription(toolCall.arguments);
1984
2222
  const running = toolCall.status === "pending" || toolCall.status === "awaiting_answer";
1985
2223
  const failed = toolCall.status === "error" || toolCall.status === "cancelled";
@@ -1988,8 +2226,8 @@ function AgentLoopBlock({ toolCall }) {
1988
2226
  "size-3.5 shrink-0",
1989
2227
  failed ? "text-red-500" : "text-[hsl(var(--muted-foreground))]"
1990
2228
  );
1991
- return /* @__PURE__ */ jsxs5("div", { className: "blade-chat-agent-loop text-xs leading-[22px]", children: [
1992
- /* @__PURE__ */ jsxs5(
2229
+ return /* @__PURE__ */ jsxs6("div", { className: "blade-chat-agent-loop text-xs leading-[22px]", children: [
2230
+ /* @__PURE__ */ jsxs6(
1993
2231
  "button",
1994
2232
  {
1995
2233
  type: "button",
@@ -2004,17 +2242,18 @@ function AgentLoopBlock({ toolCall }) {
2004
2242
  ),
2005
2243
  title: `\u5B50\u4EFB\u52A1\uFF1A${description}`,
2006
2244
  children: [
2007
- running ? /* @__PURE__ */ jsx6(LoaderCircle, { className: cn(iconClass, "animate-spin"), "aria-hidden": "true" }) : toolCall.status === "error" ? /* @__PURE__ */ jsx6(CircleAlert, { className: iconClass, "aria-hidden": "true" }) : toolCall.status === "cancelled" ? /* @__PURE__ */ jsx6(X, { className: iconClass, "aria-hidden": "true" }) : /* @__PURE__ */ jsx6(Bot, { className: iconClass, "aria-hidden": "true" }),
2008
- /* @__PURE__ */ jsxs5("span", { className: "min-w-0 truncate", children: [
2245
+ running ? /* @__PURE__ */ jsx7(LoaderCircle, { className: cn(iconClass, "animate-spin"), "aria-hidden": "true" }) : toolCall.status === "error" ? /* @__PURE__ */ jsx7(CircleAlert, { className: iconClass, "aria-hidden": "true" }) : toolCall.status === "cancelled" ? /* @__PURE__ */ jsx7(X, { className: iconClass, "aria-hidden": "true" }) : /* @__PURE__ */ jsx7(Bot, { className: iconClass, "aria-hidden": "true" }),
2246
+ /* @__PURE__ */ jsxs6("span", { className: "min-w-0 truncate", children: [
2009
2247
  "\u5B50\u4EFB\u52A1\uFF1A",
2010
2248
  description
2011
2249
  ] }),
2012
- hasResult ? /* @__PURE__ */ jsx6(
2250
+ hasResult ? /* @__PURE__ */ jsx7(
2013
2251
  ChevronRight,
2014
2252
  {
2015
2253
  size: 14,
2254
+ style: { transitionDuration: "260ms", transitionTimingFunction: "cubic-bezier(0.25, 0.1, 0.25, 1)" },
2016
2255
  className: cn(
2017
- "shrink-0 transition-transform duration-300",
2256
+ "shrink-0 transition-transform",
2018
2257
  expanded && "rotate-90"
2019
2258
  ),
2020
2259
  "aria-hidden": "true"
@@ -2023,18 +2262,18 @@ function AgentLoopBlock({ toolCall }) {
2023
2262
  ]
2024
2263
  }
2025
2264
  ),
2026
- expanded && hasResult ? /* @__PURE__ */ jsx6("div", { className: "ml-[18px] mt-1.5 max-h-[400px] overflow-auto whitespace-pre-wrap text-xs leading-[22px] text-[hsl(var(--muted-foreground))]", children: formatToolResult(toolCall.result) }) : null
2265
+ expanded && hasResult ? /* @__PURE__ */ jsx7("div", { className: "ml-[18px] mt-1.5 max-h-[400px] overflow-auto whitespace-pre-wrap text-xs leading-[22px] text-[hsl(var(--muted-foreground))]", children: formatToolResult(toolCall.result) }) : null
2027
2266
  ] });
2028
2267
  }
2029
2268
 
2030
2269
  // src/components/MarkdownContent.tsx
2031
2270
  import {
2032
- useEffect as useEffect6,
2271
+ useEffect as useEffect7,
2033
2272
  useMemo as useMemo5,
2034
- useRef as useRef7,
2035
- useState as useState7
2273
+ useRef as useRef8,
2274
+ useState as useState8
2036
2275
  } from "react";
2037
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
2276
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
2038
2277
  var SYSTEM_REMINDER_RE = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
2039
2278
  function normalizeAdjacentUrlFormatting(value) {
2040
2279
  const protectedSegments = [];
@@ -2049,10 +2288,10 @@ function normalizeAdjacentUrlFormatting(value) {
2049
2288
  return normalized.replace(/blade-url-protected-(\d+)-marker/g, (_, index) => protectedSegments[Number(index)]);
2050
2289
  }
2051
2290
  function CodeBlockPre({ children, node: _node, ...props }) {
2052
- const preRef = useRef7(null);
2053
- const [copied, setCopied] = useState7(false);
2054
- const [language, setLanguage] = useState7("");
2055
- useEffect6(() => {
2291
+ const preRef = useRef8(null);
2292
+ const [copied, setCopied] = useState8(false);
2293
+ const [language, setLanguage] = useState8("");
2294
+ useEffect7(() => {
2056
2295
  const codeEl = preRef.current?.querySelector("code");
2057
2296
  setLanguage(codeEl?.className.match(/language-(\S+)/)?.[1] ?? "");
2058
2297
  }, []);
@@ -2063,10 +2302,10 @@ function CodeBlockPre({ children, node: _node, ...props }) {
2063
2302
  setTimeout(() => setCopied(false), 2e3);
2064
2303
  }
2065
2304
  };
2066
- return /* @__PURE__ */ jsxs6("div", { className: "blade-chat-codeblock not-prose my-3 overflow-hidden rounded-xl border border-[hsl(var(--border))]", children: [
2067
- /* @__PURE__ */ jsxs6("div", { className: "blade-chat-codeblock-header flex h-[34px] items-center justify-between border-b border-[hsl(var(--border))] bg-[hsl(var(--muted))/0.5] pl-3.5 pr-1.5", children: [
2068
- /* @__PURE__ */ jsx7("span", { className: "font-mono text-[12px] text-[hsl(var(--muted-foreground))]", children: language || "code" }),
2069
- /* @__PURE__ */ jsxs6(
2305
+ return /* @__PURE__ */ jsxs7("div", { className: "blade-chat-codeblock not-prose my-3 overflow-hidden rounded-xl border border-[hsl(var(--border))]", children: [
2306
+ /* @__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: [
2307
+ /* @__PURE__ */ jsx8("span", { className: "font-mono text-[12px] text-[hsl(var(--muted-foreground))]", children: language || "code" }),
2308
+ /* @__PURE__ */ jsxs7(
2070
2309
  "button",
2071
2310
  {
2072
2311
  type: "button",
@@ -2076,13 +2315,13 @@ function CodeBlockPre({ children, node: _node, ...props }) {
2076
2315
  copied ? "text-[hsl(var(--primary))]" : "text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]"
2077
2316
  ),
2078
2317
  children: [
2079
- copied ? /* @__PURE__ */ jsx7(Check, { size: 12 }) : /* @__PURE__ */ jsx7(Copy, { size: 12 }),
2080
- /* @__PURE__ */ jsx7("span", { children: copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236" })
2318
+ copied ? /* @__PURE__ */ jsx8(Check, { size: 12 }) : /* @__PURE__ */ jsx8(Copy, { size: 12 }),
2319
+ /* @__PURE__ */ jsx8("span", { children: copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236" })
2081
2320
  ]
2082
2321
  }
2083
2322
  )
2084
2323
  ] }),
2085
- /* @__PURE__ */ jsx7(
2324
+ /* @__PURE__ */ jsx8(
2086
2325
  "pre",
2087
2326
  {
2088
2327
  ref: preRef,
@@ -2094,7 +2333,7 @@ function CodeBlockPre({ children, node: _node, ...props }) {
2094
2333
  ] });
2095
2334
  }
2096
2335
  function ExternalAnchor({ node: _node, children, ...props }) {
2097
- return /* @__PURE__ */ jsx7("a", { ...props, target: "_blank", rel: "noopener noreferrer", children });
2336
+ return /* @__PURE__ */ jsx8("a", { ...props, target: "_blank", rel: "noopener noreferrer", children });
2098
2337
  }
2099
2338
  var MARKDOWN_COMPONENTS = {
2100
2339
  pre: CodeBlockPre,
@@ -2104,7 +2343,7 @@ function MarkdownContent({ children, className, mode, sessionId }) {
2104
2343
  const resolvedChildren = useMemo5(() => {
2105
2344
  return normalizeAdjacentUrlFormatting(children.replace(SYSTEM_REMINDER_RE, ""));
2106
2345
  }, [children]);
2107
- return /* @__PURE__ */ jsx7(
2346
+ return /* @__PURE__ */ jsx8(
2108
2347
  _r,
2109
2348
  {
2110
2349
  className: cn("blade-chat-markdown break-words", className),
@@ -2117,17 +2356,17 @@ function MarkdownContent({ children, className, mode, sessionId }) {
2117
2356
  }
2118
2357
 
2119
2358
  // src/components/Shimmer.tsx
2120
- import { jsx as jsx8 } from "react/jsx-runtime";
2359
+ import { jsx as jsx9 } from "react/jsx-runtime";
2121
2360
  function Shimmer({ children = "\u6B63\u5728\u601D\u8003...", className }) {
2122
- return /* @__PURE__ */ jsx8("span", { className: cn("blade-shimmer-text text-sm font-medium", className), children });
2361
+ return /* @__PURE__ */ jsx9("span", { className: cn("blade-shimmer-text text-sm font-medium", className), children });
2123
2362
  }
2124
2363
 
2125
2364
  // src/components/ToolCallBlock.tsx
2126
- import { useState as useState9 } from "react";
2365
+ import { useState as useState10 } from "react";
2127
2366
 
2128
2367
  // src/components/AskUserQuestionBlock.tsx
2129
- import { useEffect as useEffect7, useMemo as useMemo6, useRef as useRef8, useState as useState8 } from "react";
2130
- import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
2368
+ import { useEffect as useEffect8, useMemo as useMemo6, useRef as useRef9, useState as useState9 } from "react";
2369
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
2131
2370
  var CUSTOM_TEXTAREA_MAX_HEIGHT = 160;
2132
2371
  function resizeCustomTextarea(textarea) {
2133
2372
  textarea.style.height = "auto";
@@ -2135,12 +2374,12 @@ function resizeCustomTextarea(textarea) {
2135
2374
  textarea.style.overflowY = textarea.scrollHeight > CUSTOM_TEXTAREA_MAX_HEIGHT ? "auto" : "hidden";
2136
2375
  }
2137
2376
  function useAutoResizeTextarea(value) {
2138
- const textareaRef = useRef8(null);
2139
- useEffect7(() => {
2377
+ const textareaRef = useRef9(null);
2378
+ useEffect8(() => {
2140
2379
  const textarea = textareaRef.current;
2141
2380
  if (textarea?.value === value) resizeCustomTextarea(textarea);
2142
2381
  }, [value]);
2143
- useEffect7(() => {
2382
+ useEffect8(() => {
2144
2383
  const textarea = textareaRef.current;
2145
2384
  if (!textarea || typeof ResizeObserver === "undefined") return;
2146
2385
  let previousWidth = textarea.clientWidth;
@@ -2165,12 +2404,12 @@ function AskUserQuestionBlock({
2165
2404
  answerData,
2166
2405
  onAnswer
2167
2406
  }) {
2168
- const [selections, setSelections] = useState8(/* @__PURE__ */ new Map());
2169
- const [customTexts, setCustomTexts] = useState8(/* @__PURE__ */ new Map());
2170
- const [usingCustom, setUsingCustom] = useState8(/* @__PURE__ */ new Set());
2171
- const [note, setNote] = useState8("");
2172
- const [submitted, setSubmitted] = useState8(false);
2173
- useEffect7(() => {
2407
+ const [selections, setSelections] = useState9(/* @__PURE__ */ new Map());
2408
+ const [customTexts, setCustomTexts] = useState9(/* @__PURE__ */ new Map());
2409
+ const [usingCustom, setUsingCustom] = useState9(/* @__PURE__ */ new Set());
2410
+ const [note, setNote] = useState9("");
2411
+ const [submitted, setSubmitted] = useState9(false);
2412
+ useEffect8(() => {
2174
2413
  if (sessionStatus === "failed" || sessionStatus === "interrupted") {
2175
2414
  setSubmitted(false);
2176
2415
  }
@@ -2268,7 +2507,7 @@ ${parts.join("\n")}`,
2268
2507
  setSubmitted(true);
2269
2508
  onAnswer(text, toolCallId, nextAnswerData);
2270
2509
  };
2271
- return /* @__PURE__ */ jsxs7(
2510
+ return /* @__PURE__ */ jsxs8(
2272
2511
  "div",
2273
2512
  {
2274
2513
  className: cn(
@@ -2276,12 +2515,12 @@ ${parts.join("\n")}`,
2276
2515
  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"
2277
2516
  ),
2278
2517
  children: [
2279
- data.source_loop?.description && /* @__PURE__ */ jsxs7("div", { className: "rounded-lg bg-[hsl(var(--muted)/0.35)] px-3 py-2 text-xs text-[hsl(var(--muted-foreground))]", children: [
2518
+ 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: [
2280
2519
  "\u5B50\u667A\u80FD\u4F53\u300C",
2281
2520
  data.source_loop.description,
2282
2521
  "\u300D\u5728\u7B49\u5F85\u4F60\u7684\u56DE\u7B54"
2283
2522
  ] }),
2284
- data.questions.map((q, qIdx) => /* @__PURE__ */ jsx9(
2523
+ data.questions.map((q, qIdx) => /* @__PURE__ */ jsx10(
2285
2524
  QuestionCard,
2286
2525
  {
2287
2526
  question: q,
@@ -2296,7 +2535,7 @@ ${parts.join("\n")}`,
2296
2535
  },
2297
2536
  q.question
2298
2537
  )),
2299
- /* @__PURE__ */ jsx9(
2538
+ /* @__PURE__ */ jsx10(
2300
2539
  NoteField,
2301
2540
  {
2302
2541
  answered,
@@ -2305,7 +2544,7 @@ ${parts.join("\n")}`,
2305
2544
  onChange: setNote
2306
2545
  }
2307
2546
  ),
2308
- !answered && !submitted && onAnswer && /* @__PURE__ */ jsx9(
2547
+ !answered && !submitted && onAnswer && /* @__PURE__ */ jsx10(
2309
2548
  "button",
2310
2549
  {
2311
2550
  type: "button",
@@ -2315,14 +2554,14 @@ ${parts.join("\n")}`,
2315
2554
  children: allAnswered ? "\u786E\u8BA4" : "\u8BF7\u5148\u9009\u62E9\u4E00\u4E2A\u9009\u9879"
2316
2555
  }
2317
2556
  ),
2318
- submitted && !answered && /* @__PURE__ */ jsxs7(
2557
+ submitted && !answered && /* @__PURE__ */ jsxs8(
2319
2558
  "button",
2320
2559
  {
2321
2560
  type: "button",
2322
2561
  disabled: true,
2323
2562
  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",
2324
2563
  children: [
2325
- /* @__PURE__ */ jsx9(LoaderCircle, { size: 14, className: "animate-spin" }),
2564
+ /* @__PURE__ */ jsx10(LoaderCircle, { size: 14, className: "animate-spin" }),
2326
2565
  "\u786E\u8BA4\u4E2D"
2327
2566
  ]
2328
2567
  }
@@ -2344,30 +2583,30 @@ function QuestionCard({
2344
2583
  }) {
2345
2584
  const multi = question.multiSelect ?? false;
2346
2585
  const customTextareaRef = useAutoResizeTextarea(customText);
2347
- return /* @__PURE__ */ jsxs7("div", { children: [
2348
- /* @__PURE__ */ jsxs7("div", { className: cn("flex items-start gap-2", answered ? "mb-2" : "mb-3"), children: [
2349
- /* @__PURE__ */ jsx9(
2586
+ return /* @__PURE__ */ jsxs8("div", { children: [
2587
+ /* @__PURE__ */ jsxs8("div", { className: cn("flex items-start gap-2", answered ? "mb-2" : "mb-3"), children: [
2588
+ /* @__PURE__ */ jsx10(
2350
2589
  MessageSquareMore,
2351
2590
  {
2352
2591
  size: answered ? 12 : 13,
2353
2592
  className: "mt-0.5 shrink-0 text-[hsl(var(--primary))]"
2354
2593
  }
2355
2594
  ),
2356
- /* @__PURE__ */ jsx9(
2595
+ /* @__PURE__ */ jsx10(
2357
2596
  "div",
2358
2597
  {
2359
2598
  className: cn(
2360
2599
  "min-w-0 flex-1 font-medium text-[hsl(var(--foreground))]",
2361
2600
  answered ? "text-xs" : "text-sm"
2362
2601
  ),
2363
- children: /* @__PURE__ */ jsx9(MarkdownContent, { className: "blade-chat-prose", children: question.question })
2602
+ children: /* @__PURE__ */ jsx10(MarkdownContent, { className: "blade-chat-prose", children: question.question })
2364
2603
  }
2365
2604
  )
2366
2605
  ] }),
2367
- /* @__PURE__ */ jsxs7("div", { className: cn("flex flex-col pl-5", answered ? "gap-1" : "gap-1.5"), children: [
2606
+ /* @__PURE__ */ jsxs8("div", { className: cn("flex flex-col pl-5", answered ? "gap-1" : "gap-1.5"), children: [
2368
2607
  question.options.map((opt, optIdx) => {
2369
2608
  const isSel = selected.has(optIdx);
2370
- return /* @__PURE__ */ jsxs7(
2609
+ return /* @__PURE__ */ jsxs8(
2371
2610
  "button",
2372
2611
  {
2373
2612
  type: "button",
@@ -2381,14 +2620,14 @@ function QuestionCard({
2381
2620
  answered && "cursor-default opacity-70"
2382
2621
  ),
2383
2622
  children: [
2384
- multi && /* @__PURE__ */ jsx9(
2623
+ multi && /* @__PURE__ */ jsx10(
2385
2624
  "div",
2386
2625
  {
2387
2626
  className: cn(
2388
2627
  "mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border transition-colors",
2389
2628
  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))]"
2390
2629
  ),
2391
- children: isSel && /* @__PURE__ */ jsx9(
2630
+ children: isSel && /* @__PURE__ */ jsx10(
2392
2631
  Check,
2393
2632
  {
2394
2633
  size: 9,
@@ -2397,9 +2636,9 @@ function QuestionCard({
2397
2636
  )
2398
2637
  }
2399
2638
  ),
2400
- /* @__PURE__ */ jsxs7("div", { className: "min-w-0", children: [
2401
- /* @__PURE__ */ jsx9("div", { className: cn("font-medium", answered ? "text-xs" : "text-[13px]"), children: opt.label }),
2402
- opt.description && /* @__PURE__ */ jsx9(
2639
+ /* @__PURE__ */ jsxs8("div", { className: "min-w-0", children: [
2640
+ /* @__PURE__ */ jsx10("div", { className: cn("font-medium", answered ? "text-xs" : "text-[13px]"), children: opt.label }),
2641
+ opt.description && /* @__PURE__ */ jsx10(
2403
2642
  "div",
2404
2643
  {
2405
2644
  className: cn(
@@ -2416,7 +2655,7 @@ function QuestionCard({
2416
2655
  opt.label
2417
2656
  );
2418
2657
  }),
2419
- answered && !isCustom ? null : /* @__PURE__ */ jsxs7(
2658
+ answered && !isCustom ? null : /* @__PURE__ */ jsxs8(
2420
2659
  "div",
2421
2660
  {
2422
2661
  className: cn(
@@ -2426,8 +2665,8 @@ function QuestionCard({
2426
2665
  answered && "cursor-default opacity-70"
2427
2666
  ),
2428
2667
  children: [
2429
- /* @__PURE__ */ jsx9("span", { className: "shrink-0 pt-1 text-xs text-[hsl(var(--muted-foreground))]", children: "\u5176\u4ED6\uFF1A" }),
2430
- /* @__PURE__ */ jsx9(
2668
+ /* @__PURE__ */ jsx10("span", { className: "shrink-0 pt-1 text-xs text-[hsl(var(--muted-foreground))]", children: "\u5176\u4ED6\uFF1A" }),
2669
+ /* @__PURE__ */ jsx10(
2431
2670
  "textarea",
2432
2671
  {
2433
2672
  ref: customTextareaRef,
@@ -2459,7 +2698,7 @@ function NoteField({
2459
2698
  const textareaRef = useAutoResizeTextarea(note);
2460
2699
  const readOnly = answered || submitted;
2461
2700
  if (answered && !note.trim()) return null;
2462
- return /* @__PURE__ */ jsxs7(
2701
+ return /* @__PURE__ */ jsxs8(
2463
2702
  "label",
2464
2703
  {
2465
2704
  className: cn(
@@ -2469,8 +2708,8 @@ function NoteField({
2469
2708
  readOnly && "cursor-default opacity-70"
2470
2709
  ),
2471
2710
  children: [
2472
- /* @__PURE__ */ jsx9("span", { className: "mb-1.5 block text-xs text-[hsl(var(--muted-foreground))]", children: "\u8865\u5145\u8BF4\u660E\uFF08\u53EF\u9009\uFF09" }),
2473
- /* @__PURE__ */ jsx9(
2711
+ /* @__PURE__ */ jsx10("span", { className: "mb-1.5 block text-xs text-[hsl(var(--muted-foreground))]", children: "\u8865\u5145\u8BF4\u660E\uFF08\u53EF\u9009\uFF09" }),
2712
+ /* @__PURE__ */ jsx10(
2474
2713
  "textarea",
2475
2714
  {
2476
2715
  ref: textareaRef,
@@ -2559,7 +2798,7 @@ function normalizeOptionItem(value) {
2559
2798
  }
2560
2799
 
2561
2800
  // src/components/ToolCallBlock.tsx
2562
- import { Fragment, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
2801
+ import { Fragment, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
2563
2802
  function resolveAskQuestionState({
2564
2803
  toolStatus,
2565
2804
  hasAnswerData,
@@ -2581,12 +2820,12 @@ function ToolCallBlock({
2581
2820
  isActiveQuestion,
2582
2821
  renderer
2583
2822
  }) {
2584
- const [expanded, setExpanded] = useState9(false);
2823
+ const [expanded, setExpanded] = useState10(false);
2585
2824
  const normalizedName = formatToolName(toolCall.name);
2586
2825
  if (renderer) {
2587
2826
  const custom = renderer(toolCall);
2588
2827
  if (custom !== null && custom !== void 0) {
2589
- return /* @__PURE__ */ jsx10(Fragment, { children: custom });
2828
+ return /* @__PURE__ */ jsx11(Fragment, { children: custom });
2590
2829
  }
2591
2830
  }
2592
2831
  if (normalizedName === "AskUserQuestion") {
@@ -2599,7 +2838,7 @@ function ToolCallBlock({
2599
2838
  });
2600
2839
  const canAnswer = questionState.awaitingAnswer && Boolean(onAnswer);
2601
2840
  if (askData) {
2602
- return /* @__PURE__ */ jsx10(
2841
+ return /* @__PURE__ */ jsx11(
2603
2842
  AskUserQuestionBlock,
2604
2843
  {
2605
2844
  data: askData,
@@ -2612,31 +2851,31 @@ function ToolCallBlock({
2612
2851
  );
2613
2852
  }
2614
2853
  if (toolCall.status === "pending") {
2615
- return /* @__PURE__ */ jsxs8("div", { className: "ml-4 flex max-w-lg items-center gap-2 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4 text-sm text-[hsl(var(--muted-foreground))]", children: [
2616
- /* @__PURE__ */ jsx10(LoaderCircle, { size: 14, className: "animate-spin" }),
2617
- /* @__PURE__ */ jsx10("span", { children: "\u6B63\u5728\u51C6\u5907\u95EE\u9898\u2026" })
2854
+ 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: [
2855
+ /* @__PURE__ */ jsx11(LoaderCircle, { size: 14, className: "animate-spin" }),
2856
+ /* @__PURE__ */ jsx11("span", { children: "\u6B63\u5728\u51C6\u5907\u95EE\u9898\u2026" })
2618
2857
  ] });
2619
2858
  }
2620
2859
  const errorDetail = parseAskUserQuestionError(
2621
2860
  typeof toolCall.result === "string" ? toolCall.result : null
2622
2861
  );
2623
- return /* @__PURE__ */ jsxs8("div", { className: "ml-4 max-w-lg rounded-xl border border-amber-500/35 bg-amber-500/10 p-4 text-sm text-[hsl(var(--foreground))]", children: [
2624
- /* @__PURE__ */ jsx10("div", { className: "font-semibold", children: "\u9009\u62E9\u9898\u5185\u5BB9\u6682\u65F6\u65E0\u6CD5\u663E\u793A" }),
2625
- /* @__PURE__ */ jsx10("div", { className: "mt-1 text-xs leading-5 text-[hsl(var(--muted-foreground))]", children: errorDetail?.message ?? "\u6536\u5230\u7684\u4EA4\u4E92\u6570\u636E\u4E0D\u5B8C\u6574\u3002\u8BF7\u8BA9\u667A\u80FD\u4F53\u91CD\u65B0\u63D0\u95EE\u3002" }),
2626
- errorDetail?.detail ? /* @__PURE__ */ jsxs8("details", { className: "mt-2 text-xs text-[hsl(var(--muted-foreground))]", children: [
2627
- /* @__PURE__ */ jsx10("summary", { className: "cursor-pointer", children: "\u67E5\u770B\u5177\u4F53\u539F\u56E0" }),
2628
- /* @__PURE__ */ jsx10("div", { className: "mt-1 break-words font-mono", children: errorDetail.detail })
2862
+ 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: [
2863
+ /* @__PURE__ */ jsx11("div", { className: "font-semibold", children: "\u9009\u62E9\u9898\u5185\u5BB9\u6682\u65F6\u65E0\u6CD5\u663E\u793A" }),
2864
+ /* @__PURE__ */ jsx11("div", { className: "mt-1 text-xs leading-5 text-[hsl(var(--muted-foreground))]", children: errorDetail?.message ?? "\u6536\u5230\u7684\u4EA4\u4E92\u6570\u636E\u4E0D\u5B8C\u6574\u3002\u8BF7\u8BA9\u667A\u80FD\u4F53\u91CD\u65B0\u63D0\u95EE\u3002" }),
2865
+ errorDetail?.detail ? /* @__PURE__ */ jsxs9("details", { className: "mt-2 text-xs text-[hsl(var(--muted-foreground))]", children: [
2866
+ /* @__PURE__ */ jsx11("summary", { className: "cursor-pointer", children: "\u67E5\u770B\u5177\u4F53\u539F\u56E0" }),
2867
+ /* @__PURE__ */ jsx11("div", { className: "mt-1 break-words font-mono", children: errorDetail.detail })
2629
2868
  ] }) : null
2630
2869
  ] });
2631
2870
  }
2632
2871
  const tone = getToolTone(toolCall.status);
2633
2872
  const displayName = getToolDisplayLabel(toolCall);
2634
2873
  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))]";
2635
- const statusIcon = toolCall.status === "pending" ? /* @__PURE__ */ jsx10(LoaderCircle, { size: 11, className: "animate-spin" }) : toolCall.status === "awaiting_answer" ? /* @__PURE__ */ jsx10(MessageSquareMore, { size: 11 }) : toolCall.status === "cancelled" || toolCall.status === "error" ? /* @__PURE__ */ jsx10(X, { size: 11 }) : /* @__PURE__ */ jsx10(Check, { size: 11 });
2874
+ 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 });
2636
2875
  const statusTextClass = tone === "red" ? "text-[hsl(var(--muted-foreground))]" : tone === "amber" ? "text-amber-300" : tone === "blue" ? "text-blue-300" : "text-[hsl(var(--primary))]";
2637
- return /* @__PURE__ */ jsxs8("div", { className: "blade-chat-tool ml-4 text-xs", children: [
2638
- /* @__PURE__ */ jsxs8("div", { className: cn("border-l-[3px] flex items-center gap-2 px-3 py-2", toneClass), children: [
2639
- /* @__PURE__ */ jsxs8(
2876
+ return /* @__PURE__ */ jsxs9("div", { className: "blade-chat-tool ml-4 text-xs", children: [
2877
+ /* @__PURE__ */ jsxs9("div", { className: cn("border-l-[3px] flex items-center gap-2 px-3 py-2", toneClass), children: [
2878
+ /* @__PURE__ */ jsxs9(
2640
2879
  "button",
2641
2880
  {
2642
2881
  type: "button",
@@ -2644,7 +2883,7 @@ function ToolCallBlock({
2644
2883
  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",
2645
2884
  "aria-expanded": expanded,
2646
2885
  children: [
2647
- /* @__PURE__ */ jsx10(
2886
+ /* @__PURE__ */ jsx11(
2648
2887
  ChevronRight,
2649
2888
  {
2650
2889
  size: 11,
@@ -2654,24 +2893,24 @@ function ToolCallBlock({
2654
2893
  )
2655
2894
  }
2656
2895
  ),
2657
- /* @__PURE__ */ jsxs8("span", { className: cn("flex shrink-0 items-center gap-1 text-[10px]", statusTextClass), children: [
2896
+ /* @__PURE__ */ jsxs9("span", { className: cn("flex shrink-0 items-center gap-1 text-[10px]", statusTextClass), children: [
2658
2897
  statusIcon,
2659
- /* @__PURE__ */ jsx10("span", { children: getToolStatusLabel(toolCall.status) })
2898
+ /* @__PURE__ */ jsx11("span", { children: getToolStatusLabel(toolCall.status) })
2660
2899
  ] }),
2661
- /* @__PURE__ */ jsx10("span", { className: "min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]", children: displayName })
2900
+ /* @__PURE__ */ jsx11("span", { className: "min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]", children: displayName })
2662
2901
  ]
2663
2902
  }
2664
2903
  ),
2665
- typeof toolCall.duration_ms === "number" && toolCall.duration_ms > 0 && /* @__PURE__ */ jsx10("span", { className: "shrink-0 font-mono text-[10px] text-[hsl(var(--muted-foreground))]", children: formatToolDuration(toolCall.duration_ms) })
2904
+ 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) })
2666
2905
  ] }),
2667
- expanded && /* @__PURE__ */ jsxs8("div", { className: "blade-chat-tool-detail ml-4 mt-1 rounded-xl bg-[hsl(var(--card))] px-3 py-3", children: [
2668
- /* @__PURE__ */ jsx10("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u5DE5\u5177" }),
2669
- /* @__PURE__ */ jsx10("div", { className: "mb-3 font-mono text-[11px] text-[hsl(var(--foreground))]", children: normalizedName }),
2670
- /* @__PURE__ */ jsx10("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u53C2\u6570" }),
2671
- /* @__PURE__ */ jsx10("pre", { className: "overflow-x-auto whitespace-pre-wrap rounded-md bg-[hsl(var(--muted))] p-2 font-mono text-[11px] text-[hsl(var(--foreground))]", children: formatToolArgs(toolCall.arguments) }),
2672
- toolCall.result != null && /* @__PURE__ */ jsxs8(Fragment, { children: [
2673
- /* @__PURE__ */ jsx10("div", { className: "mb-1 mt-3 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u7ED3\u679C" }),
2674
- /* @__PURE__ */ jsx10("pre", { className: "max-h-[400px] overflow-auto whitespace-pre-wrap rounded-md bg-[hsl(var(--muted))] p-2 font-mono text-[11px] text-[hsl(var(--foreground))]", children: formatToolResult(toolCall.result) })
2906
+ expanded && /* @__PURE__ */ jsxs9("div", { className: "blade-chat-tool-detail ml-4 mt-1 rounded-xl bg-[hsl(var(--card))] px-3 py-3", children: [
2907
+ /* @__PURE__ */ jsx11("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u5DE5\u5177" }),
2908
+ /* @__PURE__ */ jsx11("div", { className: "mb-3 font-mono text-[11px] text-[hsl(var(--foreground))]", children: normalizedName }),
2909
+ /* @__PURE__ */ jsx11("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u53C2\u6570" }),
2910
+ /* @__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) }),
2911
+ toolCall.result != null && /* @__PURE__ */ jsxs9(Fragment, { children: [
2912
+ /* @__PURE__ */ jsx11("div", { className: "mb-1 mt-3 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u7ED3\u679C" }),
2913
+ /* @__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) })
2675
2914
  ] })
2676
2915
  ] })
2677
2916
  ] });
@@ -2689,12 +2928,12 @@ function buildAskUserPayload(argumentsJson) {
2689
2928
  }
2690
2929
 
2691
2930
  // src/components/AssistantTurnBlock.tsx
2692
- import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
2931
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
2693
2932
  function ThinkingBlock({ reasoning, isStreaming }) {
2694
- const [open, setOpen] = useState10(false);
2933
+ const [open, setOpen] = useState11(false);
2695
2934
  if (!isStreaming) return null;
2696
- return /* @__PURE__ */ jsxs9("div", { className: "blade-chat-thinking text-xs", children: [
2697
- /* @__PURE__ */ jsxs9(
2935
+ return /* @__PURE__ */ jsxs10("div", { className: "blade-chat-thinking text-xs", children: [
2936
+ /* @__PURE__ */ jsxs10(
2698
2937
  "button",
2699
2938
  {
2700
2939
  type: "button",
@@ -2702,8 +2941,8 @@ function ThinkingBlock({ reasoning, isStreaming }) {
2702
2941
  "aria-expanded": open,
2703
2942
  className: "group/thinking inline-flex items-center gap-1 text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))]",
2704
2943
  children: [
2705
- /* @__PURE__ */ jsx11(Shimmer, { className: "text-xs", children: "\u6B63\u5728\u601D\u8003" }),
2706
- /* @__PURE__ */ jsx11(
2944
+ /* @__PURE__ */ jsx12(Shimmer, { className: "text-xs", children: "\u6B63\u5728\u601D\u8003" }),
2945
+ /* @__PURE__ */ jsx12(
2707
2946
  ChevronRight,
2708
2947
  {
2709
2948
  size: 14,
@@ -2716,7 +2955,7 @@ function ThinkingBlock({ reasoning, isStreaming }) {
2716
2955
  ]
2717
2956
  }
2718
2957
  ),
2719
- open ? /* @__PURE__ */ jsx11("div", { className: "mt-1.5 whitespace-pre-wrap text-xs leading-[22px] text-[hsl(var(--muted-foreground))]", children: reasoning }) : null
2958
+ open ? /* @__PURE__ */ jsx12("div", { className: "mt-1.5 whitespace-pre-wrap text-xs leading-[22px] text-[hsl(var(--muted-foreground))]", children: reasoning }) : null
2720
2959
  ] });
2721
2960
  }
2722
2961
  function getMessageText(message) {
@@ -2908,14 +3147,14 @@ function ExecutionToolRow({ toolCall }) {
2908
3147
  "size-3.5 shrink-0",
2909
3148
  failed ? "text-red-500" : "text-[hsl(var(--muted-foreground))]"
2910
3149
  );
2911
- const icon = toolCall.status === "pending" ? /* @__PURE__ */ jsx11(LoaderCircle, { className: cn(iconClass, "animate-spin"), "aria-hidden": "true" }) : toolCall.status === "error" ? /* @__PURE__ */ jsx11(CircleAlert, { className: iconClass, "aria-hidden": "true" }) : toolCall.status === "cancelled" ? /* @__PURE__ */ jsx11(X, { className: iconClass, "aria-hidden": "true" }) : normalizedName === "WebSearch" || normalizedName === "WebFetch" ? /* @__PURE__ */ jsx11(Earth, { className: iconClass, "aria-hidden": "true" }) : normalizedName === "Bash" || normalizedName === "BgBash" ? /* @__PURE__ */ jsx11(Terminal, { className: iconClass, "aria-hidden": "true" }) : normalizedName === "Write" || normalizedName === "Edit" || normalizedName === "MultiEdit" ? /* @__PURE__ */ jsx11(FilePenLine, { className: iconClass, "aria-hidden": "true" }) : normalizedName === "Read" || normalizedName === "ReadSkill" || normalizedName === "get_skill_content" ? /* @__PURE__ */ jsx11(BookOpen, { className: iconClass, "aria-hidden": "true" }) : normalizedName === "Grep" || normalizedName === "Glob" || normalizedName === "search_skills" ? /* @__PURE__ */ jsx11(Search, { className: iconClass, "aria-hidden": "true" }) : normalizedName === "Agent" ? /* @__PURE__ */ jsx11(Bot, { className: iconClass, "aria-hidden": "true" }) : /* @__PURE__ */ jsx11(Wrench, { className: iconClass, "aria-hidden": "true" });
3150
+ const icon = toolCall.status === "pending" ? /* @__PURE__ */ jsx12(LoaderCircle, { className: cn(iconClass, "animate-spin"), "aria-hidden": "true" }) : toolCall.status === "error" ? /* @__PURE__ */ jsx12(CircleAlert, { className: iconClass, "aria-hidden": "true" }) : toolCall.status === "cancelled" ? /* @__PURE__ */ jsx12(X, { className: iconClass, "aria-hidden": "true" }) : normalizedName === "WebSearch" || normalizedName === "WebFetch" ? /* @__PURE__ */ jsx12(Earth, { className: iconClass, "aria-hidden": "true" }) : normalizedName === "Bash" || normalizedName === "BgBash" ? /* @__PURE__ */ jsx12(Terminal, { className: iconClass, "aria-hidden": "true" }) : normalizedName === "Write" || normalizedName === "Edit" || normalizedName === "MultiEdit" ? /* @__PURE__ */ jsx12(FilePenLine, { className: iconClass, "aria-hidden": "true" }) : normalizedName === "Read" || normalizedName === "ReadSkill" || normalizedName === "get_skill_content" ? /* @__PURE__ */ jsx12(BookOpen, { className: iconClass, "aria-hidden": "true" }) : normalizedName === "Grep" || normalizedName === "Glob" || normalizedName === "search_skills" ? /* @__PURE__ */ jsx12(Search, { className: iconClass, "aria-hidden": "true" }) : normalizedName === "Agent" ? /* @__PURE__ */ jsx12(Bot, { className: iconClass, "aria-hidden": "true" }) : /* @__PURE__ */ jsx12(Wrench, { className: iconClass, "aria-hidden": "true" });
2912
3151
  const rowClassName = cn(
2913
3152
  "flex min-w-0 items-center gap-1 py-1.5 text-xs leading-[22px]",
2914
3153
  failed ? "text-red-500" : "text-[hsl(var(--muted-foreground))]"
2915
3154
  );
2916
- return /* @__PURE__ */ jsxs9("div", { "data-testid": "execution-tool-intent", className: rowClassName, title: label, children: [
3155
+ return /* @__PURE__ */ jsxs10("div", { "data-testid": "execution-tool-intent", className: rowClassName, title: label, children: [
2917
3156
  icon,
2918
- /* @__PURE__ */ jsx11("span", { className: "min-w-0 truncate", children: label })
3157
+ /* @__PURE__ */ jsx12("span", { className: "min-w-0 truncate", children: label })
2919
3158
  ] });
2920
3159
  }
2921
3160
  function AssistantTurnBlock({
@@ -2925,8 +3164,15 @@ function AssistantTurnBlock({
2925
3164
  onAnswer,
2926
3165
  sessionStatus,
2927
3166
  toolCallRenderer,
3167
+ hidePlanUpdateTools = false,
2928
3168
  sessionId
2929
3169
  }) {
3170
+ const shouldHideToolCall = (message, toolCall) => {
3171
+ if (!hidePlanUpdateTools || !isPlanUpdateTool(toolCall) || parsePlanUpdate(toolCall.arguments) === null) {
3172
+ return false;
3173
+ }
3174
+ return toolCall.status === "done" || toolCall.status === "pending" && message.status === "streaming";
3175
+ };
2930
3176
  const hasInterrupted = messages.some((message) => message.status === "interrupted");
2931
3177
  const hasFailedWithoutContent = messages.some(
2932
3178
  (message) => message.status === "failed" && !hasRenderableMessageContent(message)
@@ -2936,16 +3182,16 @@ function AssistantTurnBlock({
2936
3182
  const finalOrderedParts = finalMessage ? getOrderedMessageParts(
2937
3183
  finalMessage,
2938
3184
  (finalMessage.tool_calls ?? []).filter(
2939
- (toolCall) => formatToolName(toolCall.name) !== "AskUserQuestion"
3185
+ (toolCall) => formatToolName(toolCall.name) !== "AskUserQuestion" && !shouldHideToolCall(finalMessage, toolCall)
2940
3186
  )
2941
3187
  ) : [];
2942
3188
  const hasExecutionProcess = messages.some(
2943
- (message) => message.reasoning || (message.tool_calls?.length ?? 0) > 0
3189
+ (message) => message.reasoning || (message.tool_calls ?? []).some((toolCall) => !shouldHideToolCall(message, toolCall))
2944
3190
  );
2945
3191
  const latestReasoningIndex = isStreaming ? findLatestReasoningMessageIndex(messages) : -1;
2946
3192
  const hasActionableToolCall = messages.some(
2947
3193
  (message) => message.status === "failed" || message.status === "interrupted" || (message.tool_calls ?? []).some(
2948
- (toolCall) => toolCall.status === "error" || toolCall.status === "cancelled"
3194
+ (toolCall) => !shouldHideToolCall(message, toolCall) && (toolCall.status === "error" || toolCall.status === "cancelled")
2949
3195
  )
2950
3196
  );
2951
3197
  const questionToolCalls = messages.flatMap(
@@ -2954,12 +3200,12 @@ function AssistantTurnBlock({
2954
3200
  )
2955
3201
  );
2956
3202
  const activeQuestionId = questionToolCalls.filter((toolCall) => toolCall.status === "pending").at(-1)?.id;
2957
- const [displayMode, setDisplayMode] = useState10(
3203
+ const [displayMode, setDisplayMode] = useState11(
2958
3204
  () => isStreaming || hasActionableToolCall ? "detail" : "compact"
2959
3205
  );
2960
- const userSelectedDisplayModeRef = useRef9(false);
2961
- const wasStreamingRef = useRef9(isStreaming);
2962
- useEffect8(() => {
3206
+ const userSelectedDisplayModeRef = useRef10(false);
3207
+ const wasStreamingRef = useRef10(isStreaming);
3208
+ useEffect9(() => {
2963
3209
  if (wasStreamingRef.current && !isStreaming && !userSelectedDisplayModeRef.current) {
2964
3210
  setDisplayMode(hasActionableToolCall ? "detail" : "compact");
2965
3211
  }
@@ -2967,11 +3213,11 @@ function AssistantTurnBlock({
2967
3213
  }, [hasActionableToolCall, isStreaming]);
2968
3214
  const effectiveMode = resolveTurnDisplayMode({ isStreaming, displayMode });
2969
3215
  const executionDurationMs = getExecutionDurationMs({ messages, isStreaming });
2970
- const [clock, setClock] = useState10(() => Date.now());
3216
+ const [clock, setClock] = useState11(() => Date.now());
2971
3217
  const hasLiveStartTime = messages.some(
2972
3218
  (message) => message.timestamp != null && Number.isFinite(Date.parse(message.timestamp))
2973
3219
  );
2974
- useEffect8(() => {
3220
+ useEffect9(() => {
2975
3221
  if (!isStreaming || !hasLiveStartTime) return;
2976
3222
  const timer = window.setInterval(() => setClock(Date.now()), 1e3);
2977
3223
  return () => window.clearInterval(timer);
@@ -2979,21 +3225,21 @@ function AssistantTurnBlock({
2979
3225
  const liveExecutionDurationMs = isStreaming ? getExecutionDurationMs({ messages, isStreaming, now: clock }) : executionDurationMs;
2980
3226
  const memoryRefs = collectMemoryRefs(messages);
2981
3227
  if (!hasExecutionProcess) {
2982
- return /* @__PURE__ */ jsxs9(
3228
+ return /* @__PURE__ */ jsxs10(
2983
3229
  "div",
2984
3230
  {
2985
3231
  "aria-busy": isStreaming || void 0,
2986
3232
  className: "blade-chat-assistant-turn flex flex-col gap-3",
2987
3233
  children: [
2988
- memoryRefs.length > 0 ? /* @__PURE__ */ jsx11(MemoryRefsHint, { refs: memoryRefs }) : null,
2989
- hasInterrupted && /* @__PURE__ */ jsx11("div", { className: "ml-4 w-fit rounded-full border border-amber-500/30 bg-amber-500/10 px-2.5 py-1 text-[10px] font-medium uppercase tracking-[0.12em] text-amber-300", children: "\u5DF2\u4E2D\u65AD" }),
2990
- hasFailedWithoutContent && /* @__PURE__ */ jsx11("div", { className: "ml-4 w-fit rounded-full border border-red-500/30 bg-red-500/10 px-2.5 py-1 text-[10px] font-medium text-red-400", children: "\u751F\u6210\u5931\u8D25" }),
3234
+ memoryRefs.length > 0 ? /* @__PURE__ */ jsx12(MemoryRefsHint, { refs: memoryRefs }) : null,
3235
+ 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" }),
3236
+ hasFailedWithoutContent && /* @__PURE__ */ jsx12("div", { className: "ml-4 w-fit rounded-full border border-red-500/30 bg-red-500/10 px-2.5 py-1 text-[10px] font-medium text-red-400", children: "\u751F\u6210\u5931\u8D25" }),
2991
3237
  messages.map((message, index) => {
2992
- return hasRenderableMessageContent(message) ? /* @__PURE__ */ jsx11(
3238
+ return hasRenderableMessageContent(message) ? /* @__PURE__ */ jsx12(
2993
3239
  "div",
2994
3240
  {
2995
3241
  className: "flex flex-col gap-3",
2996
- children: /* @__PURE__ */ jsx11(
3242
+ children: /* @__PURE__ */ jsx12(
2997
3243
  AssistantMessageContent,
2998
3244
  {
2999
3245
  message,
@@ -3009,26 +3255,26 @@ function AssistantTurnBlock({
3009
3255
  }
3010
3256
  );
3011
3257
  }
3012
- return /* @__PURE__ */ jsxs9(
3258
+ return /* @__PURE__ */ jsxs10(
3013
3259
  "div",
3014
3260
  {
3015
3261
  "aria-busy": isStreaming || void 0,
3016
3262
  className: "blade-chat-assistant-turn flex flex-col gap-3",
3017
3263
  children: [
3018
- memoryRefs.length > 0 ? /* @__PURE__ */ jsx11(MemoryRefsHint, { refs: memoryRefs }) : null,
3019
- hasInterrupted && /* @__PURE__ */ jsx11("div", { className: "ml-4 w-fit rounded-full border border-amber-500/30 bg-amber-500/10 px-2.5 py-1 text-[10px] font-medium uppercase tracking-[0.12em] text-amber-300", children: "\u5DF2\u4E2D\u65AD" }),
3020
- hasFailedWithoutContent && /* @__PURE__ */ jsx11("div", { className: "ml-4 w-fit rounded-full border border-red-500/30 bg-red-500/10 px-2.5 py-1 text-[10px] font-medium text-red-400", children: "\u751F\u6210\u5931\u8D25" }),
3021
- /* @__PURE__ */ jsxs9("div", { className: "flex w-full items-start gap-2.5", children: [
3022
- /* @__PURE__ */ jsx11(
3264
+ memoryRefs.length > 0 ? /* @__PURE__ */ jsx12(MemoryRefsHint, { refs: memoryRefs }) : null,
3265
+ 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" }),
3266
+ hasFailedWithoutContent && /* @__PURE__ */ jsx12("div", { className: "ml-4 w-fit rounded-full border border-red-500/30 bg-red-500/10 px-2.5 py-1 text-[10px] font-medium text-red-400", children: "\u751F\u6210\u5931\u8D25" }),
3267
+ /* @__PURE__ */ jsxs10("div", { className: "flex w-full items-start gap-2.5", children: [
3268
+ /* @__PURE__ */ jsx12(
3023
3269
  "span",
3024
3270
  {
3025
3271
  className: "grid size-[30px] shrink-0 place-items-center rounded-full bg-[hsl(var(--muted)/0.55)] text-[hsl(var(--foreground))]",
3026
3272
  "aria-hidden": "true",
3027
- children: /* @__PURE__ */ jsx11(Bot, { size: 16 })
3273
+ children: /* @__PURE__ */ jsx12(Bot, { size: 16 })
3028
3274
  }
3029
3275
  ),
3030
- /* @__PURE__ */ jsxs9("div", { className: "min-w-0 flex-1 pt-0.5", children: [
3031
- /* @__PURE__ */ jsxs9(
3276
+ /* @__PURE__ */ jsxs10("div", { className: "min-w-0 flex-1 pt-0.5", children: [
3277
+ /* @__PURE__ */ jsxs10(
3032
3278
  "button",
3033
3279
  {
3034
3280
  type: "button",
@@ -3041,19 +3287,20 @@ function AssistantTurnBlock({
3041
3287
  "data-testid": "assistant-execution-summary",
3042
3288
  className: "inline-flex min-w-0 max-w-full select-none items-center gap-1 bg-transparent p-0 text-left text-xs leading-[22px] text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))] focus-visible:ring-1 focus-visible:ring-[hsl(var(--ring))] focus:outline-none",
3043
3289
  children: [
3044
- /* @__PURE__ */ jsx11("span", { className: "min-w-0 truncate", children: executionSummaryLabel({
3290
+ /* @__PURE__ */ jsx12("span", { className: "min-w-0 truncate", children: executionSummaryLabel({
3045
3291
  messages,
3046
3292
  isStreaming,
3047
3293
  durationMs: liveExecutionDurationMs,
3048
3294
  sessionStatus,
3049
3295
  askAnswers
3050
3296
  }) }),
3051
- /* @__PURE__ */ jsx11(
3297
+ /* @__PURE__ */ jsx12(
3052
3298
  ChevronRight,
3053
3299
  {
3054
3300
  size: 14,
3301
+ style: { transitionDuration: "260ms", transitionTimingFunction: "cubic-bezier(0.25, 0.1, 0.25, 1)" },
3055
3302
  className: cn(
3056
- "shrink-0 transition-transform duration-300",
3303
+ "shrink-0 transition-transform",
3057
3304
  effectiveMode === "detail" && "rotate-90"
3058
3305
  ),
3059
3306
  "aria-hidden": "true"
@@ -3062,26 +3309,26 @@ function AssistantTurnBlock({
3062
3309
  ]
3063
3310
  }
3064
3311
  ),
3065
- /* @__PURE__ */ jsx11("div", { className: "mt-3 h-px w-full bg-[hsl(var(--border)/0.75)]" })
3312
+ /* @__PURE__ */ jsx12("div", { className: "mt-3 h-px w-full bg-[hsl(var(--border)/0.75)]" })
3066
3313
  ] })
3067
3314
  ] }),
3068
- effectiveMode === "detail" ? /* @__PURE__ */ jsx11("div", { className: "ml-10 flex flex-col gap-3 pt-1", children: messages.map((message, index) => {
3315
+ effectiveMode === "detail" ? /* @__PURE__ */ jsx12("div", { className: "ml-10 flex flex-col gap-3 pt-1", children: messages.map((message, index) => {
3069
3316
  const isLast = index === messages.length - 1;
3070
3317
  const streamingThis = isStreaming && isLast;
3071
3318
  const text = getMessageText(message);
3072
3319
  const toolCalls = (message.tool_calls ?? []).filter(
3073
- (toolCall) => formatToolName(toolCall.name) !== "AskUserQuestion"
3320
+ (toolCall) => formatToolName(toolCall.name) !== "AskUserQuestion" && !shouldHideToolCall(message, toolCall)
3074
3321
  );
3075
3322
  const orderedParts = getOrderedMessageParts(message, toolCalls);
3076
3323
  const showReasoning = !!message.reasoning && isStreaming && index === latestReasoningIndex;
3077
- return /* @__PURE__ */ jsxs9(
3324
+ return /* @__PURE__ */ jsxs10(
3078
3325
  "div",
3079
3326
  {
3080
3327
  className: "flex flex-col gap-3",
3081
3328
  children: [
3082
- showReasoning && message.reasoning ? /* @__PURE__ */ jsx11(ThinkingBlock, { reasoning: message.reasoning, isStreaming: streamingThis && !text }) : null,
3329
+ showReasoning && message.reasoning ? /* @__PURE__ */ jsx12(ThinkingBlock, { reasoning: message.reasoning, isStreaming: streamingThis && !text }) : null,
3083
3330
  orderedParts.length > 0 ? orderedParts.map(
3084
- (part) => part.type === "text" ? /* @__PURE__ */ jsx11(
3331
+ (part) => part.type === "text" ? /* @__PURE__ */ jsx12(
3085
3332
  AssistantMessageContent,
3086
3333
  {
3087
3334
  message: { ...message, content: part.content, tool_calls: turnToolCalls },
@@ -3090,11 +3337,11 @@ function AssistantTurnBlock({
3090
3337
  compact: true
3091
3338
  },
3092
3339
  part.key
3093
- ) : /* @__PURE__ */ jsx11("div", { className: "flex flex-col gap-0.5", children: part.toolCalls.map((toolCall) => {
3340
+ ) : /* @__PURE__ */ jsx12("div", { className: "flex flex-col gap-0.5", children: part.toolCalls.map((toolCall) => {
3094
3341
  const custom = toolCallRenderer?.(toolCall);
3095
- return custom !== null && custom !== void 0 ? /* @__PURE__ */ jsx11("div", { children: custom }, toolCall.id) : formatToolName(toolCall.name) === "Agent" ? /* @__PURE__ */ jsx11(AgentLoopBlock, { toolCall }, toolCall.id) : /* @__PURE__ */ jsx11(ExecutionToolRow, { toolCall }, toolCall.id);
3342
+ return custom !== null && custom !== void 0 ? /* @__PURE__ */ jsx12("div", { children: custom }, toolCall.id) : formatToolName(toolCall.name) === "Agent" ? /* @__PURE__ */ jsx12(AgentLoopBlock, { toolCall }, toolCall.id) : /* @__PURE__ */ jsx12(ExecutionToolRow, { toolCall }, toolCall.id);
3096
3343
  }) }, part.key)
3097
- ) : hasRenderableMessageContent(message) && message !== finalMessage ? /* @__PURE__ */ jsx11(
3344
+ ) : hasRenderableMessageContent(message) && message !== finalMessage ? /* @__PURE__ */ jsx12(
3098
3345
  AssistantMessageContent,
3099
3346
  {
3100
3347
  message,
@@ -3103,16 +3350,16 @@ function AssistantTurnBlock({
3103
3350
  compact: true
3104
3351
  }
3105
3352
  ) : null,
3106
- orderedParts.length === 0 && toolCalls.length > 0 ? /* @__PURE__ */ jsx11("div", { className: "flex flex-col gap-0.5", children: toolCalls.map((toolCall) => {
3353
+ orderedParts.length === 0 && toolCalls.length > 0 ? /* @__PURE__ */ jsx12("div", { className: "flex flex-col gap-0.5", children: toolCalls.map((toolCall) => {
3107
3354
  const custom = toolCallRenderer?.(toolCall);
3108
- return custom !== null && custom !== void 0 ? /* @__PURE__ */ jsx11("div", { children: custom }, toolCall.id) : formatToolName(toolCall.name) === "Agent" ? /* @__PURE__ */ jsx11(AgentLoopBlock, { toolCall }, toolCall.id) : /* @__PURE__ */ jsx11(ExecutionToolRow, { toolCall }, toolCall.id);
3355
+ return custom !== null && custom !== void 0 ? /* @__PURE__ */ jsx12("div", { children: custom }, toolCall.id) : formatToolName(toolCall.name) === "Agent" ? /* @__PURE__ */ jsx12(AgentLoopBlock, { toolCall }, toolCall.id) : /* @__PURE__ */ jsx12(ExecutionToolRow, { toolCall }, toolCall.id);
3109
3356
  }) }) : null
3110
3357
  ]
3111
3358
  },
3112
3359
  message.entry_id ?? `${message.timestamp ?? "assistant"}-${index}`
3113
3360
  );
3114
3361
  }) }) : null,
3115
- finalMessage && (effectiveMode === "compact" || finalOrderedParts.length === 0) ? /* @__PURE__ */ jsx11("div", { className: "ml-10", children: /* @__PURE__ */ jsx11(
3362
+ finalMessage && (effectiveMode === "compact" || finalOrderedParts.length === 0) ? /* @__PURE__ */ jsx12("div", { className: "ml-10", children: /* @__PURE__ */ jsx12(
3116
3363
  AssistantMessageContent,
3117
3364
  {
3118
3365
  message: finalMessage,
@@ -3120,7 +3367,7 @@ function AssistantTurnBlock({
3120
3367
  streaming: isStreaming && finalMessage === messages[messages.length - 1]
3121
3368
  }
3122
3369
  ) }) : null,
3123
- questionToolCalls.map((toolCall) => /* @__PURE__ */ jsx11(
3370
+ questionToolCalls.map((toolCall) => /* @__PURE__ */ jsx12(
3124
3371
  ToolCallBlock,
3125
3372
  {
3126
3373
  toolCall,
@@ -3145,22 +3392,22 @@ function collectMemoryRefs(messages) {
3145
3392
  return [...refs.values()];
3146
3393
  }
3147
3394
  function MemoryRefsHint({ refs }) {
3148
- const [expanded, setExpanded] = useState10(false);
3395
+ const [expanded, setExpanded] = useState11(false);
3149
3396
  const label = refs.some((ref) => ref.skill_name) ? "\u53C2\u8003\u4E86\u8BE5\u6280\u80FD\u7684\u5386\u53F2\u7ECF\u9A8C" : "\u53C2\u8003\u4E86\u5386\u53F2\u7ECF\u9A8C";
3150
- return /* @__PURE__ */ jsxs9("div", { className: "blade-chat-memory-refs ml-1 w-full max-w-[680px]", children: [
3151
- /* @__PURE__ */ jsxs9("button", { type: "button", onClick: () => setExpanded((value) => !value), className: "inline-flex h-8 items-center gap-1.5 rounded-lg border border-[hsl(var(--primary)/0.22)] bg-[hsl(var(--primary)/0.07)] px-3 text-xs font-medium text-[hsl(var(--primary))]", children: [
3152
- /* @__PURE__ */ jsx11(BookOpen, { size: 12 }),
3153
- /* @__PURE__ */ jsxs9("span", { children: [
3397
+ return /* @__PURE__ */ jsxs10("div", { className: "blade-chat-memory-refs ml-1 w-full max-w-[680px]", children: [
3398
+ /* @__PURE__ */ jsxs10("button", { type: "button", onClick: () => setExpanded((value) => !value), className: "inline-flex h-8 items-center gap-1.5 rounded-lg border border-[hsl(var(--primary)/0.22)] bg-[hsl(var(--primary)/0.07)] px-3 text-xs font-medium text-[hsl(var(--primary))]", children: [
3399
+ /* @__PURE__ */ jsx12(BookOpen, { size: 12 }),
3400
+ /* @__PURE__ */ jsxs10("span", { children: [
3154
3401
  label,
3155
3402
  "\uFF08",
3156
3403
  refs.length,
3157
3404
  "\uFF09"
3158
3405
  ] }),
3159
- /* @__PURE__ */ jsx11(ChevronRight, { size: 10, className: cn("transition-transform", expanded && "rotate-90") })
3406
+ /* @__PURE__ */ jsx12(ChevronRight, { size: 10, className: cn("transition-transform", expanded && "rotate-90") })
3160
3407
  ] }),
3161
- expanded ? /* @__PURE__ */ jsx11("div", { className: "mt-2 flex flex-col gap-2 rounded-xl border border-[hsl(var(--border)/0.8)] bg-[hsl(var(--muted)/0.28)] p-2.5", children: refs.map((ref) => /* @__PURE__ */ jsxs9("div", { className: "rounded-lg border border-[hsl(var(--border)/0.55)] bg-[hsl(var(--background)/0.72)] px-3 py-2.5 text-xs", children: [
3162
- /* @__PURE__ */ jsx11("p", { className: "line-clamp-2 break-words leading-5", children: ref.content_preview }),
3163
- ref.skill_name ? /* @__PURE__ */ jsx11("span", { className: "mt-1 inline-flex text-[10px] text-[hsl(var(--primary))]", children: ref.skill_name }) : null
3408
+ expanded ? /* @__PURE__ */ jsx12("div", { className: "mt-2 flex flex-col gap-2 rounded-xl border border-[hsl(var(--border)/0.8)] bg-[hsl(var(--muted)/0.28)] p-2.5", children: refs.map((ref) => /* @__PURE__ */ jsxs10("div", { className: "rounded-lg border border-[hsl(var(--border)/0.55)] bg-[hsl(var(--background)/0.72)] px-3 py-2.5 text-xs", children: [
3409
+ /* @__PURE__ */ jsx12("p", { className: "line-clamp-2 break-words leading-5", children: ref.content_preview }),
3410
+ ref.skill_name ? /* @__PURE__ */ jsx12("span", { className: "mt-1 inline-flex text-[10px] text-[hsl(var(--primary))]", children: ref.skill_name }) : null
3164
3411
  ] }, ref.id)) }) : null
3165
3412
  ] });
3166
3413
  }
@@ -3174,15 +3421,15 @@ function AssistantMessageContent({
3174
3421
  const imageParts = getImageParts(message.content);
3175
3422
  const fileParts = getFileParts(message.content);
3176
3423
  const failed = message.status === "failed";
3177
- const failedBadge = failed ? /* @__PURE__ */ jsx11("div", { className: "w-fit rounded-full border border-red-500/30 bg-red-500/10 px-2.5 py-1 text-[10px] font-medium text-red-400", children: "\u751F\u6210\u5931\u8D25" }) : null;
3178
- const textContent = text ? /* @__PURE__ */ jsx11(
3424
+ const failedBadge = failed ? /* @__PURE__ */ jsx12("div", { className: "w-fit rounded-full border border-red-500/30 bg-red-500/10 px-2.5 py-1 text-[10px] font-medium text-red-400", children: "\u751F\u6210\u5931\u8D25" }) : null;
3425
+ const textContent = text ? /* @__PURE__ */ jsx12(
3179
3426
  "div",
3180
3427
  {
3181
3428
  className: cn(
3182
3429
  "blade-chat-assistant-text",
3183
3430
  compact ? "text-xs leading-[22px] text-[hsl(var(--foreground))]" : "text-[15px] leading-8 text-[hsl(var(--foreground))]"
3184
3431
  ),
3185
- children: /* @__PURE__ */ jsx11(
3432
+ children: /* @__PURE__ */ jsx12(
3186
3433
  MarkdownContent,
3187
3434
  {
3188
3435
  mode: streaming ? "streaming" : "static",
@@ -3195,14 +3442,14 @@ function AssistantMessageContent({
3195
3442
  ) : null;
3196
3443
  if (imageParts.length === 0 && fileParts.length === 0) {
3197
3444
  if (!failed) return textContent;
3198
- return failedBadge || textContent ? /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-2", children: [
3445
+ return failedBadge || textContent ? /* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-2", children: [
3199
3446
  failedBadge,
3200
3447
  textContent
3201
3448
  ] }) : null;
3202
3449
  }
3203
- return /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-3", children: [
3450
+ return /* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-3", children: [
3204
3451
  failedBadge,
3205
- imageParts.length > 0 ? /* @__PURE__ */ jsx11("div", { className: "grid gap-2", children: imageParts.map((part) => /* @__PURE__ */ jsx11(
3452
+ imageParts.length > 0 ? /* @__PURE__ */ jsx12("div", { className: "grid gap-2", children: imageParts.map((part) => /* @__PURE__ */ jsx12(
3206
3453
  "img",
3207
3454
  {
3208
3455
  src: part.image_url.url,
@@ -3211,14 +3458,14 @@ function AssistantMessageContent({
3211
3458
  },
3212
3459
  part.image_url.url
3213
3460
  )) }) : null,
3214
- fileParts.length > 0 ? /* @__PURE__ */ jsx11("div", { className: "flex flex-wrap gap-1.5", children: fileParts.map((part) => /* @__PURE__ */ jsxs9(
3461
+ fileParts.length > 0 ? /* @__PURE__ */ jsx12("div", { className: "flex flex-wrap gap-1.5", children: fileParts.map((part) => /* @__PURE__ */ jsxs10(
3215
3462
  "div",
3216
3463
  {
3217
3464
  className: "flex min-w-0 items-center gap-1.5 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.3)] px-2.5 py-1.5 text-xs text-[hsl(var(--muted-foreground))]",
3218
3465
  title: part.name,
3219
3466
  children: [
3220
- /* @__PURE__ */ jsx11(FileText, { size: 12, className: "shrink-0" }),
3221
- /* @__PURE__ */ jsx11("span", { className: "max-w-56 truncate", children: part.name })
3467
+ /* @__PURE__ */ jsx12(FileText, { size: 12, className: "shrink-0" }),
3468
+ /* @__PURE__ */ jsx12("span", { className: "max-w-56 truncate", children: part.name })
3222
3469
  ]
3223
3470
  },
3224
3471
  `${part.name}-${part.data.slice(0, 32)}`
@@ -3229,7 +3476,7 @@ function AssistantMessageContent({
3229
3476
 
3230
3477
  // src/components/RenderErrorBoundary.tsx
3231
3478
  import { Component } from "react";
3232
- import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
3479
+ import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
3233
3480
  function getFirstComponentName(componentStack) {
3234
3481
  const match = componentStack.match(/\n\s+at\s+([^\s(]+)/);
3235
3482
  return match?.[1] ?? null;
@@ -3262,26 +3509,26 @@ var RenderErrorBoundary = class extends Component {
3262
3509
  return children;
3263
3510
  }
3264
3511
  const componentName = getFirstComponentName(componentStack);
3265
- return /* @__PURE__ */ jsx12("div", { className: "blade-chat-render-error rounded-xl border border-amber-500/30 bg-amber-500/8 px-4 py-3 text-sm text-amber-100", children: /* @__PURE__ */ jsxs10("div", { className: "flex items-start gap-2", children: [
3266
- /* @__PURE__ */ jsx12(TriangleAlert, { className: "mt-0.5 h-4 w-4 shrink-0 text-amber-300" }),
3267
- /* @__PURE__ */ jsxs10("div", { className: "min-w-0 flex-1", children: [
3268
- /* @__PURE__ */ jsxs10("div", { className: "font-medium", children: [
3512
+ 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: [
3513
+ /* @__PURE__ */ jsx13(TriangleAlert, { className: "mt-0.5 h-4 w-4 shrink-0 text-amber-300" }),
3514
+ /* @__PURE__ */ jsxs11("div", { className: "min-w-0 flex-1", children: [
3515
+ /* @__PURE__ */ jsxs11("div", { className: "font-medium", children: [
3269
3516
  label,
3270
3517
  "\u6E32\u67D3\u5931\u8D25"
3271
3518
  ] }),
3272
- /* @__PURE__ */ jsxs10("div", { className: "mt-1 break-words text-xs leading-5 text-amber-100/75", children: [
3519
+ /* @__PURE__ */ jsxs11("div", { className: "mt-1 break-words text-xs leading-5 text-amber-100/75", children: [
3273
3520
  componentName ? `\u7EC4\u4EF6\uFF1A${componentName}\u3002` : null,
3274
3521
  error.message || "\u53D1\u751F\u4E86\u672A\u9884\u671F\u7684\u6E32\u67D3\u9519\u8BEF\u3002"
3275
3522
  ] }),
3276
- details ? /* @__PURE__ */ jsx12("div", { className: "mt-1 truncate text-xs text-amber-100/55", children: details }) : null
3523
+ details ? /* @__PURE__ */ jsx13("div", { className: "mt-1 truncate text-xs text-amber-100/55", children: details }) : null
3277
3524
  ] })
3278
3525
  ] }) });
3279
3526
  }
3280
3527
  };
3281
3528
 
3282
3529
  // src/components/PostChatFollowupBlock.tsx
3283
- import { useCallback as useCallback6, useEffect as useEffect9, useRef as useRef10, useState as useState11 } from "react";
3284
- import { Fragment as Fragment2, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
3530
+ import { useCallback as useCallback6, useEffect as useEffect10, useRef as useRef11, useState as useState12 } from "react";
3531
+ import { Fragment as Fragment2, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
3285
3532
  function emitInteraction(callback, event) {
3286
3533
  try {
3287
3534
  callback?.(event);
@@ -3300,10 +3547,10 @@ function ArtifactCard({
3300
3547
  onArtifactOpened
3301
3548
  }) {
3302
3549
  const client = useBladeClient();
3303
- const [downloading, setDownloading] = useState11(false);
3550
+ const [downloading, setDownloading] = useState12(false);
3304
3551
  const name = artifact.label || basename(artifact.target);
3305
3552
  if (artifact.kind === "link") {
3306
- return /* @__PURE__ */ jsxs11(
3553
+ return /* @__PURE__ */ jsxs12(
3307
3554
  "a",
3308
3555
  {
3309
3556
  href: artifact.target,
@@ -3314,9 +3561,9 @@ function ArtifactCard({
3314
3561
  ${artifact.target}`,
3315
3562
  className: "group relative flex min-w-0 items-center gap-1.5 rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-2 py-1.5 text-xs text-[hsl(var(--card-foreground))] hover:bg-[hsl(var(--accent))]",
3316
3563
  children: [
3317
- /* @__PURE__ */ jsx13(Globe, { size: 15, className: "shrink-0 text-[hsl(var(--primary))]" }),
3318
- /* @__PURE__ */ jsx13("span", { className: "min-w-0 flex-1 truncate font-medium", children: name }),
3319
- /* @__PURE__ */ jsx13(ArrowUpRight, { size: 13, className: "absolute right-2 opacity-0 group-hover:opacity-100 group-focus-visible:opacity-100" })
3564
+ /* @__PURE__ */ jsx14(Globe, { size: 15, className: "shrink-0 text-[hsl(var(--primary))]" }),
3565
+ /* @__PURE__ */ jsx14("span", { className: "min-w-0 flex-1 truncate font-medium", children: name }),
3566
+ /* @__PURE__ */ jsx14(ArrowUpRight, { size: 13, className: "absolute right-2 opacity-0 group-hover:opacity-100 group-focus-visible:opacity-100" })
3320
3567
  ]
3321
3568
  }
3322
3569
  );
@@ -3351,7 +3598,7 @@ ${artifact.target}`,
3351
3598
  setDownloading(false);
3352
3599
  }
3353
3600
  };
3354
- return /* @__PURE__ */ jsx13(
3601
+ return /* @__PURE__ */ jsx14(
3355
3602
  "a",
3356
3603
  {
3357
3604
  href: downloadUrl,
@@ -3379,17 +3626,17 @@ function feedbackReasonLabel(reason) {
3379
3626
  }
3380
3627
  function HistoricalResultFeedback({ feedback }) {
3381
3628
  const label = feedbackReasonLabel(feedback.reason);
3382
- return /* @__PURE__ */ jsxs11(
3629
+ return /* @__PURE__ */ jsxs12(
3383
3630
  "section",
3384
3631
  {
3385
3632
  "aria-label": "\u5386\u53F2\u7ED3\u679C\u53CD\u9988",
3386
3633
  className: "mt-3 w-fit max-w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.2)] px-3 py-2 text-xs text-[hsl(var(--muted-foreground))]",
3387
3634
  children: [
3388
- /* @__PURE__ */ jsxs11("span", { children: [
3635
+ /* @__PURE__ */ jsxs12("span", { children: [
3389
3636
  "\u4F60\u5BF9\u6B64\u8F6E\u7ED3\u679C\u7684\u8BC4\u4EF7\uFF1A",
3390
3637
  feedback.helpful ? "\u6709\u5E2E\u52A9" : "\u6CA1\u5E2E\u52A9"
3391
3638
  ] }),
3392
- label ? /* @__PURE__ */ jsxs11("span", { children: [
3639
+ label ? /* @__PURE__ */ jsxs12("span", { children: [
3393
3640
  " \xB7 ",
3394
3641
  label
3395
3642
  ] }) : null
@@ -3406,15 +3653,15 @@ function ResultFeedback({
3406
3653
  onFeedbackSaved
3407
3654
  }) {
3408
3655
  const client = useBladeClient();
3409
- const [saved, setSaved] = useState11(savedFeedback ?? null);
3410
- const [helpful, setHelpful] = useState11(savedFeedback?.helpful ?? null);
3411
- const [reason, setReason] = useState11(savedFeedback?.reason ?? null);
3412
- const [saving, setSaving] = useState11(false);
3413
- const [saveError, setSaveError] = useState11(false);
3414
- const reportedShown = useRef10(false);
3415
- const latestChoice = useRef10(null);
3656
+ const [saved, setSaved] = useState12(savedFeedback ?? null);
3657
+ const [helpful, setHelpful] = useState12(savedFeedback?.helpful ?? null);
3658
+ const [reason, setReason] = useState12(savedFeedback?.reason ?? null);
3659
+ const [saving, setSaving] = useState12(false);
3660
+ const [saveError, setSaveError] = useState12(false);
3661
+ const reportedShown = useRef11(false);
3662
+ const latestChoice = useRef11(null);
3416
3663
  const eligible = followup.feedback_eligible === true && Boolean(sessionId) && !isViewer;
3417
- useEffect9(() => {
3664
+ useEffect10(() => {
3418
3665
  if (!eligible || reportedShown.current) return;
3419
3666
  reportedShown.current = true;
3420
3667
  emitInteraction(onInteraction, {
@@ -3423,7 +3670,7 @@ function ResultFeedback({
3423
3670
  assistantEntryId: followup.assistant_entry_id
3424
3671
  });
3425
3672
  }, [eligible, followup.assistant_entry_id, onInteraction, sessionId]);
3426
- useEffect9(() => {
3673
+ useEffect10(() => {
3427
3674
  if (!savedFeedback || latestChoice.current) return;
3428
3675
  setSaved(savedFeedback);
3429
3676
  setHelpful(savedFeedback.helpful);
@@ -3464,15 +3711,15 @@ function ResultFeedback({
3464
3711
  [client, followup.assistant_entry_id, onFeedbackSaved, onInteraction, sessionId]
3465
3712
  );
3466
3713
  if (!eligible) return null;
3467
- return /* @__PURE__ */ jsxs11(
3714
+ return /* @__PURE__ */ jsxs12(
3468
3715
  "section",
3469
3716
  {
3470
3717
  "aria-label": "\u7ED3\u679C\u53CD\u9988",
3471
3718
  className: "flex flex-col gap-2 border-t border-[hsl(var(--border))] pt-3",
3472
3719
  children: [
3473
- /* @__PURE__ */ jsx13("div", { className: "text-xs font-medium text-[hsl(var(--muted-foreground))]", children: "\u76EE\u524D\u7684\u6574\u4F53\u7ED3\u679C\u6709\u5E2E\u52A9\u5417\uFF1F" }),
3474
- /* @__PURE__ */ jsxs11("div", { className: "flex flex-wrap gap-1.5", children: [
3475
- /* @__PURE__ */ jsx13(
3720
+ /* @__PURE__ */ jsx14("div", { className: "text-xs font-medium text-[hsl(var(--muted-foreground))]", children: "\u76EE\u524D\u7684\u6574\u4F53\u7ED3\u679C\u6709\u5E2E\u52A9\u5417\uFF1F" }),
3721
+ /* @__PURE__ */ jsxs12("div", { className: "flex flex-wrap gap-1.5", children: [
3722
+ /* @__PURE__ */ jsx14(
3476
3723
  "button",
3477
3724
  {
3478
3725
  type: "button",
@@ -3483,7 +3730,7 @@ function ResultFeedback({
3483
3730
  children: "\u6709\u5E2E\u52A9"
3484
3731
  }
3485
3732
  ),
3486
- /* @__PURE__ */ jsx13(
3733
+ /* @__PURE__ */ jsx14(
3487
3734
  "button",
3488
3735
  {
3489
3736
  type: "button",
@@ -3495,7 +3742,7 @@ function ResultFeedback({
3495
3742
  }
3496
3743
  )
3497
3744
  ] }),
3498
- helpful === false ? /* @__PURE__ */ jsx13("div", { className: "flex flex-wrap gap-1.5", "aria-label": "\u6CA1\u5E2E\u52A9\u7684\u4E3B\u8981\u539F\u56E0", children: FEEDBACK_REASONS.map((item) => /* @__PURE__ */ jsx13(
3745
+ helpful === false ? /* @__PURE__ */ jsx14("div", { className: "flex flex-wrap gap-1.5", "aria-label": "\u6CA1\u5E2E\u52A9\u7684\u4E3B\u8981\u539F\u56E0", children: FEEDBACK_REASONS.map((item) => /* @__PURE__ */ jsx14(
3499
3746
  "button",
3500
3747
  {
3501
3748
  type: "button",
@@ -3507,9 +3754,9 @@ function ResultFeedback({
3507
3754
  },
3508
3755
  item.value
3509
3756
  )) }) : null,
3510
- saveError ? /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2 text-xs text-[hsl(var(--destructive))]", children: [
3511
- /* @__PURE__ */ jsx13("span", { children: "\u53CD\u9988\u6682\u672A\u4FDD\u5B58\uFF0C\u53EF\u91CD\u8BD5" }),
3512
- /* @__PURE__ */ jsx13(
3757
+ saveError ? /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2 text-xs text-[hsl(var(--destructive))]", children: [
3758
+ /* @__PURE__ */ jsx14("span", { children: "\u53CD\u9988\u6682\u672A\u4FDD\u5B58\uFF0C\u53EF\u91CD\u8BD5" }),
3759
+ /* @__PURE__ */ jsx14(
3513
3760
  "button",
3514
3761
  {
3515
3762
  type: "button",
@@ -3521,7 +3768,7 @@ function ResultFeedback({
3521
3768
  children: "\u91CD\u8BD5"
3522
3769
  }
3523
3770
  )
3524
- ] }) : saved ? /* @__PURE__ */ jsx13("div", { className: "text-[11px] text-[hsl(var(--muted-foreground))]", children: "\u5DF2\u4FDD\u5B58\uFF0C\u53EF\u968F\u65F6\u4FEE\u6539" }) : null
3771
+ ] }) : saved ? /* @__PURE__ */ jsx14("div", { className: "text-[11px] text-[hsl(var(--muted-foreground))]", children: "\u5DF2\u4FDD\u5B58\uFF0C\u53EF\u968F\u65F6\u4FEE\u6539" }) : null
3525
3772
  ]
3526
3773
  }
3527
3774
  );
@@ -3535,14 +3782,14 @@ function PostChatFollowupBlock({
3535
3782
  savedFeedback,
3536
3783
  onFeedbackSaved
3537
3784
  }) {
3538
- const [expanded, setExpanded] = useState11(false);
3539
- const adopted = useRef10(/* @__PURE__ */ new Set());
3540
- const reportedSuggestions = useRef10(false);
3541
- const reportedArtifacts = useRef10(/* @__PURE__ */ new Set());
3542
- const openedArtifacts = useRef10(/* @__PURE__ */ new Set());
3785
+ const [expanded, setExpanded] = useState12(false);
3786
+ const adopted = useRef11(/* @__PURE__ */ new Set());
3787
+ const reportedSuggestions = useRef11(false);
3788
+ const reportedArtifacts = useRef11(/* @__PURE__ */ new Set());
3789
+ const openedArtifacts = useRef11(/* @__PURE__ */ new Set());
3543
3790
  const artifacts = followup.final_artifacts ?? [];
3544
3791
  const visibleArtifacts = expanded ? artifacts : artifacts.slice(0, 3);
3545
- useEffect9(() => {
3792
+ useEffect10(() => {
3546
3793
  if (!reportedSuggestions.current && followup.suggestions.length > 0) {
3547
3794
  reportedSuggestions.current = true;
3548
3795
  emitInteraction(onInteraction, {
@@ -3588,15 +3835,15 @@ function PostChatFollowupBlock({
3588
3835
  );
3589
3836
  if (!followup.recaption && artifacts.length === 0 && followup.suggestions.length === 0 && !followup.feedback_eligible)
3590
3837
  return null;
3591
- return /* @__PURE__ */ jsxs11("div", { className: "mt-3 flex w-fit max-w-full flex-col gap-3 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.28)] p-3 sm:max-w-[680px]", children: [
3592
- followup.recaption || artifacts.length > 0 ? /* @__PURE__ */ jsxs11("section", { "aria-label": "\u672C\u8F6E\u5C0F\u7ED3", className: "flex flex-col gap-2", children: [
3593
- /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1.5 text-xs font-medium text-[hsl(var(--muted-foreground))]", children: [
3594
- /* @__PURE__ */ jsx13(Sparkles, { size: 14 }),
3838
+ return /* @__PURE__ */ jsxs12("div", { className: "mt-3 flex w-fit max-w-full flex-col gap-3 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.28)] p-3 sm:max-w-[680px]", children: [
3839
+ followup.recaption || artifacts.length > 0 ? /* @__PURE__ */ jsxs12("section", { "aria-label": "\u672C\u8F6E\u5C0F\u7ED3", className: "flex flex-col gap-2", children: [
3840
+ /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1.5 text-xs font-medium text-[hsl(var(--muted-foreground))]", children: [
3841
+ /* @__PURE__ */ jsx14(Sparkles, { size: 14 }),
3595
3842
  "\u672C\u8F6E\u5C0F\u7ED3"
3596
3843
  ] }),
3597
- followup.recaption ? /* @__PURE__ */ jsx13("p", { className: "text-[13px] leading-5", children: followup.recaption }) : null,
3598
- artifacts.length > 0 ? /* @__PURE__ */ jsxs11(Fragment2, { children: [
3599
- /* @__PURE__ */ jsx13("div", { className: "grid max-w-full grid-cols-3 gap-1.5", children: visibleArtifacts.map((artifact, artifactIndex) => /* @__PURE__ */ jsx13(
3844
+ followup.recaption ? /* @__PURE__ */ jsx14("p", { className: "text-[13px] leading-5", children: followup.recaption }) : null,
3845
+ artifacts.length > 0 ? /* @__PURE__ */ jsxs12(Fragment2, { children: [
3846
+ /* @__PURE__ */ jsx14("div", { className: "grid max-w-full grid-cols-3 gap-1.5", children: visibleArtifacts.map((artifact, artifactIndex) => /* @__PURE__ */ jsx14(
3600
3847
  ArtifactCard,
3601
3848
  {
3602
3849
  artifact,
@@ -3608,7 +3855,7 @@ function PostChatFollowupBlock({
3608
3855
  },
3609
3856
  `${artifact.kind}:${artifactIndex}`
3610
3857
  )) }),
3611
- artifacts.length > 3 ? /* @__PURE__ */ jsxs11(
3858
+ artifacts.length > 3 ? /* @__PURE__ */ jsxs12(
3612
3859
  "button",
3613
3860
  {
3614
3861
  type: "button",
@@ -3617,15 +3864,15 @@ function PostChatFollowupBlock({
3617
3864
  className: "flex w-fit items-center gap-0.5 text-[11px] text-[hsl(var(--muted-foreground))]",
3618
3865
  children: [
3619
3866
  expanded ? "\u6536\u8D77" : `\u5C55\u5F00 ${artifacts.length - 3} \u4E2A`,
3620
- /* @__PURE__ */ jsx13(ChevronDown, { size: 13, className: expanded ? "rotate-180" : void 0 })
3867
+ /* @__PURE__ */ jsx14(ChevronDown, { size: 13, className: expanded ? "rotate-180" : void 0 })
3621
3868
  ]
3622
3869
  }
3623
3870
  ) : null
3624
3871
  ] }) : null
3625
3872
  ] }) : null,
3626
- followup.suggestions.length > 0 ? /* @__PURE__ */ jsxs11("section", { "aria-label": "\u4E0B\u4E00\u6B65\u5EFA\u8BAE", className: "flex flex-col gap-1.5", children: [
3627
- /* @__PURE__ */ jsx13("div", { className: "text-xs font-medium text-[hsl(var(--muted-foreground))]", children: "\u4E0B\u4E00\u6B65\u53EF\u4EE5" }),
3628
- followup.suggestions.map((suggestion, suggestionIndex) => /* @__PURE__ */ jsxs11(
3873
+ followup.suggestions.length > 0 ? /* @__PURE__ */ jsxs12("section", { "aria-label": "\u4E0B\u4E00\u6B65\u5EFA\u8BAE", className: "flex flex-col gap-1.5", children: [
3874
+ /* @__PURE__ */ jsx14("div", { className: "text-xs font-medium text-[hsl(var(--muted-foreground))]", children: "\u4E0B\u4E00\u6B65\u53EF\u4EE5" }),
3875
+ followup.suggestions.map((suggestion, suggestionIndex) => /* @__PURE__ */ jsxs12(
3629
3876
  "button",
3630
3877
  {
3631
3878
  type: "button",
@@ -3644,14 +3891,14 @@ function PostChatFollowupBlock({
3644
3891
  },
3645
3892
  className: "group flex items-center gap-2 rounded-xl bg-[hsl(var(--muted)/0.62)] px-3 py-2 text-left text-[13px] disabled:cursor-default disabled:opacity-60",
3646
3893
  children: [
3647
- /* @__PURE__ */ jsx13("span", { children: suggestion }),
3648
- /* @__PURE__ */ jsx13(ArrowRight, { size: 14, className: "ml-auto shrink-0" })
3894
+ /* @__PURE__ */ jsx14("span", { children: suggestion }),
3895
+ /* @__PURE__ */ jsx14(ArrowRight, { size: 14, className: "ml-auto shrink-0" })
3649
3896
  ]
3650
3897
  },
3651
3898
  suggestion
3652
3899
  ))
3653
3900
  ] }) : null,
3654
- /* @__PURE__ */ jsx13(
3901
+ /* @__PURE__ */ jsx14(
3655
3902
  ResultFeedback,
3656
3903
  {
3657
3904
  followup,
@@ -3726,31 +3973,31 @@ function parseWhatIfPrompt(text) {
3726
3973
  }
3727
3974
 
3728
3975
  // src/components/WhatIfUserBubble.tsx
3729
- import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
3976
+ import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
3730
3977
  function WhatIfUserBubble({ parsed, onQuoteClick }) {
3731
3978
  const { fromStep, quotes, userText } = parsed;
3732
- return /* @__PURE__ */ jsxs12("div", { className: "flex flex-col items-end gap-2", children: [
3733
- /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1.5 rounded-full border border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.5)] px-2.5 py-0.5 text-[10px] text-[hsl(var(--muted-foreground))]", children: [
3734
- /* @__PURE__ */ jsx14(RefreshCcw, { size: 10 }),
3735
- /* @__PURE__ */ jsx14("span", { children: fromStep != null ? `\u91CD\u8DD1\u81EA step ${fromStep}` : "\u91CD\u8DD1" })
3979
+ return /* @__PURE__ */ jsxs13("div", { className: "flex flex-col items-end gap-2", children: [
3980
+ /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-1.5 rounded-full border border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.5)] px-2.5 py-0.5 text-[10px] text-[hsl(var(--muted-foreground))]", children: [
3981
+ /* @__PURE__ */ jsx15(RefreshCcw, { size: 10 }),
3982
+ /* @__PURE__ */ jsx15("span", { children: fromStep != null ? `\u91CD\u8DD1\u81EA step ${fromStep}` : "\u91CD\u8DD1" })
3736
3983
  ] }),
3737
- quotes.length > 0 && /* @__PURE__ */ jsx14("div", { className: "flex max-w-[min(72vw,42rem)] flex-col items-stretch gap-2", children: quotes.map((quote, index) => {
3984
+ quotes.length > 0 && /* @__PURE__ */ jsx15("div", { className: "flex max-w-[min(72vw,42rem)] flex-col items-stretch gap-2", children: quotes.map((quote, index) => {
3738
3985
  const clickable = quote.stepNumber != null && !!onQuoteClick;
3739
3986
  const label = quote.stepNumber != null ? `\u6B65\u9AA4${quote.stepNumber} \xB7 ${quote.label}` : quote.label;
3740
- return /* @__PURE__ */ jsxs12("div", { className: "rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card)/0.8)] px-3 py-2 text-left", children: [
3741
- /* @__PURE__ */ jsxs12("button", { type: "button", disabled: !clickable, onClick: () => clickable && onQuoteClick(quote.stepNumber), className: "inline-flex max-w-full items-center gap-1 text-[11px] font-medium text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))] disabled:cursor-default", title: clickable ? "\u8DF3\u8F6C\u5230\u5BF9\u5E94\u6B65\u9AA4\u5361\u7247" : void 0, children: [
3742
- /* @__PURE__ */ jsx14("span", { children: "\u21B3" }),
3743
- /* @__PURE__ */ jsx14("span", { className: "truncate", children: label })
3987
+ return /* @__PURE__ */ jsxs13("div", { className: "rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card)/0.8)] px-3 py-2 text-left", children: [
3988
+ /* @__PURE__ */ jsxs13("button", { type: "button", disabled: !clickable, onClick: () => clickable && onQuoteClick(quote.stepNumber), className: "inline-flex max-w-full items-center gap-1 text-[11px] font-medium text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))] disabled:cursor-default", title: clickable ? "\u8DF3\u8F6C\u5230\u5BF9\u5E94\u6B65\u9AA4\u5361\u7247" : void 0, children: [
3989
+ /* @__PURE__ */ jsx15("span", { children: "\u21B3" }),
3990
+ /* @__PURE__ */ jsx15("span", { className: "truncate", children: label })
3744
3991
  ] }),
3745
- quote.snapshot ? /* @__PURE__ */ jsx14("div", { className: "mt-1 border-l-2 border-[hsl(var(--accent-foreground)/0.35)] pl-2 text-xs leading-relaxed text-[hsl(var(--foreground)/0.8)]", children: /* @__PURE__ */ jsx14(MarkdownContent, { className: "blade-chat-prose", children: quote.snapshot }) }) : null
3992
+ quote.snapshot ? /* @__PURE__ */ jsx15("div", { className: "mt-1 border-l-2 border-[hsl(var(--accent-foreground)/0.35)] pl-2 text-xs leading-relaxed text-[hsl(var(--foreground)/0.8)]", children: /* @__PURE__ */ jsx15(MarkdownContent, { className: "blade-chat-prose", children: quote.snapshot }) }) : null
3746
3993
  ] }, `${quote.stepNumber ?? "x"}-${index}`);
3747
3994
  }) }),
3748
- userText && /* @__PURE__ */ jsx14("div", { className: "rounded-2xl border border-[hsl(var(--user-msg-border))] bg-[hsl(var(--user-msg-bg))] px-4 py-2.5 text-sm leading-relaxed text-[hsl(var(--user-msg-fg))]", children: /* @__PURE__ */ jsx14(MarkdownContent, { className: "blade-chat-prose", children: userText }) })
3995
+ userText && /* @__PURE__ */ jsx15("div", { className: "rounded-2xl border border-[hsl(var(--user-msg-border))] bg-[hsl(var(--user-msg-bg))] px-4 py-2.5 text-sm leading-relaxed text-[hsl(var(--user-msg-fg))]", children: /* @__PURE__ */ jsx15(MarkdownContent, { className: "blade-chat-prose", children: userText }) })
3749
3996
  ] });
3750
3997
  }
3751
3998
 
3752
3999
  // src/components/UserMessageBubble.tsx
3753
- import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
4000
+ import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
3754
4001
  function isUserMessage(message) {
3755
4002
  return message.role === "user";
3756
4003
  }
@@ -3764,10 +4011,10 @@ function UserMessageBubble({ message, className }) {
3764
4011
  const imageParts = getImageParts2(message.content);
3765
4012
  const whatifParsed = text && imageParts.length === 0 && fileParts.length === 0 ? parseWhatIfPrompt(text) : null;
3766
4013
  if (whatifParsed) {
3767
- return /* @__PURE__ */ jsx15("div", { className: cn("blade-chat-user-row flex justify-end", className), children: /* @__PURE__ */ jsx15("div", { className: "blade-chat-user-col flex max-w-[72%] flex-col items-end gap-3", children: /* @__PURE__ */ jsx15(WhatIfUserBubble, { parsed: whatifParsed }) }) });
4014
+ return /* @__PURE__ */ jsx16("div", { className: cn("blade-chat-user-row flex justify-end", className), children: /* @__PURE__ */ jsx16("div", { className: "blade-chat-user-col flex max-w-[72%] flex-col items-end gap-3", children: /* @__PURE__ */ jsx16(WhatIfUserBubble, { parsed: whatifParsed }) }) });
3768
4015
  }
3769
- return /* @__PURE__ */ jsx15("div", { className: cn("blade-chat-user-row flex justify-end", className), children: /* @__PURE__ */ jsxs13("div", { className: "blade-chat-user-col flex max-w-[72%] flex-col items-end gap-3", children: [
3770
- imageParts.length > 0 && /* @__PURE__ */ jsx15("div", { className: "grid gap-2", children: imageParts.map((part) => /* @__PURE__ */ jsx15(
4016
+ return /* @__PURE__ */ jsx16("div", { className: cn("blade-chat-user-row flex justify-end", className), children: /* @__PURE__ */ jsxs14("div", { className: "blade-chat-user-col flex max-w-[72%] flex-col items-end gap-3", children: [
4017
+ imageParts.length > 0 && /* @__PURE__ */ jsx16("div", { className: "grid gap-2", children: imageParts.map((part) => /* @__PURE__ */ jsx16(
3771
4018
  "img",
3772
4019
  {
3773
4020
  src: part.image_url.url,
@@ -3776,21 +4023,21 @@ function UserMessageBubble({ message, className }) {
3776
4023
  },
3777
4024
  part.image_url.url
3778
4025
  )) }),
3779
- fileParts.length > 0 && /* @__PURE__ */ jsx15("div", { className: "flex flex-col items-end gap-1.5", children: fileParts.map((part) => /* @__PURE__ */ jsxs13(
4026
+ fileParts.length > 0 && /* @__PURE__ */ jsx16("div", { className: "flex flex-col items-end gap-1.5", children: fileParts.map((part) => /* @__PURE__ */ jsxs14(
3780
4027
  "div",
3781
4028
  {
3782
4029
  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))]",
3783
4030
  children: [
3784
- /* @__PURE__ */ jsx15(FileText, { size: 12, className: "shrink-0" }),
3785
- /* @__PURE__ */ jsx15("span", { className: "max-w-56 truncate", title: part.name, children: part.name })
4031
+ /* @__PURE__ */ jsx16(FileText, { size: 12, className: "shrink-0" }),
4032
+ /* @__PURE__ */ jsx16("span", { className: "max-w-56 truncate", title: part.name, children: part.name })
3786
4033
  ]
3787
4034
  },
3788
4035
  `${part.name}-${part.data.length}`
3789
4036
  )) }),
3790
- text && /* @__PURE__ */ jsx15("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__ */ jsx15(MarkdownContent, { className: "blade-chat-prose", children: text }) }),
3791
- text && isSending(message) && /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-1 pr-1 text-[11px] font-medium text-[hsl(var(--muted-foreground))/0.85]", children: [
3792
- /* @__PURE__ */ jsx15(LoaderCircle, { size: 11, className: "animate-spin", "aria-hidden": "true" }),
3793
- /* @__PURE__ */ jsx15("span", { children: "\u53D1\u9001\u4E2D" })
4037
+ text && /* @__PURE__ */ jsx16("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__ */ jsx16(MarkdownContent, { className: "blade-chat-prose", children: text }) }),
4038
+ text && isSending(message) && /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-1 pr-1 text-[11px] font-medium text-[hsl(var(--muted-foreground))/0.85]", children: [
4039
+ /* @__PURE__ */ jsx16(LoaderCircle, { size: 11, className: "animate-spin", "aria-hidden": "true" }),
4040
+ /* @__PURE__ */ jsx16("span", { children: "\u53D1\u9001\u4E2D" })
3794
4041
  ] })
3795
4042
  ] }) });
3796
4043
  }
@@ -3799,11 +4046,11 @@ function ErrorMessageBlock({
3799
4046
  className
3800
4047
  }) {
3801
4048
  const text = chatErrorForDisplay(getTextContent2(message.content));
3802
- return /* @__PURE__ */ jsx15("div", { className: cn("blade-chat-error-row flex min-w-0 justify-start", className), children: /* @__PURE__ */ jsx15("div", { className: "blade-chat-error-block min-w-0 max-w-full whitespace-pre-wrap break-words border-l-[3px] border-[hsl(var(--border))] px-3 py-1 text-left text-sm leading-7 text-[hsl(var(--muted-foreground))] [overflow-wrap:anywhere]", children: text }) });
4049
+ return /* @__PURE__ */ jsx16("div", { className: cn("blade-chat-error-row flex min-w-0 justify-start", className), children: /* @__PURE__ */ jsx16("div", { className: "blade-chat-error-block min-w-0 max-w-full whitespace-pre-wrap break-words border-l-[3px] border-[hsl(var(--border))] px-3 py-1 text-left text-sm leading-7 text-[hsl(var(--muted-foreground))] [overflow-wrap:anywhere]", children: text }) });
3803
4050
  }
3804
4051
 
3805
4052
  // src/components/MessageList.tsx
3806
- import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
4053
+ import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
3807
4054
  function parseModeChange(message) {
3808
4055
  if (message.kind !== "mode_change" || typeof message.content !== "string") {
3809
4056
  return null;
@@ -3844,6 +4091,7 @@ function MessageList({
3844
4091
  askAnswers,
3845
4092
  onAnswer,
3846
4093
  toolCallRenderer,
4094
+ hidePlanUpdateTools = false,
3847
4095
  emptyState,
3848
4096
  className,
3849
4097
  sessionId,
@@ -3929,23 +4177,23 @@ function MessageList({
3929
4177
  }
3930
4178
  return blocks;
3931
4179
  }, [messages, isStreaming]);
3932
- return /* @__PURE__ */ jsxs14("div", { className: cn("blade-chat-messages relative min-h-0 flex-1", className), children: [
3933
- isStreaming ? /* @__PURE__ */ jsx16("output", { className: "sr-only", children: "\u6B63\u5728\u751F\u6210\u56DE\u590D" }) : null,
3934
- /* @__PURE__ */ jsxs14(
4180
+ return /* @__PURE__ */ jsxs15("div", { className: cn("blade-chat-messages relative min-h-0 flex-1", className), children: [
4181
+ isStreaming ? /* @__PURE__ */ jsx17("output", { className: "sr-only", children: "\u6B63\u5728\u751F\u6210\u56DE\u590D" }) : null,
4182
+ /* @__PURE__ */ jsxs15(
3935
4183
  StickToBottom,
3936
4184
  {
3937
4185
  className: "h-full overflow-y-hidden",
3938
4186
  initial: "instant",
3939
4187
  resize: "instant",
3940
4188
  children: [
3941
- /* @__PURE__ */ jsx16(StickToBottom.Content, { className: "blade-chat-messages-scroll", children: /* @__PURE__ */ jsx16("div", { className: "blade-chat-messages-content mx-auto max-w-[748px]", children: /* @__PURE__ */ jsxs14("div", { className: "flex min-w-0 flex-col", children: [
3942
- renderBlocks.length === 0 ? emptyState ?? /* @__PURE__ */ jsxs14("div", { className: "blade-chat-empty", children: [
3943
- /* @__PURE__ */ jsx16(MessageSquare, { size: 40, strokeWidth: 1.5 }),
3944
- /* @__PURE__ */ jsx16("span", { className: "text-base font-medium", children: "\u5F00\u59CB\u5BF9\u8BDD" }),
3945
- /* @__PURE__ */ jsx16("span", { className: "text-sm opacity-60", children: "\u5728\u4E0B\u65B9\u8F93\u5165\u6D88\u606F\u5F00\u59CB\u804A\u5929" })
4189
+ /* @__PURE__ */ jsx17(StickToBottom.Content, { className: "blade-chat-messages-scroll", children: /* @__PURE__ */ jsx17("div", { className: "blade-chat-messages-content mx-auto max-w-[748px]", children: /* @__PURE__ */ jsxs15("div", { className: "flex min-w-0 flex-col", children: [
4190
+ renderBlocks.length === 0 ? emptyState ?? /* @__PURE__ */ jsxs15("div", { className: "blade-chat-empty", children: [
4191
+ /* @__PURE__ */ jsx17(MessageSquare, { size: 40, strokeWidth: 1.5 }),
4192
+ /* @__PURE__ */ jsx17("span", { className: "text-base font-medium", children: "\u5F00\u59CB\u5BF9\u8BDD" }),
4193
+ /* @__PURE__ */ jsx17("span", { className: "text-sm opacity-60", children: "\u5728\u4E0B\u65B9\u8F93\u5165\u6D88\u606F\u5F00\u59CB\u804A\u5929" })
3946
4194
  ] }) : renderBlocks.map((block) => {
3947
4195
  if (block.type === "message") {
3948
- return /* @__PURE__ */ jsx16("div", { "data-entry-id": block.message.entry_id, children: isUserMessage(block.message) ? /* @__PURE__ */ jsx16(UserMessageBubble, { message: block.message }) : isErrorMessage(block.message) ? /* @__PURE__ */ jsx16(ErrorMessageBlock, { message: block.message }) : null }, block.key);
4196
+ return /* @__PURE__ */ jsx17("div", { "data-entry-id": block.message.entry_id, children: isUserMessage(block.message) ? /* @__PURE__ */ jsx17(UserMessageBubble, { message: block.message }) : isErrorMessage(block.message) ? /* @__PURE__ */ jsx17(ErrorMessageBlock, { message: block.message }) : null }, block.key);
3949
4197
  }
3950
4198
  if (block.type === "assistant_turn") {
3951
4199
  const blockFeedback = block.messages.map(
@@ -3956,14 +4204,14 @@ function MessageList({
3956
4204
  (message) => message.entry_id === postChatFollowup.assistant_entry_id
3957
4205
  )
3958
4206
  );
3959
- return /* @__PURE__ */ jsx16("div", { "data-entry-id": block.messages[0]?.entry_id, children: /* @__PURE__ */ jsxs14(
4207
+ return /* @__PURE__ */ jsx17("div", { "data-entry-id": block.messages[0]?.entry_id, children: /* @__PURE__ */ jsxs15(
3960
4208
  RenderErrorBoundary,
3961
4209
  {
3962
4210
  label: "\u52A9\u624B\u6D88\u606F",
3963
4211
  details: block.key,
3964
4212
  resetKey: getMessageResetSignature(block.messages),
3965
4213
  children: [
3966
- /* @__PURE__ */ jsx16(
4214
+ /* @__PURE__ */ jsx17(
3967
4215
  AssistantTurnBlock,
3968
4216
  {
3969
4217
  messages: block.messages,
@@ -3972,11 +4220,12 @@ function MessageList({
3972
4220
  onAnswer,
3973
4221
  sessionStatus,
3974
4222
  toolCallRenderer,
4223
+ hidePlanUpdateTools,
3975
4224
  sessionId
3976
4225
  }
3977
4226
  ),
3978
- blockFeedback && !hasActiveFollowup ? /* @__PURE__ */ jsx16(HistoricalResultFeedback, { feedback: blockFeedback }) : null,
3979
- hasActiveFollowup && postChatFollowup ? /* @__PURE__ */ jsx16(
4227
+ blockFeedback && !hasActiveFollowup ? /* @__PURE__ */ jsx17(HistoricalResultFeedback, { feedback: blockFeedback }) : null,
4228
+ hasActiveFollowup && postChatFollowup ? /* @__PURE__ */ jsx17(
3980
4229
  PostChatFollowupBlock,
3981
4230
  {
3982
4231
  followup: postChatFollowup,
@@ -3993,23 +4242,23 @@ function MessageList({
3993
4242
  ) }, block.key);
3994
4243
  }
3995
4244
  if (block.type === "compaction") {
3996
- return /* @__PURE__ */ jsxs14(
4245
+ return /* @__PURE__ */ jsxs15(
3997
4246
  "div",
3998
4247
  {
3999
4248
  className: "flex items-center gap-2 text-xs text-[hsl(var(--muted-foreground))]",
4000
4249
  children: [
4001
- /* @__PURE__ */ jsx16(Layers, { size: 12 }),
4002
- /* @__PURE__ */ jsx16("span", { children: "\u4E0A\u4E0B\u6587\u5DF2\u538B\u7F29" })
4250
+ /* @__PURE__ */ jsx17(Layers, { size: 12 }),
4251
+ /* @__PURE__ */ jsx17("span", { children: "\u4E0A\u4E0B\u6587\u5DF2\u538B\u7F29" })
4003
4252
  ]
4004
4253
  },
4005
4254
  block.key
4006
4255
  );
4007
4256
  }
4008
- return /* @__PURE__ */ jsx16(PlanningDivider, { kind: block.kind }, block.key);
4257
+ return /* @__PURE__ */ jsx17(PlanningDivider, { kind: block.kind }, block.key);
4009
4258
  }),
4010
- sessionStatus === "interrupted" && !isStreaming ? /* @__PURE__ */ jsx16("div", { className: "flex", children: /* @__PURE__ */ jsx16("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
4259
+ sessionStatus === "interrupted" && !isStreaming ? /* @__PURE__ */ jsx17("div", { className: "flex", children: /* @__PURE__ */ jsx17("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
4011
4260
  ] }) }) }),
4012
- /* @__PURE__ */ jsx16(
4261
+ /* @__PURE__ */ jsx17(
4013
4262
  PinLatestUserMessage,
4014
4263
  {
4015
4264
  userMessageCount: userMessages.length,
@@ -4018,7 +4267,7 @@ function MessageList({
4018
4267
  },
4019
4268
  sessionId ?? "no-session"
4020
4269
  ),
4021
- /* @__PURE__ */ jsx16(ScrollToBottomButton, {})
4270
+ /* @__PURE__ */ jsx17(ScrollToBottomButton, {})
4022
4271
  ]
4023
4272
  },
4024
4273
  sessionId ?? "no-session"
@@ -4031,8 +4280,8 @@ function PinLatestUserMessage({
4031
4280
  targetKey
4032
4281
  }) {
4033
4282
  const { contentRef, scrollRef, scrollToBottom, stopScroll } = useStickToBottomContext();
4034
- const previousCountRef = useRef11(userMessageCount);
4035
- const spacerHeightRef = useRef11(0);
4283
+ const previousCountRef = useRef12(userMessageCount);
4284
+ const spacerHeightRef = useRef12(0);
4036
4285
  const getScrollElement = useCallback7(() => scrollRef.current, [scrollRef]);
4037
4286
  const getContentElement = useCallback7(() => contentRef.current, [contentRef]);
4038
4287
  const getTargetElement = useCallback7(() => {
@@ -4061,7 +4310,7 @@ function PinLatestUserMessage({
4061
4310
  stopAutoScroll: stopScroll,
4062
4311
  scrollToBottom
4063
4312
  });
4064
- useEffect10(() => {
4313
+ useEffect11(() => {
4065
4314
  if (userMessageCount > previousCountRef.current && !shouldPinLatestUser) {
4066
4315
  scrollToBottom("instant");
4067
4316
  }
@@ -4071,9 +4320,9 @@ function PinLatestUserMessage({
4071
4320
  }
4072
4321
  function ScrollToBottomButton() {
4073
4322
  const { isAtBottom, scrollToBottom } = useStickToBottomContext();
4074
- const [visible, setVisible] = useState12(false);
4075
- const hideTimerRef = useRef11(null);
4076
- useEffect10(() => {
4323
+ const [visible, setVisible] = useState13(false);
4324
+ const hideTimerRef = useRef12(null);
4325
+ useEffect11(() => {
4077
4326
  if (isAtBottom) {
4078
4327
  if (!hideTimerRef.current) {
4079
4328
  hideTimerRef.current = setTimeout(() => {
@@ -4104,7 +4353,7 @@ function ScrollToBottomButton() {
4104
4353
  scrollToBottom();
4105
4354
  }, [scrollToBottom]);
4106
4355
  if (!visible) return null;
4107
- return /* @__PURE__ */ jsxs14(
4356
+ return /* @__PURE__ */ jsxs15(
4108
4357
  "button",
4109
4358
  {
4110
4359
  type: "button",
@@ -4112,25 +4361,25 @@ function ScrollToBottomButton() {
4112
4361
  "aria-label": "\u6EDA\u52A8\u5230\u5E95\u90E8",
4113
4362
  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))]",
4114
4363
  children: [
4115
- /* @__PURE__ */ jsx16(ChevronDown, { size: 14 }),
4116
- /* @__PURE__ */ jsx16("span", { className: "blade-chat-scroll-bottom-label", children: "\u6EDA\u52A8\u5230\u5E95\u90E8" })
4364
+ /* @__PURE__ */ jsx17(ChevronDown, { size: 14 }),
4365
+ /* @__PURE__ */ jsx17("span", { className: "blade-chat-scroll-bottom-label", children: "\u6EDA\u52A8\u5230\u5E95\u90E8" })
4117
4366
  ]
4118
4367
  }
4119
4368
  );
4120
4369
  }
4121
4370
  function PlanningDivider({ kind }) {
4122
- return /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-3 py-1", children: [
4123
- /* @__PURE__ */ jsx16("div", { className: "h-px flex-1 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent" }),
4124
- /* @__PURE__ */ jsxs14("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: [
4125
- /* @__PURE__ */ jsx16(Lightbulb, { size: 12 }),
4126
- /* @__PURE__ */ jsx16("span", { children: kind === "enter" ? "\u8FDB\u5165\u89C4\u5212\u6A21\u5F0F" : "\u89C4\u5212\u5B8C\u6210" })
4371
+ return /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-3 py-1", children: [
4372
+ /* @__PURE__ */ jsx17("div", { className: "h-px flex-1 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent" }),
4373
+ /* @__PURE__ */ jsxs15("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: [
4374
+ /* @__PURE__ */ jsx17(Lightbulb, { size: 12 }),
4375
+ /* @__PURE__ */ jsx17("span", { children: kind === "enter" ? "\u8FDB\u5165\u89C4\u5212\u6A21\u5F0F" : "\u89C4\u5212\u5B8C\u6210" })
4127
4376
  ] }),
4128
- /* @__PURE__ */ jsx16("div", { className: "h-px flex-1 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent" })
4377
+ /* @__PURE__ */ jsx17("div", { className: "h-px flex-1 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent" })
4129
4378
  ] });
4130
4379
  }
4131
4380
 
4132
4381
  // src/components/ChatSurface.tsx
4133
- import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
4382
+ import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
4134
4383
  function themeAttr(theme) {
4135
4384
  return theme === "dark" ? "dark" : void 0;
4136
4385
  }
@@ -4160,9 +4409,11 @@ function ChatSurface({
4160
4409
  onResultFeedbackSaved,
4161
4410
  onFollowupInteraction,
4162
4411
  beforeInput,
4412
+ showPlanUpdates = false,
4413
+ planRevealRevision = 0,
4163
4414
  banner
4164
4415
  }) {
4165
- return /* @__PURE__ */ jsxs15(
4416
+ return /* @__PURE__ */ jsxs16(
4166
4417
  "div",
4167
4418
  {
4168
4419
  "data-theme": themeAttr(theme),
@@ -4171,14 +4422,14 @@ function ChatSurface({
4171
4422
  classNames?.root
4172
4423
  ),
4173
4424
  children: [
4174
- /* @__PURE__ */ jsx17(ConnectionBanner, { connection, className: classNames?.banner }),
4425
+ /* @__PURE__ */ jsx18(ConnectionBanner, { connection, className: classNames?.banner }),
4175
4426
  banner,
4176
- errorMessage && /* @__PURE__ */ jsxs15("div", { className: "blade-chat-error-bar flex items-start gap-2 border-b px-4 py-3 text-sm", children: [
4177
- /* @__PURE__ */ jsx17(CircleAlert, { size: 16, className: "mt-0.5 shrink-0" }),
4178
- /* @__PURE__ */ jsx17("span", { className: "min-w-0 whitespace-pre-wrap break-words [overflow-wrap:anywhere]", children: chatErrorForDisplay2(errorMessage) })
4427
+ errorMessage && /* @__PURE__ */ jsxs16("div", { className: "blade-chat-error-bar flex items-start gap-2 border-b px-4 py-3 text-sm", children: [
4428
+ /* @__PURE__ */ jsx18(CircleAlert, { size: 16, className: "mt-0.5 shrink-0" }),
4429
+ /* @__PURE__ */ jsx18("span", { className: "min-w-0 whitespace-pre-wrap break-words [overflow-wrap:anywhere]", children: chatErrorForDisplay2(errorMessage) })
4179
4430
  ] }),
4180
4431
  slots?.header,
4181
- /* @__PURE__ */ jsx17(
4432
+ /* @__PURE__ */ jsx18(
4182
4433
  MessageList,
4183
4434
  {
4184
4435
  messages,
@@ -4189,6 +4440,7 @@ function ChatSurface({
4189
4440
  askAnswers,
4190
4441
  onAnswer,
4191
4442
  toolCallRenderer: renderers?.toolCall,
4443
+ hidePlanUpdateTools: showPlanUpdates,
4192
4444
  emptyState: slots?.emptyState,
4193
4445
  className: classNames?.messageList,
4194
4446
  sessionId,
@@ -4198,8 +4450,18 @@ function ChatSurface({
4198
4450
  onFollowupInteraction
4199
4451
  }
4200
4452
  ),
4453
+ showPlanUpdates ? /* @__PURE__ */ jsx18(
4454
+ CurrentPlanPanel,
4455
+ {
4456
+ messages,
4457
+ running: isStreaming,
4458
+ revealRevision: planRevealRevision,
4459
+ sessionId,
4460
+ className: "border-t border-[hsl(var(--border))]"
4461
+ }
4462
+ ) : null,
4201
4463
  beforeInput,
4202
- /* @__PURE__ */ jsx17(
4464
+ /* @__PURE__ */ jsx18(
4203
4465
  ChatInput,
4204
4466
  {
4205
4467
  value: inputText,
@@ -4219,13 +4481,13 @@ function ChatSurface({
4219
4481
  }
4220
4482
 
4221
4483
  // src/components/AgentChat.tsx
4222
- import { Fragment as Fragment3, jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
4484
+ import { Fragment as Fragment3, jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
4223
4485
  function isUnauthorizedError(error) {
4224
4486
  return error instanceof BladeApiError && error.status === 401;
4225
4487
  }
4226
4488
  function LoginCard({ client, onLoggedIn }) {
4227
- const [loggingIn, setLoggingIn] = useState13(false);
4228
- const [loginError, setLoginError] = useState13(null);
4489
+ const [loggingIn, setLoggingIn] = useState14(false);
4490
+ const [loginError, setLoginError] = useState14(null);
4229
4491
  const handleLogin = async () => {
4230
4492
  setLoggingIn(true);
4231
4493
  setLoginError(null);
@@ -4238,11 +4500,11 @@ function LoginCard({ client, onLoggedIn }) {
4238
4500
  setLoggingIn(false);
4239
4501
  }
4240
4502
  };
4241
- return /* @__PURE__ */ jsx18("div", { className: "blade-chat-login flex flex-1 items-center justify-center p-6", children: /* @__PURE__ */ jsxs16("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: [
4242
- /* @__PURE__ */ jsx18(LockKeyhole, { size: 28, className: "text-[hsl(var(--muted-foreground))]" }),
4243
- /* @__PURE__ */ jsx18("div", { className: "text-base font-medium text-[hsl(var(--foreground))]", children: "\u9700\u8981\u767B\u5F55\u540E\u4F7F\u7528" }),
4244
- /* @__PURE__ */ jsx18("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" }),
4245
- /* @__PURE__ */ jsx18(
4503
+ return /* @__PURE__ */ jsx19("div", { className: "blade-chat-login flex flex-1 items-center justify-center p-6", children: /* @__PURE__ */ jsxs17("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: [
4504
+ /* @__PURE__ */ jsx19(LockKeyhole, { size: 28, className: "text-[hsl(var(--muted-foreground))]" }),
4505
+ /* @__PURE__ */ jsx19("div", { className: "text-base font-medium text-[hsl(var(--foreground))]", children: "\u9700\u8981\u767B\u5F55\u540E\u4F7F\u7528" }),
4506
+ /* @__PURE__ */ jsx19("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" }),
4507
+ /* @__PURE__ */ jsx19(
4246
4508
  "button",
4247
4509
  {
4248
4510
  type: "button",
@@ -4252,20 +4514,20 @@ function LoginCard({ client, onLoggedIn }) {
4252
4514
  children: loggingIn ? "\u767B\u5F55\u4E2D\u2026" : "\u767B\u5F55"
4253
4515
  }
4254
4516
  ),
4255
- loginError && /* @__PURE__ */ jsx18("div", { className: "text-xs text-[hsl(var(--destructive))]", children: loginError })
4517
+ loginError && /* @__PURE__ */ jsx19("div", { className: "text-xs text-[hsl(var(--destructive))]", children: loginError })
4256
4518
  ] }) });
4257
4519
  }
4258
4520
  function AgentChat(props) {
4259
4521
  const client = useBladeClient();
4260
- const [attempt, setAttempt] = useState13(0);
4261
- const [needLogin, setNeedLogin] = useState13(() => !client.hasToken());
4522
+ const [attempt, setAttempt] = useState14(0);
4523
+ const [needLogin, setNeedLogin] = useState14(() => !client.hasToken());
4262
4524
  if (needLogin) {
4263
- return /* @__PURE__ */ jsx18(
4525
+ return /* @__PURE__ */ jsx19(
4264
4526
  "div",
4265
4527
  {
4266
4528
  "data-theme": themeAttr(props.theme),
4267
4529
  className: cn("blade-chat flex min-h-0 flex-1 flex-col", props.classNames?.root),
4268
- children: /* @__PURE__ */ jsx18(
4530
+ children: /* @__PURE__ */ jsx19(
4269
4531
  LoginCard,
4270
4532
  {
4271
4533
  client,
@@ -4278,7 +4540,7 @@ function AgentChat(props) {
4278
4540
  }
4279
4541
  );
4280
4542
  }
4281
- return /* @__PURE__ */ jsx18(ChatSessionView, { ...props, onUnauthorized: () => setNeedLogin(true) }, attempt);
4543
+ return /* @__PURE__ */ jsx19(ChatSessionView, { ...props, onUnauthorized: () => setNeedLogin(true) }, attempt);
4282
4544
  }
4283
4545
  function ChatSessionView({
4284
4546
  sessionId,
@@ -4295,17 +4557,33 @@ function ChatSessionView({
4295
4557
  onUnauthorized
4296
4558
  }) {
4297
4559
  const client = useBladeClient();
4560
+ const [planRevealRevisions, setPlanRevealRevisions] = useState14(
4561
+ () => /* @__PURE__ */ new Map()
4562
+ );
4563
+ const handleSessionConnected = useCallback8((connectedSession) => {
4564
+ return connectedSession.on("toolResult", ({ toolCall, turn, source }) => {
4565
+ if (source === "reconnect_replay" || (turn.loop_id || "root") !== "root" || toolCall.status !== "done" || !isPlanUpdateTool(toolCall) || !parsePlanUpdate(toolCall.arguments)) {
4566
+ return;
4567
+ }
4568
+ setPlanRevealRevisions((current) => {
4569
+ const next = new Map(current);
4570
+ next.set(connectedSession.sessionId, (current.get(connectedSession.sessionId) ?? 0) + 1);
4571
+ return next;
4572
+ });
4573
+ });
4574
+ }, []);
4298
4575
  const { session, state, error } = useAgentSession(sessionId, {
4299
4576
  createOptions,
4300
- onSessionCreated
4577
+ onSessionCreated,
4578
+ onSessionConnected: handleSessionConnected
4301
4579
  });
4302
4580
  const replay = useReplay(session);
4303
- const [stopRequested, setStopRequested] = useState13(false);
4304
- const [inputText, setInputText] = useState13("");
4305
- const [resultFeedback, setResultFeedback] = useState13([]);
4581
+ const [stopRequested, setStopRequested] = useState14(false);
4582
+ const [inputText, setInputText] = useState14("");
4583
+ const [resultFeedback, setResultFeedback] = useState14([]);
4306
4584
  const resolvedSessionId = session?.sessionId;
4307
4585
  const isViewer = state?.viewerRole === "viewer";
4308
- useEffect11(() => {
4586
+ useEffect12(() => {
4309
4587
  setResultFeedback([]);
4310
4588
  if (!resolvedSessionId || isViewer) return;
4311
4589
  let cancelled = false;
@@ -4340,12 +4618,12 @@ function ChatSessionView({
4340
4618
  saved
4341
4619
  ]);
4342
4620
  }, []);
4343
- useEffect11(() => {
4621
+ useEffect12(() => {
4344
4622
  if (session) {
4345
4623
  onSessionReady?.(session);
4346
4624
  }
4347
4625
  }, [session, onSessionReady]);
4348
- useEffect11(() => {
4626
+ useEffect12(() => {
4349
4627
  if (!session) return;
4350
4628
  const offAttach = session.on("attachRequested", ({ label, content }) => {
4351
4629
  setInputText((prev) => `${prev ? `${prev}
@@ -4361,12 +4639,12 @@ ${content}`);
4361
4639
  offInsert();
4362
4640
  };
4363
4641
  }, [session]);
4364
- useEffect11(() => {
4642
+ useEffect12(() => {
4365
4643
  if (isUnauthorizedError(error)) {
4366
4644
  onUnauthorized();
4367
4645
  }
4368
4646
  }, [error, onUnauthorized]);
4369
- useEffect11(() => {
4647
+ useEffect12(() => {
4370
4648
  if (!session || !commands) return;
4371
4649
  const unsubscribes = Object.entries(commands).map(
4372
4650
  ([action, handler]) => session.onCommand(action, (payload) => handler(payload))
@@ -4376,6 +4654,7 @@ ${content}`);
4376
4654
  };
4377
4655
  }, [session, commands]);
4378
4656
  const isStreaming = state?.isStreaming ?? false;
4657
+ const planRevealRevision = resolvedSessionId ? planRevealRevisions.get(resolvedSessionId) ?? 0 : 0;
4379
4658
  const isStopping = stopRequested && isStreaming;
4380
4659
  const connectError = error && !isUnauthorizedError(error) ? error.message || "\u8FDE\u63A5\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5" : null;
4381
4660
  const errorMessage = connectError ?? state?.errorMessage ?? replay.error?.message ?? null;
@@ -4387,7 +4666,7 @@ ${content}`);
4387
4666
  setStopRequested(true);
4388
4667
  void session?.stop();
4389
4668
  };
4390
- return /* @__PURE__ */ jsx18(
4669
+ return /* @__PURE__ */ jsx19(
4391
4670
  ChatSurface,
4392
4671
  {
4393
4672
  theme,
@@ -4397,8 +4676,8 @@ ${content}`);
4397
4676
  slots,
4398
4677
  placeholder,
4399
4678
  connection: state?.connection ?? "connecting",
4400
- banner: /* @__PURE__ */ jsxs16(Fragment3, { children: [
4401
- /* @__PURE__ */ jsx18(
4679
+ banner: /* @__PURE__ */ jsxs17(Fragment3, { children: [
4680
+ /* @__PURE__ */ jsx19(
4402
4681
  ReplayBar,
4403
4682
  {
4404
4683
  isReplay: replay.isReplay,
@@ -4408,7 +4687,7 @@ ${content}`);
4408
4687
  onExit: () => void replay.exitToAutonomous()
4409
4688
  }
4410
4689
  ),
4411
- /* @__PURE__ */ jsx18(ReplayMismatchPrompt, { mismatch: replay.mismatch })
4690
+ /* @__PURE__ */ jsx19(ReplayMismatchPrompt, { mismatch: replay.mismatch })
4412
4691
  ] }),
4413
4692
  errorMessage,
4414
4693
  messages: state?.messages ?? [],
@@ -4416,6 +4695,8 @@ ${content}`);
4416
4695
  resultFeedbackByEntry,
4417
4696
  onResultFeedbackSaved: handleResultFeedbackSaved,
4418
4697
  isStreaming,
4698
+ showPlanUpdates: true,
4699
+ planRevealRevision,
4419
4700
  isStopping,
4420
4701
  inputText,
4421
4702
  onInputChange: setInputText,
@@ -4437,11 +4718,11 @@ ${content}`);
4437
4718
  }
4438
4719
 
4439
4720
  // src/components/LlmChat.tsx
4440
- import { useEffect as useEffect12, useMemo as useMemo9, useState as useState15 } from "react";
4721
+ import { useEffect as useEffect13, useMemo as useMemo9, useState as useState16 } from "react";
4441
4722
 
4442
4723
  // src/components/LlmAdvancedSettings.tsx
4443
- import { useState as useState14 } from "react";
4444
- import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
4724
+ import { useState as useState15 } from "react";
4725
+ import { jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
4445
4726
  var FIELDS = [
4446
4727
  { id: "baseURL", label: "\u6A21\u578B\u670D\u52A1\u5730\u5740", placeholder: "http://\u5185\u7F51\u5730\u5740/v1" },
4447
4728
  { id: "model", label: "\u6A21\u578B", placeholder: "\u6A21\u578B\u540D\u79F0" },
@@ -4487,13 +4768,13 @@ function writeOverride(settings, baseURL, override) {
4487
4768
  }
4488
4769
  function LlmAdvancedSettingsBar({ settings, defaults, override, onChange }) {
4489
4770
  const normalized = normalizeAdvanced(settings);
4490
- const [open, setOpen] = useState14(false);
4491
- const [draft, setDraft] = useState14(override);
4771
+ const [open, setOpen] = useState15(false);
4772
+ const [draft, setDraft] = useState15(override);
4492
4773
  if (!normalized) return null;
4493
4774
  const fields = FIELDS.filter((field) => normalized[field.id]);
4494
4775
  const dirty = Object.keys(override).length > 0;
4495
- return /* @__PURE__ */ jsxs17("div", { className: "blade-chat-advanced border-t border-[hsl(var(--border))] px-4 py-2 text-xs", children: [
4496
- /* @__PURE__ */ jsxs17(
4776
+ return /* @__PURE__ */ jsxs18("div", { className: "blade-chat-advanced border-t border-[hsl(var(--border))] px-4 py-2 text-xs", children: [
4777
+ /* @__PURE__ */ jsxs18(
4497
4778
  "button",
4498
4779
  {
4499
4780
  type: "button",
@@ -4503,16 +4784,16 @@ function LlmAdvancedSettingsBar({ settings, defaults, override, onChange }) {
4503
4784
  },
4504
4785
  className: "flex items-center gap-1.5 text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))]",
4505
4786
  children: [
4506
- /* @__PURE__ */ jsx19(Settings2, { size: 13 }),
4787
+ /* @__PURE__ */ jsx20(Settings2, { size: 13 }),
4507
4788
  "\u9AD8\u7EA7\u8BBE\u7F6E",
4508
- dirty && /* @__PURE__ */ jsx19("span", { className: "text-[hsl(var(--primary))]", children: "\uFF08\u5DF2\u81EA\u5B9A\u4E49\uFF09" })
4789
+ dirty && /* @__PURE__ */ jsx20("span", { className: "text-[hsl(var(--primary))]", children: "\uFF08\u5DF2\u81EA\u5B9A\u4E49\uFF09" })
4509
4790
  ]
4510
4791
  }
4511
4792
  ),
4512
- open && /* @__PURE__ */ jsxs17("div", { className: "mt-2 flex flex-col gap-2", children: [
4513
- fields.map((field) => /* @__PURE__ */ jsxs17("label", { className: "flex flex-col gap-1", children: [
4514
- /* @__PURE__ */ jsx19("span", { className: "text-[hsl(var(--muted-foreground))]", children: field.label }),
4515
- /* @__PURE__ */ jsx19(
4793
+ open && /* @__PURE__ */ jsxs18("div", { className: "mt-2 flex flex-col gap-2", children: [
4794
+ fields.map((field) => /* @__PURE__ */ jsxs18("label", { className: "flex flex-col gap-1", children: [
4795
+ /* @__PURE__ */ jsx20("span", { className: "text-[hsl(var(--muted-foreground))]", children: field.label }),
4796
+ /* @__PURE__ */ jsx20(
4516
4797
  "input",
4517
4798
  {
4518
4799
  type: field.secret ? "password" : "text",
@@ -4523,9 +4804,9 @@ function LlmAdvancedSettingsBar({ settings, defaults, override, onChange }) {
4523
4804
  }
4524
4805
  )
4525
4806
  ] }, field.id)),
4526
- normalized.apiKey && /* @__PURE__ */ jsx19("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" }),
4527
- /* @__PURE__ */ jsxs17("div", { className: "flex gap-2", children: [
4528
- /* @__PURE__ */ jsx19(
4807
+ normalized.apiKey && /* @__PURE__ */ jsx20("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" }),
4808
+ /* @__PURE__ */ jsxs18("div", { className: "flex gap-2", children: [
4809
+ /* @__PURE__ */ jsx20(
4529
4810
  "button",
4530
4811
  {
4531
4812
  type: "button",
@@ -4540,7 +4821,7 @@ function LlmAdvancedSettingsBar({ settings, defaults, override, onChange }) {
4540
4821
  children: "\u4FDD\u5B58"
4541
4822
  }
4542
4823
  ),
4543
- /* @__PURE__ */ jsx19(
4824
+ /* @__PURE__ */ jsx20(
4544
4825
  "button",
4545
4826
  {
4546
4827
  type: "button",
@@ -4559,7 +4840,7 @@ function LlmAdvancedSettingsBar({ settings, defaults, override, onChange }) {
4559
4840
  }
4560
4841
 
4561
4842
  // src/components/LlmChat.tsx
4562
- import { jsx as jsx20 } from "react/jsx-runtime";
4843
+ import { jsx as jsx21 } from "react/jsx-runtime";
4563
4844
  function LlmChat({
4564
4845
  classNames,
4565
4846
  renderers,
@@ -4571,11 +4852,11 @@ function LlmChat({
4571
4852
  onOverrideChange,
4572
4853
  ...options
4573
4854
  }) {
4574
- const [override, setOverride] = useState15(() => readOverride(advanced, options.baseURL));
4855
+ const [override, setOverride] = useState16(() => readOverride(advanced, options.baseURL));
4575
4856
  const effective = { ...options, ...override };
4576
4857
  const { messages, isStreaming, error, send, stop, reset } = useLlmChat(effective);
4577
- const [inputText, setInputText] = useState15("");
4578
- const [stopRequested, setStopRequested] = useState15(false);
4858
+ const [inputText, setInputText] = useState16("");
4859
+ const [stopRequested, setStopRequested] = useState16(false);
4579
4860
  const handle = useMemo9(
4580
4861
  () => ({
4581
4862
  insertText: (text) => setInputText((prev) => prev ? `${prev}
@@ -4585,10 +4866,10 @@ ${text}` : text),
4585
4866
  }),
4586
4867
  [send, reset]
4587
4868
  );
4588
- useEffect12(() => {
4869
+ useEffect13(() => {
4589
4870
  onReady?.(handle);
4590
4871
  }, [handle, onReady]);
4591
- return /* @__PURE__ */ jsx20(
4872
+ return /* @__PURE__ */ jsx21(
4592
4873
  ChatSurface,
4593
4874
  {
4594
4875
  theme,
@@ -4613,7 +4894,7 @@ ${text}` : text),
4613
4894
  setStopRequested(true);
4614
4895
  stop();
4615
4896
  },
4616
- beforeInput: advanced ? /* @__PURE__ */ jsx20(
4897
+ beforeInput: advanced ? /* @__PURE__ */ jsx21(
4617
4898
  LlmAdvancedSettingsBar,
4618
4899
  {
4619
4900
  settings: advanced,
@@ -4631,14 +4912,14 @@ ${text}` : text),
4631
4912
  }
4632
4913
 
4633
4914
  // src/components/ChatView.tsx
4634
- import { jsx as jsx21 } from "react/jsx-runtime";
4915
+ import { jsx as jsx22 } from "react/jsx-runtime";
4635
4916
  function ChatView(props) {
4636
4917
  const { mode, llm, onLlmReady, ...rest } = props;
4637
4918
  if (mode === "llm") {
4638
4919
  if (!llm) {
4639
4920
  throw new Error('ChatView: mode="llm" \u9700\u8981\u540C\u65F6\u4F20 llm={{ baseURL, model }}');
4640
4921
  }
4641
- return /* @__PURE__ */ jsx21(
4922
+ return /* @__PURE__ */ jsx22(
4642
4923
  LlmChat,
4643
4924
  {
4644
4925
  ...llm,
@@ -4651,7 +4932,7 @@ function ChatView(props) {
4651
4932
  }
4652
4933
  );
4653
4934
  }
4654
- return /* @__PURE__ */ jsx21(AgentChat, { ...rest });
4935
+ return /* @__PURE__ */ jsx22(AgentChat, { ...rest });
4655
4936
  }
4656
4937
 
4657
4938
  // src/components/ContextCard.tsx
@@ -4659,16 +4940,16 @@ import {
4659
4940
  getContextDisplayState,
4660
4941
  getContextGroupDisplayState
4661
4942
  } from "@blade-hq/agent-client";
4662
- import { jsx as jsx22, jsxs as jsxs18 } from "react/jsx-runtime";
4943
+ import { jsx as jsx23, jsxs as jsxs19 } from "react/jsx-runtime";
4663
4944
  function ContextCard({ context, className }) {
4664
4945
  const display = getContextDisplayState(context);
4665
- return /* @__PURE__ */ jsxs18(
4946
+ return /* @__PURE__ */ jsxs19(
4666
4947
  "details",
4667
4948
  {
4668
4949
  className: `blade-chat-context-card group/context-card rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] text-sm ${className ?? ""}`,
4669
4950
  children: [
4670
- /* @__PURE__ */ jsxs18("summary", { className: "blade-chat-context-summary flex cursor-pointer list-none items-center gap-2 px-3 py-2.5 [&::-webkit-details-marker]:hidden", children: [
4671
- /* @__PURE__ */ jsx22(
4951
+ /* @__PURE__ */ jsxs19("summary", { className: "blade-chat-context-summary flex cursor-pointer list-none items-center gap-2 px-3 py-2.5 [&::-webkit-details-marker]:hidden", children: [
4952
+ /* @__PURE__ */ jsx23(
4672
4953
  Layers,
4673
4954
  {
4674
4955
  size: 15,
@@ -4676,11 +4957,11 @@ function ContextCard({ context, className }) {
4676
4957
  "aria-hidden": "true"
4677
4958
  }
4678
4959
  ),
4679
- /* @__PURE__ */ jsxs18("span", { className: "blade-chat-context-copy min-w-0 flex-1", children: [
4680
- /* @__PURE__ */ jsx22("span", { className: "blade-chat-context-title block font-medium text-[hsl(var(--foreground))]", children: display.title }),
4681
- /* @__PURE__ */ jsx22("span", { className: "blade-chat-context-status block truncate text-xs text-[hsl(var(--muted-foreground))]", children: display.summary })
4960
+ /* @__PURE__ */ jsxs19("span", { className: "blade-chat-context-copy min-w-0 flex-1", children: [
4961
+ /* @__PURE__ */ jsx23("span", { className: "blade-chat-context-title block font-medium text-[hsl(var(--foreground))]", children: display.title }),
4962
+ /* @__PURE__ */ jsx23("span", { className: "blade-chat-context-status block truncate text-xs text-[hsl(var(--muted-foreground))]", children: display.summary })
4682
4963
  ] }),
4683
- /* @__PURE__ */ jsx22(
4964
+ /* @__PURE__ */ jsx23(
4684
4965
  ChevronDown,
4685
4966
  {
4686
4967
  size: 14,
@@ -4689,7 +4970,7 @@ function ContextCard({ context, className }) {
4689
4970
  }
4690
4971
  )
4691
4972
  ] }),
4692
- /* @__PURE__ */ jsx22("div", { className: "blade-chat-context-detail border-t border-[hsl(var(--border))] px-3 py-2.5 text-xs leading-5 text-[hsl(var(--muted-foreground))]", children: display.detail })
4973
+ /* @__PURE__ */ jsx23("div", { className: "blade-chat-context-detail border-t border-[hsl(var(--border))] px-3 py-2.5 text-xs leading-5 text-[hsl(var(--muted-foreground))]", children: display.detail })
4693
4974
  ]
4694
4975
  }
4695
4976
  );
@@ -4698,9 +4979,9 @@ function ContextGroupCard({ contexts, className }) {
4698
4979
  if (contexts.length === 0) return null;
4699
4980
  const single = contexts.length === 1 ? getContextDisplayState(contexts[0]) : null;
4700
4981
  const group = single ? null : getContextGroupDisplayState(contexts);
4701
- return /* @__PURE__ */ jsxs18("details", { className: `blade-chat-context-card group/context-group rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] text-sm ${className ?? ""}`, children: [
4702
- /* @__PURE__ */ jsxs18("summary", { className: "blade-chat-context-summary flex cursor-pointer list-none items-center gap-2 px-3 py-2.5 [&::-webkit-details-marker]:hidden", children: [
4703
- /* @__PURE__ */ jsx22(
4982
+ return /* @__PURE__ */ jsxs19("details", { className: `blade-chat-context-card group/context-group rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] text-sm ${className ?? ""}`, children: [
4983
+ /* @__PURE__ */ jsxs19("summary", { className: "blade-chat-context-summary flex cursor-pointer list-none items-center gap-2 px-3 py-2.5 [&::-webkit-details-marker]:hidden", children: [
4984
+ /* @__PURE__ */ jsx23(
4704
4985
  Layers,
4705
4986
  {
4706
4987
  size: 15,
@@ -4708,11 +4989,11 @@ function ContextGroupCard({ contexts, className }) {
4708
4989
  "aria-hidden": "true"
4709
4990
  }
4710
4991
  ),
4711
- /* @__PURE__ */ jsxs18("span", { className: "blade-chat-context-copy min-w-0 flex-1", children: [
4712
- /* @__PURE__ */ jsx22("span", { className: "blade-chat-context-title block font-medium text-[hsl(var(--foreground))]", children: single ? single.title : `${group?.title} \xB7 ${group?.count} \u9879` }),
4713
- /* @__PURE__ */ jsx22("span", { className: "blade-chat-context-status block truncate text-xs text-[hsl(var(--muted-foreground))]", children: single ? single.summary : group?.summary })
4992
+ /* @__PURE__ */ jsxs19("span", { className: "blade-chat-context-copy min-w-0 flex-1", children: [
4993
+ /* @__PURE__ */ jsx23("span", { className: "blade-chat-context-title block font-medium text-[hsl(var(--foreground))]", children: single ? single.title : `${group?.title} \xB7 ${group?.count} \u9879` }),
4994
+ /* @__PURE__ */ jsx23("span", { className: "blade-chat-context-status block truncate text-xs text-[hsl(var(--muted-foreground))]", children: single ? single.summary : group?.summary })
4714
4995
  ] }),
4715
- /* @__PURE__ */ jsx22(
4996
+ /* @__PURE__ */ jsx23(
4716
4997
  ChevronDown,
4717
4998
  {
4718
4999
  size: 14,
@@ -4721,7 +5002,7 @@ function ContextGroupCard({ contexts, className }) {
4721
5002
  }
4722
5003
  )
4723
5004
  ] }),
4724
- single ? /* @__PURE__ */ jsx22("div", { className: "blade-chat-context-detail border-t border-[hsl(var(--border))] px-3 py-2.5 text-xs leading-5 text-[hsl(var(--muted-foreground))]", children: single.detail }) : /* @__PURE__ */ jsx22("div", { className: "blade-chat-context-group-items flex flex-col gap-1.5 border-t border-[hsl(var(--border))] p-2", children: contexts.map((context) => /* @__PURE__ */ jsx22(
5005
+ single ? /* @__PURE__ */ jsx23("div", { className: "blade-chat-context-detail border-t border-[hsl(var(--border))] px-3 py-2.5 text-xs leading-5 text-[hsl(var(--muted-foreground))]", children: single.detail }) : /* @__PURE__ */ jsx23("div", { className: "blade-chat-context-group-items flex flex-col gap-1.5 border-t border-[hsl(var(--border))] p-2", children: contexts.map((context) => /* @__PURE__ */ jsx23(
4725
5006
  ContextCard,
4726
5007
  {
4727
5008
  context
@@ -4731,6 +5012,112 @@ function ContextGroupCard({ contexts, className }) {
4731
5012
  ] });
4732
5013
  }
4733
5014
 
5015
+ // src/components/SessionMemoryToggle.tsx
5016
+ import { useCallback as useCallback9, useEffect as useEffect14, useRef as useRef13, useState as useState17, useSyncExternalStore as useSyncExternalStore2 } from "react";
5017
+ import { jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
5018
+ var saveStates = /* @__PURE__ */ new WeakMap();
5019
+ function getSaveState(client, sessionId) {
5020
+ let clientStates = saveStates.get(client);
5021
+ if (!clientStates) {
5022
+ clientStates = /* @__PURE__ */ new Map();
5023
+ saveStates.set(client, clientStates);
5024
+ }
5025
+ let state = clientStates.get(sessionId);
5026
+ if (!state) {
5027
+ state = { saving: false, listeners: /* @__PURE__ */ new Set() };
5028
+ clientStates.set(sessionId, state);
5029
+ }
5030
+ return state;
5031
+ }
5032
+ function notify(state) {
5033
+ for (const listener of state.listeners) listener();
5034
+ }
5035
+ function cleanupSaveState(client, sessionId, state) {
5036
+ if (state.saving || state.listeners.size > 0) return;
5037
+ const clientStates = saveStates.get(client);
5038
+ if (clientStates?.get(sessionId) === state) clientStates.delete(sessionId);
5039
+ }
5040
+ function SessionMemoryToggle({
5041
+ sessionId,
5042
+ enabled,
5043
+ client: clientProp,
5044
+ disabled = false,
5045
+ label = "\u5F53\u524D\u4F1A\u8BDD\u4F7F\u7528\u8BB0\u5FC6",
5046
+ className,
5047
+ labelClassName,
5048
+ inputClassName,
5049
+ onSaved,
5050
+ onError
5051
+ }) {
5052
+ const contextClient = useOptionalBladeClient();
5053
+ const client = clientProp ?? contextClient;
5054
+ if (!client) {
5055
+ throw new Error("SessionMemoryToggle \u5FC5\u987B\u5728 <BladeProvider> \u5185\u4F7F\u7528\u6216\u663E\u5F0F\u4F20\u5165 client");
5056
+ }
5057
+ const saveState = getSaveState(client, sessionId);
5058
+ const subscribe = useCallback9(
5059
+ (listener) => {
5060
+ saveState.listeners.add(listener);
5061
+ return () => {
5062
+ saveState.listeners.delete(listener);
5063
+ cleanupSaveState(client, sessionId, saveState);
5064
+ };
5065
+ },
5066
+ [client, saveState, sessionId]
5067
+ );
5068
+ const getSaving = useCallback9(() => saveState.saving, [saveState]);
5069
+ const saving = useSyncExternalStore2(
5070
+ subscribe,
5071
+ getSaving,
5072
+ getSaving
5073
+ );
5074
+ const [draftEnabled, setDraftEnabled] = useState17(enabled);
5075
+ const activeSessionIdRef = useRef13(sessionId);
5076
+ activeSessionIdRef.current = sessionId;
5077
+ useEffect14(() => {
5078
+ setDraftEnabled(enabled);
5079
+ }, [enabled, sessionId]);
5080
+ const update = useCallback9(
5081
+ (nextEnabled) => {
5082
+ const currentSaveState = getSaveState(client, sessionId);
5083
+ if (currentSaveState.saving) return;
5084
+ currentSaveState.saving = true;
5085
+ notify(currentSaveState);
5086
+ setDraftEnabled(nextEnabled);
5087
+ void client.sessions.updateSessionMemory(sessionId, nextEnabled).then(
5088
+ (updated) => {
5089
+ if (activeSessionIdRef.current === sessionId) {
5090
+ setDraftEnabled(updated.memory_enabled);
5091
+ }
5092
+ onSaved?.(sessionId, updated.memory_enabled);
5093
+ },
5094
+ (error) => {
5095
+ if (activeSessionIdRef.current === sessionId) setDraftEnabled(enabled);
5096
+ onError?.(error);
5097
+ }
5098
+ ).finally(() => {
5099
+ currentSaveState.saving = false;
5100
+ notify(currentSaveState);
5101
+ cleanupSaveState(client, sessionId, currentSaveState);
5102
+ });
5103
+ },
5104
+ [client, enabled, onError, onSaved, sessionId]
5105
+ );
5106
+ return /* @__PURE__ */ jsxs20("label", { className: cn("flex items-center justify-between", className), children: [
5107
+ /* @__PURE__ */ jsx24("span", { className: labelClassName, children: label }),
5108
+ /* @__PURE__ */ jsx24(
5109
+ "input",
5110
+ {
5111
+ type: "checkbox",
5112
+ checked: draftEnabled,
5113
+ onChange: (event) => update(event.target.checked),
5114
+ disabled: disabled || saving,
5115
+ className: inputClassName
5116
+ }
5117
+ )
5118
+ ] });
5119
+ }
5120
+
4734
5121
  // src/lib/agent-computer-command.ts
4735
5122
  var COMPUTER_LAUNCH_COMMAND_PATTERN = /(?:^|[\n;&|(]\s*)computer\s+launch(?:\s|$)/;
4736
5123
  function isAgentComputerCommand(command) {
@@ -4781,18 +5168,26 @@ export {
4781
5168
  ChatView,
4782
5169
  ContextCard,
4783
5170
  ContextGroupCard,
5171
+ CurrentPlanPanel,
4784
5172
  LlmChat,
4785
5173
  MarkdownContent,
4786
5174
  MemoryRefsHint,
5175
+ PLAN_AUTO_COLLAPSE_MS,
5176
+ PlanUpdateBlock,
4787
5177
  ReplayBar,
4788
5178
  ReplayMismatchPrompt,
5179
+ SessionMemoryToggle,
4789
5180
  WhatIfUserBubble,
4790
5181
  classifyAgentComputerLaunchOutcome,
4791
5182
  collectMemoryRefs,
5183
+ getPlanUpdateDisplayState,
4792
5184
  isAgentComputerCommand,
4793
5185
  isAgentComputerToolCall,
5186
+ isPlanUpdateTool,
4794
5187
  normalizeAdjacentUrlFormatting,
5188
+ parsePlanUpdate,
4795
5189
  parseWhatIfPrompt,
5190
+ pickCurrentPlanStep,
4796
5191
  useAgentSession,
4797
5192
  useBladeClient,
4798
5193
  useLlmChat,
@@ -4814,6 +5209,8 @@ lucide-react/dist/esm/icons/check.js:
4814
5209
  lucide-react/dist/esm/icons/chevron-down.js:
4815
5210
  lucide-react/dist/esm/icons/chevron-right.js:
4816
5211
  lucide-react/dist/esm/icons/circle-alert.js:
5212
+ lucide-react/dist/esm/icons/circle-dot.js:
5213
+ lucide-react/dist/esm/icons/circle.js:
4817
5214
  lucide-react/dist/esm/icons/copy.js:
4818
5215
  lucide-react/dist/esm/icons/earth.js:
4819
5216
  lucide-react/dist/esm/icons/file-pen-line.js:
@@ -4821,6 +5218,7 @@ lucide-react/dist/esm/icons/file-text.js:
4821
5218
  lucide-react/dist/esm/icons/globe.js:
4822
5219
  lucide-react/dist/esm/icons/layers.js:
4823
5220
  lucide-react/dist/esm/icons/lightbulb.js:
5221
+ lucide-react/dist/esm/icons/list-checks.js:
4824
5222
  lucide-react/dist/esm/icons/loader-circle.js:
4825
5223
  lucide-react/dist/esm/icons/lock-keyhole.js:
4826
5224
  lucide-react/dist/esm/icons/message-square-more.js: