@blade-hq/agent-react 2610.0.0-beta.30 → 2610.0.0-beta.32

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
@@ -30,11 +30,14 @@ function useAgentSession(sessionId, options = {}) {
30
30
  const connRef = useRef({
31
31
  id: null,
32
32
  session: null,
33
+ cleanup: null,
33
34
  gen: 0
34
35
  });
35
36
  const createdIdPromiseRef = useRef(null);
36
37
  const onCreatedRef = useRef(options.onSessionCreated);
37
38
  onCreatedRef.current = options.onSessionCreated;
39
+ const onConnectedRef = useRef(options.onSessionConnected);
40
+ onConnectedRef.current = options.onSessionConnected;
38
41
  const createOptionsRef = useRef(options.createOptions);
39
42
  createOptionsRef.current = options.createOptions;
40
43
  const sessionIdRef = useRef(sessionId);
@@ -42,6 +45,7 @@ function useAgentSession(sessionId, options = {}) {
42
45
  const connect = useMemo(() => {
43
46
  return (targetId) => {
44
47
  const gen = ++connRef.current.gen;
48
+ let pendingCleanup = null;
45
49
  const idPromise = targetId ? Promise.resolve(targetId) : (
46
50
  // biome-ignore lint/suspicious/noAssignInExpressions: ??= 挂 ref 是 StrictMode 下"只创建一次"的关键
47
51
  createdIdPromiseRef.current ??= client.sessions.create(createOptionsRef.current ?? {}).then((created) => {
@@ -51,16 +55,25 @@ function useAgentSession(sessionId, options = {}) {
51
55
  return id;
52
56
  })
53
57
  );
54
- idPromise.then((id) => client.hub.connect(id)).then((next) => {
58
+ idPromise.then(
59
+ (id) => client.hub.connect(id, {
60
+ setup: (next) => {
61
+ pendingCleanup = onConnectedRef.current?.(next) ?? null;
62
+ }
63
+ })
64
+ ).then((next) => {
55
65
  if (connRef.current.gen !== gen) {
66
+ pendingCleanup?.();
56
67
  next.dispose();
57
68
  return;
58
69
  }
59
70
  connRef.current.id = next.sessionId;
60
71
  connRef.current.session = next;
72
+ connRef.current.cleanup = pendingCleanup;
61
73
  setSession(next);
62
74
  setError(null);
63
75
  }).catch((err) => {
76
+ pendingCleanup?.();
64
77
  if (connRef.current.gen !== gen) return;
65
78
  createdIdPromiseRef.current = null;
66
79
  setError(err instanceof Error ? err : new Error(String(err)));
@@ -72,8 +85,11 @@ function useAgentSession(sessionId, options = {}) {
72
85
  return () => {
73
86
  connRef.current.gen++;
74
87
  const toRelease = connRef.current.session;
88
+ const cleanup = connRef.current.cleanup;
75
89
  connRef.current.id = null;
76
90
  connRef.current.session = null;
91
+ connRef.current.cleanup = null;
92
+ cleanup?.();
77
93
  setSession(null);
78
94
  if (toRelease) setTimeout(() => toRelease.dispose(), DISPOSE_DELAY_MS);
79
95
  };
@@ -83,8 +99,11 @@ function useAgentSession(sessionId, options = {}) {
83
99
  if (connRef.current.id === null) return;
84
100
  if (sessionId === connRef.current.id) return;
85
101
  const previous = connRef.current.session;
102
+ const cleanup = connRef.current.cleanup;
86
103
  connRef.current.id = null;
87
104
  connRef.current.session = null;
105
+ connRef.current.cleanup = null;
106
+ cleanup?.();
88
107
  if (previous) setTimeout(() => previous.dispose(), DISPOSE_DELAY_MS);
89
108
  connect(sessionId);
90
109
  }, [sessionId, connect]);
@@ -900,6 +919,17 @@ var CircleAlert = createLucideIcon("CircleAlert", [
900
919
  ["line", { x1: "12", x2: "12.01", y1: "16", y2: "16", key: "4dfq90" }]
901
920
  ]);
902
921
 
922
+ // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/circle-dot.js
923
+ var CircleDot = createLucideIcon("CircleDot", [
924
+ ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
925
+ ["circle", { cx: "12", cy: "12", r: "1", key: "41hilf" }]
926
+ ]);
927
+
928
+ // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/circle.js
929
+ var Circle = createLucideIcon("Circle", [
930
+ ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }]
931
+ ]);
932
+
903
933
  // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/copy.js
904
934
  var Copy = createLucideIcon("Copy", [
905
935
  ["rect", { width: "14", height: "14", x: "8", y: "8", rx: "2", ry: "2", key: "17jyea" }],
@@ -993,6 +1023,15 @@ var Lightbulb = createLucideIcon("Lightbulb", [
993
1023
  ["path", { d: "M10 22h4", key: "ceow96" }]
994
1024
  ]);
995
1025
 
1026
+ // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/list-checks.js
1027
+ var ListChecks = createLucideIcon("ListChecks", [
1028
+ ["path", { d: "m3 17 2 2 4-4", key: "1jhpwq" }],
1029
+ ["path", { d: "m3 7 2 2 4-4", key: "1obspn" }],
1030
+ ["path", { d: "M13 6h8", key: "15sg57" }],
1031
+ ["path", { d: "M13 12h8", key: "h98zly" }],
1032
+ ["path", { d: "M13 18h8", key: "oe0vm4" }]
1033
+ ]);
1034
+
996
1035
  // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/loader-circle.js
997
1036
  var LoaderCircle = createLucideIcon("LoaderCircle", [
998
1037
  ["path", { d: "M21 12a9 9 0 1 1-6.219-8.56", key: "13zald" }]
@@ -1023,6 +1062,14 @@ var Play = createLucideIcon("Play", [
1023
1062
  ["polygon", { points: "6 3 20 12 6 21 6 3", key: "1oa8hb" }]
1024
1063
  ]);
1025
1064
 
1065
+ // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/refresh-ccw.js
1066
+ var RefreshCcw = createLucideIcon("RefreshCcw", [
1067
+ ["path", { d: "M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8", key: "14sxne" }],
1068
+ ["path", { d: "M3 3v5h5", key: "1xhq8a" }],
1069
+ ["path", { d: "M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16", key: "1hlbsb" }],
1070
+ ["path", { d: "M16 16h5v5", key: "ccwih5" }]
1071
+ ]);
1072
+
1026
1073
  // ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/search.js
1027
1074
  var Search = createLucideIcon("Search", [
1028
1075
  ["circle", { cx: "11", cy: "11", r: "8", key: "4ej97u" }],
@@ -1094,7 +1141,7 @@ var X = createLucideIcon("X", [
1094
1141
  ]);
1095
1142
 
1096
1143
  // src/components/AgentChat.tsx
1097
- import { useCallback as useCallback8, useEffect as useEffect11, useMemo as useMemo8, useState as useState13 } from "react";
1144
+ import { useCallback as useCallback8, useEffect as useEffect12, useMemo as useMemo8, useState as useState14 } from "react";
1098
1145
 
1099
1146
  // src/lib/utils.ts
1100
1147
  function cn(...inputs) {
@@ -1221,11 +1268,349 @@ function ReplayMismatchPrompt({ mismatch, className }) {
1221
1268
  );
1222
1269
  }
1223
1270
 
1271
+ // src/components/PlanUpdateBlock.tsx
1272
+ import { useEffect as useEffect4, useRef as useRef4, useState as useState4 } from "react";
1273
+
1274
+ // src/components/display-utils.ts
1275
+ var TOOL_NAME_ALIASES = {
1276
+ agent: "Agent",
1277
+ ask_user_question: "AskUserQuestion",
1278
+ bash: "Bash",
1279
+ bg_bash: "BgBash",
1280
+ edit: "Edit",
1281
+ exit_plan_mode: "ExitPlanMode",
1282
+ file_edit: "Edit",
1283
+ file_read: "Read",
1284
+ file_write: "Write",
1285
+ finish_task: "FinishTask",
1286
+ glob: "Glob",
1287
+ grep: "Grep",
1288
+ kb_search: "KbSearch",
1289
+ ls: "Ls",
1290
+ multi_edit: "MultiEdit",
1291
+ read: "Read",
1292
+ read_skill: "ReadSkill",
1293
+ update_plan: "UpdatePlan",
1294
+ web_fetch: "WebFetch",
1295
+ web_search: "WebSearch",
1296
+ write: "Write"
1297
+ };
1298
+ var TOOL_DISPLAY_LABELS = {
1299
+ Bash: "\u6267\u884C\u547D\u4EE4",
1300
+ BgBash: "\u540E\u53F0\u6267\u884C\u547D\u4EE4",
1301
+ Read: "\u8BFB\u53D6\u6587\u4EF6",
1302
+ Write: "\u5199\u5165\u6587\u4EF6",
1303
+ Edit: "\u7F16\u8F91\u6587\u4EF6",
1304
+ MultiEdit: "\u7F16\u8F91\u6587\u4EF6",
1305
+ Ls: "\u5217\u51FA\u76EE\u5F55",
1306
+ Glob: "\u5339\u914D\u6587\u4EF6",
1307
+ Grep: "\u641C\u7D22\u6587\u672C",
1308
+ KbSearch: "\u68C0\u7D22\u77E5\u8BC6\u5E93",
1309
+ WebSearch: "\u641C\u7D22\u7F51\u9875",
1310
+ WebFetch: "\u6574\u7406\u7F51\u9875\u5185\u5BB9",
1311
+ Agent: "\u6D3E\u751F\u5B50\u667A\u80FD\u4F53",
1312
+ AskUserQuestion: "\u5411\u7528\u6237\u63D0\u95EE",
1313
+ ReadSkill: "\u8BFB\u53D6\u6280\u80FD",
1314
+ FinishTask: "\u4EFB\u52A1\u5B8C\u6210",
1315
+ ExitPlanMode: "\u63D0\u4EA4\u8BA1\u5212",
1316
+ ListSessions: "\u5217\u51FA\u5386\u53F2\u4F1A\u8BDD",
1317
+ GetSessionHistory: "\u8BFB\u53D6\u4F1A\u8BDD\u5386\u53F2"
1318
+ };
1319
+ function safeParseJson(value) {
1320
+ if (!value) return null;
1321
+ try {
1322
+ return JSON.parse(value);
1323
+ } catch {
1324
+ return null;
1325
+ }
1326
+ }
1327
+ function getStringArgValue(args, key) {
1328
+ const value = args?.[key];
1329
+ return typeof value === "string" ? value.trim() : "";
1330
+ }
1331
+ var SKILL_ENTRY_FILE_NAMES = /* @__PURE__ */ new Set(["skill.md", "command.md"]);
1332
+ var NON_SKILL_DIR_NAMES = /* @__PURE__ */ new Set([".", "..", ".agent", ".agents", ".claude", "skill_data", "skills"]);
1333
+ function getSkillNameFromFilePath(filePath) {
1334
+ if (!filePath) return null;
1335
+ const segments = filePath.split(/[\\/]+/).filter(Boolean);
1336
+ const fileName = segments.pop();
1337
+ if (!fileName || !SKILL_ENTRY_FILE_NAMES.has(fileName.toLowerCase())) return null;
1338
+ const dirName = segments.pop();
1339
+ if (!dirName || NON_SKILL_DIR_NAMES.has(dirName.toLowerCase())) return null;
1340
+ return dirName;
1341
+ }
1342
+ function formatToolName(name) {
1343
+ const trimmed = name.trim();
1344
+ if (!trimmed) return name;
1345
+ const stripped = trimmed.split(":").pop()?.split("/").pop()?.split(".").pop()?.trim() || trimmed;
1346
+ const normalized = stripped.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
1347
+ return TOOL_NAME_ALIASES[normalized] ?? stripped;
1348
+ }
1349
+ function getToolDisplayLabel(toolCall) {
1350
+ const normalized = formatToolName(toolCall.name);
1351
+ const args = safeParseJson(toolCall.arguments);
1352
+ const displayName = toolCall.display_name?.trim() ?? "";
1353
+ const baseLabel = displayName || TOOL_DISPLAY_LABELS[normalized] || normalized;
1354
+ const metaDisplayName = getStringArgValue(args, "_meta_display_name");
1355
+ if (metaDisplayName) {
1356
+ return metaDisplayName;
1357
+ }
1358
+ const description = getStringArgValue(args, "description");
1359
+ if (normalized === "BgBash") {
1360
+ return description ? `\u540E\u53F0\u6267\u884C\uFF1A${description}` : "\u540E\u53F0\u6267\u884C\u547D\u4EE4";
1361
+ }
1362
+ if (normalized === "ReadSkill") {
1363
+ const skillName = getStringArgValue(args, "skill") || getStringArgValue(args, "skill_name");
1364
+ return skillName ? `${baseLabel}\u300C${skillName}\u300D` : baseLabel;
1365
+ }
1366
+ if (normalized === "Read") {
1367
+ const skillName = getSkillNameFromFilePath(
1368
+ getStringArgValue(args, "file_path") || getStringArgValue(args, "path")
1369
+ );
1370
+ if (skillName) return `\u8BFB\u53D6\u6280\u80FD\u300C${skillName}\u300D`;
1371
+ }
1372
+ if (normalized === "FinishTask") {
1373
+ const title = getStringArgValue(args, "title");
1374
+ return title ? `${baseLabel}\uFF1A${title}` : baseLabel;
1375
+ }
1376
+ return description || baseLabel;
1377
+ }
1378
+ function getToolTone(status) {
1379
+ if (status === "error" || status === "cancelled") return "red";
1380
+ if (status === "awaiting_answer") return "amber";
1381
+ if (status === "pending") return "blue";
1382
+ return "emerald";
1383
+ }
1384
+ function getToolStatusLabel(status) {
1385
+ if (status === "pending") return "\u8FD0\u884C\u4E2D";
1386
+ if (status === "awaiting_answer") return "\u7B49\u5F85\u56DE\u7B54";
1387
+ if (status === "error") return "\u9519\u8BEF";
1388
+ if (status === "cancelled") return "\u5DF2\u53D6\u6D88";
1389
+ return "\u5B8C\u6210";
1390
+ }
1391
+ function formatToolDuration(ms) {
1392
+ if (ms < 1e3) return `${Math.round(ms)}ms`;
1393
+ const seconds = ms / 1e3;
1394
+ if (seconds < 60) return `${seconds.toFixed(1)}s`;
1395
+ const minutes = Math.floor(seconds / 60);
1396
+ const remainingSeconds = Math.round(seconds % 60);
1397
+ return remainingSeconds > 0 ? `${minutes}m${remainingSeconds}s` : `${minutes}m`;
1398
+ }
1399
+ function formatToolArgs(args) {
1400
+ try {
1401
+ return JSON.stringify(JSON.parse(args), null, 2);
1402
+ } catch {
1403
+ return args;
1404
+ }
1405
+ }
1406
+ var RESULT_PREVIEW_LIMIT = 4e3;
1407
+ function formatToolResult(result) {
1408
+ const text = typeof result === "string" ? result : JSON.stringify(result, null, 2);
1409
+ if (text == null) return "";
1410
+ if (text.length <= RESULT_PREVIEW_LIMIT) return text;
1411
+ return `${text.slice(0, RESULT_PREVIEW_LIMIT)}
1412
+ \u2026\uFF08\u7ED3\u679C\u8FC7\u957F\uFF0C\u5DF2\u622A\u65AD\uFF09`;
1413
+ }
1414
+
1415
+ // src/components/PlanUpdateBlock.tsx
1416
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1417
+ var PLAN_STEP_STATUSES = /* @__PURE__ */ new Set(["pending", "in_progress", "completed"]);
1418
+ var PLAN_AUTO_COLLAPSE_MS = 5e3;
1419
+ function isPlanUpdateTool(toolCall) {
1420
+ return formatToolName(toolCall.name) === "UpdatePlan";
1421
+ }
1422
+ function getPlanUpdateDisplayState(messages) {
1423
+ let current = null;
1424
+ let latestAttempt = null;
1425
+ let latestAttemptStreaming = false;
1426
+ for (const message of messages) {
1427
+ if ((message.loop_name ?? "root") !== "root") continue;
1428
+ for (const toolCall of message.tool_calls ?? []) {
1429
+ if (!isPlanUpdateTool(toolCall)) continue;
1430
+ latestAttempt = toolCall;
1431
+ latestAttemptStreaming = message.status === "streaming";
1432
+ if (toolCall.status === "done" && parsePlanUpdate(toolCall.arguments)) current = toolCall;
1433
+ }
1434
+ }
1435
+ return {
1436
+ current,
1437
+ updating: latestAttempt?.status === "pending" && latestAttemptStreaming
1438
+ };
1439
+ }
1440
+ function parsePlanUpdate(argumentsJson) {
1441
+ try {
1442
+ const raw = JSON.parse(argumentsJson);
1443
+ if (!raw || typeof raw !== "object") return null;
1444
+ const candidate = raw;
1445
+ if (!Array.isArray(candidate.plan)) return null;
1446
+ const plan = candidate.plan.map((item) => {
1447
+ if (!item || typeof item !== "object") return null;
1448
+ const step = item.step;
1449
+ const status = item.status;
1450
+ if (typeof step !== "string" || !step.trim() || typeof status !== "string" || !PLAN_STEP_STATUSES.has(status)) {
1451
+ return null;
1452
+ }
1453
+ return { step: step.trim(), status };
1454
+ });
1455
+ if (plan.some((item) => item === null)) return null;
1456
+ if (plan.filter((item) => item?.status === "in_progress").length > 1) return null;
1457
+ return { plan };
1458
+ } catch {
1459
+ return null;
1460
+ }
1461
+ }
1462
+ function pickCurrentPlanStep(plan) {
1463
+ return plan.find((item) => item.status === "in_progress") ?? plan.find((item) => item.status === "pending") ?? plan[plan.length - 1] ?? null;
1464
+ }
1465
+ function PlanStepIcon({
1466
+ status,
1467
+ size = 17,
1468
+ running = false
1469
+ }) {
1470
+ if (status === "completed") {
1471
+ return /* @__PURE__ */ jsx4(Check, { size, strokeWidth: 2, className: "shrink-0 text-emerald-500" });
1472
+ }
1473
+ if (status === "in_progress") {
1474
+ 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" });
1475
+ }
1476
+ return /* @__PURE__ */ jsx4(Circle, { size, className: "shrink-0 text-[hsl(var(--muted-foreground))]/60" });
1477
+ }
1478
+ function PlanUpdateBlock({
1479
+ toolCall,
1480
+ running = false,
1481
+ autoReveal = false
1482
+ }) {
1483
+ const updateKey = `${toolCall.id}:${toolCall.arguments}`;
1484
+ const revealKey = autoReveal ? updateKey : null;
1485
+ const [collapsed, setCollapsed] = useState4(!autoReveal);
1486
+ const collapseTimerRef = useRef4(null);
1487
+ const data = parsePlanUpdate(toolCall.arguments);
1488
+ useEffect4(() => {
1489
+ if (!revealKey) return;
1490
+ if (collapseTimerRef.current) clearTimeout(collapseTimerRef.current);
1491
+ setCollapsed(false);
1492
+ collapseTimerRef.current = setTimeout(() => {
1493
+ setCollapsed(true);
1494
+ collapseTimerRef.current = null;
1495
+ }, PLAN_AUTO_COLLAPSE_MS);
1496
+ }, [revealKey]);
1497
+ useEffect4(
1498
+ () => () => {
1499
+ if (collapseTimerRef.current) clearTimeout(collapseTimerRef.current);
1500
+ },
1501
+ []
1502
+ );
1503
+ if (!data) return null;
1504
+ const completed = data.plan.filter((item) => item.status === "completed").length;
1505
+ const currentStep = pickCurrentPlanStep(data.plan);
1506
+ const pausedAtCurrentStep = !running && currentStep?.status === "in_progress";
1507
+ return /* @__PURE__ */ jsxs3("section", { className: "overflow-hidden", children: [
1508
+ /* @__PURE__ */ jsxs3(
1509
+ "button",
1510
+ {
1511
+ type: "button",
1512
+ "aria-expanded": !collapsed,
1513
+ onClick: () => {
1514
+ if (collapseTimerRef.current) {
1515
+ clearTimeout(collapseTimerRef.current);
1516
+ collapseTimerRef.current = null;
1517
+ }
1518
+ setCollapsed((value) => !value);
1519
+ },
1520
+ className: cn(
1521
+ "flex w-full items-center gap-2 px-3 py-2 text-left transition-colors hover:bg-[hsl(var(--muted)/0.3)]",
1522
+ !collapsed && "border-b border-[hsl(var(--border))]"
1523
+ ),
1524
+ children: [
1525
+ collapsed && currentStep ? /* @__PURE__ */ jsxs3("span", { className: "flex min-w-0 flex-1 items-center gap-1.5 text-xs text-[hsl(var(--foreground))]", children: [
1526
+ /* @__PURE__ */ jsx4(PlanStepIcon, { status: currentStep.status, size: 14, running }),
1527
+ /* @__PURE__ */ jsx4("span", { className: "truncate", children: currentStep.step }),
1528
+ pausedAtCurrentStep ? /* @__PURE__ */ jsx4("span", { className: "shrink-0 text-[11px] text-amber-500", children: "\u5DF2\u6682\u505C" }) : null
1529
+ ] }) : /* @__PURE__ */ jsxs3("span", { className: "flex min-w-0 flex-1 items-center gap-1.5 text-[11px] text-[hsl(var(--muted-foreground))]", children: [
1530
+ /* @__PURE__ */ jsx4(ListChecks, { size: 14, className: "shrink-0", "aria-hidden": "true" }),
1531
+ /* @__PURE__ */ jsx4("span", { className: "truncate", children: "\u4EFB\u52A1\u8FDB\u5EA6" }),
1532
+ pausedAtCurrentStep ? /* @__PURE__ */ jsx4("span", { className: "shrink-0 text-amber-500", children: "\u5DF2\u6682\u505C" }) : null
1533
+ ] }),
1534
+ /* @__PURE__ */ jsxs3("span", { className: "shrink-0 text-[11px] tabular-nums text-[hsl(var(--muted-foreground))]", children: [
1535
+ completed,
1536
+ "/",
1537
+ data.plan.length
1538
+ ] }),
1539
+ /* @__PURE__ */ jsx4(
1540
+ ChevronDown,
1541
+ {
1542
+ size: 14,
1543
+ className: cn(
1544
+ "shrink-0 text-[hsl(var(--muted-foreground))] transition-transform duration-300 ease-out motion-reduce:transition-none",
1545
+ !collapsed && "rotate-180"
1546
+ )
1547
+ }
1548
+ )
1549
+ ]
1550
+ }
1551
+ ),
1552
+ /* @__PURE__ */ jsx4(
1553
+ "div",
1554
+ {
1555
+ "aria-hidden": collapsed,
1556
+ className: cn(
1557
+ "grid transition-[grid-template-rows,opacity] duration-300 ease-out motion-reduce:transition-none",
1558
+ collapsed ? "grid-rows-[0fr] opacity-0" : "grid-rows-[1fr] opacity-100"
1559
+ ),
1560
+ 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: [
1561
+ /* @__PURE__ */ jsx4("span", { className: "mt-[3px] flex shrink-0", children: /* @__PURE__ */ jsx4(PlanStepIcon, { status: item.status, size: 14, running }) }),
1562
+ /* @__PURE__ */ jsx4(
1563
+ "span",
1564
+ {
1565
+ className: cn(
1566
+ "min-w-0 flex-1 break-words text-[13px] leading-5",
1567
+ item.status === "completed" ? "text-[hsl(var(--muted-foreground))]" : item.status === "in_progress" ? "font-medium text-[hsl(var(--foreground))]" : "text-[hsl(var(--muted-foreground))]"
1568
+ ),
1569
+ children: item.step
1570
+ }
1571
+ )
1572
+ ] }, `${index}-${item.step}`)) }) })
1573
+ }
1574
+ )
1575
+ ] });
1576
+ }
1577
+ function CurrentPlanPanel({
1578
+ messages,
1579
+ running = false,
1580
+ revealRevision = 0,
1581
+ sessionId,
1582
+ className
1583
+ }) {
1584
+ const { current, updating } = getPlanUpdateDisplayState(messages);
1585
+ const revealBaselinesRef = useRef4(/* @__PURE__ */ new Map([[sessionId, revealRevision]]));
1586
+ const autoReveal = (revealBaselinesRef.current.get(sessionId) ?? 0) !== revealRevision;
1587
+ useEffect4(() => {
1588
+ if (!current) return;
1589
+ revealBaselinesRef.current.set(sessionId, revealRevision);
1590
+ }, [current, revealRevision, sessionId]);
1591
+ if (!current && !updating) return null;
1592
+ return /* @__PURE__ */ jsxs3("div", { className: cn("blade-chat-plan mx-auto w-full max-w-[748px] px-4", className), children: [
1593
+ 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: [
1594
+ /* @__PURE__ */ jsx4(LoaderCircle, { size: 14, className: "shrink-0 animate-spin" }),
1595
+ /* @__PURE__ */ jsx4("span", { children: "\u6B63\u5728\u66F4\u65B0\u4EFB\u52A1\u8FDB\u5EA6\u2026" })
1596
+ ] }) : null,
1597
+ current ? /* @__PURE__ */ jsx4(
1598
+ PlanUpdateBlock,
1599
+ {
1600
+ toolCall: current,
1601
+ running,
1602
+ autoReveal
1603
+ },
1604
+ sessionId ?? "current-session"
1605
+ ) : null
1606
+ ] });
1607
+ }
1608
+
1224
1609
  // src/components/ChatSurface.tsx
1225
1610
  import { chatErrorForDisplay as chatErrorForDisplay2 } from "@blade-hq/agent-client";
1226
1611
 
1227
1612
  // src/components/ChatInput.tsx
1228
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1613
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1229
1614
  function isImeCompositionKey(event) {
1230
1615
  return event.isComposing || event.keyCode === 229;
1231
1616
  }
@@ -1261,8 +1646,8 @@ function ChatInput({
1261
1646
  void handleSend();
1262
1647
  }
1263
1648
  };
1264
- 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: [
1265
- /* @__PURE__ */ jsx4(
1649
+ 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: [
1650
+ /* @__PURE__ */ jsx5(
1266
1651
  "textarea",
1267
1652
  {
1268
1653
  value,
@@ -1279,7 +1664,7 @@ function ChatInput({
1279
1664
  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)]"
1280
1665
  }
1281
1666
  ),
1282
- isStreaming ? /* @__PURE__ */ jsx4(
1667
+ isStreaming ? /* @__PURE__ */ jsx5(
1283
1668
  "button",
1284
1669
  {
1285
1670
  type: "button",
@@ -1288,9 +1673,9 @@ function ChatInput({
1288
1673
  "aria-label": isStopping ? "\u6B63\u5728\u505C\u6B62" : "\u505C\u6B62\u56DE\u590D",
1289
1674
  title: isStopping ? "\u6B63\u5728\u505C\u6B62" : "\u505C\u6B62\u56DE\u590D",
1290
1675
  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",
1291
- children: isStopping ? /* @__PURE__ */ jsx4(LoaderCircle, { size: 14, className: "animate-spin" }) : /* @__PURE__ */ jsx4(Square, { size: 12, fill: "currentColor" })
1676
+ children: isStopping ? /* @__PURE__ */ jsx5(LoaderCircle, { size: 14, className: "animate-spin" }) : /* @__PURE__ */ jsx5(Square, { size: 12, fill: "currentColor" })
1292
1677
  }
1293
- ) : /* @__PURE__ */ jsx4(
1678
+ ) : /* @__PURE__ */ jsx5(
1294
1679
  "button",
1295
1680
  {
1296
1681
  type: "button",
@@ -1299,23 +1684,23 @@ function ChatInput({
1299
1684
  "aria-label": "\u53D1\u9001\u6D88\u606F",
1300
1685
  title: "\u53D1\u9001\u6D88\u606F",
1301
1686
  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",
1302
- children: /* @__PURE__ */ jsx4(ArrowUp, { size: 15 })
1687
+ children: /* @__PURE__ */ jsx5(ArrowUp, { size: 15 })
1303
1688
  }
1304
1689
  )
1305
1690
  ] }) });
1306
1691
  }
1307
1692
 
1308
1693
  // src/components/ConnectionBanner.tsx
1309
- import { useEffect as useEffect4, useRef as useRef4, useState as useState4 } from "react";
1310
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1694
+ import { useEffect as useEffect5, useRef as useRef5, useState as useState5 } from "react";
1695
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1311
1696
  var CONNECTION_NOTICE_DELAY_MS = 3e3;
1312
1697
  var CONNECTION_ERROR_DELAY_MS = 15e3;
1313
1698
  function useConnectionNoticePhase(connected) {
1314
- const [phase, setPhase] = useState4("hidden");
1315
- const connectedRef = useRef4(connected);
1316
- const timersRef = useRef4([]);
1699
+ const [phase, setPhase] = useState5("hidden");
1700
+ const connectedRef = useRef5(connected);
1701
+ const timersRef = useRef5([]);
1317
1702
  connectedRef.current = connected;
1318
- useEffect4(() => {
1703
+ useEffect5(() => {
1319
1704
  const clearTimers = () => {
1320
1705
  for (const timer of timersRef.current) clearTimeout(timer);
1321
1706
  timersRef.current = [];
@@ -1355,14 +1740,14 @@ function useConnectionNoticePhase(connected) {
1355
1740
  return phase;
1356
1741
  }
1357
1742
  function ConnectionBanner({ connection, className }) {
1358
- const hasConnectedRef = useRef4(connection === "connected" || connection === "reconnecting");
1743
+ const hasConnectedRef = useRef5(connection === "connected" || connection === "reconnecting");
1359
1744
  if (connection === "connected") hasConnectedRef.current = true;
1360
1745
  const connected = connection === "connected";
1361
1746
  const phase = useConnectionNoticePhase(connected);
1362
1747
  if (connected || phase === "hidden") return null;
1363
1748
  const recovering = phase === "recovering";
1364
1749
  const firstConnection = !hasConnectedRef.current;
1365
- return /* @__PURE__ */ jsx5("div", { className: cn("blade-chat-banner bg-[hsl(var(--background))] px-5 pt-3", className), children: /* @__PURE__ */ jsxs4(
1750
+ return /* @__PURE__ */ jsx6("div", { className: cn("blade-chat-banner bg-[hsl(var(--background))] px-5 pt-3", className), children: /* @__PURE__ */ jsxs5(
1366
1751
  "div",
1367
1752
  {
1368
1753
  className: cn(
@@ -1370,10 +1755,10 @@ function ConnectionBanner({ connection, className }) {
1370
1755
  recovering ? "border-amber-500/25 bg-amber-500/10 text-amber-100" : "border-rose-500/25 bg-rose-500/10 text-rose-100"
1371
1756
  ),
1372
1757
  children: [
1373
- /* @__PURE__ */ jsx5("span", { className: "mt-0.5 shrink-0", children: recovering ? /* @__PURE__ */ jsx5(LoaderCircle, { size: 14, className: "animate-spin" }) : /* @__PURE__ */ jsx5(TriangleAlert, { size: 14 }) }),
1374
- /* @__PURE__ */ jsxs4("div", { className: "min-w-0", children: [
1375
- /* @__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" }),
1376
- /* @__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" })
1758
+ /* @__PURE__ */ jsx6("span", { className: "mt-0.5 shrink-0", children: recovering ? /* @__PURE__ */ jsx6(LoaderCircle, { size: 14, className: "animate-spin" }) : /* @__PURE__ */ jsx6(TriangleAlert, { size: 14 }) }),
1759
+ /* @__PURE__ */ jsxs5("div", { className: "min-w-0", children: [
1760
+ /* @__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" }),
1761
+ /* @__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" })
1377
1762
  ] })
1378
1763
  ]
1379
1764
  }
@@ -1382,10 +1767,10 @@ function ConnectionBanner({ connection, className }) {
1382
1767
 
1383
1768
  // src/components/MessageList.tsx
1384
1769
  import { isHiddenInternalMessage } from "@blade-hq/agent-client";
1385
- import { useCallback as useCallback7, useEffect as useEffect10, useMemo as useMemo7, useRef as useRef11, useState as useState12 } from "react";
1770
+ import { useCallback as useCallback7, useEffect as useEffect11, useMemo as useMemo7, useRef as useRef12, useState as useState13 } from "react";
1386
1771
 
1387
1772
  // ../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/useStickToBottom.js
1388
- import { useCallback as useCallback4, useMemo as useMemo3, useRef as useRef5, useState as useState5 } from "react";
1773
+ import { useCallback as useCallback4, useMemo as useMemo3, useRef as useRef6, useState as useState6 } from "react";
1389
1774
  var DEFAULT_SPRING_ANIMATION = {
1390
1775
  /**
1391
1776
  * A value from 0 to 1, on how much to damp the animation.
@@ -1422,10 +1807,10 @@ globalThis.document?.addEventListener("click", () => {
1422
1807
  mouseDown = false;
1423
1808
  });
1424
1809
  var useStickToBottom = (options = {}) => {
1425
- const [escapedFromLock, updateEscapedFromLock] = useState5(false);
1426
- const [isAtBottom, updateIsAtBottom] = useState5(options.initial !== false);
1427
- const [isNearBottom, setIsNearBottom] = useState5(false);
1428
- const optionsRef = useRef5(null);
1810
+ const [escapedFromLock, updateEscapedFromLock] = useState6(false);
1811
+ const [isAtBottom, updateIsAtBottom] = useState6(options.initial !== false);
1812
+ const [isNearBottom, setIsNearBottom] = useState6(false);
1813
+ const optionsRef = useRef6(null);
1429
1814
  optionsRef.current = options;
1430
1815
  const isSelecting = useCallback4(() => {
1431
1816
  if (!mouseDown) {
@@ -1729,11 +2114,11 @@ function mergeAnimations(...animations) {
1729
2114
 
1730
2115
  // ../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/StickToBottom.js
1731
2116
  import * as React from "react";
1732
- import { createContext as createContext2, useContext as useContext2, useEffect as useEffect5, useImperativeHandle, useLayoutEffect, useMemo as useMemo4, useRef as useRef6 } from "react";
2117
+ import { createContext as createContext2, useContext as useContext2, useEffect as useEffect6, useImperativeHandle, useLayoutEffect, useMemo as useMemo4, useRef as useRef7 } from "react";
1733
2118
  var StickToBottomContext = createContext2(null);
1734
- var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect5;
2119
+ var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect6;
1735
2120
  function StickToBottom({ instance, children, resize, initial, mass, damping, stiffness, targetScrollTop: currentTargetScrollTop, contextRef, ...props }) {
1736
- const customTargetScrollTop = useRef6(null);
2121
+ const customTargetScrollTop = useRef7(null);
1737
2122
  const targetScrollTop = React.useCallback((target, elements) => {
1738
2123
  const get = context?.targetScrollTop ?? currentTargetScrollTop;
1739
2124
  return get?.(target, elements) ?? target;
@@ -1815,153 +2200,11 @@ import {
1815
2200
  getTextContent,
1816
2201
  normalizeMessageContent
1817
2202
  } from "@blade-hq/agent-client";
1818
- import { useEffect as useEffect8, useRef as useRef9, useState as useState10 } from "react";
1819
-
1820
- // src/components/AgentLoopBlock.tsx
1821
- import { useState as useState6 } from "react";
1822
-
1823
- // src/components/display-utils.ts
1824
- var TOOL_NAME_ALIASES = {
1825
- agent: "Agent",
1826
- ask_user_question: "AskUserQuestion",
1827
- bash: "Bash",
1828
- bg_bash: "BgBash",
1829
- edit: "Edit",
1830
- exit_plan_mode: "ExitPlanMode",
1831
- file_edit: "Edit",
1832
- file_read: "Read",
1833
- file_write: "Write",
1834
- finish_task: "FinishTask",
1835
- glob: "Glob",
1836
- grep: "Grep",
1837
- kb_search: "KbSearch",
1838
- ls: "Ls",
1839
- multi_edit: "MultiEdit",
1840
- read: "Read",
1841
- read_skill: "ReadSkill",
1842
- web_fetch: "WebFetch",
1843
- web_search: "WebSearch",
1844
- write: "Write"
1845
- };
1846
- var TOOL_DISPLAY_LABELS = {
1847
- Bash: "\u6267\u884C\u547D\u4EE4",
1848
- BgBash: "\u540E\u53F0\u6267\u884C\u547D\u4EE4",
1849
- Read: "\u8BFB\u53D6\u6587\u4EF6",
1850
- Write: "\u5199\u5165\u6587\u4EF6",
1851
- Edit: "\u7F16\u8F91\u6587\u4EF6",
1852
- MultiEdit: "\u7F16\u8F91\u6587\u4EF6",
1853
- Ls: "\u5217\u51FA\u76EE\u5F55",
1854
- Glob: "\u5339\u914D\u6587\u4EF6",
1855
- Grep: "\u641C\u7D22\u6587\u672C",
1856
- KbSearch: "\u68C0\u7D22\u77E5\u8BC6\u5E93",
1857
- WebSearch: "\u641C\u7D22\u7F51\u9875",
1858
- WebFetch: "\u6574\u7406\u7F51\u9875\u5185\u5BB9",
1859
- Agent: "\u6D3E\u751F\u5B50\u667A\u80FD\u4F53",
1860
- AskUserQuestion: "\u5411\u7528\u6237\u63D0\u95EE",
1861
- ReadSkill: "\u8BFB\u53D6\u6280\u80FD",
1862
- FinishTask: "\u4EFB\u52A1\u5B8C\u6210",
1863
- ExitPlanMode: "\u63D0\u4EA4\u8BA1\u5212",
1864
- ListSessions: "\u5217\u51FA\u5386\u53F2\u4F1A\u8BDD",
1865
- GetSessionHistory: "\u8BFB\u53D6\u4F1A\u8BDD\u5386\u53F2"
1866
- };
1867
- function safeParseJson(value) {
1868
- if (!value) return null;
1869
- try {
1870
- return JSON.parse(value);
1871
- } catch {
1872
- return null;
1873
- }
1874
- }
1875
- function getStringArgValue(args, key) {
1876
- const value = args?.[key];
1877
- return typeof value === "string" ? value.trim() : "";
1878
- }
1879
- var SKILL_ENTRY_FILE_NAMES = /* @__PURE__ */ new Set(["skill.md", "command.md"]);
1880
- var NON_SKILL_DIR_NAMES = /* @__PURE__ */ new Set([".", "..", ".agent", ".agents", ".claude", "skill_data", "skills"]);
1881
- function getSkillNameFromFilePath(filePath) {
1882
- if (!filePath) return null;
1883
- const segments = filePath.split(/[\\/]+/).filter(Boolean);
1884
- const fileName = segments.pop();
1885
- if (!fileName || !SKILL_ENTRY_FILE_NAMES.has(fileName.toLowerCase())) return null;
1886
- const dirName = segments.pop();
1887
- if (!dirName || NON_SKILL_DIR_NAMES.has(dirName.toLowerCase())) return null;
1888
- return dirName;
1889
- }
1890
- function formatToolName(name) {
1891
- const trimmed = name.trim();
1892
- if (!trimmed) return name;
1893
- const stripped = trimmed.split(":").pop()?.split("/").pop()?.split(".").pop()?.trim() || trimmed;
1894
- const normalized = stripped.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
1895
- return TOOL_NAME_ALIASES[normalized] ?? stripped;
1896
- }
1897
- function getToolDisplayLabel(toolCall) {
1898
- const normalized = formatToolName(toolCall.name);
1899
- const args = safeParseJson(toolCall.arguments);
1900
- const displayName = toolCall.display_name?.trim() ?? "";
1901
- const baseLabel = displayName || TOOL_DISPLAY_LABELS[normalized] || normalized;
1902
- const metaDisplayName = getStringArgValue(args, "_meta_display_name");
1903
- if (metaDisplayName) {
1904
- return metaDisplayName;
1905
- }
1906
- const description = getStringArgValue(args, "description");
1907
- if (normalized === "BgBash") {
1908
- return description ? `\u540E\u53F0\u6267\u884C\uFF1A${description}` : "\u540E\u53F0\u6267\u884C\u547D\u4EE4";
1909
- }
1910
- if (normalized === "ReadSkill") {
1911
- const skillName = getStringArgValue(args, "skill") || getStringArgValue(args, "skill_name");
1912
- return skillName ? `${baseLabel}\u300C${skillName}\u300D` : baseLabel;
1913
- }
1914
- if (normalized === "Read") {
1915
- const skillName = getSkillNameFromFilePath(
1916
- getStringArgValue(args, "file_path") || getStringArgValue(args, "path")
1917
- );
1918
- if (skillName) return `\u8BFB\u53D6\u6280\u80FD\u300C${skillName}\u300D`;
1919
- }
1920
- if (normalized === "FinishTask") {
1921
- const title = getStringArgValue(args, "title");
1922
- return title ? `${baseLabel}\uFF1A${title}` : baseLabel;
1923
- }
1924
- return description || baseLabel;
1925
- }
1926
- function getToolTone(status) {
1927
- if (status === "error" || status === "cancelled") return "red";
1928
- if (status === "awaiting_answer") return "amber";
1929
- if (status === "pending") return "blue";
1930
- return "emerald";
1931
- }
1932
- function getToolStatusLabel(status) {
1933
- if (status === "pending") return "\u8FD0\u884C\u4E2D";
1934
- if (status === "awaiting_answer") return "\u7B49\u5F85\u56DE\u7B54";
1935
- if (status === "error") return "\u9519\u8BEF";
1936
- if (status === "cancelled") return "\u5DF2\u53D6\u6D88";
1937
- return "\u5B8C\u6210";
1938
- }
1939
- function formatToolDuration(ms) {
1940
- if (ms < 1e3) return `${Math.round(ms)}ms`;
1941
- const seconds = ms / 1e3;
1942
- if (seconds < 60) return `${seconds.toFixed(1)}s`;
1943
- const minutes = Math.floor(seconds / 60);
1944
- const remainingSeconds = Math.round(seconds % 60);
1945
- return remainingSeconds > 0 ? `${minutes}m${remainingSeconds}s` : `${minutes}m`;
1946
- }
1947
- function formatToolArgs(args) {
1948
- try {
1949
- return JSON.stringify(JSON.parse(args), null, 2);
1950
- } catch {
1951
- return args;
1952
- }
1953
- }
1954
- var RESULT_PREVIEW_LIMIT = 4e3;
1955
- function formatToolResult(result) {
1956
- const text = typeof result === "string" ? result : JSON.stringify(result, null, 2);
1957
- if (text == null) return "";
1958
- if (text.length <= RESULT_PREVIEW_LIMIT) return text;
1959
- return `${text.slice(0, RESULT_PREVIEW_LIMIT)}
1960
- \u2026\uFF08\u7ED3\u679C\u8FC7\u957F\uFF0C\u5DF2\u622A\u65AD\uFF09`;
1961
- }
2203
+ import { useEffect as useEffect9, useRef as useRef10, useState as useState11 } from "react";
1962
2204
 
1963
2205
  // src/components/AgentLoopBlock.tsx
1964
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
2206
+ import { useState as useState7 } from "react";
2207
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1965
2208
  function parseAgentDescription(argumentsJson) {
1966
2209
  try {
1967
2210
  const parsed = JSON.parse(argumentsJson);
@@ -1971,7 +2214,7 @@ function parseAgentDescription(argumentsJson) {
1971
2214
  }
1972
2215
  }
1973
2216
  function AgentLoopBlock({ toolCall }) {
1974
- const [expanded, setExpanded] = useState6(false);
2217
+ const [expanded, setExpanded] = useState7(false);
1975
2218
  const description = parseAgentDescription(toolCall.arguments);
1976
2219
  const running = toolCall.status === "pending" || toolCall.status === "awaiting_answer";
1977
2220
  const failed = toolCall.status === "error" || toolCall.status === "cancelled";
@@ -1980,8 +2223,8 @@ function AgentLoopBlock({ toolCall }) {
1980
2223
  "size-3.5 shrink-0",
1981
2224
  failed ? "text-red-500" : "text-[hsl(var(--muted-foreground))]"
1982
2225
  );
1983
- return /* @__PURE__ */ jsxs5("div", { className: "blade-chat-agent-loop text-xs leading-[22px]", children: [
1984
- /* @__PURE__ */ jsxs5(
2226
+ return /* @__PURE__ */ jsxs6("div", { className: "blade-chat-agent-loop text-xs leading-[22px]", children: [
2227
+ /* @__PURE__ */ jsxs6(
1985
2228
  "button",
1986
2229
  {
1987
2230
  type: "button",
@@ -1996,12 +2239,12 @@ function AgentLoopBlock({ toolCall }) {
1996
2239
  ),
1997
2240
  title: `\u5B50\u4EFB\u52A1\uFF1A${description}`,
1998
2241
  children: [
1999
- 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" }),
2000
- /* @__PURE__ */ jsxs5("span", { className: "min-w-0 truncate", children: [
2242
+ 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" }),
2243
+ /* @__PURE__ */ jsxs6("span", { className: "min-w-0 truncate", children: [
2001
2244
  "\u5B50\u4EFB\u52A1\uFF1A",
2002
2245
  description
2003
2246
  ] }),
2004
- hasResult ? /* @__PURE__ */ jsx6(
2247
+ hasResult ? /* @__PURE__ */ jsx7(
2005
2248
  ChevronRight,
2006
2249
  {
2007
2250
  size: 14,
@@ -2015,24 +2258,36 @@ function AgentLoopBlock({ toolCall }) {
2015
2258
  ]
2016
2259
  }
2017
2260
  ),
2018
- 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
2261
+ 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
2019
2262
  ] });
2020
2263
  }
2021
2264
 
2022
2265
  // src/components/MarkdownContent.tsx
2023
2266
  import {
2024
- useEffect as useEffect6,
2267
+ useEffect as useEffect7,
2025
2268
  useMemo as useMemo5,
2026
- useRef as useRef7,
2027
- useState as useState7
2269
+ useRef as useRef8,
2270
+ useState as useState8
2028
2271
  } from "react";
2029
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
2272
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
2030
2273
  var SYSTEM_REMINDER_RE = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
2274
+ function normalizeAdjacentUrlFormatting(value) {
2275
+ const protectedSegments = [];
2276
+ const protectedValue = value.replace(/(`{1,3}[\s\S]*?`{1,3}|\[[^\]]*\]\([^)]*\))/g, (segment) => {
2277
+ const index = protectedSegments.push(segment) - 1;
2278
+ return `blade-url-protected-${index}-marker`;
2279
+ });
2280
+ const normalized = protectedValue.replace(
2281
+ /(\*\*|__|~~|\*|_)(https?:\/\/[^\s<>]+?)\1(?=[\s。,、!?;:,.!?;:]|$)/g,
2282
+ (_, marker, url) => `${marker}[${url}](<${url}>)${marker}`
2283
+ );
2284
+ return normalized.replace(/blade-url-protected-(\d+)-marker/g, (_, index) => protectedSegments[Number(index)]);
2285
+ }
2031
2286
  function CodeBlockPre({ children, node: _node, ...props }) {
2032
- const preRef = useRef7(null);
2033
- const [copied, setCopied] = useState7(false);
2034
- const [language, setLanguage] = useState7("");
2035
- useEffect6(() => {
2287
+ const preRef = useRef8(null);
2288
+ const [copied, setCopied] = useState8(false);
2289
+ const [language, setLanguage] = useState8("");
2290
+ useEffect7(() => {
2036
2291
  const codeEl = preRef.current?.querySelector("code");
2037
2292
  setLanguage(codeEl?.className.match(/language-(\S+)/)?.[1] ?? "");
2038
2293
  }, []);
@@ -2043,10 +2298,10 @@ function CodeBlockPre({ children, node: _node, ...props }) {
2043
2298
  setTimeout(() => setCopied(false), 2e3);
2044
2299
  }
2045
2300
  };
2046
- return /* @__PURE__ */ jsxs6("div", { className: "blade-chat-codeblock not-prose my-3 overflow-hidden rounded-xl border border-[hsl(var(--border))]", children: [
2047
- /* @__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: [
2048
- /* @__PURE__ */ jsx7("span", { className: "font-mono text-[12px] text-[hsl(var(--muted-foreground))]", children: language || "code" }),
2049
- /* @__PURE__ */ jsxs6(
2301
+ return /* @__PURE__ */ jsxs7("div", { className: "blade-chat-codeblock not-prose my-3 overflow-hidden rounded-xl border border-[hsl(var(--border))]", children: [
2302
+ /* @__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: [
2303
+ /* @__PURE__ */ jsx8("span", { className: "font-mono text-[12px] text-[hsl(var(--muted-foreground))]", children: language || "code" }),
2304
+ /* @__PURE__ */ jsxs7(
2050
2305
  "button",
2051
2306
  {
2052
2307
  type: "button",
@@ -2056,13 +2311,13 @@ function CodeBlockPre({ children, node: _node, ...props }) {
2056
2311
  copied ? "text-[hsl(var(--primary))]" : "text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--foreground))]"
2057
2312
  ),
2058
2313
  children: [
2059
- copied ? /* @__PURE__ */ jsx7(Check, { size: 12 }) : /* @__PURE__ */ jsx7(Copy, { size: 12 }),
2060
- /* @__PURE__ */ jsx7("span", { children: copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236" })
2314
+ copied ? /* @__PURE__ */ jsx8(Check, { size: 12 }) : /* @__PURE__ */ jsx8(Copy, { size: 12 }),
2315
+ /* @__PURE__ */ jsx8("span", { children: copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236" })
2061
2316
  ]
2062
2317
  }
2063
2318
  )
2064
2319
  ] }),
2065
- /* @__PURE__ */ jsx7(
2320
+ /* @__PURE__ */ jsx8(
2066
2321
  "pre",
2067
2322
  {
2068
2323
  ref: preRef,
@@ -2074,7 +2329,7 @@ function CodeBlockPre({ children, node: _node, ...props }) {
2074
2329
  ] });
2075
2330
  }
2076
2331
  function ExternalAnchor({ node: _node, children, ...props }) {
2077
- return /* @__PURE__ */ jsx7("a", { ...props, target: "_blank", rel: "noopener noreferrer", children });
2332
+ return /* @__PURE__ */ jsx8("a", { ...props, target: "_blank", rel: "noopener noreferrer", children });
2078
2333
  }
2079
2334
  var MARKDOWN_COMPONENTS = {
2080
2335
  pre: CodeBlockPre,
@@ -2082,9 +2337,9 @@ var MARKDOWN_COMPONENTS = {
2082
2337
  };
2083
2338
  function MarkdownContent({ children, className, mode, sessionId }) {
2084
2339
  const resolvedChildren = useMemo5(() => {
2085
- return children.replace(SYSTEM_REMINDER_RE, "");
2340
+ return normalizeAdjacentUrlFormatting(children.replace(SYSTEM_REMINDER_RE, ""));
2086
2341
  }, [children]);
2087
- return /* @__PURE__ */ jsx7(
2342
+ return /* @__PURE__ */ jsx8(
2088
2343
  _r,
2089
2344
  {
2090
2345
  className: cn("blade-chat-markdown break-words", className),
@@ -2097,17 +2352,17 @@ function MarkdownContent({ children, className, mode, sessionId }) {
2097
2352
  }
2098
2353
 
2099
2354
  // src/components/Shimmer.tsx
2100
- import { jsx as jsx8 } from "react/jsx-runtime";
2355
+ import { jsx as jsx9 } from "react/jsx-runtime";
2101
2356
  function Shimmer({ children = "\u6B63\u5728\u601D\u8003...", className }) {
2102
- return /* @__PURE__ */ jsx8("span", { className: cn("blade-shimmer-text text-sm font-medium", className), children });
2357
+ return /* @__PURE__ */ jsx9("span", { className: cn("blade-shimmer-text text-sm font-medium", className), children });
2103
2358
  }
2104
2359
 
2105
2360
  // src/components/ToolCallBlock.tsx
2106
- import { useState as useState9 } from "react";
2361
+ import { useState as useState10 } from "react";
2107
2362
 
2108
2363
  // src/components/AskUserQuestionBlock.tsx
2109
- import { useEffect as useEffect7, useMemo as useMemo6, useRef as useRef8, useState as useState8 } from "react";
2110
- import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
2364
+ import { useEffect as useEffect8, useMemo as useMemo6, useRef as useRef9, useState as useState9 } from "react";
2365
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
2111
2366
  var CUSTOM_TEXTAREA_MAX_HEIGHT = 160;
2112
2367
  function resizeCustomTextarea(textarea) {
2113
2368
  textarea.style.height = "auto";
@@ -2115,12 +2370,12 @@ function resizeCustomTextarea(textarea) {
2115
2370
  textarea.style.overflowY = textarea.scrollHeight > CUSTOM_TEXTAREA_MAX_HEIGHT ? "auto" : "hidden";
2116
2371
  }
2117
2372
  function useAutoResizeTextarea(value) {
2118
- const textareaRef = useRef8(null);
2119
- useEffect7(() => {
2373
+ const textareaRef = useRef9(null);
2374
+ useEffect8(() => {
2120
2375
  const textarea = textareaRef.current;
2121
2376
  if (textarea?.value === value) resizeCustomTextarea(textarea);
2122
2377
  }, [value]);
2123
- useEffect7(() => {
2378
+ useEffect8(() => {
2124
2379
  const textarea = textareaRef.current;
2125
2380
  if (!textarea || typeof ResizeObserver === "undefined") return;
2126
2381
  let previousWidth = textarea.clientWidth;
@@ -2145,12 +2400,12 @@ function AskUserQuestionBlock({
2145
2400
  answerData,
2146
2401
  onAnswer
2147
2402
  }) {
2148
- const [selections, setSelections] = useState8(/* @__PURE__ */ new Map());
2149
- const [customTexts, setCustomTexts] = useState8(/* @__PURE__ */ new Map());
2150
- const [usingCustom, setUsingCustom] = useState8(/* @__PURE__ */ new Set());
2151
- const [note, setNote] = useState8("");
2152
- const [submitted, setSubmitted] = useState8(false);
2153
- useEffect7(() => {
2403
+ const [selections, setSelections] = useState9(/* @__PURE__ */ new Map());
2404
+ const [customTexts, setCustomTexts] = useState9(/* @__PURE__ */ new Map());
2405
+ const [usingCustom, setUsingCustom] = useState9(/* @__PURE__ */ new Set());
2406
+ const [note, setNote] = useState9("");
2407
+ const [submitted, setSubmitted] = useState9(false);
2408
+ useEffect8(() => {
2154
2409
  if (sessionStatus === "failed" || sessionStatus === "interrupted") {
2155
2410
  setSubmitted(false);
2156
2411
  }
@@ -2248,7 +2503,7 @@ ${parts.join("\n")}`,
2248
2503
  setSubmitted(true);
2249
2504
  onAnswer(text, toolCallId, nextAnswerData);
2250
2505
  };
2251
- return /* @__PURE__ */ jsxs7(
2506
+ return /* @__PURE__ */ jsxs8(
2252
2507
  "div",
2253
2508
  {
2254
2509
  className: cn(
@@ -2256,12 +2511,12 @@ ${parts.join("\n")}`,
2256
2511
  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"
2257
2512
  ),
2258
2513
  children: [
2259
- 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: [
2514
+ 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: [
2260
2515
  "\u5B50\u667A\u80FD\u4F53\u300C",
2261
2516
  data.source_loop.description,
2262
2517
  "\u300D\u5728\u7B49\u5F85\u4F60\u7684\u56DE\u7B54"
2263
2518
  ] }),
2264
- data.questions.map((q, qIdx) => /* @__PURE__ */ jsx9(
2519
+ data.questions.map((q, qIdx) => /* @__PURE__ */ jsx10(
2265
2520
  QuestionCard,
2266
2521
  {
2267
2522
  question: q,
@@ -2276,7 +2531,7 @@ ${parts.join("\n")}`,
2276
2531
  },
2277
2532
  q.question
2278
2533
  )),
2279
- /* @__PURE__ */ jsx9(
2534
+ /* @__PURE__ */ jsx10(
2280
2535
  NoteField,
2281
2536
  {
2282
2537
  answered,
@@ -2285,7 +2540,7 @@ ${parts.join("\n")}`,
2285
2540
  onChange: setNote
2286
2541
  }
2287
2542
  ),
2288
- !answered && !submitted && onAnswer && /* @__PURE__ */ jsx9(
2543
+ !answered && !submitted && onAnswer && /* @__PURE__ */ jsx10(
2289
2544
  "button",
2290
2545
  {
2291
2546
  type: "button",
@@ -2295,14 +2550,14 @@ ${parts.join("\n")}`,
2295
2550
  children: allAnswered ? "\u786E\u8BA4" : "\u8BF7\u5148\u9009\u62E9\u4E00\u4E2A\u9009\u9879"
2296
2551
  }
2297
2552
  ),
2298
- submitted && !answered && /* @__PURE__ */ jsxs7(
2553
+ submitted && !answered && /* @__PURE__ */ jsxs8(
2299
2554
  "button",
2300
2555
  {
2301
2556
  type: "button",
2302
2557
  disabled: true,
2303
2558
  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",
2304
2559
  children: [
2305
- /* @__PURE__ */ jsx9(LoaderCircle, { size: 14, className: "animate-spin" }),
2560
+ /* @__PURE__ */ jsx10(LoaderCircle, { size: 14, className: "animate-spin" }),
2306
2561
  "\u786E\u8BA4\u4E2D"
2307
2562
  ]
2308
2563
  }
@@ -2324,30 +2579,30 @@ function QuestionCard({
2324
2579
  }) {
2325
2580
  const multi = question.multiSelect ?? false;
2326
2581
  const customTextareaRef = useAutoResizeTextarea(customText);
2327
- return /* @__PURE__ */ jsxs7("div", { children: [
2328
- /* @__PURE__ */ jsxs7("div", { className: cn("flex items-start gap-2", answered ? "mb-2" : "mb-3"), children: [
2329
- /* @__PURE__ */ jsx9(
2582
+ return /* @__PURE__ */ jsxs8("div", { children: [
2583
+ /* @__PURE__ */ jsxs8("div", { className: cn("flex items-start gap-2", answered ? "mb-2" : "mb-3"), children: [
2584
+ /* @__PURE__ */ jsx10(
2330
2585
  MessageSquareMore,
2331
2586
  {
2332
2587
  size: answered ? 12 : 13,
2333
2588
  className: "mt-0.5 shrink-0 text-[hsl(var(--primary))]"
2334
2589
  }
2335
2590
  ),
2336
- /* @__PURE__ */ jsx9(
2591
+ /* @__PURE__ */ jsx10(
2337
2592
  "div",
2338
2593
  {
2339
2594
  className: cn(
2340
2595
  "min-w-0 flex-1 font-medium text-[hsl(var(--foreground))]",
2341
2596
  answered ? "text-xs" : "text-sm"
2342
2597
  ),
2343
- children: /* @__PURE__ */ jsx9(MarkdownContent, { className: "blade-chat-prose", children: question.question })
2598
+ children: /* @__PURE__ */ jsx10(MarkdownContent, { className: "blade-chat-prose", children: question.question })
2344
2599
  }
2345
2600
  )
2346
2601
  ] }),
2347
- /* @__PURE__ */ jsxs7("div", { className: cn("flex flex-col pl-5", answered ? "gap-1" : "gap-1.5"), children: [
2602
+ /* @__PURE__ */ jsxs8("div", { className: cn("flex flex-col pl-5", answered ? "gap-1" : "gap-1.5"), children: [
2348
2603
  question.options.map((opt, optIdx) => {
2349
2604
  const isSel = selected.has(optIdx);
2350
- return /* @__PURE__ */ jsxs7(
2605
+ return /* @__PURE__ */ jsxs8(
2351
2606
  "button",
2352
2607
  {
2353
2608
  type: "button",
@@ -2361,14 +2616,14 @@ function QuestionCard({
2361
2616
  answered && "cursor-default opacity-70"
2362
2617
  ),
2363
2618
  children: [
2364
- multi && /* @__PURE__ */ jsx9(
2619
+ multi && /* @__PURE__ */ jsx10(
2365
2620
  "div",
2366
2621
  {
2367
2622
  className: cn(
2368
2623
  "mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border transition-colors",
2369
2624
  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))]"
2370
2625
  ),
2371
- children: isSel && /* @__PURE__ */ jsx9(
2626
+ children: isSel && /* @__PURE__ */ jsx10(
2372
2627
  Check,
2373
2628
  {
2374
2629
  size: 9,
@@ -2377,9 +2632,9 @@ function QuestionCard({
2377
2632
  )
2378
2633
  }
2379
2634
  ),
2380
- /* @__PURE__ */ jsxs7("div", { className: "min-w-0", children: [
2381
- /* @__PURE__ */ jsx9("div", { className: cn("font-medium", answered ? "text-xs" : "text-[13px]"), children: opt.label }),
2382
- opt.description && /* @__PURE__ */ jsx9(
2635
+ /* @__PURE__ */ jsxs8("div", { className: "min-w-0", children: [
2636
+ /* @__PURE__ */ jsx10("div", { className: cn("font-medium", answered ? "text-xs" : "text-[13px]"), children: opt.label }),
2637
+ opt.description && /* @__PURE__ */ jsx10(
2383
2638
  "div",
2384
2639
  {
2385
2640
  className: cn(
@@ -2396,7 +2651,7 @@ function QuestionCard({
2396
2651
  opt.label
2397
2652
  );
2398
2653
  }),
2399
- answered && !isCustom ? null : /* @__PURE__ */ jsxs7(
2654
+ answered && !isCustom ? null : /* @__PURE__ */ jsxs8(
2400
2655
  "div",
2401
2656
  {
2402
2657
  className: cn(
@@ -2406,8 +2661,8 @@ function QuestionCard({
2406
2661
  answered && "cursor-default opacity-70"
2407
2662
  ),
2408
2663
  children: [
2409
- /* @__PURE__ */ jsx9("span", { className: "shrink-0 pt-1 text-xs text-[hsl(var(--muted-foreground))]", children: "\u5176\u4ED6\uFF1A" }),
2410
- /* @__PURE__ */ jsx9(
2664
+ /* @__PURE__ */ jsx10("span", { className: "shrink-0 pt-1 text-xs text-[hsl(var(--muted-foreground))]", children: "\u5176\u4ED6\uFF1A" }),
2665
+ /* @__PURE__ */ jsx10(
2411
2666
  "textarea",
2412
2667
  {
2413
2668
  ref: customTextareaRef,
@@ -2439,7 +2694,7 @@ function NoteField({
2439
2694
  const textareaRef = useAutoResizeTextarea(note);
2440
2695
  const readOnly = answered || submitted;
2441
2696
  if (answered && !note.trim()) return null;
2442
- return /* @__PURE__ */ jsxs7(
2697
+ return /* @__PURE__ */ jsxs8(
2443
2698
  "label",
2444
2699
  {
2445
2700
  className: cn(
@@ -2449,8 +2704,8 @@ function NoteField({
2449
2704
  readOnly && "cursor-default opacity-70"
2450
2705
  ),
2451
2706
  children: [
2452
- /* @__PURE__ */ jsx9("span", { className: "mb-1.5 block text-xs text-[hsl(var(--muted-foreground))]", children: "\u8865\u5145\u8BF4\u660E\uFF08\u53EF\u9009\uFF09" }),
2453
- /* @__PURE__ */ jsx9(
2707
+ /* @__PURE__ */ jsx10("span", { className: "mb-1.5 block text-xs text-[hsl(var(--muted-foreground))]", children: "\u8865\u5145\u8BF4\u660E\uFF08\u53EF\u9009\uFF09" }),
2708
+ /* @__PURE__ */ jsx10(
2454
2709
  "textarea",
2455
2710
  {
2456
2711
  ref: textareaRef,
@@ -2539,7 +2794,7 @@ function normalizeOptionItem(value) {
2539
2794
  }
2540
2795
 
2541
2796
  // src/components/ToolCallBlock.tsx
2542
- import { Fragment, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
2797
+ import { Fragment, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
2543
2798
  function resolveAskQuestionState({
2544
2799
  toolStatus,
2545
2800
  hasAnswerData,
@@ -2561,12 +2816,12 @@ function ToolCallBlock({
2561
2816
  isActiveQuestion,
2562
2817
  renderer
2563
2818
  }) {
2564
- const [expanded, setExpanded] = useState9(false);
2819
+ const [expanded, setExpanded] = useState10(false);
2565
2820
  const normalizedName = formatToolName(toolCall.name);
2566
2821
  if (renderer) {
2567
2822
  const custom = renderer(toolCall);
2568
2823
  if (custom !== null && custom !== void 0) {
2569
- return /* @__PURE__ */ jsx10(Fragment, { children: custom });
2824
+ return /* @__PURE__ */ jsx11(Fragment, { children: custom });
2570
2825
  }
2571
2826
  }
2572
2827
  if (normalizedName === "AskUserQuestion") {
@@ -2579,7 +2834,7 @@ function ToolCallBlock({
2579
2834
  });
2580
2835
  const canAnswer = questionState.awaitingAnswer && Boolean(onAnswer);
2581
2836
  if (askData) {
2582
- return /* @__PURE__ */ jsx10(
2837
+ return /* @__PURE__ */ jsx11(
2583
2838
  AskUserQuestionBlock,
2584
2839
  {
2585
2840
  data: askData,
@@ -2592,31 +2847,31 @@ function ToolCallBlock({
2592
2847
  );
2593
2848
  }
2594
2849
  if (toolCall.status === "pending") {
2595
- 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: [
2596
- /* @__PURE__ */ jsx10(LoaderCircle, { size: 14, className: "animate-spin" }),
2597
- /* @__PURE__ */ jsx10("span", { children: "\u6B63\u5728\u51C6\u5907\u95EE\u9898\u2026" })
2850
+ 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: [
2851
+ /* @__PURE__ */ jsx11(LoaderCircle, { size: 14, className: "animate-spin" }),
2852
+ /* @__PURE__ */ jsx11("span", { children: "\u6B63\u5728\u51C6\u5907\u95EE\u9898\u2026" })
2598
2853
  ] });
2599
2854
  }
2600
2855
  const errorDetail = parseAskUserQuestionError(
2601
2856
  typeof toolCall.result === "string" ? toolCall.result : null
2602
2857
  );
2603
- 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: [
2604
- /* @__PURE__ */ jsx10("div", { className: "font-semibold", children: "\u9009\u62E9\u9898\u5185\u5BB9\u6682\u65F6\u65E0\u6CD5\u663E\u793A" }),
2605
- /* @__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" }),
2606
- errorDetail?.detail ? /* @__PURE__ */ jsxs8("details", { className: "mt-2 text-xs text-[hsl(var(--muted-foreground))]", children: [
2607
- /* @__PURE__ */ jsx10("summary", { className: "cursor-pointer", children: "\u67E5\u770B\u5177\u4F53\u539F\u56E0" }),
2608
- /* @__PURE__ */ jsx10("div", { className: "mt-1 break-words font-mono", children: errorDetail.detail })
2858
+ 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: [
2859
+ /* @__PURE__ */ jsx11("div", { className: "font-semibold", children: "\u9009\u62E9\u9898\u5185\u5BB9\u6682\u65F6\u65E0\u6CD5\u663E\u793A" }),
2860
+ /* @__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" }),
2861
+ errorDetail?.detail ? /* @__PURE__ */ jsxs9("details", { className: "mt-2 text-xs text-[hsl(var(--muted-foreground))]", children: [
2862
+ /* @__PURE__ */ jsx11("summary", { className: "cursor-pointer", children: "\u67E5\u770B\u5177\u4F53\u539F\u56E0" }),
2863
+ /* @__PURE__ */ jsx11("div", { className: "mt-1 break-words font-mono", children: errorDetail.detail })
2609
2864
  ] }) : null
2610
2865
  ] });
2611
2866
  }
2612
2867
  const tone = getToolTone(toolCall.status);
2613
2868
  const displayName = getToolDisplayLabel(toolCall);
2614
2869
  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))]";
2615
- 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 });
2870
+ 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 });
2616
2871
  const statusTextClass = tone === "red" ? "text-[hsl(var(--muted-foreground))]" : tone === "amber" ? "text-amber-300" : tone === "blue" ? "text-blue-300" : "text-[hsl(var(--primary))]";
2617
- return /* @__PURE__ */ jsxs8("div", { className: "blade-chat-tool ml-4 text-xs", children: [
2618
- /* @__PURE__ */ jsxs8("div", { className: cn("border-l-[3px] flex items-center gap-2 px-3 py-2", toneClass), children: [
2619
- /* @__PURE__ */ jsxs8(
2872
+ return /* @__PURE__ */ jsxs9("div", { className: "blade-chat-tool ml-4 text-xs", children: [
2873
+ /* @__PURE__ */ jsxs9("div", { className: cn("border-l-[3px] flex items-center gap-2 px-3 py-2", toneClass), children: [
2874
+ /* @__PURE__ */ jsxs9(
2620
2875
  "button",
2621
2876
  {
2622
2877
  type: "button",
@@ -2624,7 +2879,7 @@ function ToolCallBlock({
2624
2879
  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",
2625
2880
  "aria-expanded": expanded,
2626
2881
  children: [
2627
- /* @__PURE__ */ jsx10(
2882
+ /* @__PURE__ */ jsx11(
2628
2883
  ChevronRight,
2629
2884
  {
2630
2885
  size: 11,
@@ -2634,24 +2889,24 @@ function ToolCallBlock({
2634
2889
  )
2635
2890
  }
2636
2891
  ),
2637
- /* @__PURE__ */ jsxs8("span", { className: cn("flex shrink-0 items-center gap-1 text-[10px]", statusTextClass), children: [
2892
+ /* @__PURE__ */ jsxs9("span", { className: cn("flex shrink-0 items-center gap-1 text-[10px]", statusTextClass), children: [
2638
2893
  statusIcon,
2639
- /* @__PURE__ */ jsx10("span", { children: getToolStatusLabel(toolCall.status) })
2894
+ /* @__PURE__ */ jsx11("span", { children: getToolStatusLabel(toolCall.status) })
2640
2895
  ] }),
2641
- /* @__PURE__ */ jsx10("span", { className: "min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]", children: displayName })
2896
+ /* @__PURE__ */ jsx11("span", { className: "min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]", children: displayName })
2642
2897
  ]
2643
2898
  }
2644
2899
  ),
2645
- 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) })
2900
+ 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) })
2646
2901
  ] }),
2647
- expanded && /* @__PURE__ */ jsxs8("div", { className: "blade-chat-tool-detail ml-4 mt-1 rounded-xl bg-[hsl(var(--card))] px-3 py-3", children: [
2648
- /* @__PURE__ */ jsx10("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u5DE5\u5177" }),
2649
- /* @__PURE__ */ jsx10("div", { className: "mb-3 font-mono text-[11px] text-[hsl(var(--foreground))]", children: normalizedName }),
2650
- /* @__PURE__ */ jsx10("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u53C2\u6570" }),
2651
- /* @__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) }),
2652
- toolCall.result != null && /* @__PURE__ */ jsxs8(Fragment, { children: [
2653
- /* @__PURE__ */ jsx10("div", { className: "mb-1 mt-3 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u7ED3\u679C" }),
2654
- /* @__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) })
2902
+ expanded && /* @__PURE__ */ jsxs9("div", { className: "blade-chat-tool-detail ml-4 mt-1 rounded-xl bg-[hsl(var(--card))] px-3 py-3", children: [
2903
+ /* @__PURE__ */ jsx11("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u5DE5\u5177" }),
2904
+ /* @__PURE__ */ jsx11("div", { className: "mb-3 font-mono text-[11px] text-[hsl(var(--foreground))]", children: normalizedName }),
2905
+ /* @__PURE__ */ jsx11("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u53C2\u6570" }),
2906
+ /* @__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) }),
2907
+ toolCall.result != null && /* @__PURE__ */ jsxs9(Fragment, { children: [
2908
+ /* @__PURE__ */ jsx11("div", { className: "mb-1 mt-3 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u7ED3\u679C" }),
2909
+ /* @__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) })
2655
2910
  ] })
2656
2911
  ] })
2657
2912
  ] });
@@ -2669,12 +2924,12 @@ function buildAskUserPayload(argumentsJson) {
2669
2924
  }
2670
2925
 
2671
2926
  // src/components/AssistantTurnBlock.tsx
2672
- import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
2927
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
2673
2928
  function ThinkingBlock({ reasoning, isStreaming }) {
2674
- const [open, setOpen] = useState10(false);
2929
+ const [open, setOpen] = useState11(false);
2675
2930
  if (!isStreaming) return null;
2676
- return /* @__PURE__ */ jsxs9("div", { className: "blade-chat-thinking text-xs", children: [
2677
- /* @__PURE__ */ jsxs9(
2931
+ return /* @__PURE__ */ jsxs10("div", { className: "blade-chat-thinking text-xs", children: [
2932
+ /* @__PURE__ */ jsxs10(
2678
2933
  "button",
2679
2934
  {
2680
2935
  type: "button",
@@ -2682,8 +2937,8 @@ function ThinkingBlock({ reasoning, isStreaming }) {
2682
2937
  "aria-expanded": open,
2683
2938
  className: "group/thinking inline-flex items-center gap-1 text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))]",
2684
2939
  children: [
2685
- /* @__PURE__ */ jsx11(Shimmer, { className: "text-xs", children: "\u6B63\u5728\u601D\u8003" }),
2686
- /* @__PURE__ */ jsx11(
2940
+ /* @__PURE__ */ jsx12(Shimmer, { className: "text-xs", children: "\u6B63\u5728\u601D\u8003" }),
2941
+ /* @__PURE__ */ jsx12(
2687
2942
  ChevronRight,
2688
2943
  {
2689
2944
  size: 14,
@@ -2696,7 +2951,7 @@ function ThinkingBlock({ reasoning, isStreaming }) {
2696
2951
  ]
2697
2952
  }
2698
2953
  ),
2699
- open ? /* @__PURE__ */ jsx11("div", { className: "mt-1.5 whitespace-pre-wrap text-xs leading-[22px] text-[hsl(var(--muted-foreground))]", children: reasoning }) : null
2954
+ open ? /* @__PURE__ */ jsx12("div", { className: "mt-1.5 whitespace-pre-wrap text-xs leading-[22px] text-[hsl(var(--muted-foreground))]", children: reasoning }) : null
2700
2955
  ] });
2701
2956
  }
2702
2957
  function getMessageText(message) {
@@ -2888,14 +3143,14 @@ function ExecutionToolRow({ toolCall }) {
2888
3143
  "size-3.5 shrink-0",
2889
3144
  failed ? "text-red-500" : "text-[hsl(var(--muted-foreground))]"
2890
3145
  );
2891
- 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" });
3146
+ 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" });
2892
3147
  const rowClassName = cn(
2893
3148
  "flex min-w-0 items-center gap-1 py-1.5 text-xs leading-[22px]",
2894
3149
  failed ? "text-red-500" : "text-[hsl(var(--muted-foreground))]"
2895
3150
  );
2896
- return /* @__PURE__ */ jsxs9("div", { "data-testid": "execution-tool-intent", className: rowClassName, title: label, children: [
3151
+ return /* @__PURE__ */ jsxs10("div", { "data-testid": "execution-tool-intent", className: rowClassName, title: label, children: [
2897
3152
  icon,
2898
- /* @__PURE__ */ jsx11("span", { className: "min-w-0 truncate", children: label })
3153
+ /* @__PURE__ */ jsx12("span", { className: "min-w-0 truncate", children: label })
2899
3154
  ] });
2900
3155
  }
2901
3156
  function AssistantTurnBlock({
@@ -2905,8 +3160,15 @@ function AssistantTurnBlock({
2905
3160
  onAnswer,
2906
3161
  sessionStatus,
2907
3162
  toolCallRenderer,
3163
+ hidePlanUpdateTools = false,
2908
3164
  sessionId
2909
3165
  }) {
3166
+ const shouldHideToolCall = (message, toolCall) => {
3167
+ if (!hidePlanUpdateTools || !isPlanUpdateTool(toolCall) || parsePlanUpdate(toolCall.arguments) === null) {
3168
+ return false;
3169
+ }
3170
+ return toolCall.status === "done" || toolCall.status === "pending" && message.status === "streaming";
3171
+ };
2910
3172
  const hasInterrupted = messages.some((message) => message.status === "interrupted");
2911
3173
  const hasFailedWithoutContent = messages.some(
2912
3174
  (message) => message.status === "failed" && !hasRenderableMessageContent(message)
@@ -2916,16 +3178,16 @@ function AssistantTurnBlock({
2916
3178
  const finalOrderedParts = finalMessage ? getOrderedMessageParts(
2917
3179
  finalMessage,
2918
3180
  (finalMessage.tool_calls ?? []).filter(
2919
- (toolCall) => formatToolName(toolCall.name) !== "AskUserQuestion"
3181
+ (toolCall) => formatToolName(toolCall.name) !== "AskUserQuestion" && !shouldHideToolCall(finalMessage, toolCall)
2920
3182
  )
2921
3183
  ) : [];
2922
3184
  const hasExecutionProcess = messages.some(
2923
- (message) => message.reasoning || (message.tool_calls?.length ?? 0) > 0
3185
+ (message) => message.reasoning || (message.tool_calls ?? []).some((toolCall) => !shouldHideToolCall(message, toolCall))
2924
3186
  );
2925
3187
  const latestReasoningIndex = isStreaming ? findLatestReasoningMessageIndex(messages) : -1;
2926
3188
  const hasActionableToolCall = messages.some(
2927
3189
  (message) => message.status === "failed" || message.status === "interrupted" || (message.tool_calls ?? []).some(
2928
- (toolCall) => toolCall.status === "error" || toolCall.status === "cancelled"
3190
+ (toolCall) => !shouldHideToolCall(message, toolCall) && (toolCall.status === "error" || toolCall.status === "cancelled")
2929
3191
  )
2930
3192
  );
2931
3193
  const questionToolCalls = messages.flatMap(
@@ -2934,12 +3196,12 @@ function AssistantTurnBlock({
2934
3196
  )
2935
3197
  );
2936
3198
  const activeQuestionId = questionToolCalls.filter((toolCall) => toolCall.status === "pending").at(-1)?.id;
2937
- const [displayMode, setDisplayMode] = useState10(
3199
+ const [displayMode, setDisplayMode] = useState11(
2938
3200
  () => isStreaming || hasActionableToolCall ? "detail" : "compact"
2939
3201
  );
2940
- const userSelectedDisplayModeRef = useRef9(false);
2941
- const wasStreamingRef = useRef9(isStreaming);
2942
- useEffect8(() => {
3202
+ const userSelectedDisplayModeRef = useRef10(false);
3203
+ const wasStreamingRef = useRef10(isStreaming);
3204
+ useEffect9(() => {
2943
3205
  if (wasStreamingRef.current && !isStreaming && !userSelectedDisplayModeRef.current) {
2944
3206
  setDisplayMode(hasActionableToolCall ? "detail" : "compact");
2945
3207
  }
@@ -2947,11 +3209,11 @@ function AssistantTurnBlock({
2947
3209
  }, [hasActionableToolCall, isStreaming]);
2948
3210
  const effectiveMode = resolveTurnDisplayMode({ isStreaming, displayMode });
2949
3211
  const executionDurationMs = getExecutionDurationMs({ messages, isStreaming });
2950
- const [clock, setClock] = useState10(() => Date.now());
3212
+ const [clock, setClock] = useState11(() => Date.now());
2951
3213
  const hasLiveStartTime = messages.some(
2952
3214
  (message) => message.timestamp != null && Number.isFinite(Date.parse(message.timestamp))
2953
3215
  );
2954
- useEffect8(() => {
3216
+ useEffect9(() => {
2955
3217
  if (!isStreaming || !hasLiveStartTime) return;
2956
3218
  const timer = window.setInterval(() => setClock(Date.now()), 1e3);
2957
3219
  return () => window.clearInterval(timer);
@@ -2959,21 +3221,21 @@ function AssistantTurnBlock({
2959
3221
  const liveExecutionDurationMs = isStreaming ? getExecutionDurationMs({ messages, isStreaming, now: clock }) : executionDurationMs;
2960
3222
  const memoryRefs = collectMemoryRefs(messages);
2961
3223
  if (!hasExecutionProcess) {
2962
- return /* @__PURE__ */ jsxs9(
3224
+ return /* @__PURE__ */ jsxs10(
2963
3225
  "div",
2964
3226
  {
2965
3227
  "aria-busy": isStreaming || void 0,
2966
3228
  className: "blade-chat-assistant-turn flex flex-col gap-3",
2967
3229
  children: [
2968
- memoryRefs.length > 0 ? /* @__PURE__ */ jsx11(MemoryRefsHint, { refs: memoryRefs }) : null,
2969
- 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" }),
2970
- 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" }),
3230
+ memoryRefs.length > 0 ? /* @__PURE__ */ jsx12(MemoryRefsHint, { refs: memoryRefs }) : null,
3231
+ 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" }),
3232
+ 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" }),
2971
3233
  messages.map((message, index) => {
2972
- return hasRenderableMessageContent(message) ? /* @__PURE__ */ jsx11(
3234
+ return hasRenderableMessageContent(message) ? /* @__PURE__ */ jsx12(
2973
3235
  "div",
2974
3236
  {
2975
3237
  className: "flex flex-col gap-3",
2976
- children: /* @__PURE__ */ jsx11(
3238
+ children: /* @__PURE__ */ jsx12(
2977
3239
  AssistantMessageContent,
2978
3240
  {
2979
3241
  message,
@@ -2989,26 +3251,26 @@ function AssistantTurnBlock({
2989
3251
  }
2990
3252
  );
2991
3253
  }
2992
- return /* @__PURE__ */ jsxs9(
3254
+ return /* @__PURE__ */ jsxs10(
2993
3255
  "div",
2994
3256
  {
2995
3257
  "aria-busy": isStreaming || void 0,
2996
3258
  className: "blade-chat-assistant-turn flex flex-col gap-3",
2997
3259
  children: [
2998
- memoryRefs.length > 0 ? /* @__PURE__ */ jsx11(MemoryRefsHint, { refs: memoryRefs }) : null,
2999
- 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" }),
3000
- 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" }),
3001
- /* @__PURE__ */ jsxs9("div", { className: "flex w-full items-start gap-2.5", children: [
3002
- /* @__PURE__ */ jsx11(
3260
+ memoryRefs.length > 0 ? /* @__PURE__ */ jsx12(MemoryRefsHint, { refs: memoryRefs }) : null,
3261
+ 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" }),
3262
+ 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" }),
3263
+ /* @__PURE__ */ jsxs10("div", { className: "flex w-full items-start gap-2.5", children: [
3264
+ /* @__PURE__ */ jsx12(
3003
3265
  "span",
3004
3266
  {
3005
3267
  className: "grid size-[30px] shrink-0 place-items-center rounded-full bg-[hsl(var(--muted)/0.55)] text-[hsl(var(--foreground))]",
3006
3268
  "aria-hidden": "true",
3007
- children: /* @__PURE__ */ jsx11(Bot, { size: 16 })
3269
+ children: /* @__PURE__ */ jsx12(Bot, { size: 16 })
3008
3270
  }
3009
3271
  ),
3010
- /* @__PURE__ */ jsxs9("div", { className: "min-w-0 flex-1 pt-0.5", children: [
3011
- /* @__PURE__ */ jsxs9(
3272
+ /* @__PURE__ */ jsxs10("div", { className: "min-w-0 flex-1 pt-0.5", children: [
3273
+ /* @__PURE__ */ jsxs10(
3012
3274
  "button",
3013
3275
  {
3014
3276
  type: "button",
@@ -3021,14 +3283,14 @@ function AssistantTurnBlock({
3021
3283
  "data-testid": "assistant-execution-summary",
3022
3284
  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",
3023
3285
  children: [
3024
- /* @__PURE__ */ jsx11("span", { className: "min-w-0 truncate", children: executionSummaryLabel({
3286
+ /* @__PURE__ */ jsx12("span", { className: "min-w-0 truncate", children: executionSummaryLabel({
3025
3287
  messages,
3026
3288
  isStreaming,
3027
3289
  durationMs: liveExecutionDurationMs,
3028
3290
  sessionStatus,
3029
3291
  askAnswers
3030
3292
  }) }),
3031
- /* @__PURE__ */ jsx11(
3293
+ /* @__PURE__ */ jsx12(
3032
3294
  ChevronRight,
3033
3295
  {
3034
3296
  size: 14,
@@ -3042,26 +3304,26 @@ function AssistantTurnBlock({
3042
3304
  ]
3043
3305
  }
3044
3306
  ),
3045
- /* @__PURE__ */ jsx11("div", { className: "mt-3 h-px w-full bg-[hsl(var(--border)/0.75)]" })
3307
+ /* @__PURE__ */ jsx12("div", { className: "mt-3 h-px w-full bg-[hsl(var(--border)/0.75)]" })
3046
3308
  ] })
3047
3309
  ] }),
3048
- effectiveMode === "detail" ? /* @__PURE__ */ jsx11("div", { className: "ml-10 flex flex-col gap-3 pt-1", children: messages.map((message, index) => {
3310
+ effectiveMode === "detail" ? /* @__PURE__ */ jsx12("div", { className: "ml-10 flex flex-col gap-3 pt-1", children: messages.map((message, index) => {
3049
3311
  const isLast = index === messages.length - 1;
3050
3312
  const streamingThis = isStreaming && isLast;
3051
3313
  const text = getMessageText(message);
3052
3314
  const toolCalls = (message.tool_calls ?? []).filter(
3053
- (toolCall) => formatToolName(toolCall.name) !== "AskUserQuestion"
3315
+ (toolCall) => formatToolName(toolCall.name) !== "AskUserQuestion" && !shouldHideToolCall(message, toolCall)
3054
3316
  );
3055
3317
  const orderedParts = getOrderedMessageParts(message, toolCalls);
3056
3318
  const showReasoning = !!message.reasoning && isStreaming && index === latestReasoningIndex;
3057
- return /* @__PURE__ */ jsxs9(
3319
+ return /* @__PURE__ */ jsxs10(
3058
3320
  "div",
3059
3321
  {
3060
3322
  className: "flex flex-col gap-3",
3061
3323
  children: [
3062
- showReasoning && message.reasoning ? /* @__PURE__ */ jsx11(ThinkingBlock, { reasoning: message.reasoning, isStreaming: streamingThis && !text }) : null,
3324
+ showReasoning && message.reasoning ? /* @__PURE__ */ jsx12(ThinkingBlock, { reasoning: message.reasoning, isStreaming: streamingThis && !text }) : null,
3063
3325
  orderedParts.length > 0 ? orderedParts.map(
3064
- (part) => part.type === "text" ? /* @__PURE__ */ jsx11(
3326
+ (part) => part.type === "text" ? /* @__PURE__ */ jsx12(
3065
3327
  AssistantMessageContent,
3066
3328
  {
3067
3329
  message: { ...message, content: part.content, tool_calls: turnToolCalls },
@@ -3070,11 +3332,11 @@ function AssistantTurnBlock({
3070
3332
  compact: true
3071
3333
  },
3072
3334
  part.key
3073
- ) : /* @__PURE__ */ jsx11("div", { className: "flex flex-col gap-0.5", children: part.toolCalls.map((toolCall) => {
3335
+ ) : /* @__PURE__ */ jsx12("div", { className: "flex flex-col gap-0.5", children: part.toolCalls.map((toolCall) => {
3074
3336
  const custom = toolCallRenderer?.(toolCall);
3075
- 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);
3337
+ 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);
3076
3338
  }) }, part.key)
3077
- ) : hasRenderableMessageContent(message) && message !== finalMessage ? /* @__PURE__ */ jsx11(
3339
+ ) : hasRenderableMessageContent(message) && message !== finalMessage ? /* @__PURE__ */ jsx12(
3078
3340
  AssistantMessageContent,
3079
3341
  {
3080
3342
  message,
@@ -3083,16 +3345,16 @@ function AssistantTurnBlock({
3083
3345
  compact: true
3084
3346
  }
3085
3347
  ) : null,
3086
- orderedParts.length === 0 && toolCalls.length > 0 ? /* @__PURE__ */ jsx11("div", { className: "flex flex-col gap-0.5", children: toolCalls.map((toolCall) => {
3348
+ orderedParts.length === 0 && toolCalls.length > 0 ? /* @__PURE__ */ jsx12("div", { className: "flex flex-col gap-0.5", children: toolCalls.map((toolCall) => {
3087
3349
  const custom = toolCallRenderer?.(toolCall);
3088
- 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);
3350
+ 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);
3089
3351
  }) }) : null
3090
3352
  ]
3091
3353
  },
3092
3354
  message.entry_id ?? `${message.timestamp ?? "assistant"}-${index}`
3093
3355
  );
3094
3356
  }) }) : null,
3095
- finalMessage && (effectiveMode === "compact" || finalOrderedParts.length === 0) ? /* @__PURE__ */ jsx11("div", { className: "ml-10", children: /* @__PURE__ */ jsx11(
3357
+ finalMessage && (effectiveMode === "compact" || finalOrderedParts.length === 0) ? /* @__PURE__ */ jsx12("div", { className: "ml-10", children: /* @__PURE__ */ jsx12(
3096
3358
  AssistantMessageContent,
3097
3359
  {
3098
3360
  message: finalMessage,
@@ -3100,7 +3362,7 @@ function AssistantTurnBlock({
3100
3362
  streaming: isStreaming && finalMessage === messages[messages.length - 1]
3101
3363
  }
3102
3364
  ) }) : null,
3103
- questionToolCalls.map((toolCall) => /* @__PURE__ */ jsx11(
3365
+ questionToolCalls.map((toolCall) => /* @__PURE__ */ jsx12(
3104
3366
  ToolCallBlock,
3105
3367
  {
3106
3368
  toolCall,
@@ -3125,22 +3387,22 @@ function collectMemoryRefs(messages) {
3125
3387
  return [...refs.values()];
3126
3388
  }
3127
3389
  function MemoryRefsHint({ refs }) {
3128
- const [expanded, setExpanded] = useState10(false);
3390
+ const [expanded, setExpanded] = useState11(false);
3129
3391
  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";
3130
- return /* @__PURE__ */ jsxs9("div", { className: "blade-chat-memory-refs ml-1 w-full max-w-[680px]", children: [
3131
- /* @__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: [
3132
- /* @__PURE__ */ jsx11(BookOpen, { size: 12 }),
3133
- /* @__PURE__ */ jsxs9("span", { children: [
3392
+ return /* @__PURE__ */ jsxs10("div", { className: "blade-chat-memory-refs ml-1 w-full max-w-[680px]", children: [
3393
+ /* @__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: [
3394
+ /* @__PURE__ */ jsx12(BookOpen, { size: 12 }),
3395
+ /* @__PURE__ */ jsxs10("span", { children: [
3134
3396
  label,
3135
3397
  "\uFF08",
3136
3398
  refs.length,
3137
3399
  "\uFF09"
3138
3400
  ] }),
3139
- /* @__PURE__ */ jsx11(ChevronRight, { size: 10, className: cn("transition-transform", expanded && "rotate-90") })
3401
+ /* @__PURE__ */ jsx12(ChevronRight, { size: 10, className: cn("transition-transform", expanded && "rotate-90") })
3140
3402
  ] }),
3141
- 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: [
3142
- /* @__PURE__ */ jsx11("p", { className: "line-clamp-2 break-words leading-5", children: ref.content_preview }),
3143
- ref.skill_name ? /* @__PURE__ */ jsx11("span", { className: "mt-1 inline-flex text-[10px] text-[hsl(var(--primary))]", children: ref.skill_name }) : null
3403
+ 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: [
3404
+ /* @__PURE__ */ jsx12("p", { className: "line-clamp-2 break-words leading-5", children: ref.content_preview }),
3405
+ ref.skill_name ? /* @__PURE__ */ jsx12("span", { className: "mt-1 inline-flex text-[10px] text-[hsl(var(--primary))]", children: ref.skill_name }) : null
3144
3406
  ] }, ref.id)) }) : null
3145
3407
  ] });
3146
3408
  }
@@ -3154,15 +3416,15 @@ function AssistantMessageContent({
3154
3416
  const imageParts = getImageParts(message.content);
3155
3417
  const fileParts = getFileParts(message.content);
3156
3418
  const failed = message.status === "failed";
3157
- 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;
3158
- const textContent = text ? /* @__PURE__ */ jsx11(
3419
+ 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;
3420
+ const textContent = text ? /* @__PURE__ */ jsx12(
3159
3421
  "div",
3160
3422
  {
3161
3423
  className: cn(
3162
3424
  "blade-chat-assistant-text",
3163
3425
  compact ? "text-xs leading-[22px] text-[hsl(var(--foreground))]" : "text-[15px] leading-8 text-[hsl(var(--foreground))]"
3164
3426
  ),
3165
- children: /* @__PURE__ */ jsx11(
3427
+ children: /* @__PURE__ */ jsx12(
3166
3428
  MarkdownContent,
3167
3429
  {
3168
3430
  mode: streaming ? "streaming" : "static",
@@ -3175,14 +3437,14 @@ function AssistantMessageContent({
3175
3437
  ) : null;
3176
3438
  if (imageParts.length === 0 && fileParts.length === 0) {
3177
3439
  if (!failed) return textContent;
3178
- return failedBadge || textContent ? /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-2", children: [
3440
+ return failedBadge || textContent ? /* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-2", children: [
3179
3441
  failedBadge,
3180
3442
  textContent
3181
3443
  ] }) : null;
3182
3444
  }
3183
- return /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-3", children: [
3445
+ return /* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-3", children: [
3184
3446
  failedBadge,
3185
- imageParts.length > 0 ? /* @__PURE__ */ jsx11("div", { className: "grid gap-2", children: imageParts.map((part) => /* @__PURE__ */ jsx11(
3447
+ imageParts.length > 0 ? /* @__PURE__ */ jsx12("div", { className: "grid gap-2", children: imageParts.map((part) => /* @__PURE__ */ jsx12(
3186
3448
  "img",
3187
3449
  {
3188
3450
  src: part.image_url.url,
@@ -3191,14 +3453,14 @@ function AssistantMessageContent({
3191
3453
  },
3192
3454
  part.image_url.url
3193
3455
  )) }) : null,
3194
- fileParts.length > 0 ? /* @__PURE__ */ jsx11("div", { className: "flex flex-wrap gap-1.5", children: fileParts.map((part) => /* @__PURE__ */ jsxs9(
3456
+ fileParts.length > 0 ? /* @__PURE__ */ jsx12("div", { className: "flex flex-wrap gap-1.5", children: fileParts.map((part) => /* @__PURE__ */ jsxs10(
3195
3457
  "div",
3196
3458
  {
3197
3459
  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))]",
3198
3460
  title: part.name,
3199
3461
  children: [
3200
- /* @__PURE__ */ jsx11(FileText, { size: 12, className: "shrink-0" }),
3201
- /* @__PURE__ */ jsx11("span", { className: "max-w-56 truncate", children: part.name })
3462
+ /* @__PURE__ */ jsx12(FileText, { size: 12, className: "shrink-0" }),
3463
+ /* @__PURE__ */ jsx12("span", { className: "max-w-56 truncate", children: part.name })
3202
3464
  ]
3203
3465
  },
3204
3466
  `${part.name}-${part.data.slice(0, 32)}`
@@ -3209,7 +3471,7 @@ function AssistantMessageContent({
3209
3471
 
3210
3472
  // src/components/RenderErrorBoundary.tsx
3211
3473
  import { Component } from "react";
3212
- import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
3474
+ import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
3213
3475
  function getFirstComponentName(componentStack) {
3214
3476
  const match = componentStack.match(/\n\s+at\s+([^\s(]+)/);
3215
3477
  return match?.[1] ?? null;
@@ -3242,26 +3504,26 @@ var RenderErrorBoundary = class extends Component {
3242
3504
  return children;
3243
3505
  }
3244
3506
  const componentName = getFirstComponentName(componentStack);
3245
- 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: [
3246
- /* @__PURE__ */ jsx12(TriangleAlert, { className: "mt-0.5 h-4 w-4 shrink-0 text-amber-300" }),
3247
- /* @__PURE__ */ jsxs10("div", { className: "min-w-0 flex-1", children: [
3248
- /* @__PURE__ */ jsxs10("div", { className: "font-medium", children: [
3507
+ 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: [
3508
+ /* @__PURE__ */ jsx13(TriangleAlert, { className: "mt-0.5 h-4 w-4 shrink-0 text-amber-300" }),
3509
+ /* @__PURE__ */ jsxs11("div", { className: "min-w-0 flex-1", children: [
3510
+ /* @__PURE__ */ jsxs11("div", { className: "font-medium", children: [
3249
3511
  label,
3250
3512
  "\u6E32\u67D3\u5931\u8D25"
3251
3513
  ] }),
3252
- /* @__PURE__ */ jsxs10("div", { className: "mt-1 break-words text-xs leading-5 text-amber-100/75", children: [
3514
+ /* @__PURE__ */ jsxs11("div", { className: "mt-1 break-words text-xs leading-5 text-amber-100/75", children: [
3253
3515
  componentName ? `\u7EC4\u4EF6\uFF1A${componentName}\u3002` : null,
3254
3516
  error.message || "\u53D1\u751F\u4E86\u672A\u9884\u671F\u7684\u6E32\u67D3\u9519\u8BEF\u3002"
3255
3517
  ] }),
3256
- details ? /* @__PURE__ */ jsx12("div", { className: "mt-1 truncate text-xs text-amber-100/55", children: details }) : null
3518
+ details ? /* @__PURE__ */ jsx13("div", { className: "mt-1 truncate text-xs text-amber-100/55", children: details }) : null
3257
3519
  ] })
3258
3520
  ] }) });
3259
3521
  }
3260
3522
  };
3261
3523
 
3262
3524
  // src/components/PostChatFollowupBlock.tsx
3263
- import { useCallback as useCallback6, useEffect as useEffect9, useRef as useRef10, useState as useState11 } from "react";
3264
- import { Fragment as Fragment2, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
3525
+ import { useCallback as useCallback6, useEffect as useEffect10, useRef as useRef11, useState as useState12 } from "react";
3526
+ import { Fragment as Fragment2, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
3265
3527
  function emitInteraction(callback, event) {
3266
3528
  try {
3267
3529
  callback?.(event);
@@ -3280,10 +3542,10 @@ function ArtifactCard({
3280
3542
  onArtifactOpened
3281
3543
  }) {
3282
3544
  const client = useBladeClient();
3283
- const [downloading, setDownloading] = useState11(false);
3545
+ const [downloading, setDownloading] = useState12(false);
3284
3546
  const name = artifact.label || basename(artifact.target);
3285
3547
  if (artifact.kind === "link") {
3286
- return /* @__PURE__ */ jsxs11(
3548
+ return /* @__PURE__ */ jsxs12(
3287
3549
  "a",
3288
3550
  {
3289
3551
  href: artifact.target,
@@ -3294,9 +3556,9 @@ function ArtifactCard({
3294
3556
  ${artifact.target}`,
3295
3557
  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))]",
3296
3558
  children: [
3297
- /* @__PURE__ */ jsx13(Globe, { size: 15, className: "shrink-0 text-[hsl(var(--primary))]" }),
3298
- /* @__PURE__ */ jsx13("span", { className: "min-w-0 flex-1 truncate font-medium", children: name }),
3299
- /* @__PURE__ */ jsx13(ArrowUpRight, { size: 13, className: "absolute right-2 opacity-0 group-hover:opacity-100 group-focus-visible:opacity-100" })
3559
+ /* @__PURE__ */ jsx14(Globe, { size: 15, className: "shrink-0 text-[hsl(var(--primary))]" }),
3560
+ /* @__PURE__ */ jsx14("span", { className: "min-w-0 flex-1 truncate font-medium", children: name }),
3561
+ /* @__PURE__ */ jsx14(ArrowUpRight, { size: 13, className: "absolute right-2 opacity-0 group-hover:opacity-100 group-focus-visible:opacity-100" })
3300
3562
  ]
3301
3563
  }
3302
3564
  );
@@ -3331,7 +3593,7 @@ ${artifact.target}`,
3331
3593
  setDownloading(false);
3332
3594
  }
3333
3595
  };
3334
- return /* @__PURE__ */ jsx13(
3596
+ return /* @__PURE__ */ jsx14(
3335
3597
  "a",
3336
3598
  {
3337
3599
  href: downloadUrl,
@@ -3359,17 +3621,17 @@ function feedbackReasonLabel(reason) {
3359
3621
  }
3360
3622
  function HistoricalResultFeedback({ feedback }) {
3361
3623
  const label = feedbackReasonLabel(feedback.reason);
3362
- return /* @__PURE__ */ jsxs11(
3624
+ return /* @__PURE__ */ jsxs12(
3363
3625
  "section",
3364
3626
  {
3365
3627
  "aria-label": "\u5386\u53F2\u7ED3\u679C\u53CD\u9988",
3366
3628
  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))]",
3367
3629
  children: [
3368
- /* @__PURE__ */ jsxs11("span", { children: [
3630
+ /* @__PURE__ */ jsxs12("span", { children: [
3369
3631
  "\u4F60\u5BF9\u6B64\u8F6E\u7ED3\u679C\u7684\u8BC4\u4EF7\uFF1A",
3370
3632
  feedback.helpful ? "\u6709\u5E2E\u52A9" : "\u6CA1\u5E2E\u52A9"
3371
3633
  ] }),
3372
- label ? /* @__PURE__ */ jsxs11("span", { children: [
3634
+ label ? /* @__PURE__ */ jsxs12("span", { children: [
3373
3635
  " \xB7 ",
3374
3636
  label
3375
3637
  ] }) : null
@@ -3386,15 +3648,15 @@ function ResultFeedback({
3386
3648
  onFeedbackSaved
3387
3649
  }) {
3388
3650
  const client = useBladeClient();
3389
- const [saved, setSaved] = useState11(savedFeedback ?? null);
3390
- const [helpful, setHelpful] = useState11(savedFeedback?.helpful ?? null);
3391
- const [reason, setReason] = useState11(savedFeedback?.reason ?? null);
3392
- const [saving, setSaving] = useState11(false);
3393
- const [saveError, setSaveError] = useState11(false);
3394
- const reportedShown = useRef10(false);
3395
- const latestChoice = useRef10(null);
3651
+ const [saved, setSaved] = useState12(savedFeedback ?? null);
3652
+ const [helpful, setHelpful] = useState12(savedFeedback?.helpful ?? null);
3653
+ const [reason, setReason] = useState12(savedFeedback?.reason ?? null);
3654
+ const [saving, setSaving] = useState12(false);
3655
+ const [saveError, setSaveError] = useState12(false);
3656
+ const reportedShown = useRef11(false);
3657
+ const latestChoice = useRef11(null);
3396
3658
  const eligible = followup.feedback_eligible === true && Boolean(sessionId) && !isViewer;
3397
- useEffect9(() => {
3659
+ useEffect10(() => {
3398
3660
  if (!eligible || reportedShown.current) return;
3399
3661
  reportedShown.current = true;
3400
3662
  emitInteraction(onInteraction, {
@@ -3403,7 +3665,7 @@ function ResultFeedback({
3403
3665
  assistantEntryId: followup.assistant_entry_id
3404
3666
  });
3405
3667
  }, [eligible, followup.assistant_entry_id, onInteraction, sessionId]);
3406
- useEffect9(() => {
3668
+ useEffect10(() => {
3407
3669
  if (!savedFeedback || latestChoice.current) return;
3408
3670
  setSaved(savedFeedback);
3409
3671
  setHelpful(savedFeedback.helpful);
@@ -3444,15 +3706,15 @@ function ResultFeedback({
3444
3706
  [client, followup.assistant_entry_id, onFeedbackSaved, onInteraction, sessionId]
3445
3707
  );
3446
3708
  if (!eligible) return null;
3447
- return /* @__PURE__ */ jsxs11(
3709
+ return /* @__PURE__ */ jsxs12(
3448
3710
  "section",
3449
3711
  {
3450
3712
  "aria-label": "\u7ED3\u679C\u53CD\u9988",
3451
3713
  className: "flex flex-col gap-2 border-t border-[hsl(var(--border))] pt-3",
3452
3714
  children: [
3453
- /* @__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" }),
3454
- /* @__PURE__ */ jsxs11("div", { className: "flex flex-wrap gap-1.5", children: [
3455
- /* @__PURE__ */ jsx13(
3715
+ /* @__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" }),
3716
+ /* @__PURE__ */ jsxs12("div", { className: "flex flex-wrap gap-1.5", children: [
3717
+ /* @__PURE__ */ jsx14(
3456
3718
  "button",
3457
3719
  {
3458
3720
  type: "button",
@@ -3463,7 +3725,7 @@ function ResultFeedback({
3463
3725
  children: "\u6709\u5E2E\u52A9"
3464
3726
  }
3465
3727
  ),
3466
- /* @__PURE__ */ jsx13(
3728
+ /* @__PURE__ */ jsx14(
3467
3729
  "button",
3468
3730
  {
3469
3731
  type: "button",
@@ -3475,7 +3737,7 @@ function ResultFeedback({
3475
3737
  }
3476
3738
  )
3477
3739
  ] }),
3478
- 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(
3740
+ 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(
3479
3741
  "button",
3480
3742
  {
3481
3743
  type: "button",
@@ -3487,9 +3749,9 @@ function ResultFeedback({
3487
3749
  },
3488
3750
  item.value
3489
3751
  )) }) : null,
3490
- saveError ? /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2 text-xs text-[hsl(var(--destructive))]", children: [
3491
- /* @__PURE__ */ jsx13("span", { children: "\u53CD\u9988\u6682\u672A\u4FDD\u5B58\uFF0C\u53EF\u91CD\u8BD5" }),
3492
- /* @__PURE__ */ jsx13(
3752
+ saveError ? /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2 text-xs text-[hsl(var(--destructive))]", children: [
3753
+ /* @__PURE__ */ jsx14("span", { children: "\u53CD\u9988\u6682\u672A\u4FDD\u5B58\uFF0C\u53EF\u91CD\u8BD5" }),
3754
+ /* @__PURE__ */ jsx14(
3493
3755
  "button",
3494
3756
  {
3495
3757
  type: "button",
@@ -3501,7 +3763,7 @@ function ResultFeedback({
3501
3763
  children: "\u91CD\u8BD5"
3502
3764
  }
3503
3765
  )
3504
- ] }) : saved ? /* @__PURE__ */ jsx13("div", { className: "text-[11px] text-[hsl(var(--muted-foreground))]", children: "\u5DF2\u4FDD\u5B58\uFF0C\u53EF\u968F\u65F6\u4FEE\u6539" }) : null
3766
+ ] }) : saved ? /* @__PURE__ */ jsx14("div", { className: "text-[11px] text-[hsl(var(--muted-foreground))]", children: "\u5DF2\u4FDD\u5B58\uFF0C\u53EF\u968F\u65F6\u4FEE\u6539" }) : null
3505
3767
  ]
3506
3768
  }
3507
3769
  );
@@ -3515,14 +3777,14 @@ function PostChatFollowupBlock({
3515
3777
  savedFeedback,
3516
3778
  onFeedbackSaved
3517
3779
  }) {
3518
- const [expanded, setExpanded] = useState11(false);
3519
- const adopted = useRef10(/* @__PURE__ */ new Set());
3520
- const reportedSuggestions = useRef10(false);
3521
- const reportedArtifacts = useRef10(/* @__PURE__ */ new Set());
3522
- const openedArtifacts = useRef10(/* @__PURE__ */ new Set());
3780
+ const [expanded, setExpanded] = useState12(false);
3781
+ const adopted = useRef11(/* @__PURE__ */ new Set());
3782
+ const reportedSuggestions = useRef11(false);
3783
+ const reportedArtifacts = useRef11(/* @__PURE__ */ new Set());
3784
+ const openedArtifacts = useRef11(/* @__PURE__ */ new Set());
3523
3785
  const artifacts = followup.final_artifacts ?? [];
3524
3786
  const visibleArtifacts = expanded ? artifacts : artifacts.slice(0, 3);
3525
- useEffect9(() => {
3787
+ useEffect10(() => {
3526
3788
  if (!reportedSuggestions.current && followup.suggestions.length > 0) {
3527
3789
  reportedSuggestions.current = true;
3528
3790
  emitInteraction(onInteraction, {
@@ -3568,15 +3830,15 @@ function PostChatFollowupBlock({
3568
3830
  );
3569
3831
  if (!followup.recaption && artifacts.length === 0 && followup.suggestions.length === 0 && !followup.feedback_eligible)
3570
3832
  return null;
3571
- 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: [
3572
- followup.recaption || artifacts.length > 0 ? /* @__PURE__ */ jsxs11("section", { "aria-label": "\u672C\u8F6E\u5C0F\u7ED3", className: "flex flex-col gap-2", children: [
3573
- /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1.5 text-xs font-medium text-[hsl(var(--muted-foreground))]", children: [
3574
- /* @__PURE__ */ jsx13(Sparkles, { size: 14 }),
3833
+ 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: [
3834
+ followup.recaption || artifacts.length > 0 ? /* @__PURE__ */ jsxs12("section", { "aria-label": "\u672C\u8F6E\u5C0F\u7ED3", className: "flex flex-col gap-2", children: [
3835
+ /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1.5 text-xs font-medium text-[hsl(var(--muted-foreground))]", children: [
3836
+ /* @__PURE__ */ jsx14(Sparkles, { size: 14 }),
3575
3837
  "\u672C\u8F6E\u5C0F\u7ED3"
3576
3838
  ] }),
3577
- followup.recaption ? /* @__PURE__ */ jsx13("p", { className: "text-[13px] leading-5", children: followup.recaption }) : null,
3578
- artifacts.length > 0 ? /* @__PURE__ */ jsxs11(Fragment2, { children: [
3579
- /* @__PURE__ */ jsx13("div", { className: "grid max-w-full grid-cols-3 gap-1.5", children: visibleArtifacts.map((artifact, artifactIndex) => /* @__PURE__ */ jsx13(
3839
+ followup.recaption ? /* @__PURE__ */ jsx14("p", { className: "text-[13px] leading-5", children: followup.recaption }) : null,
3840
+ artifacts.length > 0 ? /* @__PURE__ */ jsxs12(Fragment2, { children: [
3841
+ /* @__PURE__ */ jsx14("div", { className: "grid max-w-full grid-cols-3 gap-1.5", children: visibleArtifacts.map((artifact, artifactIndex) => /* @__PURE__ */ jsx14(
3580
3842
  ArtifactCard,
3581
3843
  {
3582
3844
  artifact,
@@ -3588,7 +3850,7 @@ function PostChatFollowupBlock({
3588
3850
  },
3589
3851
  `${artifact.kind}:${artifactIndex}`
3590
3852
  )) }),
3591
- artifacts.length > 3 ? /* @__PURE__ */ jsxs11(
3853
+ artifacts.length > 3 ? /* @__PURE__ */ jsxs12(
3592
3854
  "button",
3593
3855
  {
3594
3856
  type: "button",
@@ -3597,15 +3859,15 @@ function PostChatFollowupBlock({
3597
3859
  className: "flex w-fit items-center gap-0.5 text-[11px] text-[hsl(var(--muted-foreground))]",
3598
3860
  children: [
3599
3861
  expanded ? "\u6536\u8D77" : `\u5C55\u5F00 ${artifacts.length - 3} \u4E2A`,
3600
- /* @__PURE__ */ jsx13(ChevronDown, { size: 13, className: expanded ? "rotate-180" : void 0 })
3862
+ /* @__PURE__ */ jsx14(ChevronDown, { size: 13, className: expanded ? "rotate-180" : void 0 })
3601
3863
  ]
3602
3864
  }
3603
3865
  ) : null
3604
3866
  ] }) : null
3605
3867
  ] }) : null,
3606
- followup.suggestions.length > 0 ? /* @__PURE__ */ jsxs11("section", { "aria-label": "\u4E0B\u4E00\u6B65\u5EFA\u8BAE", className: "flex flex-col gap-1.5", children: [
3607
- /* @__PURE__ */ jsx13("div", { className: "text-xs font-medium text-[hsl(var(--muted-foreground))]", children: "\u4E0B\u4E00\u6B65\u53EF\u4EE5" }),
3608
- followup.suggestions.map((suggestion, suggestionIndex) => /* @__PURE__ */ jsxs11(
3868
+ followup.suggestions.length > 0 ? /* @__PURE__ */ jsxs12("section", { "aria-label": "\u4E0B\u4E00\u6B65\u5EFA\u8BAE", className: "flex flex-col gap-1.5", children: [
3869
+ /* @__PURE__ */ jsx14("div", { className: "text-xs font-medium text-[hsl(var(--muted-foreground))]", children: "\u4E0B\u4E00\u6B65\u53EF\u4EE5" }),
3870
+ followup.suggestions.map((suggestion, suggestionIndex) => /* @__PURE__ */ jsxs12(
3609
3871
  "button",
3610
3872
  {
3611
3873
  type: "button",
@@ -3624,14 +3886,14 @@ function PostChatFollowupBlock({
3624
3886
  },
3625
3887
  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",
3626
3888
  children: [
3627
- /* @__PURE__ */ jsx13("span", { children: suggestion }),
3628
- /* @__PURE__ */ jsx13(ArrowRight, { size: 14, className: "ml-auto shrink-0" })
3889
+ /* @__PURE__ */ jsx14("span", { children: suggestion }),
3890
+ /* @__PURE__ */ jsx14(ArrowRight, { size: 14, className: "ml-auto shrink-0" })
3629
3891
  ]
3630
3892
  },
3631
3893
  suggestion
3632
3894
  ))
3633
3895
  ] }) : null,
3634
- /* @__PURE__ */ jsx13(
3896
+ /* @__PURE__ */ jsx14(
3635
3897
  ResultFeedback,
3636
3898
  {
3637
3899
  followup,
@@ -3652,7 +3914,85 @@ import {
3652
3914
  getImageParts as getImageParts2,
3653
3915
  getTextContent as getTextContent2
3654
3916
  } from "@blade-hq/agent-client";
3655
- import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
3917
+
3918
+ // src/lib/whatif-prompt.ts
3919
+ var HEADER_RE = /^以下消息和 step 产物标记为 deprecated_by_rerun,请基于最新用户假设从 step(\d+) 开始完整重新推演,不要复用旧结论。$/;
3920
+ var QUOTE_HEADER_RE = /^\[步骤(\d+)\s*·\s*(.+?)\]$/;
3921
+ var USER_INPUT_TAG = "[\u7528\u6237\u8F93\u5165]";
3922
+ function parseWhatIfPrompt(text) {
3923
+ const lines = text.replace(/\r\n/g, "\n").trimEnd().split("\n");
3924
+ const headerMatch = lines[0]?.match(HEADER_RE);
3925
+ if (!headerMatch) return null;
3926
+ const userTagIdx = lines.indexOf(USER_INPUT_TAG);
3927
+ const hasUserTag = userTagIdx >= 0;
3928
+ const quoteBlockEndExclusive = hasUserTag ? userTagIdx : lines.length;
3929
+ const quoteHeaderIdxs = [];
3930
+ let quoteBlockFound = false;
3931
+ for (let i = 1; i < quoteBlockEndExclusive; i++) {
3932
+ if (!quoteBlockFound && lines[i].trim() === "[\u5F15\u7528]") {
3933
+ quoteBlockFound = true;
3934
+ } else if (quoteBlockFound && QUOTE_HEADER_RE.test(lines[i])) {
3935
+ quoteHeaderIdxs.push(i);
3936
+ }
3937
+ }
3938
+ let legacyUserTextStart = -1;
3939
+ if (!hasUserTag && quoteHeaderIdxs.length > 0) {
3940
+ const lastSnapshotStart = quoteHeaderIdxs.at(-1) + 1;
3941
+ let i = lines.length - 1;
3942
+ while (i >= lastSnapshotStart && lines[i].trim() === "") i -= 1;
3943
+ while (i >= lastSnapshotStart && lines[i].trim() !== "") i -= 1;
3944
+ if (i >= lastSnapshotStart) legacyUserTextStart = i + 1;
3945
+ }
3946
+ const quotes = quoteHeaderIdxs.map((headerIdx, index) => {
3947
+ const match = lines[headerIdx].match(QUOTE_HEADER_RE);
3948
+ const nextHeader = quoteHeaderIdxs[index + 1];
3949
+ const end = nextHeader ?? (legacyUserTextStart >= 0 ? legacyUserTextStart : quoteBlockEndExclusive);
3950
+ const snapshotLines = lines.slice(headerIdx + 1, end);
3951
+ while (snapshotLines.at(-1)?.trim() === "") snapshotLines.pop();
3952
+ return {
3953
+ stepNumber: Number.parseInt(match[1], 10),
3954
+ label: match[2].trim(),
3955
+ snapshot: snapshotLines.join("\n")
3956
+ };
3957
+ });
3958
+ const userTextStart = hasUserTag ? userTagIdx + 1 : legacyUserTextStart;
3959
+ const userLines = userTextStart >= 0 ? lines.slice(userTextStart) : [];
3960
+ while (userLines[0]?.trim() === "") userLines.shift();
3961
+ while (userLines.at(-1)?.trim() === "") userLines.pop();
3962
+ const fromStep = Number.parseInt(headerMatch[1], 10);
3963
+ return {
3964
+ fromStep: Number.isFinite(fromStep) ? fromStep : null,
3965
+ quotes,
3966
+ userText: userLines.join("\n")
3967
+ };
3968
+ }
3969
+
3970
+ // src/components/WhatIfUserBubble.tsx
3971
+ import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
3972
+ function WhatIfUserBubble({ parsed, onQuoteClick }) {
3973
+ const { fromStep, quotes, userText } = parsed;
3974
+ return /* @__PURE__ */ jsxs13("div", { className: "flex flex-col items-end gap-2", children: [
3975
+ /* @__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: [
3976
+ /* @__PURE__ */ jsx15(RefreshCcw, { size: 10 }),
3977
+ /* @__PURE__ */ jsx15("span", { children: fromStep != null ? `\u91CD\u8DD1\u81EA step ${fromStep}` : "\u91CD\u8DD1" })
3978
+ ] }),
3979
+ quotes.length > 0 && /* @__PURE__ */ jsx15("div", { className: "flex max-w-[min(72vw,42rem)] flex-col items-stretch gap-2", children: quotes.map((quote, index) => {
3980
+ const clickable = quote.stepNumber != null && !!onQuoteClick;
3981
+ const label = quote.stepNumber != null ? `\u6B65\u9AA4${quote.stepNumber} \xB7 ${quote.label}` : quote.label;
3982
+ 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: [
3983
+ /* @__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: [
3984
+ /* @__PURE__ */ jsx15("span", { children: "\u21B3" }),
3985
+ /* @__PURE__ */ jsx15("span", { className: "truncate", children: label })
3986
+ ] }),
3987
+ 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
3988
+ ] }, `${quote.stepNumber ?? "x"}-${index}`);
3989
+ }) }),
3990
+ 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 }) })
3991
+ ] });
3992
+ }
3993
+
3994
+ // src/components/UserMessageBubble.tsx
3995
+ import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
3656
3996
  function isUserMessage(message) {
3657
3997
  return message.role === "user";
3658
3998
  }
@@ -3664,8 +4004,12 @@ function UserMessageBubble({ message, className }) {
3664
4004
  const text = getTextContent2(message.content).trim();
3665
4005
  const fileParts = getFileParts2(message.content);
3666
4006
  const imageParts = getImageParts2(message.content);
3667
- return /* @__PURE__ */ jsx14("div", { className: cn("blade-chat-user-row flex justify-end", className), children: /* @__PURE__ */ jsxs12("div", { className: "blade-chat-user-col flex max-w-[72%] flex-col items-end gap-3", children: [
3668
- imageParts.length > 0 && /* @__PURE__ */ jsx14("div", { className: "grid gap-2", children: imageParts.map((part) => /* @__PURE__ */ jsx14(
4007
+ const whatifParsed = text && imageParts.length === 0 && fileParts.length === 0 ? parseWhatIfPrompt(text) : null;
4008
+ if (whatifParsed) {
4009
+ 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 }) }) });
4010
+ }
4011
+ 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: [
4012
+ imageParts.length > 0 && /* @__PURE__ */ jsx16("div", { className: "grid gap-2", children: imageParts.map((part) => /* @__PURE__ */ jsx16(
3669
4013
  "img",
3670
4014
  {
3671
4015
  src: part.image_url.url,
@@ -3674,21 +4018,21 @@ function UserMessageBubble({ message, className }) {
3674
4018
  },
3675
4019
  part.image_url.url
3676
4020
  )) }),
3677
- fileParts.length > 0 && /* @__PURE__ */ jsx14("div", { className: "flex flex-col items-end gap-1.5", children: fileParts.map((part) => /* @__PURE__ */ jsxs12(
4021
+ fileParts.length > 0 && /* @__PURE__ */ jsx16("div", { className: "flex flex-col items-end gap-1.5", children: fileParts.map((part) => /* @__PURE__ */ jsxs14(
3678
4022
  "div",
3679
4023
  {
3680
4024
  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))]",
3681
4025
  children: [
3682
- /* @__PURE__ */ jsx14(FileText, { size: 12, className: "shrink-0" }),
3683
- /* @__PURE__ */ jsx14("span", { className: "max-w-56 truncate", title: part.name, children: part.name })
4026
+ /* @__PURE__ */ jsx16(FileText, { size: 12, className: "shrink-0" }),
4027
+ /* @__PURE__ */ jsx16("span", { className: "max-w-56 truncate", title: part.name, children: part.name })
3684
4028
  ]
3685
4029
  },
3686
4030
  `${part.name}-${part.data.length}`
3687
4031
  )) }),
3688
- text && /* @__PURE__ */ jsx14("div", { className: "blade-chat-user-bubble max-w-full rounded-[20px] rounded-br-[6px] border border-[hsl(var(--user-msg-border))] bg-[hsl(var(--user-msg-bg))] px-[18px] py-[13px] text-sm leading-[1.65] text-[hsl(var(--user-msg-fg))]", children: /* @__PURE__ */ jsx14(MarkdownContent, { className: "blade-chat-prose", children: text }) }),
3689
- text && isSending(message) && /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1 pr-1 text-[11px] font-medium text-[hsl(var(--muted-foreground))/0.85]", children: [
3690
- /* @__PURE__ */ jsx14(LoaderCircle, { size: 11, className: "animate-spin", "aria-hidden": "true" }),
3691
- /* @__PURE__ */ jsx14("span", { children: "\u53D1\u9001\u4E2D" })
4032
+ 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 }) }),
4033
+ 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: [
4034
+ /* @__PURE__ */ jsx16(LoaderCircle, { size: 11, className: "animate-spin", "aria-hidden": "true" }),
4035
+ /* @__PURE__ */ jsx16("span", { children: "\u53D1\u9001\u4E2D" })
3692
4036
  ] })
3693
4037
  ] }) });
3694
4038
  }
@@ -3697,11 +4041,11 @@ function ErrorMessageBlock({
3697
4041
  className
3698
4042
  }) {
3699
4043
  const text = chatErrorForDisplay(getTextContent2(message.content));
3700
- return /* @__PURE__ */ jsx14("div", { className: cn("blade-chat-error-row flex min-w-0 justify-start", className), children: /* @__PURE__ */ jsx14("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 }) });
4044
+ 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 }) });
3701
4045
  }
3702
4046
 
3703
4047
  // src/components/MessageList.tsx
3704
- import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
4048
+ import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
3705
4049
  function parseModeChange(message) {
3706
4050
  if (message.kind !== "mode_change" || typeof message.content !== "string") {
3707
4051
  return null;
@@ -3742,6 +4086,7 @@ function MessageList({
3742
4086
  askAnswers,
3743
4087
  onAnswer,
3744
4088
  toolCallRenderer,
4089
+ hidePlanUpdateTools = false,
3745
4090
  emptyState,
3746
4091
  className,
3747
4092
  sessionId,
@@ -3827,23 +4172,23 @@ function MessageList({
3827
4172
  }
3828
4173
  return blocks;
3829
4174
  }, [messages, isStreaming]);
3830
- return /* @__PURE__ */ jsxs13("div", { className: cn("blade-chat-messages relative min-h-0 flex-1", className), children: [
3831
- isStreaming ? /* @__PURE__ */ jsx15("output", { className: "sr-only", children: "\u6B63\u5728\u751F\u6210\u56DE\u590D" }) : null,
3832
- /* @__PURE__ */ jsxs13(
4175
+ return /* @__PURE__ */ jsxs15("div", { className: cn("blade-chat-messages relative min-h-0 flex-1", className), children: [
4176
+ isStreaming ? /* @__PURE__ */ jsx17("output", { className: "sr-only", children: "\u6B63\u5728\u751F\u6210\u56DE\u590D" }) : null,
4177
+ /* @__PURE__ */ jsxs15(
3833
4178
  StickToBottom,
3834
4179
  {
3835
4180
  className: "h-full overflow-y-hidden",
3836
4181
  initial: "instant",
3837
4182
  resize: "instant",
3838
4183
  children: [
3839
- /* @__PURE__ */ jsx15(StickToBottom.Content, { className: "blade-chat-messages-scroll", children: /* @__PURE__ */ jsx15("div", { className: "blade-chat-messages-content mx-auto max-w-[748px]", children: /* @__PURE__ */ jsxs13("div", { className: "flex min-w-0 flex-col", children: [
3840
- renderBlocks.length === 0 ? emptyState ?? /* @__PURE__ */ jsxs13("div", { className: "blade-chat-empty", children: [
3841
- /* @__PURE__ */ jsx15(MessageSquare, { size: 40, strokeWidth: 1.5 }),
3842
- /* @__PURE__ */ jsx15("span", { className: "text-base font-medium", children: "\u5F00\u59CB\u5BF9\u8BDD" }),
3843
- /* @__PURE__ */ jsx15("span", { className: "text-sm opacity-60", children: "\u5728\u4E0B\u65B9\u8F93\u5165\u6D88\u606F\u5F00\u59CB\u804A\u5929" })
4184
+ /* @__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: [
4185
+ renderBlocks.length === 0 ? emptyState ?? /* @__PURE__ */ jsxs15("div", { className: "blade-chat-empty", children: [
4186
+ /* @__PURE__ */ jsx17(MessageSquare, { size: 40, strokeWidth: 1.5 }),
4187
+ /* @__PURE__ */ jsx17("span", { className: "text-base font-medium", children: "\u5F00\u59CB\u5BF9\u8BDD" }),
4188
+ /* @__PURE__ */ jsx17("span", { className: "text-sm opacity-60", children: "\u5728\u4E0B\u65B9\u8F93\u5165\u6D88\u606F\u5F00\u59CB\u804A\u5929" })
3844
4189
  ] }) : renderBlocks.map((block) => {
3845
4190
  if (block.type === "message") {
3846
- return /* @__PURE__ */ jsx15("div", { "data-entry-id": block.message.entry_id, children: isUserMessage(block.message) ? /* @__PURE__ */ jsx15(UserMessageBubble, { message: block.message }) : isErrorMessage(block.message) ? /* @__PURE__ */ jsx15(ErrorMessageBlock, { message: block.message }) : null }, block.key);
4191
+ 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);
3847
4192
  }
3848
4193
  if (block.type === "assistant_turn") {
3849
4194
  const blockFeedback = block.messages.map(
@@ -3854,14 +4199,14 @@ function MessageList({
3854
4199
  (message) => message.entry_id === postChatFollowup.assistant_entry_id
3855
4200
  )
3856
4201
  );
3857
- return /* @__PURE__ */ jsx15("div", { "data-entry-id": block.messages[0]?.entry_id, children: /* @__PURE__ */ jsxs13(
4202
+ return /* @__PURE__ */ jsx17("div", { "data-entry-id": block.messages[0]?.entry_id, children: /* @__PURE__ */ jsxs15(
3858
4203
  RenderErrorBoundary,
3859
4204
  {
3860
4205
  label: "\u52A9\u624B\u6D88\u606F",
3861
4206
  details: block.key,
3862
4207
  resetKey: getMessageResetSignature(block.messages),
3863
4208
  children: [
3864
- /* @__PURE__ */ jsx15(
4209
+ /* @__PURE__ */ jsx17(
3865
4210
  AssistantTurnBlock,
3866
4211
  {
3867
4212
  messages: block.messages,
@@ -3870,11 +4215,12 @@ function MessageList({
3870
4215
  onAnswer,
3871
4216
  sessionStatus,
3872
4217
  toolCallRenderer,
4218
+ hidePlanUpdateTools,
3873
4219
  sessionId
3874
4220
  }
3875
4221
  ),
3876
- blockFeedback && !hasActiveFollowup ? /* @__PURE__ */ jsx15(HistoricalResultFeedback, { feedback: blockFeedback }) : null,
3877
- hasActiveFollowup && postChatFollowup ? /* @__PURE__ */ jsx15(
4222
+ blockFeedback && !hasActiveFollowup ? /* @__PURE__ */ jsx17(HistoricalResultFeedback, { feedback: blockFeedback }) : null,
4223
+ hasActiveFollowup && postChatFollowup ? /* @__PURE__ */ jsx17(
3878
4224
  PostChatFollowupBlock,
3879
4225
  {
3880
4226
  followup: postChatFollowup,
@@ -3891,23 +4237,23 @@ function MessageList({
3891
4237
  ) }, block.key);
3892
4238
  }
3893
4239
  if (block.type === "compaction") {
3894
- return /* @__PURE__ */ jsxs13(
4240
+ return /* @__PURE__ */ jsxs15(
3895
4241
  "div",
3896
4242
  {
3897
4243
  className: "flex items-center gap-2 text-xs text-[hsl(var(--muted-foreground))]",
3898
4244
  children: [
3899
- /* @__PURE__ */ jsx15(Layers, { size: 12 }),
3900
- /* @__PURE__ */ jsx15("span", { children: "\u4E0A\u4E0B\u6587\u5DF2\u538B\u7F29" })
4245
+ /* @__PURE__ */ jsx17(Layers, { size: 12 }),
4246
+ /* @__PURE__ */ jsx17("span", { children: "\u4E0A\u4E0B\u6587\u5DF2\u538B\u7F29" })
3901
4247
  ]
3902
4248
  },
3903
4249
  block.key
3904
4250
  );
3905
4251
  }
3906
- return /* @__PURE__ */ jsx15(PlanningDivider, { kind: block.kind }, block.key);
4252
+ return /* @__PURE__ */ jsx17(PlanningDivider, { kind: block.kind }, block.key);
3907
4253
  }),
3908
- sessionStatus === "interrupted" && !isStreaming ? /* @__PURE__ */ jsx15("div", { className: "flex", children: /* @__PURE__ */ jsx15("div", { className: "rounded-full border border-amber-500/30 bg-amber-500/10 px-2.5 py-1 text-[10px] font-medium uppercase tracking-[0.12em] text-amber-300", children: "\u5DF2\u4E2D\u65AD" }) }) : null
4254
+ 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
3909
4255
  ] }) }) }),
3910
- /* @__PURE__ */ jsx15(
4256
+ /* @__PURE__ */ jsx17(
3911
4257
  PinLatestUserMessage,
3912
4258
  {
3913
4259
  userMessageCount: userMessages.length,
@@ -3916,7 +4262,7 @@ function MessageList({
3916
4262
  },
3917
4263
  sessionId ?? "no-session"
3918
4264
  ),
3919
- /* @__PURE__ */ jsx15(ScrollToBottomButton, {})
4265
+ /* @__PURE__ */ jsx17(ScrollToBottomButton, {})
3920
4266
  ]
3921
4267
  },
3922
4268
  sessionId ?? "no-session"
@@ -3929,8 +4275,8 @@ function PinLatestUserMessage({
3929
4275
  targetKey
3930
4276
  }) {
3931
4277
  const { contentRef, scrollRef, scrollToBottom, stopScroll } = useStickToBottomContext();
3932
- const previousCountRef = useRef11(userMessageCount);
3933
- const spacerHeightRef = useRef11(0);
4278
+ const previousCountRef = useRef12(userMessageCount);
4279
+ const spacerHeightRef = useRef12(0);
3934
4280
  const getScrollElement = useCallback7(() => scrollRef.current, [scrollRef]);
3935
4281
  const getContentElement = useCallback7(() => contentRef.current, [contentRef]);
3936
4282
  const getTargetElement = useCallback7(() => {
@@ -3959,7 +4305,7 @@ function PinLatestUserMessage({
3959
4305
  stopAutoScroll: stopScroll,
3960
4306
  scrollToBottom
3961
4307
  });
3962
- useEffect10(() => {
4308
+ useEffect11(() => {
3963
4309
  if (userMessageCount > previousCountRef.current && !shouldPinLatestUser) {
3964
4310
  scrollToBottom("instant");
3965
4311
  }
@@ -3969,9 +4315,9 @@ function PinLatestUserMessage({
3969
4315
  }
3970
4316
  function ScrollToBottomButton() {
3971
4317
  const { isAtBottom, scrollToBottom } = useStickToBottomContext();
3972
- const [visible, setVisible] = useState12(false);
3973
- const hideTimerRef = useRef11(null);
3974
- useEffect10(() => {
4318
+ const [visible, setVisible] = useState13(false);
4319
+ const hideTimerRef = useRef12(null);
4320
+ useEffect11(() => {
3975
4321
  if (isAtBottom) {
3976
4322
  if (!hideTimerRef.current) {
3977
4323
  hideTimerRef.current = setTimeout(() => {
@@ -4002,7 +4348,7 @@ function ScrollToBottomButton() {
4002
4348
  scrollToBottom();
4003
4349
  }, [scrollToBottom]);
4004
4350
  if (!visible) return null;
4005
- return /* @__PURE__ */ jsxs13(
4351
+ return /* @__PURE__ */ jsxs15(
4006
4352
  "button",
4007
4353
  {
4008
4354
  type: "button",
@@ -4010,25 +4356,25 @@ function ScrollToBottomButton() {
4010
4356
  "aria-label": "\u6EDA\u52A8\u5230\u5E95\u90E8",
4011
4357
  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))]",
4012
4358
  children: [
4013
- /* @__PURE__ */ jsx15(ChevronDown, { size: 14 }),
4014
- /* @__PURE__ */ jsx15("span", { className: "blade-chat-scroll-bottom-label", children: "\u6EDA\u52A8\u5230\u5E95\u90E8" })
4359
+ /* @__PURE__ */ jsx17(ChevronDown, { size: 14 }),
4360
+ /* @__PURE__ */ jsx17("span", { className: "blade-chat-scroll-bottom-label", children: "\u6EDA\u52A8\u5230\u5E95\u90E8" })
4015
4361
  ]
4016
4362
  }
4017
4363
  );
4018
4364
  }
4019
4365
  function PlanningDivider({ kind }) {
4020
- return /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-3 py-1", children: [
4021
- /* @__PURE__ */ jsx15("div", { className: "h-px flex-1 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent" }),
4022
- /* @__PURE__ */ jsxs13("div", { className: "inline-flex items-center gap-1.5 rounded-full border border-amber-500/30 bg-amber-500/10 px-3 py-1 text-[11px] text-amber-300", children: [
4023
- /* @__PURE__ */ jsx15(Lightbulb, { size: 12 }),
4024
- /* @__PURE__ */ jsx15("span", { children: kind === "enter" ? "\u8FDB\u5165\u89C4\u5212\u6A21\u5F0F" : "\u89C4\u5212\u5B8C\u6210" })
4366
+ return /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-3 py-1", children: [
4367
+ /* @__PURE__ */ jsx17("div", { className: "h-px flex-1 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent" }),
4368
+ /* @__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: [
4369
+ /* @__PURE__ */ jsx17(Lightbulb, { size: 12 }),
4370
+ /* @__PURE__ */ jsx17("span", { children: kind === "enter" ? "\u8FDB\u5165\u89C4\u5212\u6A21\u5F0F" : "\u89C4\u5212\u5B8C\u6210" })
4025
4371
  ] }),
4026
- /* @__PURE__ */ jsx15("div", { className: "h-px flex-1 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent" })
4372
+ /* @__PURE__ */ jsx17("div", { className: "h-px flex-1 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent" })
4027
4373
  ] });
4028
4374
  }
4029
4375
 
4030
4376
  // src/components/ChatSurface.tsx
4031
- import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
4377
+ import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
4032
4378
  function themeAttr(theme) {
4033
4379
  return theme === "dark" ? "dark" : void 0;
4034
4380
  }
@@ -4058,9 +4404,11 @@ function ChatSurface({
4058
4404
  onResultFeedbackSaved,
4059
4405
  onFollowupInteraction,
4060
4406
  beforeInput,
4407
+ showPlanUpdates = false,
4408
+ planRevealRevision = 0,
4061
4409
  banner
4062
4410
  }) {
4063
- return /* @__PURE__ */ jsxs14(
4411
+ return /* @__PURE__ */ jsxs16(
4064
4412
  "div",
4065
4413
  {
4066
4414
  "data-theme": themeAttr(theme),
@@ -4069,14 +4417,14 @@ function ChatSurface({
4069
4417
  classNames?.root
4070
4418
  ),
4071
4419
  children: [
4072
- /* @__PURE__ */ jsx16(ConnectionBanner, { connection, className: classNames?.banner }),
4420
+ /* @__PURE__ */ jsx18(ConnectionBanner, { connection, className: classNames?.banner }),
4073
4421
  banner,
4074
- errorMessage && /* @__PURE__ */ jsxs14("div", { className: "blade-chat-error-bar flex items-start gap-2 border-b px-4 py-3 text-sm", children: [
4075
- /* @__PURE__ */ jsx16(CircleAlert, { size: 16, className: "mt-0.5 shrink-0" }),
4076
- /* @__PURE__ */ jsx16("span", { className: "min-w-0 whitespace-pre-wrap break-words [overflow-wrap:anywhere]", children: chatErrorForDisplay2(errorMessage) })
4422
+ errorMessage && /* @__PURE__ */ jsxs16("div", { className: "blade-chat-error-bar flex items-start gap-2 border-b px-4 py-3 text-sm", children: [
4423
+ /* @__PURE__ */ jsx18(CircleAlert, { size: 16, className: "mt-0.5 shrink-0" }),
4424
+ /* @__PURE__ */ jsx18("span", { className: "min-w-0 whitespace-pre-wrap break-words [overflow-wrap:anywhere]", children: chatErrorForDisplay2(errorMessage) })
4077
4425
  ] }),
4078
4426
  slots?.header,
4079
- /* @__PURE__ */ jsx16(
4427
+ /* @__PURE__ */ jsx18(
4080
4428
  MessageList,
4081
4429
  {
4082
4430
  messages,
@@ -4087,6 +4435,7 @@ function ChatSurface({
4087
4435
  askAnswers,
4088
4436
  onAnswer,
4089
4437
  toolCallRenderer: renderers?.toolCall,
4438
+ hidePlanUpdateTools: showPlanUpdates,
4090
4439
  emptyState: slots?.emptyState,
4091
4440
  className: classNames?.messageList,
4092
4441
  sessionId,
@@ -4096,8 +4445,18 @@ function ChatSurface({
4096
4445
  onFollowupInteraction
4097
4446
  }
4098
4447
  ),
4448
+ showPlanUpdates ? /* @__PURE__ */ jsx18(
4449
+ CurrentPlanPanel,
4450
+ {
4451
+ messages,
4452
+ running: isStreaming,
4453
+ revealRevision: planRevealRevision,
4454
+ sessionId,
4455
+ className: "border-t border-[hsl(var(--border))]"
4456
+ }
4457
+ ) : null,
4099
4458
  beforeInput,
4100
- /* @__PURE__ */ jsx16(
4459
+ /* @__PURE__ */ jsx18(
4101
4460
  ChatInput,
4102
4461
  {
4103
4462
  value: inputText,
@@ -4117,13 +4476,13 @@ function ChatSurface({
4117
4476
  }
4118
4477
 
4119
4478
  // src/components/AgentChat.tsx
4120
- import { Fragment as Fragment3, jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
4479
+ import { Fragment as Fragment3, jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
4121
4480
  function isUnauthorizedError(error) {
4122
4481
  return error instanceof BladeApiError && error.status === 401;
4123
4482
  }
4124
4483
  function LoginCard({ client, onLoggedIn }) {
4125
- const [loggingIn, setLoggingIn] = useState13(false);
4126
- const [loginError, setLoginError] = useState13(null);
4484
+ const [loggingIn, setLoggingIn] = useState14(false);
4485
+ const [loginError, setLoginError] = useState14(null);
4127
4486
  const handleLogin = async () => {
4128
4487
  setLoggingIn(true);
4129
4488
  setLoginError(null);
@@ -4136,11 +4495,11 @@ function LoginCard({ client, onLoggedIn }) {
4136
4495
  setLoggingIn(false);
4137
4496
  }
4138
4497
  };
4139
- return /* @__PURE__ */ jsx17("div", { className: "blade-chat-login flex flex-1 items-center justify-center p-6", children: /* @__PURE__ */ jsxs15("div", { className: "flex w-full max-w-sm flex-col items-center gap-4 rounded-2xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-6 py-8 text-center", children: [
4140
- /* @__PURE__ */ jsx17(LockKeyhole, { size: 28, className: "text-[hsl(var(--muted-foreground))]" }),
4141
- /* @__PURE__ */ jsx17("div", { className: "text-base font-medium text-[hsl(var(--foreground))]", children: "\u9700\u8981\u767B\u5F55\u540E\u4F7F\u7528" }),
4142
- /* @__PURE__ */ jsx17("div", { className: "text-sm text-[hsl(var(--muted-foreground))]", children: "\u767B\u5F55\u540E\u5373\u53EF\u4E0E\u667A\u80FD\u4F53\u5BF9\u8BDD\uFF0C\u4F60\u7684\u4F1A\u8BDD\u5185\u5BB9\u4EC5\u81EA\u5DF1\u53EF\u89C1\u3002" }),
4143
- /* @__PURE__ */ jsx17(
4498
+ 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: [
4499
+ /* @__PURE__ */ jsx19(LockKeyhole, { size: 28, className: "text-[hsl(var(--muted-foreground))]" }),
4500
+ /* @__PURE__ */ jsx19("div", { className: "text-base font-medium text-[hsl(var(--foreground))]", children: "\u9700\u8981\u767B\u5F55\u540E\u4F7F\u7528" }),
4501
+ /* @__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" }),
4502
+ /* @__PURE__ */ jsx19(
4144
4503
  "button",
4145
4504
  {
4146
4505
  type: "button",
@@ -4150,20 +4509,20 @@ function LoginCard({ client, onLoggedIn }) {
4150
4509
  children: loggingIn ? "\u767B\u5F55\u4E2D\u2026" : "\u767B\u5F55"
4151
4510
  }
4152
4511
  ),
4153
- loginError && /* @__PURE__ */ jsx17("div", { className: "text-xs text-[hsl(var(--destructive))]", children: loginError })
4512
+ loginError && /* @__PURE__ */ jsx19("div", { className: "text-xs text-[hsl(var(--destructive))]", children: loginError })
4154
4513
  ] }) });
4155
4514
  }
4156
4515
  function AgentChat(props) {
4157
4516
  const client = useBladeClient();
4158
- const [attempt, setAttempt] = useState13(0);
4159
- const [needLogin, setNeedLogin] = useState13(() => !client.hasToken());
4517
+ const [attempt, setAttempt] = useState14(0);
4518
+ const [needLogin, setNeedLogin] = useState14(() => !client.hasToken());
4160
4519
  if (needLogin) {
4161
- return /* @__PURE__ */ jsx17(
4520
+ return /* @__PURE__ */ jsx19(
4162
4521
  "div",
4163
4522
  {
4164
4523
  "data-theme": themeAttr(props.theme),
4165
4524
  className: cn("blade-chat flex min-h-0 flex-1 flex-col", props.classNames?.root),
4166
- children: /* @__PURE__ */ jsx17(
4525
+ children: /* @__PURE__ */ jsx19(
4167
4526
  LoginCard,
4168
4527
  {
4169
4528
  client,
@@ -4176,7 +4535,7 @@ function AgentChat(props) {
4176
4535
  }
4177
4536
  );
4178
4537
  }
4179
- return /* @__PURE__ */ jsx17(ChatSessionView, { ...props, onUnauthorized: () => setNeedLogin(true) }, attempt);
4538
+ return /* @__PURE__ */ jsx19(ChatSessionView, { ...props, onUnauthorized: () => setNeedLogin(true) }, attempt);
4180
4539
  }
4181
4540
  function ChatSessionView({
4182
4541
  sessionId,
@@ -4193,17 +4552,33 @@ function ChatSessionView({
4193
4552
  onUnauthorized
4194
4553
  }) {
4195
4554
  const client = useBladeClient();
4555
+ const [planRevealRevisions, setPlanRevealRevisions] = useState14(
4556
+ () => /* @__PURE__ */ new Map()
4557
+ );
4558
+ const handleSessionConnected = useCallback8((connectedSession) => {
4559
+ return connectedSession.on("toolResult", ({ toolCall, turn, source }) => {
4560
+ if (source === "reconnect_replay" || (turn.loop_id || "root") !== "root" || toolCall.status !== "done" || !isPlanUpdateTool(toolCall) || !parsePlanUpdate(toolCall.arguments)) {
4561
+ return;
4562
+ }
4563
+ setPlanRevealRevisions((current) => {
4564
+ const next = new Map(current);
4565
+ next.set(connectedSession.sessionId, (current.get(connectedSession.sessionId) ?? 0) + 1);
4566
+ return next;
4567
+ });
4568
+ });
4569
+ }, []);
4196
4570
  const { session, state, error } = useAgentSession(sessionId, {
4197
4571
  createOptions,
4198
- onSessionCreated
4572
+ onSessionCreated,
4573
+ onSessionConnected: handleSessionConnected
4199
4574
  });
4200
4575
  const replay = useReplay(session);
4201
- const [stopRequested, setStopRequested] = useState13(false);
4202
- const [inputText, setInputText] = useState13("");
4203
- const [resultFeedback, setResultFeedback] = useState13([]);
4576
+ const [stopRequested, setStopRequested] = useState14(false);
4577
+ const [inputText, setInputText] = useState14("");
4578
+ const [resultFeedback, setResultFeedback] = useState14([]);
4204
4579
  const resolvedSessionId = session?.sessionId;
4205
4580
  const isViewer = state?.viewerRole === "viewer";
4206
- useEffect11(() => {
4581
+ useEffect12(() => {
4207
4582
  setResultFeedback([]);
4208
4583
  if (!resolvedSessionId || isViewer) return;
4209
4584
  let cancelled = false;
@@ -4238,12 +4613,12 @@ function ChatSessionView({
4238
4613
  saved
4239
4614
  ]);
4240
4615
  }, []);
4241
- useEffect11(() => {
4616
+ useEffect12(() => {
4242
4617
  if (session) {
4243
4618
  onSessionReady?.(session);
4244
4619
  }
4245
4620
  }, [session, onSessionReady]);
4246
- useEffect11(() => {
4621
+ useEffect12(() => {
4247
4622
  if (!session) return;
4248
4623
  const offAttach = session.on("attachRequested", ({ label, content }) => {
4249
4624
  setInputText((prev) => `${prev ? `${prev}
@@ -4259,12 +4634,12 @@ ${content}`);
4259
4634
  offInsert();
4260
4635
  };
4261
4636
  }, [session]);
4262
- useEffect11(() => {
4637
+ useEffect12(() => {
4263
4638
  if (isUnauthorizedError(error)) {
4264
4639
  onUnauthorized();
4265
4640
  }
4266
4641
  }, [error, onUnauthorized]);
4267
- useEffect11(() => {
4642
+ useEffect12(() => {
4268
4643
  if (!session || !commands) return;
4269
4644
  const unsubscribes = Object.entries(commands).map(
4270
4645
  ([action, handler]) => session.onCommand(action, (payload) => handler(payload))
@@ -4274,6 +4649,7 @@ ${content}`);
4274
4649
  };
4275
4650
  }, [session, commands]);
4276
4651
  const isStreaming = state?.isStreaming ?? false;
4652
+ const planRevealRevision = resolvedSessionId ? planRevealRevisions.get(resolvedSessionId) ?? 0 : 0;
4277
4653
  const isStopping = stopRequested && isStreaming;
4278
4654
  const connectError = error && !isUnauthorizedError(error) ? error.message || "\u8FDE\u63A5\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5" : null;
4279
4655
  const errorMessage = connectError ?? state?.errorMessage ?? replay.error?.message ?? null;
@@ -4285,7 +4661,7 @@ ${content}`);
4285
4661
  setStopRequested(true);
4286
4662
  void session?.stop();
4287
4663
  };
4288
- return /* @__PURE__ */ jsx17(
4664
+ return /* @__PURE__ */ jsx19(
4289
4665
  ChatSurface,
4290
4666
  {
4291
4667
  theme,
@@ -4295,8 +4671,8 @@ ${content}`);
4295
4671
  slots,
4296
4672
  placeholder,
4297
4673
  connection: state?.connection ?? "connecting",
4298
- banner: /* @__PURE__ */ jsxs15(Fragment3, { children: [
4299
- /* @__PURE__ */ jsx17(
4674
+ banner: /* @__PURE__ */ jsxs17(Fragment3, { children: [
4675
+ /* @__PURE__ */ jsx19(
4300
4676
  ReplayBar,
4301
4677
  {
4302
4678
  isReplay: replay.isReplay,
@@ -4306,7 +4682,7 @@ ${content}`);
4306
4682
  onExit: () => void replay.exitToAutonomous()
4307
4683
  }
4308
4684
  ),
4309
- /* @__PURE__ */ jsx17(ReplayMismatchPrompt, { mismatch: replay.mismatch })
4685
+ /* @__PURE__ */ jsx19(ReplayMismatchPrompt, { mismatch: replay.mismatch })
4310
4686
  ] }),
4311
4687
  errorMessage,
4312
4688
  messages: state?.messages ?? [],
@@ -4314,6 +4690,8 @@ ${content}`);
4314
4690
  resultFeedbackByEntry,
4315
4691
  onResultFeedbackSaved: handleResultFeedbackSaved,
4316
4692
  isStreaming,
4693
+ showPlanUpdates: true,
4694
+ planRevealRevision,
4317
4695
  isStopping,
4318
4696
  inputText,
4319
4697
  onInputChange: setInputText,
@@ -4335,11 +4713,11 @@ ${content}`);
4335
4713
  }
4336
4714
 
4337
4715
  // src/components/LlmChat.tsx
4338
- import { useEffect as useEffect12, useMemo as useMemo9, useState as useState15 } from "react";
4716
+ import { useEffect as useEffect13, useMemo as useMemo9, useState as useState16 } from "react";
4339
4717
 
4340
4718
  // src/components/LlmAdvancedSettings.tsx
4341
- import { useState as useState14 } from "react";
4342
- import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
4719
+ import { useState as useState15 } from "react";
4720
+ import { jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
4343
4721
  var FIELDS = [
4344
4722
  { id: "baseURL", label: "\u6A21\u578B\u670D\u52A1\u5730\u5740", placeholder: "http://\u5185\u7F51\u5730\u5740/v1" },
4345
4723
  { id: "model", label: "\u6A21\u578B", placeholder: "\u6A21\u578B\u540D\u79F0" },
@@ -4385,13 +4763,13 @@ function writeOverride(settings, baseURL, override) {
4385
4763
  }
4386
4764
  function LlmAdvancedSettingsBar({ settings, defaults, override, onChange }) {
4387
4765
  const normalized = normalizeAdvanced(settings);
4388
- const [open, setOpen] = useState14(false);
4389
- const [draft, setDraft] = useState14(override);
4766
+ const [open, setOpen] = useState15(false);
4767
+ const [draft, setDraft] = useState15(override);
4390
4768
  if (!normalized) return null;
4391
4769
  const fields = FIELDS.filter((field) => normalized[field.id]);
4392
4770
  const dirty = Object.keys(override).length > 0;
4393
- return /* @__PURE__ */ jsxs16("div", { className: "blade-chat-advanced border-t border-[hsl(var(--border))] px-4 py-2 text-xs", children: [
4394
- /* @__PURE__ */ jsxs16(
4771
+ return /* @__PURE__ */ jsxs18("div", { className: "blade-chat-advanced border-t border-[hsl(var(--border))] px-4 py-2 text-xs", children: [
4772
+ /* @__PURE__ */ jsxs18(
4395
4773
  "button",
4396
4774
  {
4397
4775
  type: "button",
@@ -4401,16 +4779,16 @@ function LlmAdvancedSettingsBar({ settings, defaults, override, onChange }) {
4401
4779
  },
4402
4780
  className: "flex items-center gap-1.5 text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))]",
4403
4781
  children: [
4404
- /* @__PURE__ */ jsx18(Settings2, { size: 13 }),
4782
+ /* @__PURE__ */ jsx20(Settings2, { size: 13 }),
4405
4783
  "\u9AD8\u7EA7\u8BBE\u7F6E",
4406
- dirty && /* @__PURE__ */ jsx18("span", { className: "text-[hsl(var(--primary))]", children: "\uFF08\u5DF2\u81EA\u5B9A\u4E49\uFF09" })
4784
+ dirty && /* @__PURE__ */ jsx20("span", { className: "text-[hsl(var(--primary))]", children: "\uFF08\u5DF2\u81EA\u5B9A\u4E49\uFF09" })
4407
4785
  ]
4408
4786
  }
4409
4787
  ),
4410
- open && /* @__PURE__ */ jsxs16("div", { className: "mt-2 flex flex-col gap-2", children: [
4411
- fields.map((field) => /* @__PURE__ */ jsxs16("label", { className: "flex flex-col gap-1", children: [
4412
- /* @__PURE__ */ jsx18("span", { className: "text-[hsl(var(--muted-foreground))]", children: field.label }),
4413
- /* @__PURE__ */ jsx18(
4788
+ open && /* @__PURE__ */ jsxs18("div", { className: "mt-2 flex flex-col gap-2", children: [
4789
+ fields.map((field) => /* @__PURE__ */ jsxs18("label", { className: "flex flex-col gap-1", children: [
4790
+ /* @__PURE__ */ jsx20("span", { className: "text-[hsl(var(--muted-foreground))]", children: field.label }),
4791
+ /* @__PURE__ */ jsx20(
4414
4792
  "input",
4415
4793
  {
4416
4794
  type: field.secret ? "password" : "text",
@@ -4421,9 +4799,9 @@ function LlmAdvancedSettingsBar({ settings, defaults, override, onChange }) {
4421
4799
  }
4422
4800
  )
4423
4801
  ] }, field.id)),
4424
- normalized.apiKey && /* @__PURE__ */ jsx18("p", { className: "text-[hsl(var(--muted-foreground))]", children: "\u5BC6\u94A5\u4F1A\u5B58\u5728\u8FD9\u53F0\u6D4F\u89C8\u5668\u91CC\u3002\u53EA\u5728\u4F60\u4FE1\u5F97\u8FC7\u8FD9\u53F0\u673A\u5668\u65F6\u586B\u3002" }),
4425
- /* @__PURE__ */ jsxs16("div", { className: "flex gap-2", children: [
4426
- /* @__PURE__ */ jsx18(
4802
+ 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" }),
4803
+ /* @__PURE__ */ jsxs18("div", { className: "flex gap-2", children: [
4804
+ /* @__PURE__ */ jsx20(
4427
4805
  "button",
4428
4806
  {
4429
4807
  type: "button",
@@ -4438,7 +4816,7 @@ function LlmAdvancedSettingsBar({ settings, defaults, override, onChange }) {
4438
4816
  children: "\u4FDD\u5B58"
4439
4817
  }
4440
4818
  ),
4441
- /* @__PURE__ */ jsx18(
4819
+ /* @__PURE__ */ jsx20(
4442
4820
  "button",
4443
4821
  {
4444
4822
  type: "button",
@@ -4457,7 +4835,7 @@ function LlmAdvancedSettingsBar({ settings, defaults, override, onChange }) {
4457
4835
  }
4458
4836
 
4459
4837
  // src/components/LlmChat.tsx
4460
- import { jsx as jsx19 } from "react/jsx-runtime";
4838
+ import { jsx as jsx21 } from "react/jsx-runtime";
4461
4839
  function LlmChat({
4462
4840
  classNames,
4463
4841
  renderers,
@@ -4469,11 +4847,11 @@ function LlmChat({
4469
4847
  onOverrideChange,
4470
4848
  ...options
4471
4849
  }) {
4472
- const [override, setOverride] = useState15(() => readOverride(advanced, options.baseURL));
4850
+ const [override, setOverride] = useState16(() => readOverride(advanced, options.baseURL));
4473
4851
  const effective = { ...options, ...override };
4474
4852
  const { messages, isStreaming, error, send, stop, reset } = useLlmChat(effective);
4475
- const [inputText, setInputText] = useState15("");
4476
- const [stopRequested, setStopRequested] = useState15(false);
4853
+ const [inputText, setInputText] = useState16("");
4854
+ const [stopRequested, setStopRequested] = useState16(false);
4477
4855
  const handle = useMemo9(
4478
4856
  () => ({
4479
4857
  insertText: (text) => setInputText((prev) => prev ? `${prev}
@@ -4483,10 +4861,10 @@ ${text}` : text),
4483
4861
  }),
4484
4862
  [send, reset]
4485
4863
  );
4486
- useEffect12(() => {
4864
+ useEffect13(() => {
4487
4865
  onReady?.(handle);
4488
4866
  }, [handle, onReady]);
4489
- return /* @__PURE__ */ jsx19(
4867
+ return /* @__PURE__ */ jsx21(
4490
4868
  ChatSurface,
4491
4869
  {
4492
4870
  theme,
@@ -4511,7 +4889,7 @@ ${text}` : text),
4511
4889
  setStopRequested(true);
4512
4890
  stop();
4513
4891
  },
4514
- beforeInput: advanced ? /* @__PURE__ */ jsx19(
4892
+ beforeInput: advanced ? /* @__PURE__ */ jsx21(
4515
4893
  LlmAdvancedSettingsBar,
4516
4894
  {
4517
4895
  settings: advanced,
@@ -4529,14 +4907,14 @@ ${text}` : text),
4529
4907
  }
4530
4908
 
4531
4909
  // src/components/ChatView.tsx
4532
- import { jsx as jsx20 } from "react/jsx-runtime";
4910
+ import { jsx as jsx22 } from "react/jsx-runtime";
4533
4911
  function ChatView(props) {
4534
4912
  const { mode, llm, onLlmReady, ...rest } = props;
4535
4913
  if (mode === "llm") {
4536
4914
  if (!llm) {
4537
4915
  throw new Error('ChatView: mode="llm" \u9700\u8981\u540C\u65F6\u4F20 llm={{ baseURL, model }}');
4538
4916
  }
4539
- return /* @__PURE__ */ jsx20(
4917
+ return /* @__PURE__ */ jsx22(
4540
4918
  LlmChat,
4541
4919
  {
4542
4920
  ...llm,
@@ -4549,23 +4927,24 @@ function ChatView(props) {
4549
4927
  }
4550
4928
  );
4551
4929
  }
4552
- return /* @__PURE__ */ jsx20(AgentChat, { ...rest });
4930
+ return /* @__PURE__ */ jsx22(AgentChat, { ...rest });
4553
4931
  }
4554
4932
 
4555
4933
  // src/components/ContextCard.tsx
4556
4934
  import {
4557
- getContextDisplayState
4935
+ getContextDisplayState,
4936
+ getContextGroupDisplayState
4558
4937
  } from "@blade-hq/agent-client";
4559
- import { jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
4938
+ import { jsx as jsx23, jsxs as jsxs19 } from "react/jsx-runtime";
4560
4939
  function ContextCard({ context, className }) {
4561
4940
  const display = getContextDisplayState(context);
4562
- return /* @__PURE__ */ jsxs17(
4941
+ return /* @__PURE__ */ jsxs19(
4563
4942
  "details",
4564
4943
  {
4565
- className: `blade-chat-context-card group rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] text-sm ${className ?? ""}`,
4944
+ className: `blade-chat-context-card group/context-card rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] text-sm ${className ?? ""}`,
4566
4945
  children: [
4567
- /* @__PURE__ */ jsxs17("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: [
4568
- /* @__PURE__ */ jsx21(
4946
+ /* @__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: [
4947
+ /* @__PURE__ */ jsx23(
4569
4948
  Layers,
4570
4949
  {
4571
4950
  size: 15,
@@ -4573,24 +4952,60 @@ function ContextCard({ context, className }) {
4573
4952
  "aria-hidden": "true"
4574
4953
  }
4575
4954
  ),
4576
- /* @__PURE__ */ jsxs17("span", { className: "blade-chat-context-copy min-w-0 flex-1", children: [
4577
- /* @__PURE__ */ jsx21("span", { className: "blade-chat-context-title block font-medium text-[hsl(var(--foreground))]", children: display.title }),
4578
- /* @__PURE__ */ jsx21("span", { className: "blade-chat-context-status block truncate text-xs text-[hsl(var(--muted-foreground))]", children: display.summary })
4955
+ /* @__PURE__ */ jsxs19("span", { className: "blade-chat-context-copy min-w-0 flex-1", children: [
4956
+ /* @__PURE__ */ jsx23("span", { className: "blade-chat-context-title block font-medium text-[hsl(var(--foreground))]", children: display.title }),
4957
+ /* @__PURE__ */ jsx23("span", { className: "blade-chat-context-status block truncate text-xs text-[hsl(var(--muted-foreground))]", children: display.summary })
4579
4958
  ] }),
4580
- /* @__PURE__ */ jsx21(
4959
+ /* @__PURE__ */ jsx23(
4581
4960
  ChevronDown,
4582
4961
  {
4583
4962
  size: 14,
4584
- className: "blade-chat-context-chevron shrink-0 text-[hsl(var(--muted-foreground))] transition-transform group-open:rotate-180",
4963
+ className: "blade-chat-context-chevron shrink-0 text-[hsl(var(--muted-foreground))] transition-transform group-open/context-card:rotate-180",
4585
4964
  "aria-hidden": "true"
4586
4965
  }
4587
4966
  )
4588
4967
  ] }),
4589
- /* @__PURE__ */ jsx21("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 })
4968
+ /* @__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 })
4590
4969
  ]
4591
4970
  }
4592
4971
  );
4593
4972
  }
4973
+ function ContextGroupCard({ contexts, className }) {
4974
+ if (contexts.length === 0) return null;
4975
+ const single = contexts.length === 1 ? getContextDisplayState(contexts[0]) : null;
4976
+ const group = single ? null : getContextGroupDisplayState(contexts);
4977
+ 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: [
4978
+ /* @__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: [
4979
+ /* @__PURE__ */ jsx23(
4980
+ Layers,
4981
+ {
4982
+ size: 15,
4983
+ className: "blade-chat-context-icon shrink-0 text-[hsl(var(--muted-foreground))]",
4984
+ "aria-hidden": "true"
4985
+ }
4986
+ ),
4987
+ /* @__PURE__ */ jsxs19("span", { className: "blade-chat-context-copy min-w-0 flex-1", children: [
4988
+ /* @__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` }),
4989
+ /* @__PURE__ */ jsx23("span", { className: "blade-chat-context-status block truncate text-xs text-[hsl(var(--muted-foreground))]", children: single ? single.summary : group?.summary })
4990
+ ] }),
4991
+ /* @__PURE__ */ jsx23(
4992
+ ChevronDown,
4993
+ {
4994
+ size: 14,
4995
+ className: "blade-chat-context-chevron shrink-0 text-[hsl(var(--muted-foreground))] transition-transform group-open/context-group:rotate-180",
4996
+ "aria-hidden": "true"
4997
+ }
4998
+ )
4999
+ ] }),
5000
+ 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(
5001
+ ContextCard,
5002
+ {
5003
+ context
5004
+ },
5005
+ `${context.context_kind}:${context.context_key}`
5006
+ )) })
5007
+ ] });
5008
+ }
4594
5009
 
4595
5010
  // src/lib/agent-computer-command.ts
4596
5011
  var COMPUTER_LAUNCH_COMMAND_PATTERN = /(?:^|[\n;&|(]\s*)computer\s+launch(?:\s|$)/;
@@ -4641,15 +5056,26 @@ export {
4641
5056
  BladeProvider,
4642
5057
  ChatView,
4643
5058
  ContextCard,
5059
+ ContextGroupCard,
5060
+ CurrentPlanPanel,
4644
5061
  LlmChat,
4645
5062
  MarkdownContent,
4646
5063
  MemoryRefsHint,
5064
+ PLAN_AUTO_COLLAPSE_MS,
5065
+ PlanUpdateBlock,
4647
5066
  ReplayBar,
4648
5067
  ReplayMismatchPrompt,
5068
+ WhatIfUserBubble,
4649
5069
  classifyAgentComputerLaunchOutcome,
4650
5070
  collectMemoryRefs,
5071
+ getPlanUpdateDisplayState,
4651
5072
  isAgentComputerCommand,
4652
5073
  isAgentComputerToolCall,
5074
+ isPlanUpdateTool,
5075
+ normalizeAdjacentUrlFormatting,
5076
+ parsePlanUpdate,
5077
+ parseWhatIfPrompt,
5078
+ pickCurrentPlanStep,
4653
5079
  useAgentSession,
4654
5080
  useBladeClient,
4655
5081
  useLlmChat,
@@ -4671,6 +5097,8 @@ lucide-react/dist/esm/icons/check.js:
4671
5097
  lucide-react/dist/esm/icons/chevron-down.js:
4672
5098
  lucide-react/dist/esm/icons/chevron-right.js:
4673
5099
  lucide-react/dist/esm/icons/circle-alert.js:
5100
+ lucide-react/dist/esm/icons/circle-dot.js:
5101
+ lucide-react/dist/esm/icons/circle.js:
4674
5102
  lucide-react/dist/esm/icons/copy.js:
4675
5103
  lucide-react/dist/esm/icons/earth.js:
4676
5104
  lucide-react/dist/esm/icons/file-pen-line.js:
@@ -4678,11 +5106,13 @@ lucide-react/dist/esm/icons/file-text.js:
4678
5106
  lucide-react/dist/esm/icons/globe.js:
4679
5107
  lucide-react/dist/esm/icons/layers.js:
4680
5108
  lucide-react/dist/esm/icons/lightbulb.js:
5109
+ lucide-react/dist/esm/icons/list-checks.js:
4681
5110
  lucide-react/dist/esm/icons/loader-circle.js:
4682
5111
  lucide-react/dist/esm/icons/lock-keyhole.js:
4683
5112
  lucide-react/dist/esm/icons/message-square-more.js:
4684
5113
  lucide-react/dist/esm/icons/message-square.js:
4685
5114
  lucide-react/dist/esm/icons/play.js:
5115
+ lucide-react/dist/esm/icons/refresh-ccw.js:
4686
5116
  lucide-react/dist/esm/icons/search.js:
4687
5117
  lucide-react/dist/esm/icons/settings-2.js:
4688
5118
  lucide-react/dist/esm/icons/sparkles.js: