@blade-hq/agent-kit 0.4.5 → 0.4.7

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.
Files changed (52) hide show
  1. package/README.md +39 -1
  2. package/dist/{SkillStatusBar-DItrW2vv.d.ts → SkillStatusBar-DQyipdzn.d.ts} +112 -8
  3. package/dist/chunk-2UP7MG3J.js +66 -0
  4. package/dist/chunk-2UP7MG3J.js.map +1 -0
  5. package/dist/chunk-4VWLTG5L.js +2984 -0
  6. package/dist/chunk-4VWLTG5L.js.map +1 -0
  7. package/dist/chunk-7LEKQI47.js +32 -0
  8. package/dist/chunk-7LEKQI47.js.map +1 -0
  9. package/dist/chunk-CGOQI7ZL.js +8124 -0
  10. package/dist/chunk-CGOQI7ZL.js.map +1 -0
  11. package/dist/chunk-DQCXSPHP.js +33 -0
  12. package/dist/chunk-DQCXSPHP.js.map +1 -0
  13. package/dist/chunk-I3FFV63W.js +30 -0
  14. package/dist/chunk-I3FFV63W.js.map +1 -0
  15. package/dist/chunk-J3XVFPOV.js +58 -0
  16. package/dist/chunk-J3XVFPOV.js.map +1 -0
  17. package/dist/chunk-JCJFFJ42.js +39 -0
  18. package/dist/chunk-JCJFFJ42.js.map +1 -0
  19. package/dist/chunk-OKQWPNE3.js +1077 -0
  20. package/dist/chunk-OKQWPNE3.js.map +1 -0
  21. package/dist/chunk-PZ5AY32C.js +10 -0
  22. package/dist/chunk-PZ5AY32C.js.map +1 -0
  23. package/dist/chunk-TC5BBLWO.js +29 -0
  24. package/dist/chunk-TC5BBLWO.js.map +1 -0
  25. package/dist/chunk-VD4CKRMT.js +127 -0
  26. package/dist/chunk-VD4CKRMT.js.map +1 -0
  27. package/dist/chunk-X6MEYCU7.js +1401 -0
  28. package/dist/chunk-X6MEYCU7.js.map +1 -0
  29. package/dist/client/index.js +24 -1052
  30. package/dist/client/index.js.map +1 -1
  31. package/dist/react/api/licenses.js +11 -1470
  32. package/dist/react/api/licenses.js.map +1 -1
  33. package/dist/react/api/vibe-coding.js +25 -1481
  34. package/dist/react/api/vibe-coding.js.map +1 -1
  35. package/dist/react/cards/register.js +45 -138
  36. package/dist/react/cards/register.js.map +1 -1
  37. package/dist/react/components/chat/index.d.ts +7 -21
  38. package/dist/react/components/chat/index.js +28 -11366
  39. package/dist/react/components/chat/index.js.map +1 -1
  40. package/dist/react/components/plan/index.js +135 -3054
  41. package/dist/react/components/plan/index.js.map +1 -1
  42. package/dist/react/components/session/index.js +21 -1499
  43. package/dist/react/components/session/index.js.map +1 -1
  44. package/dist/react/components/workspace/index.js +116 -1715
  45. package/dist/react/components/workspace/index.js.map +1 -1
  46. package/dist/react/devtools/bridge-devtools/index.js +8 -51
  47. package/dist/react/devtools/bridge-devtools/index.js.map +1 -1
  48. package/dist/react/index.d.ts +2 -2
  49. package/dist/react/index.js +625 -14035
  50. package/dist/react/index.js.map +1 -1
  51. package/dist/style.css +1 -1
  52. package/package.json +1 -1
@@ -0,0 +1,1401 @@
1
+ import {
2
+ CardJSON,
3
+ cardRegistry
4
+ } from "./chunk-2UP7MG3J.js";
5
+ import {
6
+ getAuthedUrl,
7
+ useUiStore
8
+ } from "./chunk-4VWLTG5L.js";
9
+ import {
10
+ cn,
11
+ copyToClipboard
12
+ } from "./chunk-7LEKQI47.js";
13
+
14
+ // src/react/lib/code-highlight.ts
15
+ import { useEffect, useMemo, useState } from "react";
16
+ var DARK_THEME = "github-dark-default";
17
+ var LIGHT_THEME = "github-light-default";
18
+ var LANGUAGE_ALIASES = {
19
+ bash: "bash",
20
+ css: "css",
21
+ go: "go",
22
+ golang: "go",
23
+ htm: "html",
24
+ html: "html",
25
+ javascript: "javascript",
26
+ js: "javascript",
27
+ json: "json",
28
+ jsonc: "json",
29
+ jsx: "jsx",
30
+ markdown: "markdown",
31
+ md: "markdown",
32
+ py: "python",
33
+ python: "python",
34
+ rs: "rust",
35
+ rust: "rust",
36
+ scss: "css",
37
+ sh: "bash",
38
+ shell: "bash",
39
+ ts: "typescript",
40
+ tsx: "tsx",
41
+ typescript: "typescript",
42
+ yaml: "yaml",
43
+ yml: "yaml",
44
+ zsh: "bash"
45
+ };
46
+ var highlighterPromise = null;
47
+ var CACHE_MAX_SIZE = 200;
48
+ var highlightCache = /* @__PURE__ */ new Map();
49
+ function normalizeCodeLanguage(language) {
50
+ if (!language) return null;
51
+ return LANGUAGE_ALIASES[language.trim().toLowerCase()] ?? null;
52
+ }
53
+ function useHighlightedCodeHtml(code, language) {
54
+ const normalizedLanguage = useMemo(() => normalizeCodeLanguage(language), [language]);
55
+ const [highlightedHtml, setHighlightedHtml] = useState(null);
56
+ useEffect(() => {
57
+ let cancelled = false;
58
+ setHighlightedHtml(null);
59
+ if (!normalizedLanguage) {
60
+ return () => {
61
+ cancelled = true;
62
+ };
63
+ }
64
+ void highlightCodeToInnerHtml(code, normalizedLanguage).then((result) => {
65
+ if (!cancelled) {
66
+ setHighlightedHtml(result);
67
+ }
68
+ });
69
+ return () => {
70
+ cancelled = true;
71
+ };
72
+ }, [code, normalizedLanguage]);
73
+ return { highlightedHtml, language: normalizedLanguage };
74
+ }
75
+ async function highlightCodeToInnerHtml(code, language) {
76
+ const cacheKey = `${language}\0${code}`;
77
+ const cached = highlightCache.get(cacheKey);
78
+ if (cached) {
79
+ return cached;
80
+ }
81
+ const request = loadCodeHighlighter().then((highlighter) => {
82
+ const html = highlighter.codeToHtml(code, {
83
+ lang: language,
84
+ themes: { light: LIGHT_THEME, dark: DARK_THEME },
85
+ defaultColor: false
86
+ });
87
+ return extractInnerCodeHtml(html);
88
+ }).catch(() => {
89
+ highlightCache.delete(cacheKey);
90
+ return null;
91
+ });
92
+ if (highlightCache.size >= CACHE_MAX_SIZE) {
93
+ const oldest = highlightCache.keys().next().value;
94
+ if (oldest !== void 0) highlightCache.delete(oldest);
95
+ }
96
+ highlightCache.set(cacheKey, request);
97
+ return request;
98
+ }
99
+ async function loadCodeHighlighter() {
100
+ if (!highlighterPromise) {
101
+ highlighterPromise = Promise.all([
102
+ import("shiki/core"),
103
+ import("shiki/engine/javascript"),
104
+ import("@shikijs/langs/bash"),
105
+ import("@shikijs/langs/css"),
106
+ import("@shikijs/langs/go"),
107
+ import("@shikijs/langs/html"),
108
+ import("@shikijs/langs/javascript"),
109
+ import("@shikijs/langs/json"),
110
+ import("@shikijs/langs/jsx"),
111
+ import("@shikijs/langs/markdown"),
112
+ import("@shikijs/langs/python"),
113
+ import("@shikijs/langs/rust"),
114
+ import("@shikijs/langs/tsx"),
115
+ import("@shikijs/langs/typescript"),
116
+ import("@shikijs/langs/yaml"),
117
+ import("@shikijs/themes/github-dark-default"),
118
+ import("@shikijs/themes/github-light-default")
119
+ ]).then(
120
+ ([
121
+ core,
122
+ engine,
123
+ bash,
124
+ css,
125
+ go,
126
+ html,
127
+ javascript,
128
+ json,
129
+ jsx7,
130
+ markdown,
131
+ python,
132
+ rust,
133
+ tsx,
134
+ typescript,
135
+ yaml,
136
+ darkTheme,
137
+ lightTheme
138
+ ]) => core.createHighlighterCore({
139
+ engine: engine.createJavaScriptRegexEngine(),
140
+ langs: [
141
+ bash.default,
142
+ css.default,
143
+ go.default,
144
+ html.default,
145
+ javascript.default,
146
+ json.default,
147
+ jsx7.default,
148
+ markdown.default,
149
+ python.default,
150
+ rust.default,
151
+ tsx.default,
152
+ typescript.default,
153
+ yaml.default
154
+ ],
155
+ themes: [darkTheme.default, lightTheme.default]
156
+ })
157
+ ).catch((err) => {
158
+ highlighterPromise = null;
159
+ throw err;
160
+ });
161
+ }
162
+ return highlighterPromise;
163
+ }
164
+ function extractInnerCodeHtml(html) {
165
+ const match = html.match(/<code[^>]*>([\s\S]*)<\/code><\/pre>\s*$/);
166
+ return match?.[1] ?? null;
167
+ }
168
+
169
+ // src/react/components/card/CardContext.tsx
170
+ import { createContext, useContext } from "react";
171
+ var CardContext = createContext({});
172
+ var useCardContext = () => useContext(CardContext);
173
+
174
+ // src/react/components/card/CardCodeBlock.tsx
175
+ import { useIsCodeFenceIncomplete } from "streamdown";
176
+
177
+ // src/react/components/card/CardRenderer.tsx
178
+ import { Component } from "react";
179
+
180
+ // src/react/components/card/CardStates.tsx
181
+ import { jsx, jsxs } from "react/jsx-runtime";
182
+ function CardLoadingState({ content }) {
183
+ return /* @__PURE__ */ jsxs("div", { className: "my-4 rounded-md border border-[hsl(var(--primary)/0.2)] bg-[hsl(var(--primary)/0.05)] p-4", children: [
184
+ /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center gap-2", children: [
185
+ /* @__PURE__ */ jsx("div", { className: "h-4 w-4 animate-spin rounded-full border-2 border-[hsl(var(--primary))] border-t-transparent" }),
186
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-[hsl(var(--primary))]", children: "\u6B63\u5728\u52A0\u8F7D\u5361\u7247\u6570\u636E..." })
187
+ ] }),
188
+ /* @__PURE__ */ jsxs("details", { className: "mt-2", children: [
189
+ /* @__PURE__ */ jsx("summary", { className: "cursor-pointer text-xs text-[hsl(var(--primary)/0.7)]", children: "\u67E5\u770B\u63A5\u6536\u4E2D\u7684\u6570\u636E" }),
190
+ /* @__PURE__ */ jsx("pre", { className: "mt-1 whitespace-pre-wrap rounded bg-[hsl(var(--primary)/0.1)] p-2 font-mono text-xs text-[hsl(var(--primary)/0.8)]", children: content })
191
+ ] })
192
+ ] });
193
+ }
194
+ function CardErrorState({ content, message }) {
195
+ return /* @__PURE__ */ jsxs("div", { className: "my-4 rounded-md border border-red-500/20 bg-red-500/5 p-4", children: [
196
+ /* @__PURE__ */ jsx("p", { className: "mb-2 text-sm text-red-400", children: message || "\u5361\u7247\u6570\u636E\u89E3\u6790\u5931\u8D25" }),
197
+ /* @__PURE__ */ jsxs("details", { className: "mt-2", children: [
198
+ /* @__PURE__ */ jsx("summary", { className: "cursor-pointer text-xs text-red-400/70 hover:underline", children: "\u67E5\u770B\u539F\u59CB\u4EE3\u7801\u5757" }),
199
+ /* @__PURE__ */ jsx("pre", { className: "mt-2 overflow-x-auto whitespace-pre-wrap rounded bg-red-500/10 p-3 font-mono text-xs text-red-400/80", children: content })
200
+ ] })
201
+ ] });
202
+ }
203
+ function CardWarningState({ content, message }) {
204
+ return /* @__PURE__ */ jsxs("div", { className: "my-4 rounded-md border border-yellow-500/20 bg-yellow-500/5 p-4", children: [
205
+ /* @__PURE__ */ jsx("p", { className: "mb-2 text-sm text-yellow-400", children: message || "\u6570\u636E\u683C\u5F0F\u4E0D\u7B26\u5408\u5361\u7247\u8981\u6C42" }),
206
+ /* @__PURE__ */ jsxs("details", { className: "mt-2", children: [
207
+ /* @__PURE__ */ jsx("summary", { className: "cursor-pointer text-xs text-yellow-400/70", children: "\u67E5\u770B\u539F\u59CB\u6570\u636E" }),
208
+ /* @__PURE__ */ jsx("pre", { className: "mt-1 whitespace-pre-wrap rounded bg-yellow-500/10 p-2 font-mono text-xs text-yellow-400/80", children: content })
209
+ ] })
210
+ ] });
211
+ }
212
+
213
+ // src/react/components/card/CardRenderer.tsx
214
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
215
+ function looksIncomplete(text) {
216
+ const t = text.trim();
217
+ if (!t) return true;
218
+ const openBraces = (t.match(/{/g) || []).length;
219
+ const closeBraces = (t.match(/}/g) || []).length;
220
+ const openBrackets = (t.match(/\[/g) || []).length;
221
+ const closeBrackets = (t.match(/]/g) || []).length;
222
+ const quotes = (t.match(/"/g) || []).length;
223
+ return !t.endsWith("}") && !t.endsWith("]") || openBraces !== closeBraces || openBrackets !== closeBrackets || t.endsWith(",") || t.endsWith(":") || quotes % 2 !== 0;
224
+ }
225
+ var CardErrorBoundary = class extends Component {
226
+ state = { hasError: false };
227
+ static getDerivedStateFromError() {
228
+ return { hasError: true };
229
+ }
230
+ componentDidCatch(error, info) {
231
+ console.error("Card render error:", error, info);
232
+ }
233
+ render() {
234
+ if (this.state.hasError) return this.props.fallback;
235
+ return this.props.children;
236
+ }
237
+ };
238
+ function OpenInPreviewButton({ card }) {
239
+ const pushArtifact = useUiStore((s) => s.pushArtifact);
240
+ return /* @__PURE__ */ jsx2(
241
+ "button",
242
+ {
243
+ type: "button",
244
+ onClick: () => pushArtifact({
245
+ type: "card",
246
+ content: JSON.stringify(card, null, 2),
247
+ title: card.title || card.type,
248
+ key: `card-${card.id}`
249
+ }),
250
+ className: "absolute right-2 top-2 rounded bg-[hsl(var(--accent))] px-1.5 py-0.5 text-[10px] text-[hsl(var(--muted-foreground))] opacity-0 transition-opacity group-hover/card:opacity-100 hover:text-[hsl(var(--foreground))]",
251
+ children: "\u9884\u89C8"
252
+ }
253
+ );
254
+ }
255
+ function CardRenderer({ raw, blockPosition, isCodeFenceIncomplete }) {
256
+ const { sessionId, messageId, sendMessage } = useCardContext();
257
+ const trimmed = raw.trim();
258
+ if (!trimmed) return /* @__PURE__ */ jsx2(CardLoadingState, { content: "" });
259
+ if (isCodeFenceIncomplete) {
260
+ return /* @__PURE__ */ jsx2(CardLoadingState, { content: trimmed });
261
+ }
262
+ const parsed = CardJSON.safeParseJSON(trimmed);
263
+ if (!parsed.ok) {
264
+ if (looksIncomplete(trimmed)) {
265
+ return /* @__PURE__ */ jsx2(CardLoadingState, { content: trimmed });
266
+ }
267
+ return /* @__PURE__ */ jsx2(CardErrorState, { content: trimmed, message: parsed.error });
268
+ }
269
+ const cards = CardJSON.toCardArray(parsed.value);
270
+ if (!cards) {
271
+ return /* @__PURE__ */ jsx2(CardWarningState, { content: trimmed, message: "JSON \u683C\u5F0F\u6B63\u786E\u4F46\u4E0D\u7B26\u5408\u5361\u7247\u7ED3\u6784" });
272
+ }
273
+ const cardsWithIds = cards.map((c, i) => {
274
+ if (c.id) return c;
275
+ return {
276
+ ...c,
277
+ id: `${messageId || "msg"}-block-${blockPosition ?? 0}-card-${i}`
278
+ };
279
+ });
280
+ return /* @__PURE__ */ jsx2(Fragment, { children: cardsWithIds.map((card) => {
281
+ const CardComponent = cardRegistry.get(card.type);
282
+ if (!CardComponent) {
283
+ return /* @__PURE__ */ jsx2(
284
+ CardWarningState,
285
+ {
286
+ content: JSON.stringify(card, null, 2),
287
+ message: `\u672A\u77E5\u7684\u5361\u7247\u7C7B\u578B: ${card.type}`
288
+ },
289
+ card.id
290
+ );
291
+ }
292
+ return /* @__PURE__ */ jsx2("div", { className: "group/card relative my-4", children: /* @__PURE__ */ jsxs2(
293
+ CardErrorBoundary,
294
+ {
295
+ fallback: /* @__PURE__ */ jsx2(
296
+ CardErrorState,
297
+ {
298
+ content: JSON.stringify(card, null, 2),
299
+ message: "\u5361\u7247\u6E32\u67D3\u51FA\u9519"
300
+ }
301
+ ),
302
+ children: [
303
+ /* @__PURE__ */ jsx2(CardComponent, { card, sendMessage, sessionId }),
304
+ /* @__PURE__ */ jsx2(OpenInPreviewButton, { card })
305
+ ]
306
+ }
307
+ ) }, card.id);
308
+ }) });
309
+ }
310
+
311
+ // src/react/components/card/CardCodeBlock.tsx
312
+ import { jsx as jsx3 } from "react/jsx-runtime";
313
+ function CardCodeBlock({ className, children, node, ...props }) {
314
+ const isIncomplete = useIsCodeFenceIncomplete();
315
+ const match = /language-(\S+)/.exec(className || "");
316
+ const lang = match?.[1];
317
+ const isInline = !className;
318
+ const raw = String(children ?? "");
319
+ const normalizedLang = normalizeCodeLanguage(lang);
320
+ const { highlightedHtml } = useHighlightedCodeHtml(raw, normalizedLang);
321
+ if (!isInline && CardJSON.isCardsLanguage(lang)) {
322
+ return /* @__PURE__ */ jsx3(
323
+ CardRenderer,
324
+ {
325
+ raw,
326
+ blockPosition: node?.position?.start?.line,
327
+ isCodeFenceIncomplete: isIncomplete
328
+ }
329
+ );
330
+ }
331
+ if (!isInline && highlightedHtml) {
332
+ return /* @__PURE__ */ jsx3(
333
+ "code",
334
+ {
335
+ className,
336
+ ...props,
337
+ dangerouslySetInnerHTML: { __html: highlightedHtml }
338
+ }
339
+ );
340
+ }
341
+ return /* @__PURE__ */ jsx3("code", { className, ...props, children });
342
+ }
343
+
344
+ // src/react/components/markdown/MarkdownContent.tsx
345
+ import { mermaid } from "@streamdown/mermaid";
346
+ import { Check, Copy, Download } from "lucide-react";
347
+ import {
348
+ useEffect as useEffect2,
349
+ useMemo as useMemo2,
350
+ useRef,
351
+ useState as useState2
352
+ } from "react";
353
+ import { Streamdown } from "streamdown";
354
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
355
+ var STREAMDOWN_PLUGINS = { mermaid };
356
+ var SYSTEM_REMINDER_TAG = "system-reminder";
357
+ var HIDDEN_SYSTEM_REMINDER_TAGS = {
358
+ [SYSTEM_REMINDER_TAG]: []
359
+ };
360
+ var HIDDEN_SYSTEM_REMINDER_COMPONENTS = {
361
+ [SYSTEM_REMINDER_TAG]: () => null
362
+ };
363
+ function CodeBlockPre({ children, node: _node, ...props }) {
364
+ const preRef = useRef(null);
365
+ const [copied, setCopied] = useState2(false);
366
+ const [hasCodeNode, setHasCodeNode] = useState2(false);
367
+ useEffect2(() => {
368
+ setHasCodeNode(!!preRef.current?.querySelector("code"));
369
+ }, []);
370
+ const getRawCode = () => preRef.current?.querySelector("code")?.textContent ?? "";
371
+ const handleCopy = async () => {
372
+ const ok = await copyToClipboard(getRawCode());
373
+ if (ok) {
374
+ setCopied(true);
375
+ setTimeout(() => setCopied(false), 2e3);
376
+ }
377
+ };
378
+ const handleDownload = () => {
379
+ const codeEl = preRef.current?.querySelector("code");
380
+ const text = codeEl?.textContent ?? "";
381
+ const ext = codeEl?.className.match(/language-(\S+)/)?.[1] ?? "txt";
382
+ const blob = new Blob([text], { type: "text/plain" });
383
+ const url = URL.createObjectURL(blob);
384
+ const a = document.createElement("a");
385
+ a.href = url;
386
+ a.download = `code.${ext}`;
387
+ a.click();
388
+ URL.revokeObjectURL(url);
389
+ };
390
+ return /* @__PURE__ */ jsxs3("div", { className: "relative group", children: [
391
+ /* @__PURE__ */ jsx4("pre", { ref: preRef, ...props, children }),
392
+ hasCodeNode && /* @__PURE__ */ jsxs3("div", { className: "absolute top-2 right-2 flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity", children: [
393
+ /* @__PURE__ */ jsxs3(
394
+ "button",
395
+ {
396
+ type: "button",
397
+ onClick: handleCopy,
398
+ className: cn(
399
+ "flex items-center gap-1 rounded-md px-1.5 py-0.5 text-[11px] transition-colors",
400
+ copied ? "text-[hsl(var(--primary))]" : "text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))] hover:bg-[hsl(var(--accent))]"
401
+ ),
402
+ children: [
403
+ copied ? /* @__PURE__ */ jsx4(Check, { size: 12 }) : /* @__PURE__ */ jsx4(Copy, { size: 12 }),
404
+ /* @__PURE__ */ jsx4("span", { children: copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236" })
405
+ ]
406
+ }
407
+ ),
408
+ /* @__PURE__ */ jsxs3(
409
+ "button",
410
+ {
411
+ type: "button",
412
+ onClick: handleDownload,
413
+ className: "flex items-center gap-1 rounded-md px-1.5 py-0.5 text-[11px] transition-colors text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))] hover:bg-[hsl(var(--accent))]",
414
+ children: [
415
+ /* @__PURE__ */ jsx4(Download, { size: 12 }),
416
+ /* @__PURE__ */ jsx4("span", { children: "\u4E0B\u8F7D" })
417
+ ]
418
+ }
419
+ )
420
+ ] })
421
+ ] });
422
+ }
423
+ function isExternalImageSrc(src) {
424
+ if (!src) return false;
425
+ if (src.startsWith("data:") || src.startsWith("blob:")) return true;
426
+ if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(src)) return true;
427
+ if (src.startsWith("/")) return true;
428
+ return false;
429
+ }
430
+ function decodeMarkdownPath(src) {
431
+ try {
432
+ return decodeURIComponent(src);
433
+ } catch {
434
+ return src;
435
+ }
436
+ }
437
+ var MARKDOWN_IMAGE_PATTERN = /!\[((?:\\.|[^\]\\])*)\]\(\s*(?:<([^>\n]+)>|([^\s)]+))(\s+(?:"[^"]*"|'[^']*'|\([^)]*\)))?\s*\)/g;
438
+ function resolveSessionImageMarkdown(children, sessionId) {
439
+ return children.replace(
440
+ MARKDOWN_IMAGE_PATTERN,
441
+ (match, alt, angleSrc, plainSrc, title = "") => {
442
+ const src = angleSrc ?? plainSrc;
443
+ if (!src || isExternalImageSrc(src)) {
444
+ return match;
445
+ }
446
+ const resolved = getAuthedUrl(
447
+ `/api/sessions/${encodeURIComponent(sessionId)}/files/${encodeURIComponent(decodeMarkdownPath(src))}`
448
+ );
449
+ return `![${alt}](<${resolved}>${title})`;
450
+ }
451
+ );
452
+ }
453
+ function MarkdownContent({
454
+ allowedTags,
455
+ children,
456
+ components,
457
+ mode,
458
+ plugins,
459
+ sessionId,
460
+ ...props
461
+ }) {
462
+ const resolvedChildren = useMemo2(
463
+ () => sessionId && typeof children === "string" ? resolveSessionImageMarkdown(children, sessionId) : children,
464
+ [children, sessionId]
465
+ );
466
+ return /* @__PURE__ */ jsx4(
467
+ Streamdown,
468
+ {
469
+ ...props,
470
+ mode: mode ?? "static",
471
+ allowedTags: {
472
+ ...allowedTags ?? {},
473
+ ...HIDDEN_SYSTEM_REMINDER_TAGS
474
+ },
475
+ components: {
476
+ code: CardCodeBlock,
477
+ pre: CodeBlockPre,
478
+ ...components ?? {},
479
+ ...HIDDEN_SYSTEM_REMINDER_COMPONENTS
480
+ },
481
+ plugins: {
482
+ ...STREAMDOWN_PLUGINS,
483
+ ...plugins ?? {}
484
+ },
485
+ children: resolvedChildren
486
+ }
487
+ );
488
+ }
489
+
490
+ // src/react/components/chat/AskUserQuestionBlock.tsx
491
+ import { Check as Check2, Loader2, MessageSquareMore } from "lucide-react";
492
+ import { useEffect as useEffect3, useMemo as useMemo3, useState as useState3 } from "react";
493
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
494
+ function AskUserQuestionBlock({
495
+ data,
496
+ answered,
497
+ toolCallId,
498
+ sessionStatus,
499
+ answerData,
500
+ onAnswer
501
+ }) {
502
+ const [selections, setSelections] = useState3(/* @__PURE__ */ new Map());
503
+ const [customTexts, setCustomTexts] = useState3(/* @__PURE__ */ new Map());
504
+ const [usingCustom, setUsingCustom] = useState3(/* @__PURE__ */ new Set());
505
+ const [submitted, setSubmitted] = useState3(false);
506
+ useEffect3(() => {
507
+ if (sessionStatus === "failed" || sessionStatus === "interrupted") {
508
+ setSubmitted(false);
509
+ }
510
+ }, [sessionStatus]);
511
+ const displayAnswerState = useMemo3(() => {
512
+ if (!(answered && answerData)) {
513
+ return {
514
+ selections,
515
+ customTexts,
516
+ usingCustom
517
+ };
518
+ }
519
+ const nextSelections = /* @__PURE__ */ new Map();
520
+ const nextCustomTexts = /* @__PURE__ */ new Map();
521
+ const nextUsingCustom = /* @__PURE__ */ new Set();
522
+ for (const [questionKey, optionIndexes] of Object.entries(answerData.selections)) {
523
+ nextSelections.set(Number(questionKey), new Set(optionIndexes));
524
+ }
525
+ for (const [questionKey, text] of Object.entries(answerData.custom)) {
526
+ const qIdx = Number(questionKey);
527
+ nextCustomTexts.set(qIdx, text);
528
+ nextUsingCustom.add(qIdx);
529
+ }
530
+ return {
531
+ selections: nextSelections,
532
+ customTexts: nextCustomTexts,
533
+ usingCustom: nextUsingCustom
534
+ };
535
+ }, [answerData, answered, customTexts, selections, usingCustom]);
536
+ const displaySelections = displayAnswerState.selections;
537
+ const displayCustomTexts = displayAnswerState.customTexts;
538
+ const displayUsingCustom = displayAnswerState.usingCustom;
539
+ const toggleOption = (qIdx, optIdx, multi) => {
540
+ if (answered || submitted) return;
541
+ setSelections((prev) => {
542
+ const next = new Map(prev);
543
+ const current = new Set(next.get(qIdx) ?? []);
544
+ if (multi) {
545
+ if (current.has(optIdx)) current.delete(optIdx);
546
+ else current.add(optIdx);
547
+ } else {
548
+ current.clear();
549
+ current.add(optIdx);
550
+ }
551
+ next.set(qIdx, current);
552
+ return next;
553
+ });
554
+ setUsingCustom((prev) => {
555
+ const next = new Set(prev);
556
+ next.delete(qIdx);
557
+ return next;
558
+ });
559
+ };
560
+ const handleCustomFocus = (qIdx) => {
561
+ if (answered || submitted) return;
562
+ setSelections((prev) => {
563
+ const next = new Map(prev);
564
+ next.delete(qIdx);
565
+ return next;
566
+ });
567
+ setUsingCustom((prev) => new Set(prev).add(qIdx));
568
+ };
569
+ const setCustomText = (qIdx, text) => {
570
+ if (answered || submitted) return;
571
+ setCustomTexts((prev) => new Map(prev).set(qIdx, text));
572
+ };
573
+ const getAnswer = (qIdx) => {
574
+ const q = data.questions[qIdx];
575
+ if (usingCustom.has(qIdx)) {
576
+ const text = (customTexts.get(qIdx) ?? "").trim();
577
+ return text || null;
578
+ }
579
+ const sel = selections.get(qIdx);
580
+ if (!sel || sel.size === 0) return null;
581
+ return [...sel].sort().map((i) => q.options[i].label).join(", ");
582
+ };
583
+ const allAnswered = data.questions.every((_, i) => getAnswer(i) !== null);
584
+ const handleSubmit = () => {
585
+ if (answered || submitted || !allAnswered || !onAnswer) return;
586
+ const nextAnswerData = {
587
+ selections: Object.fromEntries(
588
+ Array.from(selections.entries()).map(([qIdx, optionIndexes]) => [
589
+ qIdx,
590
+ Array.from(optionIndexes).sort((a, b) => a - b)
591
+ ])
592
+ ),
593
+ custom: Object.fromEntries(
594
+ Array.from(usingCustom).map((qIdx) => [qIdx, (customTexts.get(qIdx) ?? "").trim()]).filter(([, text2]) => text2.length > 0)
595
+ )
596
+ };
597
+ const parts = data.questions.map((q, i) => `- ${q.question} -> ${getAnswer(i)}`);
598
+ const text = `\u5173\u4E8E\u9700\u8981\u786E\u8BA4\u7684\u95EE\u9898\uFF0C\u7528\u6237\u7684\u56DE\u7B54\u5982\u4E0B\uFF1A
599
+ ${parts.join("\n")}`;
600
+ setSubmitted(true);
601
+ onAnswer(text, toolCallId, nextAnswerData);
602
+ };
603
+ return /* @__PURE__ */ jsxs4(
604
+ "div",
605
+ {
606
+ className: cn(
607
+ "ml-4 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))]",
608
+ 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"
609
+ ),
610
+ children: [
611
+ data.source_loop?.description && /* @__PURE__ */ jsxs4("div", { className: "rounded-lg bg-[hsl(var(--muted)/0.35)] px-3 py-2 text-xs text-[hsl(var(--muted-foreground))]", children: [
612
+ "\u5B50\u667A\u80FD\u4F53\u300C",
613
+ data.source_loop.description,
614
+ "\u300D\u5728\u7B49\u5F85\u4F60\u7684\u56DE\u7B54"
615
+ ] }),
616
+ data.questions.map((q, qIdx) => /* @__PURE__ */ jsx5(
617
+ QuestionCard,
618
+ {
619
+ question: q,
620
+ qIdx,
621
+ answered,
622
+ selected: displaySelections.get(qIdx) ?? /* @__PURE__ */ new Set(),
623
+ isCustom: displayUsingCustom.has(qIdx),
624
+ customText: displayCustomTexts.get(qIdx) ?? "",
625
+ onToggle: toggleOption,
626
+ onCustomFocus: handleCustomFocus,
627
+ onCustomChange: setCustomText
628
+ },
629
+ q.question
630
+ )),
631
+ !answered && !submitted && onAnswer && /* @__PURE__ */ jsx5(
632
+ "button",
633
+ {
634
+ type: "button",
635
+ onClick: handleSubmit,
636
+ disabled: !allAnswered,
637
+ className: "w-full rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-xs font-semibold text-[hsl(var(--primary-foreground))] transition-opacity hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-60",
638
+ children: allAnswered ? "\u786E\u8BA4" : "\u8BF7\u5148\u9009\u62E9\u4E00\u4E2A\u9009\u9879"
639
+ }
640
+ ),
641
+ submitted && !answered && /* @__PURE__ */ jsxs4(
642
+ "button",
643
+ {
644
+ type: "button",
645
+ disabled: true,
646
+ 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",
647
+ children: [
648
+ /* @__PURE__ */ jsx5(Loader2, { size: 14, className: "animate-spin" }),
649
+ "\u786E\u8BA4\u4E2D"
650
+ ]
651
+ }
652
+ )
653
+ ]
654
+ }
655
+ );
656
+ }
657
+ function QuestionCard({
658
+ question,
659
+ qIdx,
660
+ answered,
661
+ selected,
662
+ isCustom,
663
+ customText,
664
+ onToggle,
665
+ onCustomFocus,
666
+ onCustomChange
667
+ }) {
668
+ const multi = question.multiSelect ?? false;
669
+ return /* @__PURE__ */ jsxs4("div", { children: [
670
+ /* @__PURE__ */ jsxs4("div", { className: cn("flex items-start gap-2", answered ? "mb-2" : "mb-3"), children: [
671
+ /* @__PURE__ */ jsx5(
672
+ MessageSquareMore,
673
+ {
674
+ size: answered ? 12 : 13,
675
+ className: "mt-0.5 shrink-0 text-[hsl(var(--primary))]"
676
+ }
677
+ ),
678
+ /* @__PURE__ */ jsx5(
679
+ "div",
680
+ {
681
+ className: cn(
682
+ "min-w-0 flex-1 font-medium text-[hsl(var(--foreground))]",
683
+ answered ? "text-xs" : "text-sm"
684
+ ),
685
+ children: /* @__PURE__ */ jsx5(
686
+ MarkdownContent,
687
+ {
688
+ className: cn(
689
+ "prose prose-sm prose-invert max-w-none [&_li>p]:inline [&_ol]:my-1 [&_p]:my-1 [&_p:first-child]:mt-0 [&_p:last-child]:mb-0 [&_ul]:my-1",
690
+ answered ? "text-xs" : "text-sm"
691
+ ),
692
+ controls: { code: true },
693
+ children: question.question
694
+ }
695
+ )
696
+ }
697
+ )
698
+ ] }),
699
+ /* @__PURE__ */ jsxs4("div", { className: cn("flex flex-col pl-5", answered ? "gap-1" : "gap-1.5"), children: [
700
+ question.options.map((opt, optIdx) => {
701
+ const isSel = selected.has(optIdx);
702
+ return /* @__PURE__ */ jsxs4(
703
+ "button",
704
+ {
705
+ type: "button",
706
+ disabled: answered,
707
+ onClick: () => onToggle(qIdx, optIdx, multi),
708
+ className: cn(
709
+ "flex items-start gap-2.5 rounded-lg border text-left transition-all",
710
+ answered ? "px-2.5 py-1.5" : "px-3 py-2.5",
711
+ isSel && !answered ? "border-[hsl(var(--primary)/0.8)] bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))]" : isSel ? "border-[hsl(var(--primary)/0.45)] bg-[hsl(var(--primary)/0.08)] text-[hsl(var(--foreground))]" : "border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.2)]",
712
+ !answered && !isSel && "hover:border-[hsl(var(--ring)/0.4)] hover:bg-[hsl(var(--accent))]",
713
+ answered && "cursor-default opacity-70"
714
+ ),
715
+ children: [
716
+ multi && /* @__PURE__ */ jsx5(
717
+ "div",
718
+ {
719
+ className: cn(
720
+ "mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border transition-colors",
721
+ 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))]"
722
+ ),
723
+ children: isSel && /* @__PURE__ */ jsx5(
724
+ Check2,
725
+ {
726
+ size: 9,
727
+ className: answered ? "text-[hsl(var(--primary))]" : "text-[hsl(var(--primary-foreground))]"
728
+ }
729
+ )
730
+ }
731
+ ),
732
+ /* @__PURE__ */ jsxs4("div", { className: "min-w-0", children: [
733
+ /* @__PURE__ */ jsx5("div", { className: cn("font-medium", answered ? "text-xs" : "text-[13px]"), children: opt.label }),
734
+ opt.description && /* @__PURE__ */ jsx5(
735
+ "div",
736
+ {
737
+ className: cn(
738
+ "mt-0.5",
739
+ answered ? "text-[11px]" : "text-xs",
740
+ isSel && !answered ? "opacity-75" : "text-[hsl(var(--muted-foreground))]"
741
+ ),
742
+ children: opt.description
743
+ }
744
+ )
745
+ ] })
746
+ ]
747
+ },
748
+ opt.label
749
+ );
750
+ }),
751
+ answered && !isCustom ? null : /* @__PURE__ */ jsxs4(
752
+ "div",
753
+ {
754
+ className: cn(
755
+ "flex items-center gap-2 rounded-lg border transition-all",
756
+ answered ? "px-2.5 py-1.5" : "px-3 py-2.5",
757
+ isCustom ? "border-[hsl(var(--ring)/0.6)] bg-[hsl(var(--accent))]" : "border-[hsl(var(--border))] hover:border-[hsl(var(--ring)/0.3)] hover:bg-[hsl(var(--accent))]",
758
+ answered && "cursor-default opacity-70"
759
+ ),
760
+ children: [
761
+ /* @__PURE__ */ jsx5("span", { className: "shrink-0 text-xs text-[hsl(var(--muted-foreground))]", children: "\u5176\u4ED6\uFF1A" }),
762
+ /* @__PURE__ */ jsx5(
763
+ "input",
764
+ {
765
+ type: "text",
766
+ value: customText,
767
+ disabled: answered,
768
+ onChange: (e) => onCustomChange(qIdx, e.target.value),
769
+ onFocus: () => onCustomFocus(qIdx),
770
+ "aria-label": "\u81EA\u5B9A\u4E49\u56DE\u7B54",
771
+ placeholder: "\u8F93\u5165\u4F60\u7684\u7B54\u6848...",
772
+ className: cn(
773
+ "min-w-0 flex-1 bg-transparent text-[hsl(var(--foreground))] outline-none placeholder:text-[hsl(var(--muted-foreground)/0.5)]",
774
+ answered ? "text-xs" : "text-sm"
775
+ )
776
+ }
777
+ )
778
+ ]
779
+ }
780
+ )
781
+ ] })
782
+ ] });
783
+ }
784
+ function parseAskUserQuestion(toolResult) {
785
+ if (!toolResult) return null;
786
+ try {
787
+ const parsed = JSON.parse(toolResult);
788
+ let questions = parsed?.questions;
789
+ if (typeof questions === "string") {
790
+ try {
791
+ questions = JSON.parse(questions);
792
+ } catch {
793
+ return null;
794
+ }
795
+ }
796
+ if (Array.isArray(questions) && questions.every(isQuestionItem)) {
797
+ return { ...parsed, questions };
798
+ }
799
+ } catch {
800
+ }
801
+ return null;
802
+ }
803
+ function isQuestionItem(value) {
804
+ if (!value || typeof value !== "object") return false;
805
+ const question = value.question;
806
+ const options = value.options;
807
+ return typeof question === "string" && Array.isArray(options) && options.every(isOptionItem);
808
+ }
809
+ function isOptionItem(value) {
810
+ if (!value || typeof value !== "object") return false;
811
+ const option = value;
812
+ return typeof option.label === "string" && typeof option.description === "string";
813
+ }
814
+
815
+ // src/react/components/plan/parse-plan-tree.ts
816
+ import { load } from "js-yaml";
817
+ function isNonEmptyString(value) {
818
+ return typeof value === "string" && value.trim().length > 0;
819
+ }
820
+ function isPlanStep(value) {
821
+ if (!value || typeof value !== "object") return false;
822
+ const step = value;
823
+ if (!isNonEmptyString(step.label)) return false;
824
+ if ("skill" in step && step.skill !== void 0 && !isNonEmptyString(step.skill)) return false;
825
+ if ("children" in step && !Array.isArray(step.children)) return false;
826
+ const children = Array.isArray(step.children) ? step.children : [];
827
+ if (children.length > 0 && !isNonEmptyString(step.skill)) return false;
828
+ return children.every((child) => isPlanStep(child));
829
+ }
830
+ function isPlanDocument(value) {
831
+ if (!value || typeof value !== "object") return false;
832
+ const doc = value;
833
+ if (!isNonEmptyString(doc.title) || !isNonEmptyString(doc.objective)) return false;
834
+ if (!Array.isArray(doc.steps) || doc.steps.length === 0) return false;
835
+ if (!doc.steps.every((step) => isPlanStep(step))) return false;
836
+ if ("notes" in doc) {
837
+ if (!Array.isArray(doc.notes)) return false;
838
+ if (!doc.notes.every((note) => isNonEmptyString(note))) return false;
839
+ }
840
+ return true;
841
+ }
842
+ function getPlanDocumentError(value) {
843
+ if (!value || typeof value !== "object") return "PLAN.yaml \u9876\u5C42\u7ED3\u6784\u5FC5\u987B\u662F\u5BF9\u8C61";
844
+ const doc = value;
845
+ if (!isNonEmptyString(doc.title)) return "PLAN.yaml \u7F3A\u5C11\u975E\u7A7A title";
846
+ if (!isNonEmptyString(doc.objective)) return "PLAN.yaml \u7F3A\u5C11\u975E\u7A7A objective";
847
+ if (!Array.isArray(doc.steps) || doc.steps.length === 0) return "PLAN.yaml \u7F3A\u5C11\u975E\u7A7A steps";
848
+ if (!doc.steps.every((step) => isPlanStep(step))) {
849
+ return "PLAN.yaml \u6B65\u9AA4\u7ED3\u6784\u65E0\u6548\uFF0C\u8BF7\u68C0\u67E5 label / skill / children";
850
+ }
851
+ if ("notes" in doc) {
852
+ if (!Array.isArray(doc.notes) || !doc.notes.every((note) => isNonEmptyString(note))) {
853
+ return "PLAN.yaml notes \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32\u5217\u8868";
854
+ }
855
+ }
856
+ return "PLAN.yaml \u7ED3\u6784\u65E0\u6548";
857
+ }
858
+ function toPlanStatus(value) {
859
+ if (!isNonEmptyString(value)) return "pending";
860
+ const normalized = value.trim().toLowerCase();
861
+ return normalized === "pending" || normalized === "running" || normalized === "done" || normalized === "failed" ? normalized : "pending";
862
+ }
863
+ function applyPlanStatusesById(root, statuses) {
864
+ const nextStatus = statuses[root.id] ?? root.status;
865
+ const children = Array.isArray(root.children) ? root.children : [];
866
+ return {
867
+ ...root,
868
+ status: nextStatus,
869
+ children: children.map((child) => applyPlanStatusesById(child, statuses))
870
+ };
871
+ }
872
+ function parsePlanTreeResult(content) {
873
+ if (!content.trim()) {
874
+ return { plan: null, error: null };
875
+ }
876
+ let parsed;
877
+ try {
878
+ parsed = load(content);
879
+ } catch (error) {
880
+ return {
881
+ plan: null,
882
+ error: error instanceof Error ? `PLAN.yaml YAML \u8BED\u6CD5\u9519\u8BEF\uFF1A${error.message}` : "PLAN.yaml YAML \u8BED\u6CD5\u9519\u8BEF"
883
+ };
884
+ }
885
+ if (!isPlanDocument(parsed)) {
886
+ return { plan: null, error: getPlanDocumentError(parsed) };
887
+ }
888
+ const createNode = (step, depth, fallbackId) => {
889
+ const children = Array.isArray(step.children) ? step.children : [];
890
+ return {
891
+ id: isNonEmptyString(step.id) ? step.id.trim() : fallbackId,
892
+ label: step.label.trim(),
893
+ skillRef: isNonEmptyString(step.skill) ? step.skill.trim() : null,
894
+ status: toPlanStatus(step.status),
895
+ children: children.map((child, index) => createNode(child, depth + 1, `${fallbackId}.${index + 1}`)),
896
+ depth
897
+ };
898
+ };
899
+ const root = {
900
+ id: "plan-root",
901
+ label: parsed.title.trim(),
902
+ skillRef: null,
903
+ status: "pending",
904
+ children: parsed.steps.map((step, index) => createNode(step, 1, `${index + 1}`)),
905
+ depth: 0
906
+ };
907
+ const notes = [parsed.objective.trim(), ...(parsed.notes ?? []).map((note) => note.trim())];
908
+ return { plan: { root, notes }, error: null };
909
+ }
910
+
911
+ // src/react/components/plan/parse-plan-messages.ts
912
+ function resultAsString(result) {
913
+ if (typeof result === "string") return result;
914
+ return null;
915
+ }
916
+ var PAUSE_TOOLS = /* @__PURE__ */ new Set(["AskUserQuestion", "ExitPlanMode"]);
917
+ var READ_FILE_TOOLS = /* @__PURE__ */ new Set(["Read"]);
918
+ var PLAN_WRITE_TOOLS = /* @__PURE__ */ new Set(["Write"]);
919
+ var PLAN_EDIT_TOOLS = /* @__PURE__ */ new Set(["Edit"]);
920
+ function getSkillId(payload) {
921
+ if (!payload) {
922
+ return "";
923
+ }
924
+ if (typeof payload.skill_id === "string" && payload.skill_id) {
925
+ return payload.skill_id;
926
+ }
927
+ if (typeof payload.name === "string" && payload.name) {
928
+ return payload.name;
929
+ }
930
+ return "";
931
+ }
932
+ function getSkillDisplayName(payload) {
933
+ if (!payload || typeof payload.display_name !== "string") {
934
+ return void 0;
935
+ }
936
+ const displayName = payload.display_name.trim();
937
+ if (!displayName) {
938
+ return void 0;
939
+ }
940
+ const skillId = getSkillId(payload);
941
+ return displayName === skillId ? void 0 : displayName;
942
+ }
943
+ function parseModeChange(message) {
944
+ if (message.kind !== "mode_change" || typeof message.content !== "string") {
945
+ return null;
946
+ }
947
+ try {
948
+ const parsed = JSON.parse(message.content);
949
+ if (typeof parsed.from === "string" && typeof parsed.to === "string") {
950
+ return { from: parsed.from, to: parsed.to };
951
+ }
952
+ } catch {
953
+ }
954
+ return null;
955
+ }
956
+ function isPlanningBoundaryMessage(message) {
957
+ if (message.kind === "planning_enter" || message.kind === "planning_exit") {
958
+ return true;
959
+ }
960
+ const modeChange = parseModeChange(message);
961
+ if (!modeChange) {
962
+ return false;
963
+ }
964
+ return modeChange.to === "planning" || modeChange.from === "planning";
965
+ }
966
+ function parsePlanMessages(allMessages) {
967
+ const messages = Array.isArray(allMessages) ? allMessages : [];
968
+ let intent = "";
969
+ for (const msg of messages) {
970
+ if (!msg.tool_calls) continue;
971
+ const searchTc = msg.tool_calls.find(
972
+ (tc) => tc.name === "search_skills" || tc.name === "SearchSkills"
973
+ );
974
+ if (searchTc) {
975
+ try {
976
+ intent = JSON.parse(searchTc.arguments).query ?? "";
977
+ } catch {
978
+ }
979
+ if (intent) break;
980
+ }
981
+ }
982
+ const plannerMsgs = messages.filter(
983
+ (message) => !isPlanningBoundaryMessage(message)
984
+ );
985
+ const searchResults = [];
986
+ const searchMap = /* @__PURE__ */ new Map();
987
+ const selectedSkills = [];
988
+ const selectedSet = /* @__PURE__ */ new Set();
989
+ const askQuestions = [];
990
+ const readFiles = [];
991
+ let lastPauseTool = null;
992
+ let planContent = "";
993
+ let planContentPriority = 0;
994
+ let latestStatuses = {};
995
+ const setPlanContent = (nextContent, priority) => {
996
+ if (typeof nextContent !== "string" || !nextContent.trim()) {
997
+ return;
998
+ }
999
+ if (priority < planContentPriority) {
1000
+ return;
1001
+ }
1002
+ planContent = nextContent;
1003
+ planContentPriority = priority;
1004
+ };
1005
+ const applyPlanEdit = (args) => {
1006
+ const oldString = typeof args.old_string === "string" ? args.old_string : "";
1007
+ const newString = typeof args.new_string === "string" ? args.new_string : "";
1008
+ if (!planContent || !oldString || !planContent.includes(oldString)) {
1009
+ return;
1010
+ }
1011
+ planContent = planContent.replace(oldString, newString);
1012
+ };
1013
+ const maybeParsePlanStatus = (msg) => {
1014
+ if (msg.kind !== "plan_status") return;
1015
+ if (typeof msg.content !== "string" || !msg.content.trim()) return;
1016
+ try {
1017
+ const data = JSON.parse(msg.content);
1018
+ setPlanContent(data.plan_yaml, 3);
1019
+ if (data.statuses && typeof data.statuses === "object" && !Array.isArray(data.statuses)) {
1020
+ latestStatuses = Object.fromEntries(
1021
+ Object.entries(data.statuses).filter((entry) => typeof entry[0] === "string" && typeof entry[1] === "string").map(([stepId, status]) => [stepId, normalizePlanStatus(status)])
1022
+ );
1023
+ }
1024
+ } catch {
1025
+ }
1026
+ };
1027
+ for (const msg of plannerMsgs) {
1028
+ maybeParsePlanStatus(msg);
1029
+ if (!msg.tool_calls) continue;
1030
+ for (const tc of msg.tool_calls) {
1031
+ if ((tc.name === "search_skills" || tc.name === "SearchSkills") && resultAsString(tc.result)) {
1032
+ try {
1033
+ const parsed = JSON.parse(resultAsString(tc.result));
1034
+ const results = Array.isArray(parsed) ? parsed : Array.isArray(parsed?.results) ? parsed.results : [];
1035
+ for (const r of results) {
1036
+ const skillId = getSkillId(r);
1037
+ if (!skillId || searchMap.has(skillId)) continue;
1038
+ const skill = {
1039
+ skillId,
1040
+ displayName: getSkillDisplayName(r),
1041
+ description: r.description ?? ""
1042
+ };
1043
+ searchMap.set(skillId, skill);
1044
+ searchResults.push(skill);
1045
+ }
1046
+ } catch {
1047
+ }
1048
+ }
1049
+ if ((tc.name === "get_skill_content" || tc.name === "GetSkillContent") && resultAsString(tc.result)) {
1050
+ try {
1051
+ const data = JSON.parse(resultAsString(tc.result));
1052
+ const skillId = getSkillId(data);
1053
+ if (!skillId || selectedSet.has(skillId)) continue;
1054
+ let existing = searchMap.get(skillId);
1055
+ if (!existing) {
1056
+ existing = {
1057
+ skillId,
1058
+ displayName: getSkillDisplayName(data),
1059
+ description: data.description ?? ""
1060
+ };
1061
+ searchMap.set(skillId, existing);
1062
+ searchResults.push(existing);
1063
+ }
1064
+ existing.displayName ??= getSkillDisplayName(data);
1065
+ existing.content = data.content;
1066
+ existing.references = extractRefs(data.content ?? "");
1067
+ selectedSkills.push(existing);
1068
+ selectedSet.add(skillId);
1069
+ } catch {
1070
+ }
1071
+ }
1072
+ if (tc.name === "AskUserQuestion" && resultAsString(tc.result)) {
1073
+ const data = parseAskUserQuestion(resultAsString(tc.result));
1074
+ if (data) {
1075
+ askQuestions.push({ toolCallId: tc.id, data });
1076
+ }
1077
+ }
1078
+ if (READ_FILE_TOOLS.has(tc.name) && tc.arguments) {
1079
+ try {
1080
+ const args = JSON.parse(tc.arguments);
1081
+ if (typeof args.file_path === "string" && args.file_path) {
1082
+ readFiles.push({ path: args.file_path, status: tc.status ?? "done" });
1083
+ }
1084
+ } catch {
1085
+ }
1086
+ }
1087
+ if (PLAN_WRITE_TOOLS.has(tc.name) && tc.arguments && tc.status === "done" && !(typeof tc.result === "string" && tc.result.startsWith("\u9519\u8BEF"))) {
1088
+ try {
1089
+ const args = JSON.parse(tc.arguments);
1090
+ if (typeof args.file_path === "string" && (args.file_path === "PLAN.yaml" || args.file_path.endsWith("/PLAN.yaml")) && typeof args.content === "string") {
1091
+ setPlanContent(args.content, 1);
1092
+ }
1093
+ } catch {
1094
+ }
1095
+ }
1096
+ if (PLAN_EDIT_TOOLS.has(tc.name) && tc.arguments && tc.status === "done" && !(typeof tc.result === "string" && tc.result.startsWith("\u9519\u8BEF"))) {
1097
+ try {
1098
+ const args = JSON.parse(tc.arguments);
1099
+ if (typeof args.file_path === "string" && (args.file_path === "PLAN.yaml" || args.file_path.endsWith("/PLAN.yaml"))) {
1100
+ applyPlanEdit(args);
1101
+ }
1102
+ } catch {
1103
+ }
1104
+ }
1105
+ if (PAUSE_TOOLS.has(tc.name) && tc.status === "done") {
1106
+ lastPauseTool = tc.name;
1107
+ }
1108
+ if (tc.name === "ExitPlanMode" && resultAsString(tc.result)) {
1109
+ try {
1110
+ const data = JSON.parse(resultAsString(tc.result));
1111
+ setPlanContent(data.plan, 2);
1112
+ } catch {
1113
+ }
1114
+ }
1115
+ }
1116
+ }
1117
+ const { plan: parsedPlan, error: parseError } = parsePlanTreeResult(planContent);
1118
+ const plan = parsedPlan == null ? null : {
1119
+ ...parsedPlan,
1120
+ root: applyPlanStatusesById(parsedPlan.root, latestStatuses)
1121
+ };
1122
+ return {
1123
+ intent,
1124
+ searchResults,
1125
+ selectedSkills,
1126
+ plan,
1127
+ hasPlanContent: planContent.trim().length > 0,
1128
+ parseError,
1129
+ askQuestions,
1130
+ readFiles,
1131
+ lastPauseTool
1132
+ };
1133
+ }
1134
+ function normalizePlanStatus(value) {
1135
+ const normalized = value.trim().toLowerCase();
1136
+ return normalized === "running" || normalized === "done" || normalized === "failed" ? normalized : "pending";
1137
+ }
1138
+ function extractRefs(content) {
1139
+ const seen = /* @__PURE__ */ new Set();
1140
+ const refs = [];
1141
+ for (const m of content.matchAll(/\[\[([^\]]+)\]\]/g)) {
1142
+ const ref = m[1].trim();
1143
+ if (ref && !seen.has(ref)) {
1144
+ seen.add(ref);
1145
+ refs.push(ref);
1146
+ }
1147
+ }
1148
+ return refs;
1149
+ }
1150
+
1151
+ // src/react/components/plan/PlanSummaryCard.tsx
1152
+ import { CheckCircle2, ChevronRight, PencilLine, Play, Sparkles } from "lucide-react";
1153
+ import { useMemo as useMemo4, useState as useState4 } from "react";
1154
+ import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1155
+ function PlanSummaryCard({
1156
+ messages,
1157
+ sessionStatus,
1158
+ onConfirmPlan
1159
+ }) {
1160
+ const data = useMemo4(() => parsePlanMessages(messages), [messages]);
1161
+ const rightPanelCollapsed = useUiStore((state) => state.rightPanelCollapsed);
1162
+ const toggleRightPanel = useUiStore((state) => state.toggleRightPanel);
1163
+ const setActiveRightTab = useUiStore((state) => state.setActiveRightTab);
1164
+ const summaryState = useMemo4(() => getSummaryState(data, sessionStatus), [data, sessionStatus]);
1165
+ const stepCount = useMemo4(() => countPlanSteps(data.plan), [data.plan]);
1166
+ const isWaitingForInput = sessionStatus === "waiting_for_input";
1167
+ const canConfirm = Boolean(onConfirmPlan) && isWaitingForInput && data.lastPauseTool === "ExitPlanMode";
1168
+ const openPlanPanel = () => {
1169
+ if (rightPanelCollapsed) {
1170
+ toggleRightPanel();
1171
+ }
1172
+ setActiveRightTab("situation");
1173
+ };
1174
+ return /* @__PURE__ */ jsxs5("div", { className: "flex w-full flex-col gap-4 rounded-2xl border border-[hsl(var(--border))] bg-[hsl(var(--card)/0.55)] p-4", children: [
1175
+ /* @__PURE__ */ jsxs5(
1176
+ "button",
1177
+ {
1178
+ type: "button",
1179
+ onClick: openPlanPanel,
1180
+ className: "flex w-full flex-col gap-4 text-left transition-colors hover:text-[hsl(var(--foreground))]",
1181
+ children: [
1182
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-start justify-between gap-3", children: [
1183
+ /* @__PURE__ */ jsxs5("div", { className: "min-w-0 space-y-2", children: [
1184
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2 text-xs text-[hsl(var(--muted-foreground))]", children: [
1185
+ /* @__PURE__ */ jsx6(Sparkles, { size: 12 }),
1186
+ /* @__PURE__ */ jsx6("span", { children: "\u89C4\u5212\u6458\u8981" })
1187
+ ] }),
1188
+ /* @__PURE__ */ jsx6("div", { className: "truncate text-base font-medium text-[hsl(var(--foreground))]", children: data.intent || "\u5F53\u524D\u89C4\u5212" }),
1189
+ /* @__PURE__ */ jsx6("div", { className: "flex flex-wrap items-center gap-2 text-xs", children: summaryState.kind === "progress" ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
1190
+ /* @__PURE__ */ jsx6("span", { className: "rounded-full bg-[hsl(var(--accent))] px-2.5 py-1 text-[hsl(var(--foreground))]", children: summaryState.label }),
1191
+ /* @__PURE__ */ jsx6("span", { className: "font-mono tracking-[0.25em] text-[hsl(var(--muted-foreground))]", children: summaryState.dots })
1192
+ ] }) : /* @__PURE__ */ jsx6(
1193
+ "span",
1194
+ {
1195
+ className: cn(
1196
+ "rounded-full px-2.5 py-1",
1197
+ summaryState.kind === "complete" && "bg-[hsl(var(--primary)/0.12)] text-[hsl(var(--primary))]",
1198
+ summaryState.kind === "failed" && "bg-[hsl(var(--destructive)/0.12)] text-[hsl(var(--destructive))]",
1199
+ summaryState.kind === "interrupted" && "bg-orange-500/12 text-orange-300"
1200
+ ),
1201
+ children: summaryState.label
1202
+ }
1203
+ ) })
1204
+ ] }),
1205
+ /* @__PURE__ */ jsx6(
1206
+ ChevronRight,
1207
+ {
1208
+ size: 16,
1209
+ className: "mt-1 shrink-0 text-[hsl(var(--muted-foreground))]",
1210
+ "aria-hidden": "true"
1211
+ }
1212
+ )
1213
+ ] }),
1214
+ summaryState.kind === "complete" && /* @__PURE__ */ jsxs5("div", { className: "flex flex-wrap items-center gap-2 text-xs text-[hsl(var(--muted-foreground))]", children: [
1215
+ /* @__PURE__ */ jsxs5("span", { className: "rounded-full border border-[hsl(var(--border))] px-2.5 py-1", children: [
1216
+ data.selectedSkills.length,
1217
+ " \u4E2A\u6280\u80FD"
1218
+ ] }),
1219
+ /* @__PURE__ */ jsxs5("span", { className: "rounded-full border border-[hsl(var(--border))] px-2.5 py-1", children: [
1220
+ stepCount,
1221
+ " \u4E2A\u6B65\u9AA4"
1222
+ ] }),
1223
+ /* @__PURE__ */ jsxs5("span", { className: "inline-flex items-center gap-1 text-[hsl(var(--muted-foreground))]", children: [
1224
+ /* @__PURE__ */ jsx6(CheckCircle2, { size: 12 }),
1225
+ "\u53F3\u4FA7\u67E5\u770B\u8BE6\u60C5"
1226
+ ] })
1227
+ ] })
1228
+ ]
1229
+ }
1230
+ ),
1231
+ canConfirm && /* @__PURE__ */ jsx6(PlanConfirmationPanel, { onConfirmPlan })
1232
+ ] });
1233
+ }
1234
+ function PlanConfirmationPanel({
1235
+ onConfirmPlan
1236
+ }) {
1237
+ const [isEditing, setIsEditing] = useState4(false);
1238
+ const [revisionText, setRevisionText] = useState4("");
1239
+ const submitRevision = () => {
1240
+ const trimmed = revisionText.trim();
1241
+ if (!trimmed) return;
1242
+ onConfirmPlan?.("revise", trimmed);
1243
+ setRevisionText("");
1244
+ setIsEditing(false);
1245
+ };
1246
+ return /* @__PURE__ */ jsxs5("div", { className: "space-y-3 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--background)/0.45)] p-3", children: [
1247
+ /* @__PURE__ */ jsxs5("div", { className: "flex flex-wrap gap-2", children: [
1248
+ /* @__PURE__ */ jsxs5(
1249
+ "button",
1250
+ {
1251
+ type: "button",
1252
+ onClick: () => onConfirmPlan?.("execute"),
1253
+ className: "inline-flex items-center gap-1.5 rounded-lg bg-[hsl(var(--primary))] px-3 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))] transition-opacity hover:opacity-90",
1254
+ children: [
1255
+ /* @__PURE__ */ jsx6(Play, { size: 14 }),
1256
+ "\u5F00\u59CB\u6267\u884C"
1257
+ ]
1258
+ }
1259
+ ),
1260
+ /* @__PURE__ */ jsxs5(
1261
+ "button",
1262
+ {
1263
+ type: "button",
1264
+ onClick: () => setIsEditing((value) => !value),
1265
+ className: "inline-flex items-center gap-1.5 rounded-lg border border-[hsl(var(--border))] px-3 py-2 text-sm text-[hsl(var(--foreground))] transition-colors hover:bg-[hsl(var(--accent))]",
1266
+ children: [
1267
+ /* @__PURE__ */ jsx6(PencilLine, { size: 14 }),
1268
+ "\u4FEE\u6539"
1269
+ ]
1270
+ }
1271
+ )
1272
+ ] }),
1273
+ isEditing && /* @__PURE__ */ jsxs5("div", { className: "space-y-2", children: [
1274
+ /* @__PURE__ */ jsx6(
1275
+ "textarea",
1276
+ {
1277
+ value: revisionText,
1278
+ onChange: (event) => setRevisionText(event.target.value),
1279
+ placeholder: "\u8F93\u5165\u4FEE\u6539\u5EFA\u8BAE...",
1280
+ className: "min-h-24 w-full resize-y rounded-lg border border-[hsl(var(--border))] bg-transparent px-3 py-2 text-sm text-[hsl(var(--foreground))] outline-none placeholder:text-[hsl(var(--muted-foreground))]"
1281
+ }
1282
+ ),
1283
+ /* @__PURE__ */ jsx6("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx6(
1284
+ "button",
1285
+ {
1286
+ type: "button",
1287
+ disabled: !revisionText.trim(),
1288
+ onClick: submitRevision,
1289
+ className: "rounded-lg bg-[hsl(var(--primary))] px-3 py-1.5 text-xs font-medium text-[hsl(var(--primary-foreground))] transition-opacity hover:opacity-90 disabled:opacity-30",
1290
+ children: "\u63D0\u4EA4\u4FEE\u6539\u610F\u89C1"
1291
+ }
1292
+ ) })
1293
+ ] })
1294
+ ] });
1295
+ }
1296
+ function getSummaryState(data, sessionStatus) {
1297
+ if (sessionStatus === "failed") {
1298
+ return { kind: "failed", label: "\u51FA\u9519" };
1299
+ }
1300
+ if (sessionStatus === "interrupted") {
1301
+ return { kind: "interrupted", label: "\u5DF2\u4E2D\u65AD" };
1302
+ }
1303
+ if (data.parseError) {
1304
+ return { kind: "failed", label: "\u89E3\u6790\u5931\u8D25" };
1305
+ }
1306
+ if (data.hasPlanContent || data.plan != null) {
1307
+ return { kind: "complete", label: "\u5DF2\u751F\u6210" };
1308
+ }
1309
+ if (data.selectedSkills.length > 0) {
1310
+ return { kind: "progress", label: "\u6280\u80FD\u5206\u6790\u4E2D", dots: "\u25CF\u25CF\u25CB" };
1311
+ }
1312
+ if (data.searchResults.length > 0) {
1313
+ return { kind: "progress", label: "\u6280\u80FD\u53D1\u73B0\u4E2D", dots: "\u25CF\u25CB\u25CB" };
1314
+ }
1315
+ return { kind: "progress", label: "\u89C4\u5212\u51C6\u5907\u4E2D", dots: "\u25CB\u25CB\u25CB" };
1316
+ }
1317
+ function countPlanSteps(plan) {
1318
+ if (!plan) return 0;
1319
+ const countNode = (node) => {
1320
+ const children = Array.isArray(node.children) ? node.children : [];
1321
+ return 1 + children.reduce((total, child) => total + countNode(child), 0);
1322
+ };
1323
+ if (plan.root.id === "plan-root") {
1324
+ const children = Array.isArray(plan.root.children) ? plan.root.children : [];
1325
+ return children.reduce((total, child) => total + countNode(child), 0);
1326
+ }
1327
+ return countNode(plan.root);
1328
+ }
1329
+
1330
+ // src/react/components/plan/extract-plan-messages.ts
1331
+ function parseModeChange2(message) {
1332
+ if (message.kind !== "mode_change" || typeof message.content !== "string") {
1333
+ return null;
1334
+ }
1335
+ try {
1336
+ const parsed = JSON.parse(message.content);
1337
+ if (typeof parsed.from === "string" && typeof parsed.to === "string") {
1338
+ return { from: parsed.from, to: parsed.to };
1339
+ }
1340
+ } catch {
1341
+ }
1342
+ return null;
1343
+ }
1344
+ function isPlanningEnter(message) {
1345
+ if (message.kind === "planning_enter") {
1346
+ return true;
1347
+ }
1348
+ const modeChange = parseModeChange2(message);
1349
+ return modeChange?.to === "planning";
1350
+ }
1351
+ function isPlanningExit(message) {
1352
+ if (message.kind === "planning_exit") {
1353
+ return true;
1354
+ }
1355
+ const modeChange = parseModeChange2(message);
1356
+ return modeChange?.from === "planning";
1357
+ }
1358
+ function extractLatestPlanMessages(messages) {
1359
+ const safeMessages = Array.isArray(messages) ? messages : [];
1360
+ let enterIndex = -1;
1361
+ let exitIndex = -1;
1362
+ for (let i = safeMessages.length - 1; i >= 0; i -= 1) {
1363
+ const message = safeMessages[i];
1364
+ if (isPlanningExit(message) && exitIndex === -1) {
1365
+ exitIndex = i;
1366
+ continue;
1367
+ }
1368
+ if (isPlanningEnter(message)) {
1369
+ enterIndex = i;
1370
+ break;
1371
+ }
1372
+ }
1373
+ if (enterIndex === -1) {
1374
+ return [];
1375
+ }
1376
+ return safeMessages.slice(enterIndex + 1).filter((message, offset) => {
1377
+ const absoluteIndex = enterIndex + 1 + offset;
1378
+ if (isPlanningEnter(message) || isPlanningExit(message)) {
1379
+ return false;
1380
+ }
1381
+ if (exitIndex !== -1 && absoluteIndex > exitIndex) {
1382
+ return message.kind === "plan_status";
1383
+ }
1384
+ return true;
1385
+ });
1386
+ }
1387
+
1388
+ export {
1389
+ normalizeCodeLanguage,
1390
+ useHighlightedCodeHtml,
1391
+ CardContext,
1392
+ useCardContext,
1393
+ CardCodeBlock,
1394
+ MarkdownContent,
1395
+ AskUserQuestionBlock,
1396
+ parseAskUserQuestion,
1397
+ parsePlanMessages,
1398
+ PlanSummaryCard,
1399
+ extractLatestPlanMessages
1400
+ };
1401
+ //# sourceMappingURL=chunk-X6MEYCU7.js.map