@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.
- package/README.md +39 -1
- package/dist/{SkillStatusBar-DItrW2vv.d.ts → SkillStatusBar-DQyipdzn.d.ts} +112 -8
- package/dist/chunk-2UP7MG3J.js +66 -0
- package/dist/chunk-2UP7MG3J.js.map +1 -0
- package/dist/chunk-4VWLTG5L.js +2984 -0
- package/dist/chunk-4VWLTG5L.js.map +1 -0
- package/dist/chunk-7LEKQI47.js +32 -0
- package/dist/chunk-7LEKQI47.js.map +1 -0
- package/dist/chunk-CGOQI7ZL.js +8124 -0
- package/dist/chunk-CGOQI7ZL.js.map +1 -0
- package/dist/chunk-DQCXSPHP.js +33 -0
- package/dist/chunk-DQCXSPHP.js.map +1 -0
- package/dist/chunk-I3FFV63W.js +30 -0
- package/dist/chunk-I3FFV63W.js.map +1 -0
- package/dist/chunk-J3XVFPOV.js +58 -0
- package/dist/chunk-J3XVFPOV.js.map +1 -0
- package/dist/chunk-JCJFFJ42.js +39 -0
- package/dist/chunk-JCJFFJ42.js.map +1 -0
- package/dist/chunk-OKQWPNE3.js +1077 -0
- package/dist/chunk-OKQWPNE3.js.map +1 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-PZ5AY32C.js.map +1 -0
- package/dist/chunk-TC5BBLWO.js +29 -0
- package/dist/chunk-TC5BBLWO.js.map +1 -0
- package/dist/chunk-VD4CKRMT.js +127 -0
- package/dist/chunk-VD4CKRMT.js.map +1 -0
- package/dist/chunk-X6MEYCU7.js +1401 -0
- package/dist/chunk-X6MEYCU7.js.map +1 -0
- package/dist/client/index.js +24 -1052
- package/dist/client/index.js.map +1 -1
- package/dist/react/api/licenses.js +11 -1470
- package/dist/react/api/licenses.js.map +1 -1
- package/dist/react/api/vibe-coding.js +25 -1481
- package/dist/react/api/vibe-coding.js.map +1 -1
- package/dist/react/cards/register.js +45 -138
- package/dist/react/cards/register.js.map +1 -1
- package/dist/react/components/chat/index.d.ts +7 -21
- package/dist/react/components/chat/index.js +28 -11366
- package/dist/react/components/chat/index.js.map +1 -1
- package/dist/react/components/plan/index.js +135 -3054
- package/dist/react/components/plan/index.js.map +1 -1
- package/dist/react/components/session/index.js +21 -1499
- package/dist/react/components/session/index.js.map +1 -1
- package/dist/react/components/workspace/index.js +116 -1715
- package/dist/react/components/workspace/index.js.map +1 -1
- package/dist/react/devtools/bridge-devtools/index.js +8 -51
- package/dist/react/devtools/bridge-devtools/index.js.map +1 -1
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +625 -14035
- package/dist/react/index.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +1 -1
|
@@ -1,2980 +1,61 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
// src/react/components/plan/extract-plan-messages.ts
|
|
18
|
-
function parseModeChange(message) {
|
|
19
|
-
if (message.kind !== "mode_change" || typeof message.content !== "string") {
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
try {
|
|
23
|
-
const parsed = JSON.parse(message.content);
|
|
24
|
-
if (typeof parsed.from === "string" && typeof parsed.to === "string") {
|
|
25
|
-
return { from: parsed.from, to: parsed.to };
|
|
26
|
-
}
|
|
27
|
-
} catch {
|
|
28
|
-
}
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
function isPlanningEnter(message) {
|
|
32
|
-
if (message.kind === "planning_enter") {
|
|
33
|
-
return true;
|
|
34
|
-
}
|
|
35
|
-
const modeChange = parseModeChange(message);
|
|
36
|
-
return modeChange?.to === "planning";
|
|
37
|
-
}
|
|
38
|
-
function isPlanningExit(message) {
|
|
39
|
-
if (message.kind === "planning_exit") {
|
|
40
|
-
return true;
|
|
41
|
-
}
|
|
42
|
-
const modeChange = parseModeChange(message);
|
|
43
|
-
return modeChange?.from === "planning";
|
|
44
|
-
}
|
|
45
|
-
function extractLatestPlanMessages(messages) {
|
|
46
|
-
const safeMessages = Array.isArray(messages) ? messages : [];
|
|
47
|
-
let enterIndex = -1;
|
|
48
|
-
let exitIndex = -1;
|
|
49
|
-
for (let i = safeMessages.length - 1; i >= 0; i -= 1) {
|
|
50
|
-
const message = safeMessages[i];
|
|
51
|
-
if (isPlanningExit(message) && exitIndex === -1) {
|
|
52
|
-
exitIndex = i;
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
if (isPlanningEnter(message)) {
|
|
56
|
-
enterIndex = i;
|
|
57
|
-
break;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
if (enterIndex === -1) {
|
|
61
|
-
return [];
|
|
62
|
-
}
|
|
63
|
-
return safeMessages.slice(enterIndex + 1).filter((message, offset) => {
|
|
64
|
-
const absoluteIndex = enterIndex + 1 + offset;
|
|
65
|
-
if (isPlanningEnter(message) || isPlanningExit(message)) {
|
|
66
|
-
return false;
|
|
67
|
-
}
|
|
68
|
-
if (exitIndex !== -1 && absoluteIndex > exitIndex) {
|
|
69
|
-
return message.kind === "plan_status";
|
|
70
|
-
}
|
|
71
|
-
return true;
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// src/react/components/plan/PlanSummaryCard.tsx
|
|
76
|
-
import { CheckCircle2, ChevronRight, PencilLine, Play, Sparkles } from "lucide-react";
|
|
77
|
-
import { useMemo as useMemo4, useState as useState4 } from "react";
|
|
78
|
-
|
|
79
|
-
// src/react/lib/utils.ts
|
|
80
|
-
import { clsx } from "clsx";
|
|
81
|
-
import { twMerge } from "tailwind-merge";
|
|
82
|
-
function cn(...inputs) {
|
|
83
|
-
return twMerge(clsx(inputs));
|
|
84
|
-
}
|
|
85
|
-
async function copyToClipboard(text) {
|
|
86
|
-
if (navigator.clipboard) {
|
|
87
|
-
try {
|
|
88
|
-
await navigator.clipboard.writeText(text);
|
|
89
|
-
return true;
|
|
90
|
-
} catch {
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
const textarea = document.createElement("textarea");
|
|
94
|
-
textarea.value = text;
|
|
95
|
-
textarea.style.position = "fixed";
|
|
96
|
-
textarea.style.opacity = "0";
|
|
97
|
-
document.body.appendChild(textarea);
|
|
98
|
-
textarea.select();
|
|
99
|
-
try {
|
|
100
|
-
return document.execCommand("copy");
|
|
101
|
-
} finally {
|
|
102
|
-
document.body.removeChild(textarea);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// src/react/stores/ui-store.ts
|
|
107
|
-
import { create } from "zustand";
|
|
108
|
-
|
|
109
|
-
// src/react/stores/client-aware.ts
|
|
110
|
-
function createClientActions(set) {
|
|
111
|
-
return {
|
|
112
|
-
_client: null,
|
|
113
|
-
setClient: (client) => set({ _client: client })
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// src/react/stores/ui-store.ts
|
|
118
|
-
var isBrowser = typeof window !== "undefined" && typeof document !== "undefined";
|
|
119
|
-
function resolveEffectiveTheme(theme) {
|
|
120
|
-
if (theme !== "system") return theme;
|
|
121
|
-
return isBrowser && window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
122
|
-
}
|
|
123
|
-
function applyTheme(theme) {
|
|
124
|
-
if (!isBrowser) return;
|
|
125
|
-
const effective = resolveEffectiveTheme(theme);
|
|
126
|
-
document.documentElement.setAttribute("data-theme", effective);
|
|
127
|
-
}
|
|
128
|
-
var storedTheme = isBrowser ? localStorage.getItem("blade-theme") ?? "light" : "light";
|
|
129
|
-
applyTheme(storedTheme);
|
|
130
|
-
if (isBrowser) {
|
|
131
|
-
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => {
|
|
132
|
-
const current = useUiStore?.getState?.()?.theme;
|
|
133
|
-
if (current === "system") applyTheme("system");
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
function removeArtifactAtIndex(state, index) {
|
|
137
|
-
const next = state.artifacts.filter((_, i) => i !== index);
|
|
138
|
-
let nextActive = state.activeArtifactIndex;
|
|
139
|
-
if (next.length === 0) {
|
|
140
|
-
return { artifacts: [], activeArtifactIndex: -1, rightPanelCollapsed: true };
|
|
141
|
-
}
|
|
142
|
-
if (index < nextActive) {
|
|
143
|
-
nextActive -= 1;
|
|
144
|
-
} else if (index === nextActive) {
|
|
145
|
-
nextActive = Math.min(index, next.length - 1);
|
|
146
|
-
}
|
|
147
|
-
return { artifacts: next, activeArtifactIndex: nextActive };
|
|
148
|
-
}
|
|
149
|
-
function upsertArtifactState(state, target, options) {
|
|
150
|
-
const reveal = options?.reveal ?? true;
|
|
151
|
-
const activate = options?.activate ?? true;
|
|
152
|
-
const targetKey = target.key ?? target.title;
|
|
153
|
-
const applyUiState = (partial) => ({
|
|
154
|
-
...partial,
|
|
155
|
-
...reveal ? { rightPanelCollapsed: false, activeRightTab: "preview" } : {}
|
|
156
|
-
});
|
|
157
|
-
const existing = state.artifacts.findIndex((artifact) => targetKey && (artifact.key ?? artifact.title) === targetKey);
|
|
158
|
-
if (existing >= 0) {
|
|
159
|
-
const updated = [...state.artifacts];
|
|
160
|
-
updated[existing] = target;
|
|
161
|
-
return applyUiState({
|
|
162
|
-
artifacts: updated,
|
|
163
|
-
activeArtifactIndex: activate ? existing : state.activeArtifactIndex
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
const next = [...state.artifacts, target];
|
|
167
|
-
return applyUiState({
|
|
168
|
-
artifacts: next,
|
|
169
|
-
activeArtifactIndex: activate ? next.length - 1 : state.activeArtifactIndex >= 0 ? state.activeArtifactIndex : 0
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
var useUiStore = create()((set) => ({
|
|
173
|
-
...createClientActions(set),
|
|
174
|
-
leftPanelSize: 20,
|
|
175
|
-
rightPanelSize: 25,
|
|
176
|
-
leftPanelCollapsed: false,
|
|
177
|
-
rightPanelCollapsed: false,
|
|
178
|
-
activeRightTab: "situation",
|
|
179
|
-
artifacts: [],
|
|
180
|
-
activeArtifactIndex: -1,
|
|
181
|
-
theme: storedTheme,
|
|
182
|
-
setLeftPanelSize: (size) => set({ leftPanelSize: size }),
|
|
183
|
-
setRightPanelSize: (size) => set({ rightPanelSize: size }),
|
|
184
|
-
setLeftPanelCollapsed: (collapsed) => set({ leftPanelCollapsed: collapsed }),
|
|
185
|
-
setRightPanelCollapsed: (collapsed) => set({ rightPanelCollapsed: collapsed }),
|
|
186
|
-
toggleLeftPanel: () => set((s) => ({ leftPanelCollapsed: !s.leftPanelCollapsed })),
|
|
187
|
-
toggleRightPanel: () => set((s) => ({ rightPanelCollapsed: !s.rightPanelCollapsed })),
|
|
188
|
-
setActiveRightTab: (tab) => set({ activeRightTab: tab }),
|
|
189
|
-
pushArtifact: (target) => set((state) => upsertArtifactState(state, target)),
|
|
190
|
-
upsertArtifact: (target, options) => set((state) => upsertArtifactState(state, target, options)),
|
|
191
|
-
setActiveArtifact: (index) => set({ activeArtifactIndex: index }),
|
|
192
|
-
closeArtifact: (index) => set((state) => removeArtifactAtIndex(state, index)),
|
|
193
|
-
removeArtifactByKey: (key) => set((state) => {
|
|
194
|
-
const index = state.artifacts.findIndex((artifact) => (artifact.key ?? artifact.title) === key);
|
|
195
|
-
if (index < 0) return state;
|
|
196
|
-
return removeArtifactAtIndex(state, index);
|
|
197
|
-
}),
|
|
198
|
-
clearArtifacts: () => set({ artifacts: [], activeArtifactIndex: -1 }),
|
|
199
|
-
setPreviewTarget: (target) => set(() => {
|
|
200
|
-
if (target === null) {
|
|
201
|
-
return { artifacts: [], activeArtifactIndex: -1 };
|
|
202
|
-
}
|
|
203
|
-
return {
|
|
204
|
-
artifacts: [target],
|
|
205
|
-
activeArtifactIndex: 0,
|
|
206
|
-
rightPanelCollapsed: false,
|
|
207
|
-
activeRightTab: "preview"
|
|
208
|
-
};
|
|
209
|
-
}),
|
|
210
|
-
setTheme: (theme) => {
|
|
211
|
-
localStorage.setItem("blade-theme", theme);
|
|
212
|
-
applyTheme(theme);
|
|
213
|
-
set({ theme });
|
|
214
|
-
}
|
|
215
|
-
}));
|
|
216
|
-
|
|
217
|
-
// src/react/components/chat/AskUserQuestionBlock.tsx
|
|
218
|
-
import { Check as Check2, Loader2, MessageSquareMore } from "lucide-react";
|
|
219
|
-
import { useEffect as useEffect3, useMemo as useMemo3, useState as useState3 } from "react";
|
|
220
|
-
|
|
221
|
-
// src/react/components/markdown/MarkdownContent.tsx
|
|
222
|
-
import { mermaid } from "@streamdown/mermaid";
|
|
223
|
-
import { Check, Copy, Download } from "lucide-react";
|
|
224
|
-
import {
|
|
225
|
-
useEffect as useEffect2,
|
|
226
|
-
useMemo as useMemo2,
|
|
227
|
-
useRef,
|
|
228
|
-
useState as useState2
|
|
229
|
-
} from "react";
|
|
230
|
-
import { Streamdown } from "streamdown";
|
|
231
|
-
|
|
232
|
-
// src/client/resources/models.ts
|
|
233
|
-
import { type } from "arktype";
|
|
234
|
-
var ModelOption = type({
|
|
235
|
-
id: "string",
|
|
236
|
-
label: "string"
|
|
237
|
-
});
|
|
238
|
-
var ModelsConfig = type({
|
|
239
|
-
default: "string",
|
|
240
|
-
models: ModelOption.array()
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
// src/client/socket.ts
|
|
244
|
-
import { io } from "socket.io-client";
|
|
245
|
-
|
|
246
|
-
// src/react/schemas/partner-skill.ts
|
|
247
|
-
import { type as type2 } from "arktype";
|
|
248
|
-
var PartnerSkillName = type2("/^[a-z0-9-]+\\/[a-z0-9-]+$/");
|
|
249
|
-
var PartnerSkillFile = type2({
|
|
250
|
-
path: "string > 0",
|
|
251
|
-
content: "string"
|
|
252
|
-
});
|
|
253
|
-
var PartnerSkillInstallPayload = type2({
|
|
254
|
-
name: PartnerSkillName,
|
|
255
|
-
files: PartnerSkillFile.array().atLeastLength(1)
|
|
256
|
-
});
|
|
257
|
-
var PartnerSkillInstallResult = type2({
|
|
258
|
-
name: PartnerSkillName,
|
|
259
|
-
skill_dir: "string",
|
|
260
|
-
file_count: "number.integer >= 0",
|
|
261
|
-
overwritten: "boolean"
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
// src/react/stores/ui-bridge-store.ts
|
|
265
|
-
import { create as create2 } from "zustand";
|
|
266
|
-
function buildSignalId() {
|
|
267
|
-
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
268
|
-
return crypto.randomUUID();
|
|
269
|
-
}
|
|
270
|
-
return `bridge-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
271
|
-
}
|
|
272
|
-
function clearSignalRecord(record, sessionId) {
|
|
273
|
-
if (!(sessionId in record)) {
|
|
274
|
-
return record;
|
|
275
|
-
}
|
|
276
|
-
const next = { ...record };
|
|
277
|
-
delete next[sessionId];
|
|
278
|
-
return next;
|
|
279
|
-
}
|
|
280
|
-
var useUiBridgeStore = create2()((set, get) => ({
|
|
281
|
-
...createClientActions(set),
|
|
282
|
-
pendingContexts: {},
|
|
283
|
-
draftAppends: {},
|
|
284
|
-
sendRequests: {},
|
|
285
|
-
addPendingContext: (sessionId, context) => set((state) => ({
|
|
286
|
-
pendingContexts: {
|
|
287
|
-
...state.pendingContexts,
|
|
288
|
-
[sessionId]: [
|
|
289
|
-
...state.pendingContexts[sessionId] ?? [],
|
|
290
|
-
{
|
|
291
|
-
id: buildSignalId(),
|
|
292
|
-
label: context.label,
|
|
293
|
-
content: context.content
|
|
294
|
-
}
|
|
295
|
-
]
|
|
296
|
-
}
|
|
297
|
-
})),
|
|
298
|
-
removePendingContext: (sessionId, contextId) => set((state) => {
|
|
299
|
-
const contexts = state.pendingContexts[sessionId];
|
|
300
|
-
if (!contexts?.length) {
|
|
301
|
-
return state;
|
|
302
|
-
}
|
|
303
|
-
const nextContexts = contexts.filter((context) => context.id !== contextId);
|
|
304
|
-
if (nextContexts.length === contexts.length) {
|
|
305
|
-
return state;
|
|
306
|
-
}
|
|
307
|
-
return {
|
|
308
|
-
pendingContexts: nextContexts.length > 0 ? {
|
|
309
|
-
...state.pendingContexts,
|
|
310
|
-
[sessionId]: nextContexts
|
|
311
|
-
} : clearSignalRecord(state.pendingContexts, sessionId)
|
|
312
|
-
};
|
|
313
|
-
}),
|
|
314
|
-
consumePendingContexts: (sessionId) => {
|
|
315
|
-
const signals = get().pendingContexts[sessionId] ?? [];
|
|
316
|
-
if (signals.length === 0) return [];
|
|
317
|
-
set((state) => ({
|
|
318
|
-
pendingContexts: clearSignalRecord(state.pendingContexts, sessionId)
|
|
319
|
-
}));
|
|
320
|
-
return signals;
|
|
321
|
-
},
|
|
322
|
-
clearPendingContexts: (sessionId) => set((state) => ({
|
|
323
|
-
pendingContexts: clearSignalRecord(state.pendingContexts, sessionId)
|
|
324
|
-
})),
|
|
325
|
-
addDraftAppend: (sessionId, text) => set((state) => ({
|
|
326
|
-
draftAppends: {
|
|
327
|
-
...state.draftAppends,
|
|
328
|
-
[sessionId]: [
|
|
329
|
-
...state.draftAppends[sessionId] ?? [],
|
|
330
|
-
{
|
|
331
|
-
id: buildSignalId(),
|
|
332
|
-
text
|
|
333
|
-
}
|
|
334
|
-
]
|
|
335
|
-
}
|
|
336
|
-
})),
|
|
337
|
-
consumeDraftAppends: (sessionId) => {
|
|
338
|
-
const signals = get().draftAppends[sessionId] ?? [];
|
|
339
|
-
if (signals.length === 0) return [];
|
|
340
|
-
set((state) => ({
|
|
341
|
-
draftAppends: clearSignalRecord(state.draftAppends, sessionId)
|
|
342
|
-
}));
|
|
343
|
-
return signals;
|
|
344
|
-
},
|
|
345
|
-
clearDraftAppends: (sessionId) => set((state) => ({
|
|
346
|
-
draftAppends: clearSignalRecord(state.draftAppends, sessionId)
|
|
347
|
-
})),
|
|
348
|
-
addSendRequest: (sessionId) => set((state) => ({
|
|
349
|
-
sendRequests: {
|
|
350
|
-
...state.sendRequests,
|
|
351
|
-
[sessionId]: [
|
|
352
|
-
...state.sendRequests[sessionId] ?? [],
|
|
353
|
-
{
|
|
354
|
-
id: buildSignalId()
|
|
355
|
-
}
|
|
356
|
-
]
|
|
357
|
-
}
|
|
358
|
-
})),
|
|
359
|
-
consumeSendRequests: (sessionId) => {
|
|
360
|
-
const signals = get().sendRequests[sessionId] ?? [];
|
|
361
|
-
if (signals.length === 0) return [];
|
|
362
|
-
set((state) => ({
|
|
363
|
-
sendRequests: clearSignalRecord(state.sendRequests, sessionId)
|
|
364
|
-
}));
|
|
365
|
-
return signals;
|
|
366
|
-
},
|
|
367
|
-
clearSendRequests: (sessionId) => set((state) => ({
|
|
368
|
-
sendRequests: clearSignalRecord(state.sendRequests, sessionId)
|
|
369
|
-
})),
|
|
370
|
-
clearSession: (sessionId) => set((state) => ({
|
|
371
|
-
pendingContexts: clearSignalRecord(state.pendingContexts, sessionId),
|
|
372
|
-
draftAppends: clearSignalRecord(state.draftAppends, sessionId),
|
|
373
|
-
sendRequests: clearSignalRecord(state.sendRequests, sessionId)
|
|
374
|
-
}))
|
|
375
|
-
}));
|
|
376
|
-
|
|
377
|
-
// src/react/lib/chat.ts
|
|
378
|
-
function normalizeMessageContent(content) {
|
|
379
|
-
if (typeof content === "string") return content;
|
|
380
|
-
if (Array.isArray(content)) return content;
|
|
381
|
-
return String(content ?? "");
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
// src/react/stores/auth-store.ts
|
|
385
|
-
import { create as create6 } from "zustand";
|
|
386
|
-
import { createJSONStorage, persist } from "zustand/middleware";
|
|
387
|
-
|
|
388
|
-
// src/react/api/auth.ts
|
|
389
|
-
var r = () => getClient().auth;
|
|
390
|
-
var getMe = (...args) => r().getMe(...args);
|
|
391
|
-
var logout = (...args) => r().logout(...args);
|
|
392
|
-
|
|
393
|
-
// src/react/sockets/socket-state.ts
|
|
394
|
-
var agentSocket = null;
|
|
395
|
-
|
|
396
|
-
// src/react/stores/session-store.ts
|
|
397
|
-
import { create as create5 } from "zustand";
|
|
398
|
-
|
|
399
|
-
// src/react/api/sessions.ts
|
|
400
|
-
var r2 = () => getClient().sessions;
|
|
401
|
-
var listSessions = (...args) => r2().listSessions(...args);
|
|
402
|
-
var createSession = (...args) => r2().createSession(...args);
|
|
403
|
-
var getSession = (...args) => r2().getSession(...args);
|
|
404
|
-
var pinSession = (...args) => r2().pinSession(...args);
|
|
405
|
-
var updateSharing = (...args) => r2().updateSharing(...args);
|
|
406
|
-
var getSessionTasks = (...args) => r2().getSessionTasks(...args);
|
|
407
|
-
var getSessionTurns = (...args) => r2().getSessionTurns(...args);
|
|
408
|
-
var deleteSession = (...args) => r2().deleteSession(...args);
|
|
409
|
-
|
|
410
|
-
// src/react/stores/chat-store.ts
|
|
411
|
-
import { create as create3 } from "zustand";
|
|
412
|
-
|
|
413
|
-
// src/react/components/chat/display-utils.ts
|
|
414
|
-
var TOOL_NAME_ALIASES = {
|
|
415
|
-
agent: "Agent",
|
|
416
|
-
ask_user_question: "AskUserQuestion",
|
|
417
|
-
bash: "Bash",
|
|
418
|
-
bg_bash: "BgBash",
|
|
419
|
-
edit: "Edit",
|
|
420
|
-
exit_plan_mode: "ExitPlanMode",
|
|
421
|
-
file_edit: "Edit",
|
|
422
|
-
file_read: "Read",
|
|
423
|
-
file_write: "Write",
|
|
424
|
-
finish_task: "FinishTask",
|
|
425
|
-
get_skill_content: "GetSkillContent",
|
|
426
|
-
glob: "Glob",
|
|
427
|
-
grep: "Grep",
|
|
428
|
-
list_skill_tools: "ListSkillTools",
|
|
429
|
-
load_skill_tools: "LoadSkillTools",
|
|
430
|
-
ls: "Ls",
|
|
431
|
-
read: "Read",
|
|
432
|
-
run_skill_tool: "RunSkillTool",
|
|
433
|
-
search_skills: "SearchSkills",
|
|
434
|
-
web_fetch: "WebFetch",
|
|
435
|
-
web_search: "WebSearch",
|
|
436
|
-
write: "Write"
|
|
437
|
-
};
|
|
438
|
-
var TOOL_DISPLAY_LABELS = {
|
|
439
|
-
Bash: "\u6267\u884C\u547D\u4EE4",
|
|
440
|
-
BgBash: "\u540E\u53F0\u6267\u884C\u547D\u4EE4",
|
|
441
|
-
Read: "\u8BFB\u53D6\u6587\u4EF6",
|
|
442
|
-
Write: "\u5199\u5165\u6587\u4EF6",
|
|
443
|
-
Edit: "\u7F16\u8F91\u6587\u4EF6",
|
|
444
|
-
Ls: "\u5217\u51FA\u76EE\u5F55",
|
|
445
|
-
Glob: "\u5339\u914D\u6587\u4EF6",
|
|
446
|
-
Grep: "\u641C\u7D22\u6587\u672C",
|
|
447
|
-
WebSearch: "\u641C\u7D22\u7F51\u9875",
|
|
448
|
-
WebFetch: "\u6574\u7406\u7F51\u9875\u5185\u5BB9",
|
|
449
|
-
Agent: "\u6D3E\u751F\u5B50\u667A\u80FD\u4F53",
|
|
450
|
-
AskUserQuestion: "\u5411\u7528\u6237\u63D0\u95EE",
|
|
451
|
-
SearchSkills: "\u641C\u7D22\u6280\u80FD",
|
|
452
|
-
GetSkillContent: "\u8BFB\u53D6\u6280\u80FD",
|
|
453
|
-
ListSkillTools: "\u67E5\u770B\u5DE5\u5177\u5217\u8868",
|
|
454
|
-
LoadSkillTools: "\u52A0\u8F7D\u6280\u80FD",
|
|
455
|
-
RunSkillTool: "\u6267\u884C\u6280\u80FD\u5DE5\u5177",
|
|
456
|
-
FinishTask: "\u4EFB\u52A1\u5B8C\u6210",
|
|
457
|
-
ExitPlanMode: "\u63D0\u4EA4\u8BA1\u5212",
|
|
458
|
-
ListSessions: "\u5217\u51FA\u5386\u53F2\u4F1A\u8BDD",
|
|
459
|
-
GetSessionHistory: "\u8BFB\u53D6\u4F1A\u8BDD\u5386\u53F2"
|
|
460
|
-
};
|
|
461
|
-
var GENERIC_DISPLAY_NAMES = new Set(Object.values(TOOL_DISPLAY_LABELS));
|
|
462
|
-
function formatToolName(name) {
|
|
463
|
-
const trimmed = name.trim();
|
|
464
|
-
if (!trimmed) return name;
|
|
465
|
-
const stripped = trimmed.split(":").pop()?.split("/").pop()?.split(".").pop()?.trim() || trimmed;
|
|
466
|
-
const normalized = stripped.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
|
|
467
|
-
return TOOL_NAME_ALIASES[normalized] ?? stripped;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// src/react/stores/chat-store.ts
|
|
471
|
-
var _getActiveSessionId = null;
|
|
472
|
-
function setChatStoreSessionAccessor(fn) {
|
|
473
|
-
_getActiveSessionId = fn;
|
|
474
|
-
}
|
|
475
|
-
function parseAgentDescription(argumentsJson) {
|
|
476
|
-
try {
|
|
477
|
-
return JSON.parse(argumentsJson)?.description ?? "\u5B50\u667A\u80FD\u4F53";
|
|
478
|
-
} catch {
|
|
479
|
-
return "\u5B50\u667A\u80FD\u4F53";
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
function inferLoopStatusFromMessages(messages) {
|
|
483
|
-
const toolCalls = messages.flatMap((message) => message.tool_calls ?? []);
|
|
484
|
-
if (messages.some((message) => message.status === "streaming")) return "running";
|
|
485
|
-
if (toolCalls.some((toolCall) => toolCall.status === "awaiting_answer")) return "awaiting_answer";
|
|
486
|
-
if (toolCalls.some((toolCall) => toolCall.status === "error")) return "error";
|
|
487
|
-
if (toolCalls.some((toolCall) => toolCall.status === "cancelled")) return "cancelled";
|
|
488
|
-
if (toolCalls.some((toolCall) => toolCall.status === "pending")) return "running";
|
|
489
|
-
return "done";
|
|
490
|
-
}
|
|
491
|
-
function inferLoopStatusFromTurns(turns, messages) {
|
|
492
|
-
const latestAgentNotification = [...turns].reverse().flatMap((turn) => turn.blocks).find((block) => {
|
|
493
|
-
if (block.type !== "system_notification" || !isRecord(block.content)) return false;
|
|
494
|
-
return block.content.notification_type === "agent:start" || block.content.notification_type === "agent:end";
|
|
495
|
-
});
|
|
496
|
-
if (latestAgentNotification?.type === "system_notification" && isRecord(latestAgentNotification.content)) {
|
|
497
|
-
const notificationType = latestAgentNotification.content.notification_type;
|
|
498
|
-
const status = latestAgentNotification.content.status;
|
|
499
|
-
if (notificationType === "agent:start" || status === "running") return "running";
|
|
500
|
-
if (status === "error") return "error";
|
|
501
|
-
if (status === "cancelled") return "cancelled";
|
|
502
|
-
}
|
|
503
|
-
return inferLoopStatusFromMessages(messages);
|
|
504
|
-
}
|
|
505
|
-
function isRecord(value) {
|
|
506
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
507
|
-
}
|
|
508
|
-
function parentForkToolCallIdFromTurn(turn) {
|
|
509
|
-
if (typeof turn.parent_fork_tool_call_id === "string" && turn.parent_fork_tool_call_id.length > 0) {
|
|
510
|
-
return turn.parent_fork_tool_call_id;
|
|
511
|
-
}
|
|
512
|
-
for (const block of turn.blocks) {
|
|
513
|
-
if (block.type !== "system_notification" || !isRecord(block.content)) continue;
|
|
514
|
-
const metadata = block.content.metadata;
|
|
515
|
-
if (!isRecord(metadata)) continue;
|
|
516
|
-
const parentId = metadata.parent_fork_tool_call_id;
|
|
517
|
-
if (typeof parentId === "string" && parentId.length > 0) return parentId;
|
|
518
|
-
}
|
|
519
|
-
return null;
|
|
520
|
-
}
|
|
521
|
-
function buildMessageContent(turn) {
|
|
522
|
-
const textBlocks = turn.blocks.filter((block) => block.type === "text");
|
|
523
|
-
if (textBlocks.length === 0) return "";
|
|
524
|
-
if (textBlocks.length === 1) return normalizeMessageContent(textBlocks[0].content);
|
|
525
|
-
return textBlocks.map((block) => {
|
|
526
|
-
if (typeof block.content === "string") return block.content;
|
|
527
|
-
return JSON.stringify(block.content);
|
|
528
|
-
}).join("");
|
|
529
|
-
}
|
|
530
|
-
function buildReasoning(turn) {
|
|
531
|
-
const thinking = turn.blocks.filter((block) => block.type === "thinking").map((block) => typeof block.content === "string" ? block.content : JSON.stringify(block.content)).filter(Boolean);
|
|
532
|
-
if (thinking.length === 0) return void 0;
|
|
533
|
-
return thinking.join("\n\n");
|
|
534
|
-
}
|
|
535
|
-
function isModeChangeContent(value) {
|
|
536
|
-
return typeof value === "object" && value !== null && typeof value.from === "string" && typeof value.to === "string";
|
|
537
|
-
}
|
|
538
|
-
function extractModeFromBlocks(blocks) {
|
|
539
|
-
const modeBlock = blocks.find((block) => block.type === "mode_change");
|
|
540
|
-
if (modeBlock && isModeChangeContent(modeBlock.content)) {
|
|
541
|
-
return modeBlock.content.to === "planning" || modeBlock.content.to === "executing" ? modeBlock.content.to : null;
|
|
542
|
-
}
|
|
543
|
-
if (blocks.some((block) => block.type === "planning_enter")) return "planning";
|
|
544
|
-
if (blocks.some((block) => block.type === "planning_exit")) return "executing";
|
|
545
|
-
return null;
|
|
546
|
-
}
|
|
547
|
-
function projectionToMessage(turn) {
|
|
548
|
-
if (turn.kind === "compaction" && turn.compaction_id) {
|
|
549
|
-
return {
|
|
550
|
-
role: "assistant",
|
|
551
|
-
content: turn.summary_preview ?? "",
|
|
552
|
-
kind: "compaction",
|
|
553
|
-
loop_name: turn.loop_id,
|
|
554
|
-
entry_id: turn.turn_id,
|
|
555
|
-
status: turn.status,
|
|
556
|
-
compaction: {
|
|
557
|
-
compaction_id: turn.compaction_id,
|
|
558
|
-
summary_preview: turn.summary_preview,
|
|
559
|
-
summary_full: turn.summary_full,
|
|
560
|
-
archived_count: turn.archived_count,
|
|
561
|
-
archived_files: turn.archived_files,
|
|
562
|
-
archived_tool_calls: turn.archived_tool_calls,
|
|
563
|
-
tokens_before: turn.tokens_before,
|
|
564
|
-
tokens_after: turn.tokens_after,
|
|
565
|
-
saved_ratio: turn.saved_ratio,
|
|
566
|
-
trigger: turn.trigger,
|
|
567
|
-
failure_reason: turn.failure_reason,
|
|
568
|
-
fallback_applied: turn.fallback_applied
|
|
569
|
-
}
|
|
570
|
-
};
|
|
571
|
-
}
|
|
572
|
-
const planningBlock = turn.blocks.find(
|
|
573
|
-
(block) => block.type === "mode_change" || block.type === "planning_enter" || block.type === "planning_exit" || block.type === "plan_status"
|
|
574
|
-
);
|
|
575
|
-
if (planningBlock) {
|
|
576
|
-
if (planningBlock.type === "plan_status") {
|
|
577
|
-
return {
|
|
578
|
-
role: "tool",
|
|
579
|
-
content: typeof planningBlock.content === "string" ? planningBlock.content : JSON.stringify(planningBlock.content ?? {}, null, 2),
|
|
580
|
-
kind: "plan_status",
|
|
581
|
-
loop_name: turn.loop_id,
|
|
582
|
-
entry_id: turn.turn_id,
|
|
583
|
-
status: turn.status
|
|
584
|
-
};
|
|
585
|
-
}
|
|
586
|
-
return {
|
|
587
|
-
role: "assistant",
|
|
588
|
-
content: planningBlock.type === "mode_change" ? typeof planningBlock.content === "string" ? planningBlock.content : JSON.stringify(planningBlock.content ?? {}) : "",
|
|
589
|
-
kind: planningBlock.type,
|
|
590
|
-
loop_name: turn.loop_id,
|
|
591
|
-
entry_id: turn.turn_id,
|
|
592
|
-
status: turn.status
|
|
593
|
-
};
|
|
594
|
-
}
|
|
595
|
-
if (turn.blocks.some((block) => block.type === "ask_user_answer")) {
|
|
596
|
-
return null;
|
|
597
|
-
}
|
|
598
|
-
const content = buildMessageContent(turn);
|
|
599
|
-
const reasoning = buildReasoning(turn);
|
|
600
|
-
const toolCalls = turn.tool_calls.length > 0 ? turn.tool_calls.map((toolCall) => ({
|
|
601
|
-
id: toolCall.id,
|
|
602
|
-
name: toolCall.tool_name,
|
|
603
|
-
display_name: toolCall.display_name,
|
|
604
|
-
arguments: toolCall.arguments,
|
|
605
|
-
result: toolCall.result ?? void 0,
|
|
606
|
-
pending_question_ref: toolCall.pending_question_ref ?? void 0,
|
|
607
|
-
status: toolCall.status === "pending" || toolCall.status === "awaiting_answer" || toolCall.status === "done" || toolCall.status === "error" || toolCall.status === "cancelled" ? toolCall.status : "pending",
|
|
608
|
-
...typeof toolCall.duration_ms === "number" ? { duration_ms: toolCall.duration_ms } : {}
|
|
609
|
-
})) : void 0;
|
|
610
|
-
if (turn.role === "system" && !content && !toolCalls?.length) {
|
|
611
|
-
return null;
|
|
612
|
-
}
|
|
613
|
-
return {
|
|
614
|
-
role: turn.role === "system" ? "assistant" : turn.role,
|
|
615
|
-
content,
|
|
616
|
-
blocks: turn.blocks,
|
|
617
|
-
...reasoning ? { reasoning } : {},
|
|
618
|
-
...toolCalls ? { tool_calls: toolCalls } : {},
|
|
619
|
-
loop_name: turn.loop_id,
|
|
620
|
-
entry_id: turn.turn_id,
|
|
621
|
-
status: turn.status,
|
|
622
|
-
...typeof turn.duration_ms === "number" ? { duration_ms: turn.duration_ms } : {},
|
|
623
|
-
...turn.started_at ? { timestamp: turn.started_at } : {},
|
|
624
|
-
...turn.memory_refs?.length ? { memory_refs: turn.memory_refs } : {}
|
|
625
|
-
};
|
|
626
|
-
}
|
|
627
|
-
function rebuildAgentLoops(turns) {
|
|
628
|
-
const messages = turns.map(projectionToMessage).filter(Boolean);
|
|
629
|
-
const childLoopNames = [...new Set(turns.map((turn) => turn.loop_id).filter((name) => name !== "root"))];
|
|
630
|
-
if (childLoopNames.length === 0) return {};
|
|
631
|
-
const agentToolCalls = messages.filter((message) => message.role === "assistant" && (message.loop_name ?? "root") === "root").flatMap((message) => message.tool_calls ?? []).filter((toolCall) => formatToolName(toolCall.name) === "Agent");
|
|
632
|
-
const loops = {};
|
|
633
|
-
const agentToolCallsById = new Map(agentToolCalls.map((toolCall) => [toolCall.id, toolCall]));
|
|
634
|
-
const explicitParentToolCallIds = new Set(
|
|
635
|
-
turns.map(parentForkToolCallIdFromTurn).filter((id) => id !== null)
|
|
636
|
-
);
|
|
637
|
-
const usedToolCallIds = /* @__PURE__ */ new Set();
|
|
638
|
-
for (const loopName of childLoopNames) {
|
|
639
|
-
const loopMessages = messages.filter((message) => (message.loop_name ?? "root") === loopName);
|
|
640
|
-
const loopTurns = turns.filter((turn) => turn.loop_id === loopName);
|
|
641
|
-
const parentToolCallId = loopTurns.map(parentForkToolCallIdFromTurn).find(Boolean);
|
|
642
|
-
const explicitToolCall = parentToolCallId && !usedToolCallIds.has(parentToolCallId) ? agentToolCallsById.get(parentToolCallId) ?? null : null;
|
|
643
|
-
const fallbackToolCall = explicitToolCall === null ? agentToolCalls.find(
|
|
644
|
-
(toolCall2) => !usedToolCallIds.has(toolCall2.id) && !explicitParentToolCallIds.has(toolCall2.id)
|
|
645
|
-
) : null;
|
|
646
|
-
const toolCall = explicitToolCall ?? fallbackToolCall;
|
|
647
|
-
if (!toolCall) continue;
|
|
648
|
-
usedToolCallIds.add(toolCall.id);
|
|
649
|
-
loops[loopName] = {
|
|
650
|
-
toolCallId: toolCall.id,
|
|
651
|
-
description: parseAgentDescription(toolCall.arguments),
|
|
652
|
-
status: inferLoopStatusFromTurns(loopTurns, loopMessages)
|
|
653
|
-
};
|
|
654
|
-
}
|
|
655
|
-
return loops;
|
|
656
|
-
}
|
|
657
|
-
function applyPlanningSideEffects(sessionId, turns) {
|
|
658
|
-
const latestMode = [...turns].reverse().map((turn) => extractModeFromBlocks(turn.blocks)).find((mode) => mode !== null);
|
|
659
|
-
if (latestMode !== "planning") return;
|
|
660
|
-
if (_getActiveSessionId?.() !== sessionId) return;
|
|
661
|
-
const ui = useUiStore.getState();
|
|
662
|
-
ui.setActiveRightTab("situation");
|
|
663
|
-
if (ui.rightPanelCollapsed) {
|
|
664
|
-
ui.toggleRightPanel();
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
function materialize(turns) {
|
|
668
|
-
const messages = turns.map(projectionToMessage).filter((message) => message !== null);
|
|
669
|
-
const activeCompaction = [...turns].reverse().find(
|
|
670
|
-
(turn) => turn.kind === "compaction" && turn.status === "streaming" && typeof turn.compaction_id === "string"
|
|
671
|
-
);
|
|
672
|
-
return {
|
|
673
|
-
messages,
|
|
674
|
-
agentLoops: rebuildAgentLoops(turns),
|
|
675
|
-
activeCompaction: activeCompaction ? {
|
|
676
|
-
turn_id: activeCompaction.turn_id,
|
|
677
|
-
status: activeCompaction.status,
|
|
678
|
-
compaction_id: activeCompaction.compaction_id,
|
|
679
|
-
summary_preview: activeCompaction.summary_preview,
|
|
680
|
-
summary_full: activeCompaction.summary_full,
|
|
681
|
-
archived_count: activeCompaction.archived_count,
|
|
682
|
-
archived_files: activeCompaction.archived_files,
|
|
683
|
-
archived_tool_calls: activeCompaction.archived_tool_calls,
|
|
684
|
-
tokens_before: activeCompaction.tokens_before,
|
|
685
|
-
tokens_after: activeCompaction.tokens_after,
|
|
686
|
-
saved_ratio: activeCompaction.saved_ratio,
|
|
687
|
-
trigger: activeCompaction.trigger,
|
|
688
|
-
failure_reason: activeCompaction.failure_reason,
|
|
689
|
-
fallback_applied: activeCompaction.fallback_applied
|
|
690
|
-
} : null
|
|
691
|
-
};
|
|
692
|
-
}
|
|
693
|
-
var ERROR_ANCHOR_PREFIX = "error-anchor:";
|
|
694
|
-
function updateSessionState(state, sessionId, turns) {
|
|
695
|
-
const orderedTurns = [...turns].sort((left, right) => left.sequence - right.sequence);
|
|
696
|
-
const { messages, agentLoops, activeCompaction } = materialize(orderedTurns);
|
|
697
|
-
applyPlanningSideEffects(sessionId, orderedTurns);
|
|
698
|
-
const lastTurnId = orderedTurns[orderedTurns.length - 1]?.turn_id ?? null;
|
|
699
|
-
const preservedErrors = lastTurnId ? (state.messages[sessionId] ?? []).filter(
|
|
700
|
-
(m) => m.role === "error" && typeof m.entry_id === "string" && m.entry_id.startsWith(`${ERROR_ANCHOR_PREFIX}${lastTurnId}:`)
|
|
701
|
-
) : [];
|
|
702
|
-
const mergedMessages = preservedErrors.length > 0 ? [...messages, ...preservedErrors] : messages;
|
|
703
|
-
return {
|
|
704
|
-
turns: { ...state.turns, [sessionId]: orderedTurns },
|
|
705
|
-
messages: { ...state.messages, [sessionId]: mergedMessages },
|
|
706
|
-
agentLoops: { ...state.agentLoops, [sessionId]: agentLoops },
|
|
707
|
-
activeCompactions: { ...state.activeCompactions, [sessionId]: activeCompaction }
|
|
708
|
-
};
|
|
709
|
-
}
|
|
710
|
-
var useChatStore = create3()((set) => ({
|
|
711
|
-
...createClientActions(set),
|
|
712
|
-
turns: {},
|
|
713
|
-
messages: {},
|
|
714
|
-
askAnswers: {},
|
|
715
|
-
isStreaming: {},
|
|
716
|
-
agentLoops: {},
|
|
717
|
-
activeCompactions: {},
|
|
718
|
-
addUserMessage: (sessionId, content) => {
|
|
719
|
-
set((state) => {
|
|
720
|
-
const existing = state.turns[sessionId] ?? [];
|
|
721
|
-
const turnId = `local-user-${Date.now()}`;
|
|
722
|
-
const optimisticTurn = {
|
|
723
|
-
id: turnId,
|
|
724
|
-
sequence: Math.max(0, ...existing.map((turn) => turn.sequence)) + 1,
|
|
725
|
-
turn_id: turnId,
|
|
726
|
-
loop_id: "root",
|
|
727
|
-
role: "user",
|
|
728
|
-
status: "completed",
|
|
729
|
-
blocks: [{ type: "text", content }],
|
|
730
|
-
tool_calls: [],
|
|
731
|
-
model: null,
|
|
732
|
-
usage: null,
|
|
733
|
-
duration_ms: 0
|
|
734
|
-
};
|
|
735
|
-
return updateSessionState(state, sessionId, [...existing, optimisticTurn]);
|
|
736
|
-
});
|
|
737
|
-
},
|
|
738
|
-
setTurns: (sessionId, turns) => {
|
|
739
|
-
set((state) => updateSessionState(state, sessionId, [...turns]));
|
|
740
|
-
},
|
|
741
|
-
upsertTurn: (sessionId, turn) => {
|
|
742
|
-
set((state) => {
|
|
743
|
-
const existing = [...state.turns[sessionId] ?? []];
|
|
744
|
-
const index = existing.findIndex((item) => item.turn_id === turn.turn_id);
|
|
745
|
-
if (index >= 0) {
|
|
746
|
-
existing[index] = turn;
|
|
747
|
-
} else {
|
|
748
|
-
existing.push(turn);
|
|
749
|
-
}
|
|
750
|
-
return {
|
|
751
|
-
...updateSessionState(state, sessionId, existing)
|
|
752
|
-
};
|
|
753
|
-
});
|
|
754
|
-
},
|
|
755
|
-
applyTurnPatch: (sessionId, patch) => {
|
|
756
|
-
set((state) => {
|
|
757
|
-
const turn = patch.data.turn;
|
|
758
|
-
if (!turn) return state;
|
|
759
|
-
const existing = [...state.turns[sessionId] ?? []];
|
|
760
|
-
const index = existing.findIndex((item) => item.turn_id === turn.turn_id);
|
|
761
|
-
const lastSequence = index >= 0 ? existing[index].sequence : null;
|
|
762
|
-
if (lastSequence !== null && patch.sequence <= lastSequence) {
|
|
763
|
-
return state;
|
|
764
|
-
}
|
|
765
|
-
const nextTurn = {
|
|
766
|
-
...turn,
|
|
767
|
-
sequence: patch.sequence
|
|
768
|
-
};
|
|
769
|
-
if (index >= 0) {
|
|
770
|
-
existing[index] = nextTurn;
|
|
771
|
-
} else {
|
|
772
|
-
existing.push(nextTurn);
|
|
773
|
-
}
|
|
774
|
-
return {
|
|
775
|
-
...updateSessionState(state, sessionId, existing)
|
|
776
|
-
};
|
|
777
|
-
});
|
|
778
|
-
},
|
|
779
|
-
addErrorMessage: (sessionId, content) => {
|
|
780
|
-
set((state) => {
|
|
781
|
-
const turns = state.turns[sessionId] ?? [];
|
|
782
|
-
const anchorTurnId = turns[turns.length - 1]?.turn_id ?? null;
|
|
783
|
-
const entry_id = anchorTurnId ? `${ERROR_ANCHOR_PREFIX}${anchorTurnId}:${Date.now()}` : void 0;
|
|
784
|
-
return {
|
|
785
|
-
messages: {
|
|
786
|
-
...state.messages,
|
|
787
|
-
[sessionId]: [
|
|
788
|
-
...state.messages[sessionId] ?? [],
|
|
789
|
-
{ role: "error", content, loop_name: "root", entry_id }
|
|
790
|
-
]
|
|
791
|
-
}
|
|
792
|
-
};
|
|
793
|
-
});
|
|
794
|
-
},
|
|
795
|
-
markInterrupted: (sessionId) => {
|
|
796
|
-
set((state) => {
|
|
797
|
-
const turns = (state.turns[sessionId] ?? []).map((turn) => {
|
|
798
|
-
if (turn.status !== "streaming") return turn;
|
|
799
|
-
return {
|
|
800
|
-
...turn,
|
|
801
|
-
status: "interrupted",
|
|
802
|
-
tool_calls: turn.tool_calls.map(
|
|
803
|
-
(toolCall) => toolCall.status === "pending" || toolCall.status === "awaiting_answer" ? { ...toolCall, status: "cancelled" } : toolCall
|
|
804
|
-
)
|
|
805
|
-
};
|
|
806
|
-
});
|
|
807
|
-
return {
|
|
808
|
-
...updateSessionState(state, sessionId, turns)
|
|
809
|
-
};
|
|
810
|
-
});
|
|
811
|
-
},
|
|
812
|
-
markFailed: (sessionId) => {
|
|
813
|
-
set((state) => {
|
|
814
|
-
const turns = (state.turns[sessionId] ?? []).map((turn) => {
|
|
815
|
-
if (turn.status !== "streaming") return turn;
|
|
816
|
-
return {
|
|
817
|
-
...turn,
|
|
818
|
-
status: "failed",
|
|
819
|
-
tool_calls: turn.tool_calls.map(
|
|
820
|
-
(toolCall) => toolCall.status === "pending" || toolCall.status === "awaiting_answer" ? { ...toolCall, status: "error" } : toolCall
|
|
821
|
-
)
|
|
822
|
-
};
|
|
823
|
-
});
|
|
824
|
-
return {
|
|
825
|
-
...updateSessionState(state, sessionId, turns)
|
|
826
|
-
};
|
|
827
|
-
});
|
|
828
|
-
},
|
|
829
|
-
setStreaming: (sessionId, streaming) => {
|
|
830
|
-
set((state) => ({
|
|
831
|
-
isStreaming: { ...state.isStreaming, [sessionId]: streaming }
|
|
832
|
-
}));
|
|
833
|
-
},
|
|
834
|
-
setAskAnswers: (sessionId, answers) => {
|
|
835
|
-
set((state) => ({
|
|
836
|
-
askAnswers: {
|
|
837
|
-
...state.askAnswers,
|
|
838
|
-
[sessionId]: answers
|
|
839
|
-
}
|
|
840
|
-
}));
|
|
841
|
-
},
|
|
842
|
-
clearMessages: (sessionId) => {
|
|
843
|
-
set((state) => {
|
|
844
|
-
const { [sessionId]: _turns, ...restTurns } = state.turns;
|
|
845
|
-
const { [sessionId]: _messages, ...restMessages } = state.messages;
|
|
846
|
-
const { [sessionId]: _answers, ...restAnswers } = state.askAnswers;
|
|
847
|
-
const { [sessionId]: _loops, ...restLoops } = state.agentLoops;
|
|
848
|
-
const { [sessionId]: _compaction, ...restCompactions } = state.activeCompactions;
|
|
849
|
-
return {
|
|
850
|
-
turns: restTurns,
|
|
851
|
-
messages: restMessages,
|
|
852
|
-
askAnswers: restAnswers,
|
|
853
|
-
agentLoops: restLoops,
|
|
854
|
-
activeCompactions: restCompactions
|
|
855
|
-
};
|
|
856
|
-
});
|
|
857
|
-
}
|
|
858
|
-
}));
|
|
859
|
-
|
|
860
|
-
// src/react/stores/task-store.ts
|
|
861
|
-
import { create as create4 } from "zustand";
|
|
862
|
-
var EMPTY_TASKS = [];
|
|
863
|
-
var useTaskStore = create4()((set, get) => ({
|
|
864
|
-
...createClientActions(set),
|
|
865
|
-
tasks: {},
|
|
866
|
-
setTasks: (sessionId, tasks) => {
|
|
867
|
-
set((state) => ({
|
|
868
|
-
tasks: { ...state.tasks, [sessionId]: tasks }
|
|
869
|
-
}));
|
|
870
|
-
},
|
|
871
|
-
getTasks: (sessionId) => {
|
|
872
|
-
return get().tasks[sessionId] ?? EMPTY_TASKS;
|
|
873
|
-
}
|
|
874
|
-
}));
|
|
875
|
-
|
|
876
|
-
// src/react/stores/session-store.ts
|
|
877
|
-
var DEFAULT_SESSION_MODE = "executing";
|
|
878
|
-
var onSessionChange = null;
|
|
879
|
-
function invalidateHomeSidebarSessions() {
|
|
880
|
-
const queryClient = globalThis.__agentQueryClient;
|
|
881
|
-
if (!queryClient) return;
|
|
882
|
-
void queryClient.invalidateQueries({ queryKey: ["sessions", "home-sidebar"] });
|
|
883
|
-
}
|
|
884
|
-
function isSessionAccessRevoked(error) {
|
|
885
|
-
if (error instanceof Error) {
|
|
886
|
-
const msg = error.message;
|
|
887
|
-
return msg.includes("API 403") || msg.includes("API 404");
|
|
888
|
-
}
|
|
889
|
-
return false;
|
|
890
|
-
}
|
|
891
|
-
function removeSessionArtifacts(sessionId) {
|
|
892
|
-
useChatStore.getState().clearMessages(sessionId);
|
|
893
|
-
useTaskStore.getState().setTasks(sessionId, []);
|
|
894
|
-
}
|
|
895
|
-
function pruneSessionState(state, sessionId) {
|
|
896
|
-
const nextFresh = new Set(state._freshSessions);
|
|
897
|
-
nextFresh.delete(sessionId);
|
|
898
|
-
const { [sessionId]: _mode, ...modes } = state.modes;
|
|
899
|
-
const { [sessionId]: _rewindDraft, ...rewindDrafts } = state.rewindDrafts;
|
|
900
|
-
return {
|
|
901
|
-
sessions: state.sessions.filter((session) => session.id !== sessionId),
|
|
902
|
-
activeSessionId: state.activeSessionId === sessionId ? null : state.activeSessionId,
|
|
903
|
-
modes,
|
|
904
|
-
rewindDrafts,
|
|
905
|
-
_freshSessions: nextFresh
|
|
906
|
-
};
|
|
907
|
-
}
|
|
908
|
-
function navigateAwayFromSession(sessionId) {
|
|
909
|
-
if (typeof window === "undefined") return;
|
|
910
|
-
if (window.location.pathname !== `/chat/${sessionId}`) return;
|
|
911
|
-
window.history.replaceState(window.history.state, "", "/chat");
|
|
912
|
-
window.dispatchEvent(new PopStateEvent("popstate"));
|
|
913
|
-
}
|
|
914
|
-
function handleUnreadableSession(set, get, sessionId) {
|
|
915
|
-
const wasActive = get().activeSessionId === sessionId;
|
|
916
|
-
removeSessionArtifacts(sessionId);
|
|
917
|
-
set((state) => pruneSessionState(state, sessionId));
|
|
918
|
-
if (wasActive) {
|
|
919
|
-
onSessionChange?.(null);
|
|
920
|
-
navigateAwayFromSession(sessionId);
|
|
921
|
-
}
|
|
922
|
-
invalidateHomeSidebarSessions();
|
|
923
|
-
}
|
|
924
|
-
function isPlainRecord(value) {
|
|
925
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
926
|
-
}
|
|
927
|
-
function extractModeFromBlocks2(blocks) {
|
|
928
|
-
const modeBlock = blocks.find((block) => block.type === "mode_change");
|
|
929
|
-
if (modeBlock && isPlainRecord(modeBlock.content) && (modeBlock.content.to === "planning" || modeBlock.content.to === "executing")) {
|
|
930
|
-
return modeBlock.content.to;
|
|
931
|
-
}
|
|
932
|
-
if (blocks.some((block) => block.type === "planning_enter")) return "planning";
|
|
933
|
-
if (blocks.some((block) => block.type === "planning_exit")) return "executing";
|
|
934
|
-
return null;
|
|
935
|
-
}
|
|
936
|
-
function toSelectionMap(value) {
|
|
937
|
-
if (!isPlainRecord(value)) return {};
|
|
938
|
-
const entries = Object.entries(value).map(([questionKey, optionIndexes]) => {
|
|
939
|
-
if (!Array.isArray(optionIndexes)) return null;
|
|
940
|
-
const parsedIndexes = optionIndexes.map((item) => typeof item === "number" ? item : Number(item)).filter((item) => Number.isInteger(item));
|
|
941
|
-
return [Number(questionKey), parsedIndexes];
|
|
942
|
-
}).filter((entry) => entry !== null);
|
|
943
|
-
return Object.fromEntries(entries);
|
|
944
|
-
}
|
|
945
|
-
function toCustomMap(value) {
|
|
946
|
-
if (!isPlainRecord(value)) return {};
|
|
947
|
-
const entries = Object.entries(value).filter(([, text]) => typeof text === "string").map(([questionKey, text]) => [Number(questionKey), text]);
|
|
948
|
-
return Object.fromEntries(entries);
|
|
949
|
-
}
|
|
950
|
-
function extractAskAnswers(turns) {
|
|
951
|
-
const answers = {};
|
|
952
|
-
for (const turn of turns) {
|
|
953
|
-
for (const block of turn.blocks) {
|
|
954
|
-
if (block.type !== "ask_user_answer" || typeof block.tool_call_id !== "string") continue;
|
|
955
|
-
answers[block.tool_call_id] = {
|
|
956
|
-
selections: toSelectionMap(isPlainRecord(block.content) ? block.content.selections : void 0),
|
|
957
|
-
custom: toCustomMap(isPlainRecord(block.content) ? block.content.custom : void 0)
|
|
958
|
-
};
|
|
959
|
-
}
|
|
960
|
-
}
|
|
961
|
-
return answers;
|
|
962
|
-
}
|
|
963
|
-
function registerCreatedSessionState(set, session, mode = DEFAULT_SESSION_MODE) {
|
|
964
|
-
useChatStore.getState().setTurns(session.id, []);
|
|
965
|
-
useTaskStore.getState().setTasks(session.id, []);
|
|
966
|
-
set((state) => ({
|
|
967
|
-
sessions: [session, ...state.sessions.filter((item) => item.id !== session.id)],
|
|
968
|
-
modes: { ...state.modes, [session.id]: state.modes[session.id] ?? mode },
|
|
969
|
-
_freshSessions: new Set(state._freshSessions).add(session.id)
|
|
970
|
-
}));
|
|
971
|
-
}
|
|
972
|
-
async function revalidateViewerSessions(existingSessions) {
|
|
973
|
-
const viewerSessions = existingSessions.filter((session) => session.viewer_role === "viewer");
|
|
974
|
-
if (viewerSessions.length === 0) {
|
|
975
|
-
return [];
|
|
976
|
-
}
|
|
977
|
-
const refreshed = await Promise.all(
|
|
978
|
-
viewerSessions.map(async (session) => {
|
|
979
|
-
try {
|
|
980
|
-
return await getSession(session.id);
|
|
981
|
-
} catch (error) {
|
|
982
|
-
if (isSessionAccessRevoked(error)) {
|
|
983
|
-
return null;
|
|
984
|
-
}
|
|
985
|
-
return session;
|
|
986
|
-
}
|
|
987
|
-
})
|
|
988
|
-
);
|
|
989
|
-
return refreshed.filter((session) => session !== null);
|
|
990
|
-
}
|
|
991
|
-
var useSessionStore = create5()((set, get) => ({
|
|
992
|
-
...createClientActions(set),
|
|
993
|
-
sessions: [],
|
|
994
|
-
activeSessionId: null,
|
|
995
|
-
loading: false,
|
|
996
|
-
modes: {},
|
|
997
|
-
rewindDrafts: {},
|
|
998
|
-
_freshSessions: /* @__PURE__ */ new Set(),
|
|
999
|
-
fetchSessions: async () => {
|
|
1000
|
-
set({ loading: true });
|
|
1001
|
-
try {
|
|
1002
|
-
const currentState = get();
|
|
1003
|
-
const [sessions, viewerSessions] = await Promise.all([
|
|
1004
|
-
listSessions(),
|
|
1005
|
-
revalidateViewerSessions(currentState.sessions)
|
|
1006
|
-
]);
|
|
1007
|
-
const knownSessionIds = new Set(sessions.map((session) => session.id));
|
|
1008
|
-
const mergedSessions = [
|
|
1009
|
-
...sessions,
|
|
1010
|
-
...viewerSessions.filter((session) => !knownSessionIds.has(session.id))
|
|
1011
|
-
];
|
|
1012
|
-
const removedSessionIds = currentState.sessions.filter(
|
|
1013
|
-
(session) => session.viewer_role === "viewer" && !mergedSessions.some((candidate) => candidate.id === session.id)
|
|
1014
|
-
).map((session) => session.id);
|
|
1015
|
-
const activeSessionId = currentState.activeSessionId;
|
|
1016
|
-
for (const sessionId of removedSessionIds) {
|
|
1017
|
-
removeSessionArtifacts(sessionId);
|
|
1018
|
-
}
|
|
1019
|
-
let nextState = {
|
|
1020
|
-
sessions: mergedSessions,
|
|
1021
|
-
activeSessionId: currentState.activeSessionId,
|
|
1022
|
-
loading: false,
|
|
1023
|
-
modes: currentState.modes,
|
|
1024
|
-
rewindDrafts: currentState.rewindDrafts,
|
|
1025
|
-
_freshSessions: currentState._freshSessions
|
|
1026
|
-
};
|
|
1027
|
-
for (const sessionId of removedSessionIds) {
|
|
1028
|
-
nextState = {
|
|
1029
|
-
...nextState,
|
|
1030
|
-
...pruneSessionState(
|
|
1031
|
-
{
|
|
1032
|
-
...currentState,
|
|
1033
|
-
sessions: nextState.sessions ?? currentState.sessions,
|
|
1034
|
-
activeSessionId: nextState.activeSessionId ?? currentState.activeSessionId,
|
|
1035
|
-
loading: nextState.loading ?? currentState.loading,
|
|
1036
|
-
modes: nextState.modes ?? currentState.modes,
|
|
1037
|
-
rewindDrafts: nextState.rewindDrafts ?? currentState.rewindDrafts,
|
|
1038
|
-
_freshSessions: nextState._freshSessions ?? currentState._freshSessions
|
|
1039
|
-
},
|
|
1040
|
-
sessionId
|
|
1041
|
-
)
|
|
1042
|
-
};
|
|
1043
|
-
}
|
|
1044
|
-
set(nextState);
|
|
1045
|
-
if (activeSessionId && removedSessionIds.includes(activeSessionId)) {
|
|
1046
|
-
onSessionChange?.(null);
|
|
1047
|
-
navigateAwayFromSession(activeSessionId);
|
|
1048
|
-
}
|
|
1049
|
-
invalidateHomeSidebarSessions();
|
|
1050
|
-
} catch (error) {
|
|
1051
|
-
set({ loading: false });
|
|
1052
|
-
throw error;
|
|
1053
|
-
}
|
|
1054
|
-
},
|
|
1055
|
-
createSession: async (intent) => {
|
|
1056
|
-
const { session_id } = await createSession(intent);
|
|
1057
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1058
|
-
get().registerCreatedSession({
|
|
1059
|
-
id: session_id,
|
|
1060
|
-
intent: intent ?? "",
|
|
1061
|
-
status: "created",
|
|
1062
|
-
created_at: now,
|
|
1063
|
-
updated_at: now
|
|
1064
|
-
});
|
|
1065
|
-
get().setActiveSession(session_id);
|
|
1066
|
-
invalidateHomeSidebarSessions();
|
|
1067
|
-
get().fetchSessions().catch(() => {
|
|
1068
|
-
});
|
|
1069
|
-
return session_id;
|
|
1070
|
-
},
|
|
1071
|
-
registerCreatedSession: (session, mode = DEFAULT_SESSION_MODE) => {
|
|
1072
|
-
registerCreatedSessionState(set, session, mode);
|
|
1073
|
-
},
|
|
1074
|
-
upsertSession: (session) => {
|
|
1075
|
-
set((state) => ({
|
|
1076
|
-
sessions: state.sessions.some((item) => item.id === session.id) ? state.sessions.map((item) => item.id === session.id ? { ...item, ...session } : item) : [session, ...state.sessions]
|
|
1077
|
-
}));
|
|
1078
|
-
},
|
|
1079
|
-
isFreshSession: (sessionId) => get()._freshSessions.has(sessionId),
|
|
1080
|
-
setActiveSession: (id) => {
|
|
1081
|
-
set({ activeSessionId: id });
|
|
1082
|
-
onSessionChange?.(id);
|
|
1083
|
-
getSession(id).then((detail) => {
|
|
1084
|
-
set((state) => ({
|
|
1085
|
-
sessions: state.sessions.some((s) => s.id === id) ? state.sessions.map(
|
|
1086
|
-
(s) => s.id === id ? { ...s, status: detail.status, updated_at: detail.updated_at } : s
|
|
1087
|
-
) : [detail, ...state.sessions]
|
|
1088
|
-
}));
|
|
1089
|
-
}).catch(() => {
|
|
1090
|
-
});
|
|
1091
|
-
const tasksPromise = getSessionTasks(id).catch(() => null);
|
|
1092
|
-
Promise.all([getSessionTurns(id), tasksPromise]).then(([turns, tasks]) => {
|
|
1093
|
-
if (tasks) useTaskStore.getState().setTasks(id, tasks);
|
|
1094
|
-
useChatStore.getState().setAskAnswers(id, extractAskAnswers(turns));
|
|
1095
|
-
let inferredMode = DEFAULT_SESSION_MODE;
|
|
1096
|
-
for (const turn of turns) {
|
|
1097
|
-
const nextMode = extractModeFromBlocks2(turn.blocks);
|
|
1098
|
-
if (nextMode) {
|
|
1099
|
-
inferredMode = nextMode;
|
|
1100
|
-
}
|
|
1101
|
-
}
|
|
1102
|
-
set((state) => {
|
|
1103
|
-
if (state.modes[id] == null) {
|
|
1104
|
-
return { modes: { ...state.modes, [id]: inferredMode } };
|
|
1105
|
-
}
|
|
1106
|
-
return state;
|
|
1107
|
-
});
|
|
1108
|
-
const isFresh = get()._freshSessions.has(id);
|
|
1109
|
-
if (isFresh) {
|
|
1110
|
-
set((state) => {
|
|
1111
|
-
const next = new Set(state._freshSessions);
|
|
1112
|
-
next.delete(id);
|
|
1113
|
-
return { _freshSessions: next };
|
|
1114
|
-
});
|
|
1115
|
-
if (turns.length > 0) {
|
|
1116
|
-
useChatStore.getState().setTurns(id, turns);
|
|
1117
|
-
}
|
|
1118
|
-
} else {
|
|
1119
|
-
useChatStore.getState().setTurns(id, turns);
|
|
1120
|
-
}
|
|
1121
|
-
}).catch((error) => {
|
|
1122
|
-
if (isSessionAccessRevoked(error)) {
|
|
1123
|
-
handleUnreadableSession(set, get, id);
|
|
1124
|
-
return;
|
|
1125
|
-
}
|
|
1126
|
-
console.error("Failed to load session data", error);
|
|
1127
|
-
});
|
|
1128
|
-
},
|
|
1129
|
-
clearActiveSession: () => {
|
|
1130
|
-
set({ activeSessionId: null });
|
|
1131
|
-
onSessionChange?.(null);
|
|
1132
|
-
},
|
|
1133
|
-
deleteSession: async (id) => {
|
|
1134
|
-
await deleteSession(id);
|
|
1135
|
-
invalidateHomeSidebarSessions();
|
|
1136
|
-
const wasActive = get().activeSessionId === id;
|
|
1137
|
-
await get().fetchSessions();
|
|
1138
|
-
if (!wasActive) {
|
|
1139
|
-
return;
|
|
1140
|
-
}
|
|
1141
|
-
const nextId = get().sessions[0]?.id ?? null;
|
|
1142
|
-
if (nextId) {
|
|
1143
|
-
get().setActiveSession(nextId);
|
|
1144
|
-
return;
|
|
1145
|
-
}
|
|
1146
|
-
get().clearActiveSession();
|
|
1147
|
-
},
|
|
1148
|
-
updateSessionStatus: (sessionId, status) => {
|
|
1149
|
-
set((state) => ({
|
|
1150
|
-
sessions: state.sessions.map((s) => s.id === sessionId ? { ...s, status } : s)
|
|
1151
|
-
}));
|
|
1152
|
-
if (status === "failed" || status === "interrupted") {
|
|
1153
|
-
useChatStore.getState().setStreaming(sessionId, false);
|
|
1154
|
-
if (status === "interrupted") {
|
|
1155
|
-
useChatStore.getState().markInterrupted(sessionId);
|
|
1156
|
-
} else {
|
|
1157
|
-
useChatStore.getState().markFailed(sessionId);
|
|
1158
|
-
}
|
|
1159
|
-
}
|
|
1160
|
-
},
|
|
1161
|
-
updateSessionIntent: (sessionId, intent) => {
|
|
1162
|
-
set((state) => ({
|
|
1163
|
-
sessions: state.sessions.map((s) => s.id === sessionId ? { ...s, intent } : s)
|
|
1164
|
-
}));
|
|
1165
|
-
},
|
|
1166
|
-
patchSession: (sessionId, patch) => {
|
|
1167
|
-
set((state) => ({
|
|
1168
|
-
sessions: state.sessions.map(
|
|
1169
|
-
(s) => s.id === sessionId ? { ...s, ...patch } : s
|
|
1170
|
-
)
|
|
1171
|
-
}));
|
|
1172
|
-
},
|
|
1173
|
-
pinSession: async (sessionId, pinned) => {
|
|
1174
|
-
const updated = await pinSession(sessionId, pinned);
|
|
1175
|
-
set((state) => ({
|
|
1176
|
-
sessions: state.sessions.map(
|
|
1177
|
-
(s) => s.id === sessionId ? { ...s, is_pinned: updated.is_pinned, pinned_at: updated.pinned_at } : s
|
|
1178
|
-
)
|
|
1179
|
-
}));
|
|
1180
|
-
invalidateHomeSidebarSessions();
|
|
1181
|
-
},
|
|
1182
|
-
setRewindDraft: (sessionId, text) => {
|
|
1183
|
-
set((state) => {
|
|
1184
|
-
if (text == null) {
|
|
1185
|
-
const { [sessionId]: _, ...rest } = state.rewindDrafts;
|
|
1186
|
-
return { rewindDrafts: rest };
|
|
1187
|
-
}
|
|
1188
|
-
return {
|
|
1189
|
-
rewindDrafts: {
|
|
1190
|
-
...state.rewindDrafts,
|
|
1191
|
-
[sessionId]: text
|
|
1192
|
-
}
|
|
1193
|
-
};
|
|
1194
|
-
});
|
|
1195
|
-
},
|
|
1196
|
-
setMode: (sessionId, mode) => {
|
|
1197
|
-
set((state) => ({ modes: { ...state.modes, [sessionId]: mode } }));
|
|
1198
|
-
},
|
|
1199
|
-
togglePlanningMode: (sessionId) => {
|
|
1200
|
-
const current = get().modes[sessionId] ?? DEFAULT_SESSION_MODE;
|
|
1201
|
-
const next = current === "executing" ? "planning" : "executing";
|
|
1202
|
-
set((state) => ({ modes: { ...state.modes, [sessionId]: next } }));
|
|
1203
|
-
},
|
|
1204
|
-
toggleSharing: async (sessionId) => {
|
|
1205
|
-
const session = get().sessions.find((s) => s.id === sessionId);
|
|
1206
|
-
if (!session) return;
|
|
1207
|
-
const newShared = !session.shared;
|
|
1208
|
-
const result = await updateSharing(sessionId, newShared);
|
|
1209
|
-
set((state) => ({
|
|
1210
|
-
sessions: state.sessions.map(
|
|
1211
|
-
(s) => s.id === sessionId ? { ...s, shared: result.shared } : s
|
|
1212
|
-
)
|
|
1213
|
-
}));
|
|
1214
|
-
},
|
|
1215
|
-
reset: () => {
|
|
1216
|
-
set({
|
|
1217
|
-
sessions: [],
|
|
1218
|
-
activeSessionId: null,
|
|
1219
|
-
loading: false,
|
|
1220
|
-
modes: {},
|
|
1221
|
-
rewindDrafts: {},
|
|
1222
|
-
_freshSessions: /* @__PURE__ */ new Set()
|
|
1223
|
-
});
|
|
1224
|
-
invalidateHomeSidebarSessions();
|
|
1225
|
-
}
|
|
1226
|
-
}));
|
|
1227
|
-
setChatStoreSessionAccessor(() => useSessionStore.getState().activeSessionId);
|
|
1228
|
-
|
|
1229
|
-
// src/react/stores/auth-store.ts
|
|
1230
|
-
var noopStorage = {
|
|
1231
|
-
getItem: () => null,
|
|
1232
|
-
setItem: () => {
|
|
1233
|
-
},
|
|
1234
|
-
removeItem: () => {
|
|
1235
|
-
}
|
|
1236
|
-
};
|
|
1237
|
-
function shouldClearPersistedAuthState(value) {
|
|
1238
|
-
try {
|
|
1239
|
-
const parsed = JSON.parse(value);
|
|
1240
|
-
return parsed.state?.token == null && parsed.state?.user == null;
|
|
1241
|
-
} catch {
|
|
1242
|
-
return false;
|
|
1243
|
-
}
|
|
1244
|
-
}
|
|
1245
|
-
var authStorage = createJSONStorage(() => {
|
|
1246
|
-
if (typeof localStorage === "undefined") {
|
|
1247
|
-
return noopStorage;
|
|
1248
|
-
}
|
|
1249
|
-
return {
|
|
1250
|
-
getItem: (name) => localStorage.getItem(name),
|
|
1251
|
-
setItem: (name, value) => {
|
|
1252
|
-
if (shouldClearPersistedAuthState(value)) {
|
|
1253
|
-
localStorage.removeItem(name);
|
|
1254
|
-
return;
|
|
1255
|
-
}
|
|
1256
|
-
localStorage.setItem(name, value);
|
|
1257
|
-
},
|
|
1258
|
-
removeItem: (name) => localStorage.removeItem(name)
|
|
1259
|
-
};
|
|
1260
|
-
});
|
|
1261
|
-
function finishAuth(set, payload) {
|
|
1262
|
-
set({
|
|
1263
|
-
token: payload.token,
|
|
1264
|
-
socketAuthToken: null,
|
|
1265
|
-
user: payload.user,
|
|
1266
|
-
loading: false,
|
|
1267
|
-
error: null
|
|
1268
|
-
});
|
|
1269
|
-
agentSocket?.reconnect();
|
|
1270
|
-
useSessionStore.getState().fetchSessions().catch(() => {
|
|
1271
|
-
});
|
|
1272
|
-
}
|
|
1273
|
-
function finishCookieHydration(set, payload) {
|
|
1274
|
-
set({
|
|
1275
|
-
token: null,
|
|
1276
|
-
socketAuthToken: payload.token,
|
|
1277
|
-
user: payload.user,
|
|
1278
|
-
loading: false,
|
|
1279
|
-
error: null
|
|
1280
|
-
});
|
|
1281
|
-
agentSocket?.reconnect();
|
|
1282
|
-
useSessionStore.getState().fetchSessions().catch(() => {
|
|
1283
|
-
});
|
|
1284
|
-
}
|
|
1285
|
-
function toUser(info) {
|
|
1286
|
-
return {
|
|
1287
|
-
id: info.id,
|
|
1288
|
-
username: info.username,
|
|
1289
|
-
display_name: info.display_name,
|
|
1290
|
-
avatar_url: info.avatar_url,
|
|
1291
|
-
is_admin: info.is_admin
|
|
1292
|
-
};
|
|
1293
|
-
}
|
|
1294
|
-
var useAuthStore = create6()(
|
|
1295
|
-
persist(
|
|
1296
|
-
(set, get) => ({
|
|
1297
|
-
...createClientActions(set),
|
|
1298
|
-
token: null,
|
|
1299
|
-
socketAuthToken: null,
|
|
1300
|
-
user: null,
|
|
1301
|
-
loading: false,
|
|
1302
|
-
error: null,
|
|
1303
|
-
logout: () => {
|
|
1304
|
-
void logout().catch(() => {
|
|
1305
|
-
});
|
|
1306
|
-
set({ token: null, socketAuthToken: null, user: null, error: null });
|
|
1307
|
-
agentSocket?.disconnect();
|
|
1308
|
-
useSessionStore.getState().reset();
|
|
1309
|
-
},
|
|
1310
|
-
checkAuth: async () => {
|
|
1311
|
-
const token = get().token;
|
|
1312
|
-
if (!token) return;
|
|
1313
|
-
try {
|
|
1314
|
-
const auth = await getMe();
|
|
1315
|
-
finishAuth(set, { token: auth.token, user: toUser(auth) });
|
|
1316
|
-
} catch {
|
|
1317
|
-
set({ token: null, socketAuthToken: null, user: null, error: null });
|
|
1318
|
-
useSessionStore.getState().reset();
|
|
1319
|
-
}
|
|
1320
|
-
},
|
|
1321
|
-
hydrateFromCookie: async () => {
|
|
1322
|
-
set({ loading: true, error: null });
|
|
1323
|
-
const previousToken = get().token;
|
|
1324
|
-
if (previousToken) {
|
|
1325
|
-
try {
|
|
1326
|
-
const auth = await getMe();
|
|
1327
|
-
finishAuth(set, { token: auth.token, user: toUser(auth) });
|
|
1328
|
-
return;
|
|
1329
|
-
} catch {
|
|
1330
|
-
set({ token: null, socketAuthToken: null, user: null, error: null });
|
|
1331
|
-
useSessionStore.getState().reset();
|
|
1332
|
-
}
|
|
1333
|
-
}
|
|
1334
|
-
try {
|
|
1335
|
-
const auth = await getMe();
|
|
1336
|
-
finishCookieHydration(set, { token: auth.token, user: toUser(auth) });
|
|
1337
|
-
} catch {
|
|
1338
|
-
set({
|
|
1339
|
-
token: null,
|
|
1340
|
-
socketAuthToken: null,
|
|
1341
|
-
user: null,
|
|
1342
|
-
loading: false,
|
|
1343
|
-
error: null
|
|
1344
|
-
});
|
|
1345
|
-
useSessionStore.getState().reset();
|
|
1346
|
-
}
|
|
1347
|
-
}
|
|
1348
|
-
}),
|
|
1349
|
-
{
|
|
1350
|
-
name: "agent-auth",
|
|
1351
|
-
storage: authStorage,
|
|
1352
|
-
partialize: (state) => {
|
|
1353
|
-
if (state.socketAuthToken) {
|
|
1354
|
-
return { token: null, user: null };
|
|
1355
|
-
}
|
|
1356
|
-
return { token: state.token, user: state.user };
|
|
1357
|
-
}
|
|
1358
|
-
}
|
|
1359
|
-
)
|
|
1360
|
-
);
|
|
1361
|
-
|
|
1362
|
-
// src/react/stores/background-store.ts
|
|
1363
|
-
import { create as create7 } from "zustand";
|
|
1364
|
-
var useBackgroundStore = create7()((set) => ({
|
|
1365
|
-
...createClientActions(set),
|
|
1366
|
-
tasks: {},
|
|
1367
|
-
selectedTaskId: {},
|
|
1368
|
-
setTasks: (sessionId, tasks) => set((state) => ({
|
|
1369
|
-
tasks: { ...state.tasks, [sessionId]: tasks },
|
|
1370
|
-
selectedTaskId: {
|
|
1371
|
-
...state.selectedTaskId,
|
|
1372
|
-
[sessionId]: state.selectedTaskId[sessionId] ?? tasks[0]?.id ?? null
|
|
1373
|
-
}
|
|
1374
|
-
})),
|
|
1375
|
-
upsertTask: (sessionId, task) => set((state) => {
|
|
1376
|
-
const existing = state.tasks[sessionId] ?? [];
|
|
1377
|
-
const index = existing.findIndex((item) => item.id === task.id);
|
|
1378
|
-
const next = [...existing];
|
|
1379
|
-
if (index >= 0) {
|
|
1380
|
-
next[index] = { ...next[index], ...task };
|
|
1381
|
-
} else {
|
|
1382
|
-
next.unshift(task);
|
|
1383
|
-
}
|
|
1384
|
-
return {
|
|
1385
|
-
tasks: { ...state.tasks, [sessionId]: next },
|
|
1386
|
-
selectedTaskId: {
|
|
1387
|
-
...state.selectedTaskId,
|
|
1388
|
-
[sessionId]: state.selectedTaskId[sessionId] ?? task.id
|
|
1389
|
-
}
|
|
1390
|
-
};
|
|
1391
|
-
}),
|
|
1392
|
-
selectTask: (sessionId, taskId) => set((state) => ({
|
|
1393
|
-
selectedTaskId: { ...state.selectedTaskId, [sessionId]: taskId }
|
|
1394
|
-
}))
|
|
1395
|
-
}));
|
|
1396
|
-
|
|
1397
|
-
// src/react/stores/card-state-store.ts
|
|
1398
|
-
import { create as create8 } from "zustand";
|
|
1399
|
-
var useCardStateStore = create8((set, get) => ({
|
|
1400
|
-
...createClientActions(set),
|
|
1401
|
-
states: {},
|
|
1402
|
-
getCardState: (cardId) => {
|
|
1403
|
-
return get().states[cardId];
|
|
1404
|
-
},
|
|
1405
|
-
setCardState: (cardId, state) => {
|
|
1406
|
-
set((prev) => ({
|
|
1407
|
-
states: { ...prev.states, [cardId]: state }
|
|
1408
|
-
}));
|
|
1409
|
-
},
|
|
1410
|
-
removeCardState: (cardId) => {
|
|
1411
|
-
set((prev) => {
|
|
1412
|
-
const { [cardId]: _, ...rest } = prev.states;
|
|
1413
|
-
return { states: rest };
|
|
1414
|
-
});
|
|
1415
|
-
},
|
|
1416
|
-
clearAllStates: () => {
|
|
1417
|
-
set({ states: {} });
|
|
1418
|
-
}
|
|
1419
|
-
}));
|
|
1420
|
-
|
|
1421
|
-
// src/react/stores/connection-store.ts
|
|
1422
|
-
import { create as create9 } from "zustand";
|
|
1423
|
-
var initialConnectionState = {
|
|
1424
|
-
status: "disconnected",
|
|
1425
|
-
reconnectAttempt: 0,
|
|
1426
|
-
lastConnectedAt: null,
|
|
1427
|
-
lastDisconnectedAt: null,
|
|
1428
|
-
hasEverConnected: false
|
|
1429
|
-
};
|
|
1430
|
-
var useConnectionStore = create9()((set) => ({
|
|
1431
|
-
...createClientActions(set),
|
|
1432
|
-
...initialConnectionState,
|
|
1433
|
-
markConnecting: (attempt = 0) => set({
|
|
1434
|
-
status: "connecting",
|
|
1435
|
-
reconnectAttempt: attempt
|
|
1436
|
-
}),
|
|
1437
|
-
markConnected: () => set({
|
|
1438
|
-
status: "connected",
|
|
1439
|
-
reconnectAttempt: 0,
|
|
1440
|
-
lastConnectedAt: Date.now(),
|
|
1441
|
-
hasEverConnected: true
|
|
1442
|
-
}),
|
|
1443
|
-
markDisconnected: () => set({
|
|
1444
|
-
status: "disconnected",
|
|
1445
|
-
reconnectAttempt: 0,
|
|
1446
|
-
lastDisconnectedAt: Date.now()
|
|
1447
|
-
}),
|
|
1448
|
-
reset: () => set(initialConnectionState)
|
|
1449
|
-
}));
|
|
1450
|
-
|
|
1451
|
-
// src/react/stores/runtime-store.ts
|
|
1452
|
-
import { create as create10 } from "zustand";
|
|
1453
|
-
var useRuntimeStore = create10()((set) => ({
|
|
1454
|
-
...createClientActions(set),
|
|
1455
|
-
events: {},
|
|
1456
|
-
addEvent: (sessionId, event) => set((state) => {
|
|
1457
|
-
const current = state.events[sessionId] ?? [];
|
|
1458
|
-
const nextEvent = {
|
|
1459
|
-
...event,
|
|
1460
|
-
id: `${Date.now()}-${current.length}`,
|
|
1461
|
-
sessionId,
|
|
1462
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1463
|
-
};
|
|
1464
|
-
return {
|
|
1465
|
-
events: {
|
|
1466
|
-
...state.events,
|
|
1467
|
-
[sessionId]: [...current.slice(-59), nextEvent]
|
|
1468
|
-
}
|
|
1469
|
-
};
|
|
1470
|
-
}),
|
|
1471
|
-
clearSession: (sessionId) => set((state) => ({
|
|
1472
|
-
events: { ...state.events, [sessionId]: [] }
|
|
1473
|
-
}))
|
|
1474
|
-
}));
|
|
1475
|
-
|
|
1476
|
-
// src/react/stores/gis-store.ts
|
|
1477
|
-
import { create as create11 } from "zustand";
|
|
1478
|
-
var EMPTY_GOALS = [];
|
|
1479
|
-
var EMPTY_RESOURCES = [];
|
|
1480
|
-
var EMPTY_TARGETS = [];
|
|
1481
|
-
var EMPTY_COMMANDS = [];
|
|
1482
|
-
function newCommandId() {
|
|
1483
|
-
if (typeof globalThis !== "undefined" && "crypto" in globalThis) {
|
|
1484
|
-
return globalThis.crypto?.randomUUID?.() ?? `gis-map-${Date.now()}-${Math.random()}`;
|
|
1485
|
-
}
|
|
1486
|
-
return `gis-map-${Date.now()}-${Math.random()}`;
|
|
1487
|
-
}
|
|
1488
|
-
var useGisStore = create11()((set, get) => ({
|
|
1489
|
-
...createClientActions(set),
|
|
1490
|
-
goalsBySession: {},
|
|
1491
|
-
resourcesBySession: {},
|
|
1492
|
-
targetsBySession: {},
|
|
1493
|
-
pendingMapCommandsBySession: {},
|
|
1494
|
-
setGoals: (sessionId, goals) => {
|
|
1495
|
-
set((state) => ({
|
|
1496
|
-
goalsBySession: { ...state.goalsBySession, [sessionId]: goals }
|
|
1497
|
-
}));
|
|
1498
|
-
},
|
|
1499
|
-
setResources: (sessionId, resources) => {
|
|
1500
|
-
set((state) => ({
|
|
1501
|
-
resourcesBySession: { ...state.resourcesBySession, [sessionId]: resources }
|
|
1502
|
-
}));
|
|
1503
|
-
},
|
|
1504
|
-
setTargets: (sessionId, targets) => {
|
|
1505
|
-
set((state) => ({
|
|
1506
|
-
targetsBySession: { ...state.targetsBySession, [sessionId]: targets }
|
|
1507
|
-
}));
|
|
1508
|
-
},
|
|
1509
|
-
pushMapCommand: (sessionId, command) => {
|
|
1510
|
-
set((state) => ({
|
|
1511
|
-
pendingMapCommandsBySession: {
|
|
1512
|
-
...state.pendingMapCommandsBySession,
|
|
1513
|
-
[sessionId]: [
|
|
1514
|
-
...state.pendingMapCommandsBySession[sessionId] ?? EMPTY_COMMANDS,
|
|
1515
|
-
{
|
|
1516
|
-
...command,
|
|
1517
|
-
id: newCommandId(),
|
|
1518
|
-
createdAt: Date.now()
|
|
1519
|
-
}
|
|
1520
|
-
]
|
|
1521
|
-
}
|
|
1522
|
-
}));
|
|
1523
|
-
},
|
|
1524
|
-
consumeMapCommand: (sessionId, commandId) => {
|
|
1525
|
-
set((state) => ({
|
|
1526
|
-
pendingMapCommandsBySession: {
|
|
1527
|
-
...state.pendingMapCommandsBySession,
|
|
1528
|
-
[sessionId]: (state.pendingMapCommandsBySession[sessionId] ?? EMPTY_COMMANDS).filter(
|
|
1529
|
-
(command) => command.id !== commandId
|
|
1530
|
-
)
|
|
1531
|
-
}
|
|
1532
|
-
}));
|
|
1533
|
-
},
|
|
1534
|
-
getGoals: (sessionId) => get().goalsBySession[sessionId] ?? EMPTY_GOALS,
|
|
1535
|
-
getResources: (sessionId) => get().resourcesBySession[sessionId] ?? EMPTY_RESOURCES,
|
|
1536
|
-
getTargets: (sessionId) => get().targetsBySession[sessionId] ?? EMPTY_TARGETS,
|
|
1537
|
-
getPendingMapCommands: (sessionId) => get().pendingMapCommandsBySession[sessionId] ?? EMPTY_COMMANDS
|
|
1538
|
-
}));
|
|
1539
|
-
|
|
1540
|
-
// src/react/stores/answer-callback-store.ts
|
|
1541
|
-
import { create as create12 } from "zustand";
|
|
1542
|
-
var useAnswerCallbackStore = create12()((set) => ({
|
|
1543
|
-
...createClientActions(set),
|
|
1544
|
-
callbacks: {},
|
|
1545
|
-
setAnswerCallback: (sessionId, callback) => {
|
|
1546
|
-
set((state) => ({
|
|
1547
|
-
callbacks: {
|
|
1548
|
-
...state.callbacks,
|
|
1549
|
-
[sessionId]: callback
|
|
1550
|
-
}
|
|
1551
|
-
}));
|
|
1552
|
-
}
|
|
1553
|
-
}));
|
|
1554
|
-
|
|
1555
|
-
// src/react/stores/runtime-features-store.ts
|
|
1556
|
-
import { create as create13 } from "zustand";
|
|
1557
|
-
var useRuntimeFeaturesStore = create13((set) => ({
|
|
1558
|
-
...createClientActions(set),
|
|
1559
|
-
asrEnabled: false,
|
|
1560
|
-
asrProvider: "volcengine",
|
|
1561
|
-
publicSharingEnabled: false,
|
|
1562
|
-
memoryEnabled: false,
|
|
1563
|
-
setFeatures: (features) => set((prev) => ({
|
|
1564
|
-
asrEnabled: features.asrEnabled ?? prev.asrEnabled,
|
|
1565
|
-
asrProvider: features.asrProvider ?? prev.asrProvider,
|
|
1566
|
-
publicSharingEnabled: features.publicSharingEnabled ?? prev.publicSharingEnabled,
|
|
1567
|
-
memoryEnabled: features.memoryEnabled ?? prev.memoryEnabled
|
|
1568
|
-
}))
|
|
1569
|
-
}));
|
|
1570
|
-
|
|
1571
|
-
// src/react/bootstrap.ts
|
|
1572
|
-
var bootstrappedClient = null;
|
|
1573
|
-
function getBootstrappedClient() {
|
|
1574
|
-
if (!bootstrappedClient) {
|
|
1575
|
-
throw new Error("bootstrapBladeClient() must be called before any SDK usage");
|
|
1576
|
-
}
|
|
1577
|
-
return bootstrappedClient;
|
|
1578
|
-
}
|
|
1579
|
-
|
|
1580
|
-
// src/react/api/client.ts
|
|
1581
|
-
function getAuthedUrl(path) {
|
|
1582
|
-
return getClient().buildAuthedUrl(path);
|
|
1583
|
-
}
|
|
1584
|
-
function getClient() {
|
|
1585
|
-
return getBootstrappedClient();
|
|
1586
|
-
}
|
|
1587
|
-
|
|
1588
|
-
// src/react/components/card/CardCodeBlock.tsx
|
|
1589
|
-
import { useIsCodeFenceIncomplete } from "streamdown";
|
|
1590
|
-
|
|
1591
|
-
// src/react/lib/card-registry.ts
|
|
1592
|
-
var CardComponentRegistry = class {
|
|
1593
|
-
components;
|
|
1594
|
-
constructor(initial) {
|
|
1595
|
-
if (initial instanceof Map) {
|
|
1596
|
-
this.components = new Map(initial);
|
|
1597
|
-
} else if (initial) {
|
|
1598
|
-
this.components = new Map(Object.entries(initial));
|
|
1599
|
-
} else {
|
|
1600
|
-
this.components = /* @__PURE__ */ new Map();
|
|
1601
|
-
}
|
|
1602
|
-
}
|
|
1603
|
-
register(type3, component) {
|
|
1604
|
-
this.components.set(type3, component);
|
|
1605
|
-
}
|
|
1606
|
-
get(type3) {
|
|
1607
|
-
return this.components.get(type3);
|
|
1608
|
-
}
|
|
1609
|
-
has(type3) {
|
|
1610
|
-
return this.components.has(type3);
|
|
1611
|
-
}
|
|
1612
|
-
keys() {
|
|
1613
|
-
return Array.from(this.components.keys());
|
|
1614
|
-
}
|
|
1615
|
-
clear() {
|
|
1616
|
-
this.components.clear();
|
|
1617
|
-
}
|
|
1618
|
-
get size() {
|
|
1619
|
-
return this.components.size;
|
|
1620
|
-
}
|
|
1621
|
-
};
|
|
1622
|
-
var cardRegistry = new CardComponentRegistry();
|
|
1623
|
-
var CardJSON = {
|
|
1624
|
-
safeParseJSON(text) {
|
|
1625
|
-
try {
|
|
1626
|
-
const value = JSON.parse(text);
|
|
1627
|
-
return { ok: true, value };
|
|
1628
|
-
} catch (error) {
|
|
1629
|
-
return {
|
|
1630
|
-
ok: false,
|
|
1631
|
-
error: error instanceof Error ? error.message : "Unknown parsing error"
|
|
1632
|
-
};
|
|
1633
|
-
}
|
|
1634
|
-
},
|
|
1635
|
-
toCardArray(value) {
|
|
1636
|
-
if (value && typeof value === "object" && "type" in value && typeof value.type === "string") {
|
|
1637
|
-
return [value];
|
|
1638
|
-
}
|
|
1639
|
-
if (Array.isArray(value)) {
|
|
1640
|
-
const cards = value.filter(
|
|
1641
|
-
(item) => item && typeof item === "object" && "type" in item && typeof item.type === "string"
|
|
1642
|
-
);
|
|
1643
|
-
return cards.length > 0 ? cards : null;
|
|
1644
|
-
}
|
|
1645
|
-
return null;
|
|
1646
|
-
},
|
|
1647
|
-
isCardsLanguage(lang) {
|
|
1648
|
-
return lang === "card+json";
|
|
1649
|
-
}
|
|
1650
|
-
};
|
|
1651
|
-
|
|
1652
|
-
// src/react/lib/code-highlight.ts
|
|
1653
|
-
import { useEffect, useMemo, useState } from "react";
|
|
1654
|
-
var DARK_THEME = "github-dark-default";
|
|
1655
|
-
var LIGHT_THEME = "github-light-default";
|
|
1656
|
-
var LANGUAGE_ALIASES = {
|
|
1657
|
-
bash: "bash",
|
|
1658
|
-
css: "css",
|
|
1659
|
-
go: "go",
|
|
1660
|
-
golang: "go",
|
|
1661
|
-
htm: "html",
|
|
1662
|
-
html: "html",
|
|
1663
|
-
javascript: "javascript",
|
|
1664
|
-
js: "javascript",
|
|
1665
|
-
json: "json",
|
|
1666
|
-
jsonc: "json",
|
|
1667
|
-
jsx: "jsx",
|
|
1668
|
-
markdown: "markdown",
|
|
1669
|
-
md: "markdown",
|
|
1670
|
-
py: "python",
|
|
1671
|
-
python: "python",
|
|
1672
|
-
rs: "rust",
|
|
1673
|
-
rust: "rust",
|
|
1674
|
-
scss: "css",
|
|
1675
|
-
sh: "bash",
|
|
1676
|
-
shell: "bash",
|
|
1677
|
-
ts: "typescript",
|
|
1678
|
-
tsx: "tsx",
|
|
1679
|
-
typescript: "typescript",
|
|
1680
|
-
yaml: "yaml",
|
|
1681
|
-
yml: "yaml",
|
|
1682
|
-
zsh: "bash"
|
|
1683
|
-
};
|
|
1684
|
-
var highlighterPromise = null;
|
|
1685
|
-
var CACHE_MAX_SIZE = 200;
|
|
1686
|
-
var highlightCache = /* @__PURE__ */ new Map();
|
|
1687
|
-
function normalizeCodeLanguage(language) {
|
|
1688
|
-
if (!language) return null;
|
|
1689
|
-
return LANGUAGE_ALIASES[language.trim().toLowerCase()] ?? null;
|
|
1690
|
-
}
|
|
1691
|
-
function useHighlightedCodeHtml(code, language) {
|
|
1692
|
-
const normalizedLanguage = useMemo(() => normalizeCodeLanguage(language), [language]);
|
|
1693
|
-
const [highlightedHtml, setHighlightedHtml] = useState(null);
|
|
1694
|
-
useEffect(() => {
|
|
1695
|
-
let cancelled = false;
|
|
1696
|
-
setHighlightedHtml(null);
|
|
1697
|
-
if (!normalizedLanguage) {
|
|
1698
|
-
return () => {
|
|
1699
|
-
cancelled = true;
|
|
1700
|
-
};
|
|
1701
|
-
}
|
|
1702
|
-
void highlightCodeToInnerHtml(code, normalizedLanguage).then((result) => {
|
|
1703
|
-
if (!cancelled) {
|
|
1704
|
-
setHighlightedHtml(result);
|
|
1705
|
-
}
|
|
1706
|
-
});
|
|
1707
|
-
return () => {
|
|
1708
|
-
cancelled = true;
|
|
1709
|
-
};
|
|
1710
|
-
}, [code, normalizedLanguage]);
|
|
1711
|
-
return { highlightedHtml, language: normalizedLanguage };
|
|
1712
|
-
}
|
|
1713
|
-
async function highlightCodeToInnerHtml(code, language) {
|
|
1714
|
-
const cacheKey = `${language}\0${code}`;
|
|
1715
|
-
const cached = highlightCache.get(cacheKey);
|
|
1716
|
-
if (cached) {
|
|
1717
|
-
return cached;
|
|
1718
|
-
}
|
|
1719
|
-
const request = loadCodeHighlighter().then((highlighter) => {
|
|
1720
|
-
const html = highlighter.codeToHtml(code, {
|
|
1721
|
-
lang: language,
|
|
1722
|
-
themes: { light: LIGHT_THEME, dark: DARK_THEME },
|
|
1723
|
-
defaultColor: false
|
|
1724
|
-
});
|
|
1725
|
-
return extractInnerCodeHtml(html);
|
|
1726
|
-
}).catch(() => {
|
|
1727
|
-
highlightCache.delete(cacheKey);
|
|
1728
|
-
return null;
|
|
1729
|
-
});
|
|
1730
|
-
if (highlightCache.size >= CACHE_MAX_SIZE) {
|
|
1731
|
-
const oldest = highlightCache.keys().next().value;
|
|
1732
|
-
if (oldest !== void 0) highlightCache.delete(oldest);
|
|
1733
|
-
}
|
|
1734
|
-
highlightCache.set(cacheKey, request);
|
|
1735
|
-
return request;
|
|
1736
|
-
}
|
|
1737
|
-
async function loadCodeHighlighter() {
|
|
1738
|
-
if (!highlighterPromise) {
|
|
1739
|
-
highlighterPromise = Promise.all([
|
|
1740
|
-
import("shiki/core"),
|
|
1741
|
-
import("shiki/engine/javascript"),
|
|
1742
|
-
import("@shikijs/langs/bash"),
|
|
1743
|
-
import("@shikijs/langs/css"),
|
|
1744
|
-
import("@shikijs/langs/go"),
|
|
1745
|
-
import("@shikijs/langs/html"),
|
|
1746
|
-
import("@shikijs/langs/javascript"),
|
|
1747
|
-
import("@shikijs/langs/json"),
|
|
1748
|
-
import("@shikijs/langs/jsx"),
|
|
1749
|
-
import("@shikijs/langs/markdown"),
|
|
1750
|
-
import("@shikijs/langs/python"),
|
|
1751
|
-
import("@shikijs/langs/rust"),
|
|
1752
|
-
import("@shikijs/langs/tsx"),
|
|
1753
|
-
import("@shikijs/langs/typescript"),
|
|
1754
|
-
import("@shikijs/langs/yaml"),
|
|
1755
|
-
import("@shikijs/themes/github-dark-default"),
|
|
1756
|
-
import("@shikijs/themes/github-light-default")
|
|
1757
|
-
]).then(
|
|
1758
|
-
([
|
|
1759
|
-
core,
|
|
1760
|
-
engine,
|
|
1761
|
-
bash,
|
|
1762
|
-
css,
|
|
1763
|
-
go,
|
|
1764
|
-
html,
|
|
1765
|
-
javascript,
|
|
1766
|
-
json,
|
|
1767
|
-
jsx13,
|
|
1768
|
-
markdown,
|
|
1769
|
-
python,
|
|
1770
|
-
rust,
|
|
1771
|
-
tsx,
|
|
1772
|
-
typescript,
|
|
1773
|
-
yaml,
|
|
1774
|
-
darkTheme,
|
|
1775
|
-
lightTheme
|
|
1776
|
-
]) => core.createHighlighterCore({
|
|
1777
|
-
engine: engine.createJavaScriptRegexEngine(),
|
|
1778
|
-
langs: [
|
|
1779
|
-
bash.default,
|
|
1780
|
-
css.default,
|
|
1781
|
-
go.default,
|
|
1782
|
-
html.default,
|
|
1783
|
-
javascript.default,
|
|
1784
|
-
json.default,
|
|
1785
|
-
jsx13.default,
|
|
1786
|
-
markdown.default,
|
|
1787
|
-
python.default,
|
|
1788
|
-
rust.default,
|
|
1789
|
-
tsx.default,
|
|
1790
|
-
typescript.default,
|
|
1791
|
-
yaml.default
|
|
1792
|
-
],
|
|
1793
|
-
themes: [darkTheme.default, lightTheme.default]
|
|
1794
|
-
})
|
|
1795
|
-
).catch((err) => {
|
|
1796
|
-
highlighterPromise = null;
|
|
1797
|
-
throw err;
|
|
1798
|
-
});
|
|
1799
|
-
}
|
|
1800
|
-
return highlighterPromise;
|
|
1801
|
-
}
|
|
1802
|
-
function extractInnerCodeHtml(html) {
|
|
1803
|
-
const match = html.match(/<code[^>]*>([\s\S]*)<\/code><\/pre>\s*$/);
|
|
1804
|
-
return match?.[1] ?? null;
|
|
1805
|
-
}
|
|
1806
|
-
|
|
1807
|
-
// src/react/components/card/CardRenderer.tsx
|
|
1808
|
-
import { Component } from "react";
|
|
1809
|
-
|
|
1810
|
-
// src/react/components/card/CardContext.tsx
|
|
1811
|
-
import { createContext, useContext } from "react";
|
|
1812
|
-
var CardContext = createContext({});
|
|
1813
|
-
var useCardContext = () => useContext(CardContext);
|
|
1814
|
-
|
|
1815
|
-
// src/react/components/card/CardStates.tsx
|
|
1816
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
1817
|
-
function CardLoadingState({ content }) {
|
|
1818
|
-
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: [
|
|
1819
|
-
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center gap-2", children: [
|
|
1820
|
-
/* @__PURE__ */ jsx("div", { className: "h-4 w-4 animate-spin rounded-full border-2 border-[hsl(var(--primary))] border-t-transparent" }),
|
|
1821
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm text-[hsl(var(--primary))]", children: "\u6B63\u5728\u52A0\u8F7D\u5361\u7247\u6570\u636E..." })
|
|
1822
|
-
] }),
|
|
1823
|
-
/* @__PURE__ */ jsxs("details", { className: "mt-2", children: [
|
|
1824
|
-
/* @__PURE__ */ jsx("summary", { className: "cursor-pointer text-xs text-[hsl(var(--primary)/0.7)]", children: "\u67E5\u770B\u63A5\u6536\u4E2D\u7684\u6570\u636E" }),
|
|
1825
|
-
/* @__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 })
|
|
1826
|
-
] })
|
|
1827
|
-
] });
|
|
1828
|
-
}
|
|
1829
|
-
function CardErrorState({ content, message }) {
|
|
1830
|
-
return /* @__PURE__ */ jsxs("div", { className: "my-4 rounded-md border border-red-500/20 bg-red-500/5 p-4", children: [
|
|
1831
|
-
/* @__PURE__ */ jsx("p", { className: "mb-2 text-sm text-red-400", children: message || "\u5361\u7247\u6570\u636E\u89E3\u6790\u5931\u8D25" }),
|
|
1832
|
-
/* @__PURE__ */ jsxs("details", { className: "mt-2", children: [
|
|
1833
|
-
/* @__PURE__ */ jsx("summary", { className: "cursor-pointer text-xs text-red-400/70 hover:underline", children: "\u67E5\u770B\u539F\u59CB\u4EE3\u7801\u5757" }),
|
|
1834
|
-
/* @__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 })
|
|
1835
|
-
] })
|
|
1836
|
-
] });
|
|
1837
|
-
}
|
|
1838
|
-
function CardWarningState({ content, message }) {
|
|
1839
|
-
return /* @__PURE__ */ jsxs("div", { className: "my-4 rounded-md border border-yellow-500/20 bg-yellow-500/5 p-4", children: [
|
|
1840
|
-
/* @__PURE__ */ jsx("p", { className: "mb-2 text-sm text-yellow-400", children: message || "\u6570\u636E\u683C\u5F0F\u4E0D\u7B26\u5408\u5361\u7247\u8981\u6C42" }),
|
|
1841
|
-
/* @__PURE__ */ jsxs("details", { className: "mt-2", children: [
|
|
1842
|
-
/* @__PURE__ */ jsx("summary", { className: "cursor-pointer text-xs text-yellow-400/70", children: "\u67E5\u770B\u539F\u59CB\u6570\u636E" }),
|
|
1843
|
-
/* @__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 })
|
|
1844
|
-
] })
|
|
1845
|
-
] });
|
|
1846
|
-
}
|
|
1847
|
-
|
|
1848
|
-
// src/react/components/card/CardRenderer.tsx
|
|
1849
|
-
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1850
|
-
function looksIncomplete(text) {
|
|
1851
|
-
const t = text.trim();
|
|
1852
|
-
if (!t) return true;
|
|
1853
|
-
const openBraces = (t.match(/{/g) || []).length;
|
|
1854
|
-
const closeBraces = (t.match(/}/g) || []).length;
|
|
1855
|
-
const openBrackets = (t.match(/\[/g) || []).length;
|
|
1856
|
-
const closeBrackets = (t.match(/]/g) || []).length;
|
|
1857
|
-
const quotes = (t.match(/"/g) || []).length;
|
|
1858
|
-
return !t.endsWith("}") && !t.endsWith("]") || openBraces !== closeBraces || openBrackets !== closeBrackets || t.endsWith(",") || t.endsWith(":") || quotes % 2 !== 0;
|
|
1859
|
-
}
|
|
1860
|
-
var CardErrorBoundary = class extends Component {
|
|
1861
|
-
state = { hasError: false };
|
|
1862
|
-
static getDerivedStateFromError() {
|
|
1863
|
-
return { hasError: true };
|
|
1864
|
-
}
|
|
1865
|
-
componentDidCatch(error, info) {
|
|
1866
|
-
console.error("Card render error:", error, info);
|
|
1867
|
-
}
|
|
1868
|
-
render() {
|
|
1869
|
-
if (this.state.hasError) return this.props.fallback;
|
|
1870
|
-
return this.props.children;
|
|
1871
|
-
}
|
|
1872
|
-
};
|
|
1873
|
-
function OpenInPreviewButton({ card }) {
|
|
1874
|
-
const pushArtifact = useUiStore((s) => s.pushArtifact);
|
|
1875
|
-
return /* @__PURE__ */ jsx2(
|
|
1876
|
-
"button",
|
|
1877
|
-
{
|
|
1878
|
-
type: "button",
|
|
1879
|
-
onClick: () => pushArtifact({
|
|
1880
|
-
type: "card",
|
|
1881
|
-
content: JSON.stringify(card, null, 2),
|
|
1882
|
-
title: card.title || card.type,
|
|
1883
|
-
key: `card-${card.id}`
|
|
1884
|
-
}),
|
|
1885
|
-
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))]",
|
|
1886
|
-
children: "\u9884\u89C8"
|
|
1887
|
-
}
|
|
1888
|
-
);
|
|
1889
|
-
}
|
|
1890
|
-
function CardRenderer({ raw, blockPosition, isCodeFenceIncomplete }) {
|
|
1891
|
-
const { sessionId, messageId, sendMessage } = useCardContext();
|
|
1892
|
-
const trimmed = raw.trim();
|
|
1893
|
-
if (!trimmed) return /* @__PURE__ */ jsx2(CardLoadingState, { content: "" });
|
|
1894
|
-
if (isCodeFenceIncomplete) {
|
|
1895
|
-
return /* @__PURE__ */ jsx2(CardLoadingState, { content: trimmed });
|
|
1896
|
-
}
|
|
1897
|
-
const parsed = CardJSON.safeParseJSON(trimmed);
|
|
1898
|
-
if (!parsed.ok) {
|
|
1899
|
-
if (looksIncomplete(trimmed)) {
|
|
1900
|
-
return /* @__PURE__ */ jsx2(CardLoadingState, { content: trimmed });
|
|
1901
|
-
}
|
|
1902
|
-
return /* @__PURE__ */ jsx2(CardErrorState, { content: trimmed, message: parsed.error });
|
|
1903
|
-
}
|
|
1904
|
-
const cards = CardJSON.toCardArray(parsed.value);
|
|
1905
|
-
if (!cards) {
|
|
1906
|
-
return /* @__PURE__ */ jsx2(CardWarningState, { content: trimmed, message: "JSON \u683C\u5F0F\u6B63\u786E\u4F46\u4E0D\u7B26\u5408\u5361\u7247\u7ED3\u6784" });
|
|
1907
|
-
}
|
|
1908
|
-
const cardsWithIds = cards.map((c, i) => {
|
|
1909
|
-
if (c.id) return c;
|
|
1910
|
-
return {
|
|
1911
|
-
...c,
|
|
1912
|
-
id: `${messageId || "msg"}-block-${blockPosition ?? 0}-card-${i}`
|
|
1913
|
-
};
|
|
1914
|
-
});
|
|
1915
|
-
return /* @__PURE__ */ jsx2(Fragment, { children: cardsWithIds.map((card) => {
|
|
1916
|
-
const CardComponent = cardRegistry.get(card.type);
|
|
1917
|
-
if (!CardComponent) {
|
|
1918
|
-
return /* @__PURE__ */ jsx2(
|
|
1919
|
-
CardWarningState,
|
|
1920
|
-
{
|
|
1921
|
-
content: JSON.stringify(card, null, 2),
|
|
1922
|
-
message: `\u672A\u77E5\u7684\u5361\u7247\u7C7B\u578B: ${card.type}`
|
|
1923
|
-
},
|
|
1924
|
-
card.id
|
|
1925
|
-
);
|
|
1926
|
-
}
|
|
1927
|
-
return /* @__PURE__ */ jsx2("div", { className: "group/card relative my-4", children: /* @__PURE__ */ jsxs2(
|
|
1928
|
-
CardErrorBoundary,
|
|
1929
|
-
{
|
|
1930
|
-
fallback: /* @__PURE__ */ jsx2(
|
|
1931
|
-
CardErrorState,
|
|
1932
|
-
{
|
|
1933
|
-
content: JSON.stringify(card, null, 2),
|
|
1934
|
-
message: "\u5361\u7247\u6E32\u67D3\u51FA\u9519"
|
|
1935
|
-
}
|
|
1936
|
-
),
|
|
1937
|
-
children: [
|
|
1938
|
-
/* @__PURE__ */ jsx2(CardComponent, { card, sendMessage, sessionId }),
|
|
1939
|
-
/* @__PURE__ */ jsx2(OpenInPreviewButton, { card })
|
|
1940
|
-
]
|
|
1941
|
-
}
|
|
1942
|
-
) }, card.id);
|
|
1943
|
-
}) });
|
|
1944
|
-
}
|
|
1945
|
-
|
|
1946
|
-
// src/react/components/card/CardCodeBlock.tsx
|
|
1947
|
-
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
1948
|
-
function CardCodeBlock({ className, children, node, ...props }) {
|
|
1949
|
-
const isIncomplete = useIsCodeFenceIncomplete();
|
|
1950
|
-
const match = /language-(\S+)/.exec(className || "");
|
|
1951
|
-
const lang = match?.[1];
|
|
1952
|
-
const isInline = !className;
|
|
1953
|
-
const raw = String(children ?? "");
|
|
1954
|
-
const normalizedLang = normalizeCodeLanguage(lang);
|
|
1955
|
-
const { highlightedHtml } = useHighlightedCodeHtml(raw, normalizedLang);
|
|
1956
|
-
if (!isInline && CardJSON.isCardsLanguage(lang)) {
|
|
1957
|
-
return /* @__PURE__ */ jsx3(
|
|
1958
|
-
CardRenderer,
|
|
1959
|
-
{
|
|
1960
|
-
raw,
|
|
1961
|
-
blockPosition: node?.position?.start?.line,
|
|
1962
|
-
isCodeFenceIncomplete: isIncomplete
|
|
1963
|
-
}
|
|
1964
|
-
);
|
|
1965
|
-
}
|
|
1966
|
-
if (!isInline && highlightedHtml) {
|
|
1967
|
-
return /* @__PURE__ */ jsx3(
|
|
1968
|
-
"code",
|
|
1969
|
-
{
|
|
1970
|
-
className,
|
|
1971
|
-
...props,
|
|
1972
|
-
dangerouslySetInnerHTML: { __html: highlightedHtml }
|
|
1973
|
-
}
|
|
1974
|
-
);
|
|
1975
|
-
}
|
|
1976
|
-
return /* @__PURE__ */ jsx3("code", { className, ...props, children });
|
|
1977
|
-
}
|
|
1978
|
-
|
|
1979
|
-
// src/react/components/markdown/MarkdownContent.tsx
|
|
1980
|
-
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1981
|
-
var STREAMDOWN_PLUGINS = { mermaid };
|
|
1982
|
-
var SYSTEM_REMINDER_TAG = "system-reminder";
|
|
1983
|
-
var HIDDEN_SYSTEM_REMINDER_TAGS = {
|
|
1984
|
-
[SYSTEM_REMINDER_TAG]: []
|
|
1985
|
-
};
|
|
1986
|
-
var HIDDEN_SYSTEM_REMINDER_COMPONENTS = {
|
|
1987
|
-
[SYSTEM_REMINDER_TAG]: () => null
|
|
1988
|
-
};
|
|
1989
|
-
function CodeBlockPre({ children, node: _node, ...props }) {
|
|
1990
|
-
const preRef = useRef(null);
|
|
1991
|
-
const [copied, setCopied] = useState2(false);
|
|
1992
|
-
const [hasCodeNode, setHasCodeNode] = useState2(false);
|
|
1993
|
-
useEffect2(() => {
|
|
1994
|
-
setHasCodeNode(!!preRef.current?.querySelector("code"));
|
|
1995
|
-
}, []);
|
|
1996
|
-
const getRawCode = () => preRef.current?.querySelector("code")?.textContent ?? "";
|
|
1997
|
-
const handleCopy = async () => {
|
|
1998
|
-
const ok = await copyToClipboard(getRawCode());
|
|
1999
|
-
if (ok) {
|
|
2000
|
-
setCopied(true);
|
|
2001
|
-
setTimeout(() => setCopied(false), 2e3);
|
|
2002
|
-
}
|
|
2003
|
-
};
|
|
2004
|
-
const handleDownload = () => {
|
|
2005
|
-
const codeEl = preRef.current?.querySelector("code");
|
|
2006
|
-
const text = codeEl?.textContent ?? "";
|
|
2007
|
-
const ext = codeEl?.className.match(/language-(\S+)/)?.[1] ?? "txt";
|
|
2008
|
-
const blob = new Blob([text], { type: "text/plain" });
|
|
2009
|
-
const url = URL.createObjectURL(blob);
|
|
2010
|
-
const a = document.createElement("a");
|
|
2011
|
-
a.href = url;
|
|
2012
|
-
a.download = `code.${ext}`;
|
|
2013
|
-
a.click();
|
|
2014
|
-
URL.revokeObjectURL(url);
|
|
2015
|
-
};
|
|
2016
|
-
return /* @__PURE__ */ jsxs3("div", { className: "relative group", children: [
|
|
2017
|
-
/* @__PURE__ */ jsx4("pre", { ref: preRef, ...props, children }),
|
|
2018
|
-
hasCodeNode && /* @__PURE__ */ jsxs3("div", { className: "absolute top-2 right-2 flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity", children: [
|
|
2019
|
-
/* @__PURE__ */ jsxs3(
|
|
2020
|
-
"button",
|
|
2021
|
-
{
|
|
2022
|
-
type: "button",
|
|
2023
|
-
onClick: handleCopy,
|
|
2024
|
-
className: cn(
|
|
2025
|
-
"flex items-center gap-1 rounded-md px-1.5 py-0.5 text-[11px] transition-colors",
|
|
2026
|
-
copied ? "text-[hsl(var(--primary))]" : "text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))] hover:bg-[hsl(var(--accent))]"
|
|
2027
|
-
),
|
|
2028
|
-
children: [
|
|
2029
|
-
copied ? /* @__PURE__ */ jsx4(Check, { size: 12 }) : /* @__PURE__ */ jsx4(Copy, { size: 12 }),
|
|
2030
|
-
/* @__PURE__ */ jsx4("span", { children: copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236" })
|
|
2031
|
-
]
|
|
2032
|
-
}
|
|
2033
|
-
),
|
|
2034
|
-
/* @__PURE__ */ jsxs3(
|
|
2035
|
-
"button",
|
|
2036
|
-
{
|
|
2037
|
-
type: "button",
|
|
2038
|
-
onClick: handleDownload,
|
|
2039
|
-
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))]",
|
|
2040
|
-
children: [
|
|
2041
|
-
/* @__PURE__ */ jsx4(Download, { size: 12 }),
|
|
2042
|
-
/* @__PURE__ */ jsx4("span", { children: "\u4E0B\u8F7D" })
|
|
2043
|
-
]
|
|
2044
|
-
}
|
|
2045
|
-
)
|
|
2046
|
-
] })
|
|
2047
|
-
] });
|
|
2048
|
-
}
|
|
2049
|
-
function isExternalImageSrc(src) {
|
|
2050
|
-
if (!src) return false;
|
|
2051
|
-
if (src.startsWith("data:") || src.startsWith("blob:")) return true;
|
|
2052
|
-
if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(src)) return true;
|
|
2053
|
-
if (src.startsWith("/")) return true;
|
|
2054
|
-
return false;
|
|
2055
|
-
}
|
|
2056
|
-
function decodeMarkdownPath(src) {
|
|
2057
|
-
try {
|
|
2058
|
-
return decodeURIComponent(src);
|
|
2059
|
-
} catch {
|
|
2060
|
-
return src;
|
|
2061
|
-
}
|
|
2062
|
-
}
|
|
2063
|
-
var MARKDOWN_IMAGE_PATTERN = /!\[((?:\\.|[^\]\\])*)\]\(\s*(?:<([^>\n]+)>|([^\s)]+))(\s+(?:"[^"]*"|'[^']*'|\([^)]*\)))?\s*\)/g;
|
|
2064
|
-
function resolveSessionImageMarkdown(children, sessionId) {
|
|
2065
|
-
return children.replace(
|
|
2066
|
-
MARKDOWN_IMAGE_PATTERN,
|
|
2067
|
-
(match, alt, angleSrc, plainSrc, title = "") => {
|
|
2068
|
-
const src = angleSrc ?? plainSrc;
|
|
2069
|
-
if (!src || isExternalImageSrc(src)) {
|
|
2070
|
-
return match;
|
|
2071
|
-
}
|
|
2072
|
-
const resolved = getAuthedUrl(
|
|
2073
|
-
`/api/sessions/${encodeURIComponent(sessionId)}/files/${encodeURIComponent(decodeMarkdownPath(src))}`
|
|
2074
|
-
);
|
|
2075
|
-
return ``;
|
|
2076
|
-
}
|
|
2077
|
-
);
|
|
2078
|
-
}
|
|
2079
|
-
function MarkdownContent({
|
|
2080
|
-
allowedTags,
|
|
2081
|
-
children,
|
|
2082
|
-
components,
|
|
2083
|
-
mode,
|
|
2084
|
-
plugins,
|
|
2085
|
-
sessionId,
|
|
2086
|
-
...props
|
|
2087
|
-
}) {
|
|
2088
|
-
const resolvedChildren = useMemo2(
|
|
2089
|
-
() => sessionId && typeof children === "string" ? resolveSessionImageMarkdown(children, sessionId) : children,
|
|
2090
|
-
[children, sessionId]
|
|
2091
|
-
);
|
|
2092
|
-
return /* @__PURE__ */ jsx4(
|
|
2093
|
-
Streamdown,
|
|
2094
|
-
{
|
|
2095
|
-
...props,
|
|
2096
|
-
mode: mode ?? "static",
|
|
2097
|
-
allowedTags: {
|
|
2098
|
-
...allowedTags ?? {},
|
|
2099
|
-
...HIDDEN_SYSTEM_REMINDER_TAGS
|
|
2100
|
-
},
|
|
2101
|
-
components: {
|
|
2102
|
-
code: CardCodeBlock,
|
|
2103
|
-
pre: CodeBlockPre,
|
|
2104
|
-
...components ?? {},
|
|
2105
|
-
...HIDDEN_SYSTEM_REMINDER_COMPONENTS
|
|
2106
|
-
},
|
|
2107
|
-
plugins: {
|
|
2108
|
-
...STREAMDOWN_PLUGINS,
|
|
2109
|
-
...plugins ?? {}
|
|
2110
|
-
},
|
|
2111
|
-
children: resolvedChildren
|
|
2112
|
-
}
|
|
2113
|
-
);
|
|
2114
|
-
}
|
|
2115
|
-
|
|
2116
|
-
// src/react/components/chat/AskUserQuestionBlock.tsx
|
|
2117
|
-
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2118
|
-
function AskUserQuestionBlock({
|
|
2119
|
-
data,
|
|
2120
|
-
answered,
|
|
2121
|
-
toolCallId,
|
|
2122
|
-
sessionStatus,
|
|
2123
|
-
answerData,
|
|
2124
|
-
onAnswer
|
|
2125
|
-
}) {
|
|
2126
|
-
const [selections, setSelections] = useState3(/* @__PURE__ */ new Map());
|
|
2127
|
-
const [customTexts, setCustomTexts] = useState3(/* @__PURE__ */ new Map());
|
|
2128
|
-
const [usingCustom, setUsingCustom] = useState3(/* @__PURE__ */ new Set());
|
|
2129
|
-
const [submitted, setSubmitted] = useState3(false);
|
|
2130
|
-
useEffect3(() => {
|
|
2131
|
-
if (sessionStatus === "failed" || sessionStatus === "interrupted") {
|
|
2132
|
-
setSubmitted(false);
|
|
2133
|
-
}
|
|
2134
|
-
}, [sessionStatus]);
|
|
2135
|
-
const displayAnswerState = useMemo3(() => {
|
|
2136
|
-
if (!(answered && answerData)) {
|
|
2137
|
-
return {
|
|
2138
|
-
selections,
|
|
2139
|
-
customTexts,
|
|
2140
|
-
usingCustom
|
|
2141
|
-
};
|
|
2142
|
-
}
|
|
2143
|
-
const nextSelections = /* @__PURE__ */ new Map();
|
|
2144
|
-
const nextCustomTexts = /* @__PURE__ */ new Map();
|
|
2145
|
-
const nextUsingCustom = /* @__PURE__ */ new Set();
|
|
2146
|
-
for (const [questionKey, optionIndexes] of Object.entries(answerData.selections)) {
|
|
2147
|
-
nextSelections.set(Number(questionKey), new Set(optionIndexes));
|
|
2148
|
-
}
|
|
2149
|
-
for (const [questionKey, text] of Object.entries(answerData.custom)) {
|
|
2150
|
-
const qIdx = Number(questionKey);
|
|
2151
|
-
nextCustomTexts.set(qIdx, text);
|
|
2152
|
-
nextUsingCustom.add(qIdx);
|
|
2153
|
-
}
|
|
2154
|
-
return {
|
|
2155
|
-
selections: nextSelections,
|
|
2156
|
-
customTexts: nextCustomTexts,
|
|
2157
|
-
usingCustom: nextUsingCustom
|
|
2158
|
-
};
|
|
2159
|
-
}, [answerData, answered, customTexts, selections, usingCustom]);
|
|
2160
|
-
const displaySelections = displayAnswerState.selections;
|
|
2161
|
-
const displayCustomTexts = displayAnswerState.customTexts;
|
|
2162
|
-
const displayUsingCustom = displayAnswerState.usingCustom;
|
|
2163
|
-
const toggleOption = (qIdx, optIdx, multi) => {
|
|
2164
|
-
if (answered || submitted) return;
|
|
2165
|
-
setSelections((prev) => {
|
|
2166
|
-
const next = new Map(prev);
|
|
2167
|
-
const current = new Set(next.get(qIdx) ?? []);
|
|
2168
|
-
if (multi) {
|
|
2169
|
-
if (current.has(optIdx)) current.delete(optIdx);
|
|
2170
|
-
else current.add(optIdx);
|
|
2171
|
-
} else {
|
|
2172
|
-
current.clear();
|
|
2173
|
-
current.add(optIdx);
|
|
2174
|
-
}
|
|
2175
|
-
next.set(qIdx, current);
|
|
2176
|
-
return next;
|
|
2177
|
-
});
|
|
2178
|
-
setUsingCustom((prev) => {
|
|
2179
|
-
const next = new Set(prev);
|
|
2180
|
-
next.delete(qIdx);
|
|
2181
|
-
return next;
|
|
2182
|
-
});
|
|
2183
|
-
};
|
|
2184
|
-
const handleCustomFocus = (qIdx) => {
|
|
2185
|
-
if (answered || submitted) return;
|
|
2186
|
-
setSelections((prev) => {
|
|
2187
|
-
const next = new Map(prev);
|
|
2188
|
-
next.delete(qIdx);
|
|
2189
|
-
return next;
|
|
2190
|
-
});
|
|
2191
|
-
setUsingCustom((prev) => new Set(prev).add(qIdx));
|
|
2192
|
-
};
|
|
2193
|
-
const setCustomText = (qIdx, text) => {
|
|
2194
|
-
if (answered || submitted) return;
|
|
2195
|
-
setCustomTexts((prev) => new Map(prev).set(qIdx, text));
|
|
2196
|
-
};
|
|
2197
|
-
const getAnswer = (qIdx) => {
|
|
2198
|
-
const q = data.questions[qIdx];
|
|
2199
|
-
if (usingCustom.has(qIdx)) {
|
|
2200
|
-
const text = (customTexts.get(qIdx) ?? "").trim();
|
|
2201
|
-
return text || null;
|
|
2202
|
-
}
|
|
2203
|
-
const sel = selections.get(qIdx);
|
|
2204
|
-
if (!sel || sel.size === 0) return null;
|
|
2205
|
-
return [...sel].sort().map((i) => q.options[i].label).join(", ");
|
|
2206
|
-
};
|
|
2207
|
-
const allAnswered = data.questions.every((_, i) => getAnswer(i) !== null);
|
|
2208
|
-
const handleSubmit = () => {
|
|
2209
|
-
if (answered || submitted || !allAnswered || !onAnswer) return;
|
|
2210
|
-
const nextAnswerData = {
|
|
2211
|
-
selections: Object.fromEntries(
|
|
2212
|
-
Array.from(selections.entries()).map(([qIdx, optionIndexes]) => [
|
|
2213
|
-
qIdx,
|
|
2214
|
-
Array.from(optionIndexes).sort((a, b) => a - b)
|
|
2215
|
-
])
|
|
2216
|
-
),
|
|
2217
|
-
custom: Object.fromEntries(
|
|
2218
|
-
Array.from(usingCustom).map((qIdx) => [qIdx, (customTexts.get(qIdx) ?? "").trim()]).filter(([, text2]) => text2.length > 0)
|
|
2219
|
-
)
|
|
2220
|
-
};
|
|
2221
|
-
const parts = data.questions.map((q, i) => `- ${q.question} -> ${getAnswer(i)}`);
|
|
2222
|
-
const text = `\u5173\u4E8E\u9700\u8981\u786E\u8BA4\u7684\u95EE\u9898\uFF0C\u7528\u6237\u7684\u56DE\u7B54\u5982\u4E0B\uFF1A
|
|
2223
|
-
${parts.join("\n")}`;
|
|
2224
|
-
setSubmitted(true);
|
|
2225
|
-
onAnswer(text, toolCallId, nextAnswerData);
|
|
2226
|
-
};
|
|
2227
|
-
return /* @__PURE__ */ jsxs4(
|
|
2228
|
-
"div",
|
|
2229
|
-
{
|
|
2230
|
-
className: cn(
|
|
2231
|
-
"ml-4 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))]",
|
|
2232
|
-
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"
|
|
2233
|
-
),
|
|
2234
|
-
children: [
|
|
2235
|
-
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: [
|
|
2236
|
-
"\u5B50\u667A\u80FD\u4F53\u300C",
|
|
2237
|
-
data.source_loop.description,
|
|
2238
|
-
"\u300D\u5728\u7B49\u5F85\u4F60\u7684\u56DE\u7B54"
|
|
2239
|
-
] }),
|
|
2240
|
-
data.questions.map((q, qIdx) => /* @__PURE__ */ jsx5(
|
|
2241
|
-
QuestionCard,
|
|
2242
|
-
{
|
|
2243
|
-
question: q,
|
|
2244
|
-
qIdx,
|
|
2245
|
-
answered,
|
|
2246
|
-
selected: displaySelections.get(qIdx) ?? /* @__PURE__ */ new Set(),
|
|
2247
|
-
isCustom: displayUsingCustom.has(qIdx),
|
|
2248
|
-
customText: displayCustomTexts.get(qIdx) ?? "",
|
|
2249
|
-
onToggle: toggleOption,
|
|
2250
|
-
onCustomFocus: handleCustomFocus,
|
|
2251
|
-
onCustomChange: setCustomText
|
|
2252
|
-
},
|
|
2253
|
-
q.question
|
|
2254
|
-
)),
|
|
2255
|
-
!answered && !submitted && onAnswer && /* @__PURE__ */ jsx5(
|
|
2256
|
-
"button",
|
|
2257
|
-
{
|
|
2258
|
-
type: "button",
|
|
2259
|
-
onClick: handleSubmit,
|
|
2260
|
-
disabled: !allAnswered,
|
|
2261
|
-
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",
|
|
2262
|
-
children: allAnswered ? "\u786E\u8BA4" : "\u8BF7\u5148\u9009\u62E9\u4E00\u4E2A\u9009\u9879"
|
|
2263
|
-
}
|
|
2264
|
-
),
|
|
2265
|
-
submitted && !answered && /* @__PURE__ */ jsxs4(
|
|
2266
|
-
"button",
|
|
2267
|
-
{
|
|
2268
|
-
type: "button",
|
|
2269
|
-
disabled: true,
|
|
2270
|
-
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",
|
|
2271
|
-
children: [
|
|
2272
|
-
/* @__PURE__ */ jsx5(Loader2, { size: 14, className: "animate-spin" }),
|
|
2273
|
-
"\u786E\u8BA4\u4E2D"
|
|
2274
|
-
]
|
|
2275
|
-
}
|
|
2276
|
-
)
|
|
2277
|
-
]
|
|
2278
|
-
}
|
|
2279
|
-
);
|
|
2280
|
-
}
|
|
2281
|
-
function QuestionCard({
|
|
2282
|
-
question,
|
|
2283
|
-
qIdx,
|
|
2284
|
-
answered,
|
|
2285
|
-
selected,
|
|
2286
|
-
isCustom,
|
|
2287
|
-
customText,
|
|
2288
|
-
onToggle,
|
|
2289
|
-
onCustomFocus,
|
|
2290
|
-
onCustomChange
|
|
2291
|
-
}) {
|
|
2292
|
-
const multi = question.multiSelect ?? false;
|
|
2293
|
-
return /* @__PURE__ */ jsxs4("div", { children: [
|
|
2294
|
-
/* @__PURE__ */ jsxs4("div", { className: cn("flex items-start gap-2", answered ? "mb-2" : "mb-3"), children: [
|
|
2295
|
-
/* @__PURE__ */ jsx5(
|
|
2296
|
-
MessageSquareMore,
|
|
2297
|
-
{
|
|
2298
|
-
size: answered ? 12 : 13,
|
|
2299
|
-
className: "mt-0.5 shrink-0 text-[hsl(var(--primary))]"
|
|
2300
|
-
}
|
|
2301
|
-
),
|
|
2302
|
-
/* @__PURE__ */ jsx5(
|
|
2303
|
-
"div",
|
|
2304
|
-
{
|
|
2305
|
-
className: cn(
|
|
2306
|
-
"min-w-0 flex-1 font-medium text-[hsl(var(--foreground))]",
|
|
2307
|
-
answered ? "text-xs" : "text-sm"
|
|
2308
|
-
),
|
|
2309
|
-
children: /* @__PURE__ */ jsx5(
|
|
2310
|
-
MarkdownContent,
|
|
2311
|
-
{
|
|
2312
|
-
className: cn(
|
|
2313
|
-
"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",
|
|
2314
|
-
answered ? "text-xs" : "text-sm"
|
|
2315
|
-
),
|
|
2316
|
-
controls: { code: true },
|
|
2317
|
-
children: question.question
|
|
2318
|
-
}
|
|
2319
|
-
)
|
|
2320
|
-
}
|
|
2321
|
-
)
|
|
2322
|
-
] }),
|
|
2323
|
-
/* @__PURE__ */ jsxs4("div", { className: cn("flex flex-col pl-5", answered ? "gap-1" : "gap-1.5"), children: [
|
|
2324
|
-
question.options.map((opt, optIdx) => {
|
|
2325
|
-
const isSel = selected.has(optIdx);
|
|
2326
|
-
return /* @__PURE__ */ jsxs4(
|
|
2327
|
-
"button",
|
|
2328
|
-
{
|
|
2329
|
-
type: "button",
|
|
2330
|
-
disabled: answered,
|
|
2331
|
-
onClick: () => onToggle(qIdx, optIdx, multi),
|
|
2332
|
-
className: cn(
|
|
2333
|
-
"flex items-start gap-2.5 rounded-lg border text-left transition-all",
|
|
2334
|
-
answered ? "px-2.5 py-1.5" : "px-3 py-2.5",
|
|
2335
|
-
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)]",
|
|
2336
|
-
!answered && !isSel && "hover:border-[hsl(var(--ring)/0.4)] hover:bg-[hsl(var(--accent))]",
|
|
2337
|
-
answered && "cursor-default opacity-70"
|
|
2338
|
-
),
|
|
2339
|
-
children: [
|
|
2340
|
-
multi && /* @__PURE__ */ jsx5(
|
|
2341
|
-
"div",
|
|
2342
|
-
{
|
|
2343
|
-
className: cn(
|
|
2344
|
-
"mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border transition-colors",
|
|
2345
|
-
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))]"
|
|
2346
|
-
),
|
|
2347
|
-
children: isSel && /* @__PURE__ */ jsx5(
|
|
2348
|
-
Check2,
|
|
2349
|
-
{
|
|
2350
|
-
size: 9,
|
|
2351
|
-
className: answered ? "text-[hsl(var(--primary))]" : "text-[hsl(var(--primary-foreground))]"
|
|
2352
|
-
}
|
|
2353
|
-
)
|
|
2354
|
-
}
|
|
2355
|
-
),
|
|
2356
|
-
/* @__PURE__ */ jsxs4("div", { className: "min-w-0", children: [
|
|
2357
|
-
/* @__PURE__ */ jsx5("div", { className: cn("font-medium", answered ? "text-xs" : "text-[13px]"), children: opt.label }),
|
|
2358
|
-
opt.description && /* @__PURE__ */ jsx5(
|
|
2359
|
-
"div",
|
|
2360
|
-
{
|
|
2361
|
-
className: cn(
|
|
2362
|
-
"mt-0.5",
|
|
2363
|
-
answered ? "text-[11px]" : "text-xs",
|
|
2364
|
-
isSel && !answered ? "opacity-75" : "text-[hsl(var(--muted-foreground))]"
|
|
2365
|
-
),
|
|
2366
|
-
children: opt.description
|
|
2367
|
-
}
|
|
2368
|
-
)
|
|
2369
|
-
] })
|
|
2370
|
-
]
|
|
2371
|
-
},
|
|
2372
|
-
opt.label
|
|
2373
|
-
);
|
|
2374
|
-
}),
|
|
2375
|
-
answered && !isCustom ? null : /* @__PURE__ */ jsxs4(
|
|
2376
|
-
"div",
|
|
2377
|
-
{
|
|
2378
|
-
className: cn(
|
|
2379
|
-
"flex items-center gap-2 rounded-lg border transition-all",
|
|
2380
|
-
answered ? "px-2.5 py-1.5" : "px-3 py-2.5",
|
|
2381
|
-
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))]",
|
|
2382
|
-
answered && "cursor-default opacity-70"
|
|
2383
|
-
),
|
|
2384
|
-
children: [
|
|
2385
|
-
/* @__PURE__ */ jsx5("span", { className: "shrink-0 text-xs text-[hsl(var(--muted-foreground))]", children: "\u5176\u4ED6\uFF1A" }),
|
|
2386
|
-
/* @__PURE__ */ jsx5(
|
|
2387
|
-
"input",
|
|
2388
|
-
{
|
|
2389
|
-
type: "text",
|
|
2390
|
-
value: customText,
|
|
2391
|
-
disabled: answered,
|
|
2392
|
-
onChange: (e) => onCustomChange(qIdx, e.target.value),
|
|
2393
|
-
onFocus: () => onCustomFocus(qIdx),
|
|
2394
|
-
"aria-label": "\u81EA\u5B9A\u4E49\u56DE\u7B54",
|
|
2395
|
-
placeholder: "\u8F93\u5165\u4F60\u7684\u7B54\u6848...",
|
|
2396
|
-
className: cn(
|
|
2397
|
-
"min-w-0 flex-1 bg-transparent text-[hsl(var(--foreground))] outline-none placeholder:text-[hsl(var(--muted-foreground)/0.5)]",
|
|
2398
|
-
answered ? "text-xs" : "text-sm"
|
|
2399
|
-
)
|
|
2400
|
-
}
|
|
2401
|
-
)
|
|
2402
|
-
]
|
|
2403
|
-
}
|
|
2404
|
-
)
|
|
2405
|
-
] })
|
|
2406
|
-
] });
|
|
2407
|
-
}
|
|
2408
|
-
function parseAskUserQuestion(toolResult) {
|
|
2409
|
-
if (!toolResult) return null;
|
|
2410
|
-
try {
|
|
2411
|
-
const parsed = JSON.parse(toolResult);
|
|
2412
|
-
let questions = parsed?.questions;
|
|
2413
|
-
if (typeof questions === "string") {
|
|
2414
|
-
try {
|
|
2415
|
-
questions = JSON.parse(questions);
|
|
2416
|
-
} catch {
|
|
2417
|
-
return null;
|
|
2418
|
-
}
|
|
2419
|
-
}
|
|
2420
|
-
if (Array.isArray(questions) && questions.every(isQuestionItem)) {
|
|
2421
|
-
return { ...parsed, questions };
|
|
2422
|
-
}
|
|
2423
|
-
} catch {
|
|
2424
|
-
}
|
|
2425
|
-
return null;
|
|
2426
|
-
}
|
|
2427
|
-
function isQuestionItem(value) {
|
|
2428
|
-
if (!value || typeof value !== "object") return false;
|
|
2429
|
-
const question = value.question;
|
|
2430
|
-
const options = value.options;
|
|
2431
|
-
return typeof question === "string" && Array.isArray(options) && options.every(isOptionItem);
|
|
2432
|
-
}
|
|
2433
|
-
function isOptionItem(value) {
|
|
2434
|
-
if (!value || typeof value !== "object") return false;
|
|
2435
|
-
const option = value;
|
|
2436
|
-
return typeof option.label === "string" && typeof option.description === "string";
|
|
2437
|
-
}
|
|
2438
|
-
|
|
2439
|
-
// src/react/components/plan/parse-plan-tree.ts
|
|
2440
|
-
import { load } from "js-yaml";
|
|
2441
|
-
function isNonEmptyString(value) {
|
|
2442
|
-
return typeof value === "string" && value.trim().length > 0;
|
|
2443
|
-
}
|
|
2444
|
-
function isPlanStep(value) {
|
|
2445
|
-
if (!value || typeof value !== "object") return false;
|
|
2446
|
-
const step = value;
|
|
2447
|
-
if (!isNonEmptyString(step.label)) return false;
|
|
2448
|
-
if ("skill" in step && step.skill !== void 0 && !isNonEmptyString(step.skill)) return false;
|
|
2449
|
-
if ("children" in step && !Array.isArray(step.children)) return false;
|
|
2450
|
-
const children = Array.isArray(step.children) ? step.children : [];
|
|
2451
|
-
if (children.length > 0 && !isNonEmptyString(step.skill)) return false;
|
|
2452
|
-
return children.every((child) => isPlanStep(child));
|
|
2453
|
-
}
|
|
2454
|
-
function isPlanDocument(value) {
|
|
2455
|
-
if (!value || typeof value !== "object") return false;
|
|
2456
|
-
const doc = value;
|
|
2457
|
-
if (!isNonEmptyString(doc.title) || !isNonEmptyString(doc.objective)) return false;
|
|
2458
|
-
if (!Array.isArray(doc.steps) || doc.steps.length === 0) return false;
|
|
2459
|
-
if (!doc.steps.every((step) => isPlanStep(step))) return false;
|
|
2460
|
-
if ("notes" in doc) {
|
|
2461
|
-
if (!Array.isArray(doc.notes)) return false;
|
|
2462
|
-
if (!doc.notes.every((note) => isNonEmptyString(note))) return false;
|
|
2463
|
-
}
|
|
2464
|
-
return true;
|
|
2465
|
-
}
|
|
2466
|
-
function getPlanDocumentError(value) {
|
|
2467
|
-
if (!value || typeof value !== "object") return "PLAN.yaml \u9876\u5C42\u7ED3\u6784\u5FC5\u987B\u662F\u5BF9\u8C61";
|
|
2468
|
-
const doc = value;
|
|
2469
|
-
if (!isNonEmptyString(doc.title)) return "PLAN.yaml \u7F3A\u5C11\u975E\u7A7A title";
|
|
2470
|
-
if (!isNonEmptyString(doc.objective)) return "PLAN.yaml \u7F3A\u5C11\u975E\u7A7A objective";
|
|
2471
|
-
if (!Array.isArray(doc.steps) || doc.steps.length === 0) return "PLAN.yaml \u7F3A\u5C11\u975E\u7A7A steps";
|
|
2472
|
-
if (!doc.steps.every((step) => isPlanStep(step))) {
|
|
2473
|
-
return "PLAN.yaml \u6B65\u9AA4\u7ED3\u6784\u65E0\u6548\uFF0C\u8BF7\u68C0\u67E5 label / skill / children";
|
|
2474
|
-
}
|
|
2475
|
-
if ("notes" in doc) {
|
|
2476
|
-
if (!Array.isArray(doc.notes) || !doc.notes.every((note) => isNonEmptyString(note))) {
|
|
2477
|
-
return "PLAN.yaml notes \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32\u5217\u8868";
|
|
2478
|
-
}
|
|
2479
|
-
}
|
|
2480
|
-
return "PLAN.yaml \u7ED3\u6784\u65E0\u6548";
|
|
2481
|
-
}
|
|
2482
|
-
function toPlanStatus(value) {
|
|
2483
|
-
if (!isNonEmptyString(value)) return "pending";
|
|
2484
|
-
const normalized = value.trim().toLowerCase();
|
|
2485
|
-
return normalized === "pending" || normalized === "running" || normalized === "done" || normalized === "failed" ? normalized : "pending";
|
|
2486
|
-
}
|
|
2487
|
-
function applyPlanStatusesById(root, statuses) {
|
|
2488
|
-
const nextStatus = statuses[root.id] ?? root.status;
|
|
2489
|
-
const children = Array.isArray(root.children) ? root.children : [];
|
|
2490
|
-
return {
|
|
2491
|
-
...root,
|
|
2492
|
-
status: nextStatus,
|
|
2493
|
-
children: children.map((child) => applyPlanStatusesById(child, statuses))
|
|
2494
|
-
};
|
|
2495
|
-
}
|
|
2496
|
-
function parsePlanTreeResult(content) {
|
|
2497
|
-
if (!content.trim()) {
|
|
2498
|
-
return { plan: null, error: null };
|
|
2499
|
-
}
|
|
2500
|
-
let parsed;
|
|
2501
|
-
try {
|
|
2502
|
-
parsed = load(content);
|
|
2503
|
-
} catch (error) {
|
|
2504
|
-
return {
|
|
2505
|
-
plan: null,
|
|
2506
|
-
error: error instanceof Error ? `PLAN.yaml YAML \u8BED\u6CD5\u9519\u8BEF\uFF1A${error.message}` : "PLAN.yaml YAML \u8BED\u6CD5\u9519\u8BEF"
|
|
2507
|
-
};
|
|
2508
|
-
}
|
|
2509
|
-
if (!isPlanDocument(parsed)) {
|
|
2510
|
-
return { plan: null, error: getPlanDocumentError(parsed) };
|
|
2511
|
-
}
|
|
2512
|
-
const createNode = (step, depth, fallbackId) => {
|
|
2513
|
-
const children = Array.isArray(step.children) ? step.children : [];
|
|
2514
|
-
return {
|
|
2515
|
-
id: isNonEmptyString(step.id) ? step.id.trim() : fallbackId,
|
|
2516
|
-
label: step.label.trim(),
|
|
2517
|
-
skillRef: isNonEmptyString(step.skill) ? step.skill.trim() : null,
|
|
2518
|
-
status: toPlanStatus(step.status),
|
|
2519
|
-
children: children.map((child, index) => createNode(child, depth + 1, `${fallbackId}.${index + 1}`)),
|
|
2520
|
-
depth
|
|
2521
|
-
};
|
|
2522
|
-
};
|
|
2523
|
-
const root = {
|
|
2524
|
-
id: "plan-root",
|
|
2525
|
-
label: parsed.title.trim(),
|
|
2526
|
-
skillRef: null,
|
|
2527
|
-
status: "pending",
|
|
2528
|
-
children: parsed.steps.map((step, index) => createNode(step, 1, `${index + 1}`)),
|
|
2529
|
-
depth: 0
|
|
2530
|
-
};
|
|
2531
|
-
const notes = [parsed.objective.trim(), ...(parsed.notes ?? []).map((note) => note.trim())];
|
|
2532
|
-
return { plan: { root, notes }, error: null };
|
|
2533
|
-
}
|
|
2534
|
-
|
|
2535
|
-
// src/react/components/plan/parse-plan-messages.ts
|
|
2536
|
-
function resultAsString(result) {
|
|
2537
|
-
if (typeof result === "string") return result;
|
|
2538
|
-
return null;
|
|
2539
|
-
}
|
|
2540
|
-
var PAUSE_TOOLS = /* @__PURE__ */ new Set(["AskUserQuestion", "ExitPlanMode"]);
|
|
2541
|
-
var READ_FILE_TOOLS = /* @__PURE__ */ new Set(["Read"]);
|
|
2542
|
-
var PLAN_WRITE_TOOLS = /* @__PURE__ */ new Set(["Write"]);
|
|
2543
|
-
var PLAN_EDIT_TOOLS = /* @__PURE__ */ new Set(["Edit"]);
|
|
2544
|
-
function getSkillId(payload) {
|
|
2545
|
-
if (!payload) {
|
|
2546
|
-
return "";
|
|
2547
|
-
}
|
|
2548
|
-
if (typeof payload.skill_id === "string" && payload.skill_id) {
|
|
2549
|
-
return payload.skill_id;
|
|
2550
|
-
}
|
|
2551
|
-
if (typeof payload.name === "string" && payload.name) {
|
|
2552
|
-
return payload.name;
|
|
2553
|
-
}
|
|
2554
|
-
return "";
|
|
2555
|
-
}
|
|
2556
|
-
function getSkillDisplayName(payload) {
|
|
2557
|
-
if (!payload || typeof payload.display_name !== "string") {
|
|
2558
|
-
return void 0;
|
|
2559
|
-
}
|
|
2560
|
-
const displayName = payload.display_name.trim();
|
|
2561
|
-
if (!displayName) {
|
|
2562
|
-
return void 0;
|
|
2563
|
-
}
|
|
2564
|
-
const skillId = getSkillId(payload);
|
|
2565
|
-
return displayName === skillId ? void 0 : displayName;
|
|
2566
|
-
}
|
|
2567
|
-
function parseModeChange2(message) {
|
|
2568
|
-
if (message.kind !== "mode_change" || typeof message.content !== "string") {
|
|
2569
|
-
return null;
|
|
2570
|
-
}
|
|
2571
|
-
try {
|
|
2572
|
-
const parsed = JSON.parse(message.content);
|
|
2573
|
-
if (typeof parsed.from === "string" && typeof parsed.to === "string") {
|
|
2574
|
-
return { from: parsed.from, to: parsed.to };
|
|
2575
|
-
}
|
|
2576
|
-
} catch {
|
|
2577
|
-
}
|
|
2578
|
-
return null;
|
|
2579
|
-
}
|
|
2580
|
-
function isPlanningBoundaryMessage(message) {
|
|
2581
|
-
if (message.kind === "planning_enter" || message.kind === "planning_exit") {
|
|
2582
|
-
return true;
|
|
2583
|
-
}
|
|
2584
|
-
const modeChange = parseModeChange2(message);
|
|
2585
|
-
if (!modeChange) {
|
|
2586
|
-
return false;
|
|
2587
|
-
}
|
|
2588
|
-
return modeChange.to === "planning" || modeChange.from === "planning";
|
|
2589
|
-
}
|
|
2590
|
-
function parsePlanMessages(allMessages) {
|
|
2591
|
-
const messages = Array.isArray(allMessages) ? allMessages : [];
|
|
2592
|
-
let intent = "";
|
|
2593
|
-
for (const msg of messages) {
|
|
2594
|
-
if (!msg.tool_calls) continue;
|
|
2595
|
-
const searchTc = msg.tool_calls.find(
|
|
2596
|
-
(tc) => tc.name === "search_skills" || tc.name === "SearchSkills"
|
|
2597
|
-
);
|
|
2598
|
-
if (searchTc) {
|
|
2599
|
-
try {
|
|
2600
|
-
intent = JSON.parse(searchTc.arguments).query ?? "";
|
|
2601
|
-
} catch {
|
|
2602
|
-
}
|
|
2603
|
-
if (intent) break;
|
|
2604
|
-
}
|
|
2605
|
-
}
|
|
2606
|
-
const plannerMsgs = messages.filter(
|
|
2607
|
-
(message) => !isPlanningBoundaryMessage(message)
|
|
2608
|
-
);
|
|
2609
|
-
const searchResults = [];
|
|
2610
|
-
const searchMap = /* @__PURE__ */ new Map();
|
|
2611
|
-
const selectedSkills = [];
|
|
2612
|
-
const selectedSet = /* @__PURE__ */ new Set();
|
|
2613
|
-
const askQuestions = [];
|
|
2614
|
-
const readFiles = [];
|
|
2615
|
-
let lastPauseTool = null;
|
|
2616
|
-
let planContent = "";
|
|
2617
|
-
let planContentPriority = 0;
|
|
2618
|
-
let latestStatuses = {};
|
|
2619
|
-
const setPlanContent = (nextContent, priority) => {
|
|
2620
|
-
if (typeof nextContent !== "string" || !nextContent.trim()) {
|
|
2621
|
-
return;
|
|
2622
|
-
}
|
|
2623
|
-
if (priority < planContentPriority) {
|
|
2624
|
-
return;
|
|
2625
|
-
}
|
|
2626
|
-
planContent = nextContent;
|
|
2627
|
-
planContentPriority = priority;
|
|
2628
|
-
};
|
|
2629
|
-
const applyPlanEdit = (args) => {
|
|
2630
|
-
const oldString = typeof args.old_string === "string" ? args.old_string : "";
|
|
2631
|
-
const newString = typeof args.new_string === "string" ? args.new_string : "";
|
|
2632
|
-
if (!planContent || !oldString || !planContent.includes(oldString)) {
|
|
2633
|
-
return;
|
|
2634
|
-
}
|
|
2635
|
-
planContent = planContent.replace(oldString, newString);
|
|
2636
|
-
};
|
|
2637
|
-
const maybeParsePlanStatus = (msg) => {
|
|
2638
|
-
if (msg.kind !== "plan_status") return;
|
|
2639
|
-
if (typeof msg.content !== "string" || !msg.content.trim()) return;
|
|
2640
|
-
try {
|
|
2641
|
-
const data = JSON.parse(msg.content);
|
|
2642
|
-
setPlanContent(data.plan_yaml, 3);
|
|
2643
|
-
if (data.statuses && typeof data.statuses === "object" && !Array.isArray(data.statuses)) {
|
|
2644
|
-
latestStatuses = Object.fromEntries(
|
|
2645
|
-
Object.entries(data.statuses).filter((entry) => typeof entry[0] === "string" && typeof entry[1] === "string").map(([stepId, status]) => [stepId, normalizePlanStatus(status)])
|
|
2646
|
-
);
|
|
2647
|
-
}
|
|
2648
|
-
} catch {
|
|
2649
|
-
}
|
|
2650
|
-
};
|
|
2651
|
-
for (const msg of plannerMsgs) {
|
|
2652
|
-
maybeParsePlanStatus(msg);
|
|
2653
|
-
if (!msg.tool_calls) continue;
|
|
2654
|
-
for (const tc of msg.tool_calls) {
|
|
2655
|
-
if ((tc.name === "search_skills" || tc.name === "SearchSkills") && resultAsString(tc.result)) {
|
|
2656
|
-
try {
|
|
2657
|
-
const parsed = JSON.parse(resultAsString(tc.result));
|
|
2658
|
-
const results = Array.isArray(parsed) ? parsed : Array.isArray(parsed?.results) ? parsed.results : [];
|
|
2659
|
-
for (const r3 of results) {
|
|
2660
|
-
const skillId = getSkillId(r3);
|
|
2661
|
-
if (!skillId || searchMap.has(skillId)) continue;
|
|
2662
|
-
const skill = {
|
|
2663
|
-
skillId,
|
|
2664
|
-
displayName: getSkillDisplayName(r3),
|
|
2665
|
-
description: r3.description ?? ""
|
|
2666
|
-
};
|
|
2667
|
-
searchMap.set(skillId, skill);
|
|
2668
|
-
searchResults.push(skill);
|
|
2669
|
-
}
|
|
2670
|
-
} catch {
|
|
2671
|
-
}
|
|
2672
|
-
}
|
|
2673
|
-
if ((tc.name === "get_skill_content" || tc.name === "GetSkillContent") && resultAsString(tc.result)) {
|
|
2674
|
-
try {
|
|
2675
|
-
const data = JSON.parse(resultAsString(tc.result));
|
|
2676
|
-
const skillId = getSkillId(data);
|
|
2677
|
-
if (!skillId || selectedSet.has(skillId)) continue;
|
|
2678
|
-
let existing = searchMap.get(skillId);
|
|
2679
|
-
if (!existing) {
|
|
2680
|
-
existing = {
|
|
2681
|
-
skillId,
|
|
2682
|
-
displayName: getSkillDisplayName(data),
|
|
2683
|
-
description: data.description ?? ""
|
|
2684
|
-
};
|
|
2685
|
-
searchMap.set(skillId, existing);
|
|
2686
|
-
searchResults.push(existing);
|
|
2687
|
-
}
|
|
2688
|
-
existing.displayName ??= getSkillDisplayName(data);
|
|
2689
|
-
existing.content = data.content;
|
|
2690
|
-
existing.references = extractRefs(data.content ?? "");
|
|
2691
|
-
selectedSkills.push(existing);
|
|
2692
|
-
selectedSet.add(skillId);
|
|
2693
|
-
} catch {
|
|
2694
|
-
}
|
|
2695
|
-
}
|
|
2696
|
-
if (tc.name === "AskUserQuestion" && resultAsString(tc.result)) {
|
|
2697
|
-
const data = parseAskUserQuestion(resultAsString(tc.result));
|
|
2698
|
-
if (data) {
|
|
2699
|
-
askQuestions.push({ toolCallId: tc.id, data });
|
|
2700
|
-
}
|
|
2701
|
-
}
|
|
2702
|
-
if (READ_FILE_TOOLS.has(tc.name) && tc.arguments) {
|
|
2703
|
-
try {
|
|
2704
|
-
const args = JSON.parse(tc.arguments);
|
|
2705
|
-
if (typeof args.file_path === "string" && args.file_path) {
|
|
2706
|
-
readFiles.push({ path: args.file_path, status: tc.status ?? "done" });
|
|
2707
|
-
}
|
|
2708
|
-
} catch {
|
|
2709
|
-
}
|
|
2710
|
-
}
|
|
2711
|
-
if (PLAN_WRITE_TOOLS.has(tc.name) && tc.arguments && tc.status === "done" && !(typeof tc.result === "string" && tc.result.startsWith("\u9519\u8BEF"))) {
|
|
2712
|
-
try {
|
|
2713
|
-
const args = JSON.parse(tc.arguments);
|
|
2714
|
-
if (typeof args.file_path === "string" && (args.file_path === "PLAN.yaml" || args.file_path.endsWith("/PLAN.yaml")) && typeof args.content === "string") {
|
|
2715
|
-
setPlanContent(args.content, 1);
|
|
2716
|
-
}
|
|
2717
|
-
} catch {
|
|
2718
|
-
}
|
|
2719
|
-
}
|
|
2720
|
-
if (PLAN_EDIT_TOOLS.has(tc.name) && tc.arguments && tc.status === "done" && !(typeof tc.result === "string" && tc.result.startsWith("\u9519\u8BEF"))) {
|
|
2721
|
-
try {
|
|
2722
|
-
const args = JSON.parse(tc.arguments);
|
|
2723
|
-
if (typeof args.file_path === "string" && (args.file_path === "PLAN.yaml" || args.file_path.endsWith("/PLAN.yaml"))) {
|
|
2724
|
-
applyPlanEdit(args);
|
|
2725
|
-
}
|
|
2726
|
-
} catch {
|
|
2727
|
-
}
|
|
2728
|
-
}
|
|
2729
|
-
if (PAUSE_TOOLS.has(tc.name) && tc.status === "done") {
|
|
2730
|
-
lastPauseTool = tc.name;
|
|
2731
|
-
}
|
|
2732
|
-
if (tc.name === "ExitPlanMode" && resultAsString(tc.result)) {
|
|
2733
|
-
try {
|
|
2734
|
-
const data = JSON.parse(resultAsString(tc.result));
|
|
2735
|
-
setPlanContent(data.plan, 2);
|
|
2736
|
-
} catch {
|
|
2737
|
-
}
|
|
2738
|
-
}
|
|
2739
|
-
}
|
|
2740
|
-
}
|
|
2741
|
-
const { plan: parsedPlan, error: parseError } = parsePlanTreeResult(planContent);
|
|
2742
|
-
const plan = parsedPlan == null ? null : {
|
|
2743
|
-
...parsedPlan,
|
|
2744
|
-
root: applyPlanStatusesById(parsedPlan.root, latestStatuses)
|
|
2745
|
-
};
|
|
2746
|
-
return {
|
|
2747
|
-
intent,
|
|
2748
|
-
searchResults,
|
|
2749
|
-
selectedSkills,
|
|
2750
|
-
plan,
|
|
2751
|
-
hasPlanContent: planContent.trim().length > 0,
|
|
2752
|
-
parseError,
|
|
2753
|
-
askQuestions,
|
|
2754
|
-
readFiles,
|
|
2755
|
-
lastPauseTool
|
|
2756
|
-
};
|
|
2757
|
-
}
|
|
2758
|
-
function normalizePlanStatus(value) {
|
|
2759
|
-
const normalized = value.trim().toLowerCase();
|
|
2760
|
-
return normalized === "running" || normalized === "done" || normalized === "failed" ? normalized : "pending";
|
|
2761
|
-
}
|
|
2762
|
-
function extractRefs(content) {
|
|
2763
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2764
|
-
const refs = [];
|
|
2765
|
-
for (const m of content.matchAll(/\[\[([^\]]+)\]\]/g)) {
|
|
2766
|
-
const ref = m[1].trim();
|
|
2767
|
-
if (ref && !seen.has(ref)) {
|
|
2768
|
-
seen.add(ref);
|
|
2769
|
-
refs.push(ref);
|
|
2770
|
-
}
|
|
2771
|
-
}
|
|
2772
|
-
return refs;
|
|
2773
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
AskUserQuestionBlock,
|
|
3
|
+
PlanSummaryCard,
|
|
4
|
+
extractLatestPlanMessages,
|
|
5
|
+
parsePlanMessages
|
|
6
|
+
} from "../../../chunk-X6MEYCU7.js";
|
|
7
|
+
import "../../../chunk-2UP7MG3J.js";
|
|
8
|
+
import "../../../chunk-4VWLTG5L.js";
|
|
9
|
+
import "../../../chunk-J3XVFPOV.js";
|
|
10
|
+
import "../../../chunk-OKQWPNE3.js";
|
|
11
|
+
import {
|
|
12
|
+
cn
|
|
13
|
+
} from "../../../chunk-7LEKQI47.js";
|
|
14
|
+
import "../../../chunk-JCJFFJ42.js";
|
|
15
|
+
import "../../../chunk-PZ5AY32C.js";
|
|
2774
16
|
|
|
2775
|
-
// src/react/components/plan/
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
onConfirmPlan
|
|
2781
|
-
}) {
|
|
2782
|
-
const data = useMemo4(() => parsePlanMessages(messages), [messages]);
|
|
2783
|
-
const rightPanelCollapsed = useUiStore((state) => state.rightPanelCollapsed);
|
|
2784
|
-
const toggleRightPanel = useUiStore((state) => state.toggleRightPanel);
|
|
2785
|
-
const setActiveRightTab = useUiStore((state) => state.setActiveRightTab);
|
|
2786
|
-
const summaryState = useMemo4(() => getSummaryState(data, sessionStatus), [data, sessionStatus]);
|
|
2787
|
-
const stepCount = useMemo4(() => countPlanSteps(data.plan), [data.plan]);
|
|
2788
|
-
const isWaitingForInput = sessionStatus === "waiting_for_input";
|
|
2789
|
-
const canConfirm = Boolean(onConfirmPlan) && isWaitingForInput && data.lastPauseTool === "ExitPlanMode";
|
|
2790
|
-
const openPlanPanel = () => {
|
|
2791
|
-
if (rightPanelCollapsed) {
|
|
2792
|
-
toggleRightPanel();
|
|
2793
|
-
}
|
|
2794
|
-
setActiveRightTab("situation");
|
|
2795
|
-
};
|
|
2796
|
-
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: [
|
|
2797
|
-
/* @__PURE__ */ jsxs5(
|
|
2798
|
-
"button",
|
|
2799
|
-
{
|
|
2800
|
-
type: "button",
|
|
2801
|
-
onClick: openPlanPanel,
|
|
2802
|
-
className: "flex w-full flex-col gap-4 text-left transition-colors hover:text-[hsl(var(--foreground))]",
|
|
2803
|
-
children: [
|
|
2804
|
-
/* @__PURE__ */ jsxs5("div", { className: "flex items-start justify-between gap-3", children: [
|
|
2805
|
-
/* @__PURE__ */ jsxs5("div", { className: "min-w-0 space-y-2", children: [
|
|
2806
|
-
/* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2 text-xs text-[hsl(var(--muted-foreground))]", children: [
|
|
2807
|
-
/* @__PURE__ */ jsx6(Sparkles, { size: 12 }),
|
|
2808
|
-
/* @__PURE__ */ jsx6("span", { children: "\u89C4\u5212\u6458\u8981" })
|
|
2809
|
-
] }),
|
|
2810
|
-
/* @__PURE__ */ jsx6("div", { className: "truncate text-base font-medium text-[hsl(var(--foreground))]", children: data.intent || "\u5F53\u524D\u89C4\u5212" }),
|
|
2811
|
-
/* @__PURE__ */ jsx6("div", { className: "flex flex-wrap items-center gap-2 text-xs", children: summaryState.kind === "progress" ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
2812
|
-
/* @__PURE__ */ jsx6("span", { className: "rounded-full bg-[hsl(var(--accent))] px-2.5 py-1 text-[hsl(var(--foreground))]", children: summaryState.label }),
|
|
2813
|
-
/* @__PURE__ */ jsx6("span", { className: "font-mono tracking-[0.25em] text-[hsl(var(--muted-foreground))]", children: summaryState.dots })
|
|
2814
|
-
] }) : /* @__PURE__ */ jsx6(
|
|
2815
|
-
"span",
|
|
2816
|
-
{
|
|
2817
|
-
className: cn(
|
|
2818
|
-
"rounded-full px-2.5 py-1",
|
|
2819
|
-
summaryState.kind === "complete" && "bg-[hsl(var(--primary)/0.12)] text-[hsl(var(--primary))]",
|
|
2820
|
-
summaryState.kind === "failed" && "bg-[hsl(var(--destructive)/0.12)] text-[hsl(var(--destructive))]",
|
|
2821
|
-
summaryState.kind === "interrupted" && "bg-orange-500/12 text-orange-300"
|
|
2822
|
-
),
|
|
2823
|
-
children: summaryState.label
|
|
2824
|
-
}
|
|
2825
|
-
) })
|
|
2826
|
-
] }),
|
|
2827
|
-
/* @__PURE__ */ jsx6(
|
|
2828
|
-
ChevronRight,
|
|
2829
|
-
{
|
|
2830
|
-
size: 16,
|
|
2831
|
-
className: "mt-1 shrink-0 text-[hsl(var(--muted-foreground))]",
|
|
2832
|
-
"aria-hidden": "true"
|
|
2833
|
-
}
|
|
2834
|
-
)
|
|
2835
|
-
] }),
|
|
2836
|
-
summaryState.kind === "complete" && /* @__PURE__ */ jsxs5("div", { className: "flex flex-wrap items-center gap-2 text-xs text-[hsl(var(--muted-foreground))]", children: [
|
|
2837
|
-
/* @__PURE__ */ jsxs5("span", { className: "rounded-full border border-[hsl(var(--border))] px-2.5 py-1", children: [
|
|
2838
|
-
data.selectedSkills.length,
|
|
2839
|
-
" \u4E2A\u6280\u80FD"
|
|
2840
|
-
] }),
|
|
2841
|
-
/* @__PURE__ */ jsxs5("span", { className: "rounded-full border border-[hsl(var(--border))] px-2.5 py-1", children: [
|
|
2842
|
-
stepCount,
|
|
2843
|
-
" \u4E2A\u6B65\u9AA4"
|
|
2844
|
-
] }),
|
|
2845
|
-
/* @__PURE__ */ jsxs5("span", { className: "inline-flex items-center gap-1 text-[hsl(var(--muted-foreground))]", children: [
|
|
2846
|
-
/* @__PURE__ */ jsx6(CheckCircle2, { size: 12 }),
|
|
2847
|
-
"\u53F3\u4FA7\u67E5\u770B\u8BE6\u60C5"
|
|
2848
|
-
] })
|
|
2849
|
-
] })
|
|
2850
|
-
]
|
|
2851
|
-
}
|
|
2852
|
-
),
|
|
2853
|
-
canConfirm && /* @__PURE__ */ jsx6(PlanConfirmationPanel, { onConfirmPlan })
|
|
2854
|
-
] });
|
|
2855
|
-
}
|
|
2856
|
-
function PlanConfirmationPanel({
|
|
2857
|
-
onConfirmPlan
|
|
2858
|
-
}) {
|
|
2859
|
-
const [isEditing, setIsEditing] = useState4(false);
|
|
2860
|
-
const [revisionText, setRevisionText] = useState4("");
|
|
2861
|
-
const submitRevision = () => {
|
|
2862
|
-
const trimmed = revisionText.trim();
|
|
2863
|
-
if (!trimmed) return;
|
|
2864
|
-
onConfirmPlan?.("revise", trimmed);
|
|
2865
|
-
setRevisionText("");
|
|
2866
|
-
setIsEditing(false);
|
|
2867
|
-
};
|
|
2868
|
-
return /* @__PURE__ */ jsxs5("div", { className: "space-y-3 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--background)/0.45)] p-3", children: [
|
|
2869
|
-
/* @__PURE__ */ jsxs5("div", { className: "flex flex-wrap gap-2", children: [
|
|
2870
|
-
/* @__PURE__ */ jsxs5(
|
|
2871
|
-
"button",
|
|
2872
|
-
{
|
|
2873
|
-
type: "button",
|
|
2874
|
-
onClick: () => onConfirmPlan?.("execute"),
|
|
2875
|
-
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",
|
|
2876
|
-
children: [
|
|
2877
|
-
/* @__PURE__ */ jsx6(Play, { size: 14 }),
|
|
2878
|
-
"\u5F00\u59CB\u6267\u884C"
|
|
2879
|
-
]
|
|
2880
|
-
}
|
|
2881
|
-
),
|
|
2882
|
-
/* @__PURE__ */ jsxs5(
|
|
2883
|
-
"button",
|
|
2884
|
-
{
|
|
2885
|
-
type: "button",
|
|
2886
|
-
onClick: () => setIsEditing((value) => !value),
|
|
2887
|
-
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))]",
|
|
2888
|
-
children: [
|
|
2889
|
-
/* @__PURE__ */ jsx6(PencilLine, { size: 14 }),
|
|
2890
|
-
"\u4FEE\u6539"
|
|
2891
|
-
]
|
|
2892
|
-
}
|
|
2893
|
-
)
|
|
2894
|
-
] }),
|
|
2895
|
-
isEditing && /* @__PURE__ */ jsxs5("div", { className: "space-y-2", children: [
|
|
2896
|
-
/* @__PURE__ */ jsx6(
|
|
2897
|
-
"textarea",
|
|
2898
|
-
{
|
|
2899
|
-
value: revisionText,
|
|
2900
|
-
onChange: (event) => setRevisionText(event.target.value),
|
|
2901
|
-
placeholder: "\u8F93\u5165\u4FEE\u6539\u5EFA\u8BAE...",
|
|
2902
|
-
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))]"
|
|
2903
|
-
}
|
|
2904
|
-
),
|
|
2905
|
-
/* @__PURE__ */ jsx6("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx6(
|
|
2906
|
-
"button",
|
|
2907
|
-
{
|
|
2908
|
-
type: "button",
|
|
2909
|
-
disabled: !revisionText.trim(),
|
|
2910
|
-
onClick: submitRevision,
|
|
2911
|
-
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",
|
|
2912
|
-
children: "\u63D0\u4EA4\u4FEE\u6539\u610F\u89C1"
|
|
2913
|
-
}
|
|
2914
|
-
) })
|
|
2915
|
-
] })
|
|
2916
|
-
] });
|
|
2917
|
-
}
|
|
2918
|
-
function getSummaryState(data, sessionStatus) {
|
|
2919
|
-
if (sessionStatus === "failed") {
|
|
2920
|
-
return { kind: "failed", label: "\u51FA\u9519" };
|
|
2921
|
-
}
|
|
2922
|
-
if (sessionStatus === "interrupted") {
|
|
2923
|
-
return { kind: "interrupted", label: "\u5DF2\u4E2D\u65AD" };
|
|
2924
|
-
}
|
|
2925
|
-
if (data.parseError) {
|
|
2926
|
-
return { kind: "failed", label: "\u89E3\u6790\u5931\u8D25" };
|
|
2927
|
-
}
|
|
2928
|
-
if (data.hasPlanContent || data.plan != null) {
|
|
2929
|
-
return { kind: "complete", label: "\u5DF2\u751F\u6210" };
|
|
2930
|
-
}
|
|
2931
|
-
if (data.selectedSkills.length > 0) {
|
|
2932
|
-
return { kind: "progress", label: "\u6280\u80FD\u5206\u6790\u4E2D", dots: "\u25CF\u25CF\u25CB" };
|
|
2933
|
-
}
|
|
2934
|
-
if (data.searchResults.length > 0) {
|
|
2935
|
-
return { kind: "progress", label: "\u6280\u80FD\u53D1\u73B0\u4E2D", dots: "\u25CF\u25CB\u25CB" };
|
|
2936
|
-
}
|
|
2937
|
-
return { kind: "progress", label: "\u89C4\u5212\u51C6\u5907\u4E2D", dots: "\u25CB\u25CB\u25CB" };
|
|
17
|
+
// src/react/components/plan/debug-log.ts
|
|
18
|
+
var events = [];
|
|
19
|
+
var verbose = typeof window !== "undefined" && (window.location?.hostname === "localhost" || window.location?.hostname === "127.0.0.1");
|
|
20
|
+
function exportPlanLog() {
|
|
21
|
+
return [...events];
|
|
2938
22
|
}
|
|
2939
|
-
function
|
|
2940
|
-
|
|
2941
|
-
const
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
}
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
return children.reduce((total, child) => total + countNode(child), 0);
|
|
2948
|
-
}
|
|
2949
|
-
return countNode(plan.root);
|
|
23
|
+
function downloadPlanLog() {
|
|
24
|
+
const blob = new Blob([JSON.stringify(events, null, 2)], { type: "application/json" });
|
|
25
|
+
const url = URL.createObjectURL(blob);
|
|
26
|
+
const a = document.createElement("a");
|
|
27
|
+
a.href = url;
|
|
28
|
+
a.download = `plan-timeline-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace(/:/g, "-")}.json`;
|
|
29
|
+
a.click();
|
|
30
|
+
URL.revokeObjectURL(url);
|
|
2950
31
|
}
|
|
2951
32
|
|
|
2952
33
|
// src/react/components/plan/PlanVisualization.tsx
|
|
2953
34
|
import { BookOpen as BookOpen2, GitBranch as GitBranch2, Search as Search2 } from "lucide-react";
|
|
2954
|
-
import { useMemo as
|
|
35
|
+
import { useMemo as useMemo3, useRef, useState as useState2 } from "react";
|
|
2955
36
|
|
|
2956
37
|
// src/react/components/plan/phases/PlanTree.tsx
|
|
2957
38
|
import {
|
|
2958
39
|
CheckSquare,
|
|
2959
|
-
ChevronRight as
|
|
40
|
+
ChevronRight as ChevronRight2,
|
|
2960
41
|
GitBranch,
|
|
2961
42
|
List,
|
|
2962
43
|
Workflow as Workflow2
|
|
2963
44
|
} from "lucide-react";
|
|
2964
|
-
import { useCallback, useMemo
|
|
45
|
+
import { useCallback, useMemo, useState } from "react";
|
|
2965
46
|
|
|
2966
47
|
// src/react/components/plan/phases/PlanMindMap.tsx
|
|
2967
|
-
import { ChevronRight
|
|
48
|
+
import { ChevronRight, Workflow } from "lucide-react";
|
|
2968
49
|
|
|
2969
50
|
// src/react/components/plan/phases/PlanStatusIcon.tsx
|
|
2970
|
-
import { CheckCircle2
|
|
2971
|
-
import { jsx
|
|
51
|
+
import { CheckCircle2, Circle, Loader2, XCircle } from "lucide-react";
|
|
52
|
+
import { jsx } from "react/jsx-runtime";
|
|
2972
53
|
function PlanStatusIcon({
|
|
2973
54
|
status,
|
|
2974
55
|
size = 16
|
|
2975
56
|
}) {
|
|
2976
57
|
if (status === "pending") {
|
|
2977
|
-
return /* @__PURE__ */
|
|
58
|
+
return /* @__PURE__ */ jsx(
|
|
2978
59
|
Circle,
|
|
2979
60
|
{
|
|
2980
61
|
size,
|
|
@@ -2983,8 +64,8 @@ function PlanStatusIcon({
|
|
|
2983
64
|
);
|
|
2984
65
|
}
|
|
2985
66
|
if (status === "running") {
|
|
2986
|
-
return /* @__PURE__ */
|
|
2987
|
-
|
|
67
|
+
return /* @__PURE__ */ jsx(
|
|
68
|
+
Loader2,
|
|
2988
69
|
{
|
|
2989
70
|
size,
|
|
2990
71
|
className: "shrink-0 animate-spin text-[hsl(var(--primary))] transition-transform duration-300"
|
|
@@ -2992,15 +73,15 @@ function PlanStatusIcon({
|
|
|
2992
73
|
);
|
|
2993
74
|
}
|
|
2994
75
|
if (status === "done") {
|
|
2995
|
-
return /* @__PURE__ */
|
|
2996
|
-
|
|
76
|
+
return /* @__PURE__ */ jsx(
|
|
77
|
+
CheckCircle2,
|
|
2997
78
|
{
|
|
2998
79
|
size,
|
|
2999
80
|
className: "shrink-0 scale-105 text-green-500 transition-transform duration-300"
|
|
3000
81
|
}
|
|
3001
82
|
);
|
|
3002
83
|
}
|
|
3003
|
-
return /* @__PURE__ */
|
|
84
|
+
return /* @__PURE__ */ jsx(
|
|
3004
85
|
XCircle,
|
|
3005
86
|
{
|
|
3006
87
|
size,
|
|
@@ -3010,9 +91,9 @@ function PlanStatusIcon({
|
|
|
3010
91
|
}
|
|
3011
92
|
|
|
3012
93
|
// src/react/components/plan/phases/PlanMindMap.tsx
|
|
3013
|
-
import { Fragment
|
|
94
|
+
import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
3014
95
|
function PlanMindMap({ root, expandedIds, onToggle }) {
|
|
3015
|
-
return /* @__PURE__ */
|
|
96
|
+
return /* @__PURE__ */ jsx2("div", { className: "overflow-x-auto py-2", children: /* @__PURE__ */ jsx2(MindMapSubtree, { node: root, expandedIds, onToggle, isRoot: true }) });
|
|
3016
97
|
}
|
|
3017
98
|
function MindMapSubtree({
|
|
3018
99
|
node,
|
|
@@ -3024,8 +105,8 @@ function MindMapSubtree({
|
|
|
3024
105
|
const hasChildren = children.length > 0;
|
|
3025
106
|
const isExpanded = expandedIds.has(node.id);
|
|
3026
107
|
const visibleChildren = hasChildren && isExpanded ? children : [];
|
|
3027
|
-
return /* @__PURE__ */
|
|
3028
|
-
/* @__PURE__ */
|
|
108
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
|
|
109
|
+
/* @__PURE__ */ jsx2(
|
|
3029
110
|
"div",
|
|
3030
111
|
{
|
|
3031
112
|
role: hasChildren ? "button" : void 0,
|
|
@@ -3043,12 +124,12 @@ function MindMapSubtree({
|
|
|
3043
124
|
hasChildren && "cursor-pointer",
|
|
3044
125
|
isRoot ? "border-[hsl(var(--primary))] bg-[hsl(var(--primary)/0.15)] text-[hsl(var(--primary))]" : node.skillRef ? "border-[hsl(var(--primary)/0.4)] bg-[hsl(var(--primary)/0.08)] text-[hsl(var(--foreground))]" : "border-[hsl(var(--border))] bg-[hsl(var(--card))] text-[hsl(var(--foreground))]"
|
|
3045
126
|
),
|
|
3046
|
-
children: /* @__PURE__ */
|
|
3047
|
-
/* @__PURE__ */
|
|
3048
|
-
node.skillRef && /* @__PURE__ */
|
|
127
|
+
children: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5", children: [
|
|
128
|
+
/* @__PURE__ */ jsx2(PlanStatusIcon, { status: node.status, size: 14 }),
|
|
129
|
+
node.skillRef && /* @__PURE__ */ jsx2(Workflow, { size: 12, className: "text-[hsl(var(--primary))]" }),
|
|
3049
130
|
node.label,
|
|
3050
|
-
hasChildren && /* @__PURE__ */
|
|
3051
|
-
|
|
131
|
+
hasChildren && /* @__PURE__ */ jsx2(
|
|
132
|
+
ChevronRight,
|
|
3052
133
|
{
|
|
3053
134
|
size: 14,
|
|
3054
135
|
className: cn(
|
|
@@ -3057,28 +138,28 @@ function MindMapSubtree({
|
|
|
3057
138
|
)
|
|
3058
139
|
}
|
|
3059
140
|
),
|
|
3060
|
-
hasChildren && !isExpanded && /* @__PURE__ */
|
|
141
|
+
hasChildren && !isExpanded && /* @__PURE__ */ jsxs("span", { className: "ml-0.5 rounded-full bg-[hsl(var(--muted))] px-1.5 py-px text-[10px] text-[hsl(var(--muted-foreground))]", children: [
|
|
3061
142
|
"+",
|
|
3062
143
|
children.length
|
|
3063
144
|
] })
|
|
3064
145
|
] })
|
|
3065
146
|
}
|
|
3066
147
|
),
|
|
3067
|
-
visibleChildren.length > 0 && /* @__PURE__ */
|
|
3068
|
-
/* @__PURE__ */
|
|
3069
|
-
/* @__PURE__ */
|
|
3070
|
-
/* @__PURE__ */
|
|
3071
|
-
/* @__PURE__ */
|
|
148
|
+
visibleChildren.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
149
|
+
/* @__PURE__ */ jsx2("div", { className: "h-px w-6 shrink-0 bg-[hsl(var(--border))]" }),
|
|
150
|
+
/* @__PURE__ */ jsx2(VerticalRail, { count: visibleChildren.length, children: visibleChildren.map((child) => /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
|
|
151
|
+
/* @__PURE__ */ jsx2("div", { className: "h-px w-4 shrink-0 bg-[hsl(var(--border))]" }),
|
|
152
|
+
/* @__PURE__ */ jsx2(MindMapSubtree, { node: child, expandedIds, onToggle })
|
|
3072
153
|
] }, child.id)) })
|
|
3073
154
|
] })
|
|
3074
155
|
] });
|
|
3075
156
|
}
|
|
3076
157
|
function VerticalRail({ count, children }) {
|
|
3077
158
|
if (count <= 1) {
|
|
3078
|
-
return /* @__PURE__ */
|
|
159
|
+
return /* @__PURE__ */ jsx2("div", { className: "flex flex-col", children });
|
|
3079
160
|
}
|
|
3080
|
-
return /* @__PURE__ */
|
|
3081
|
-
/* @__PURE__ */
|
|
161
|
+
return /* @__PURE__ */ jsx2("div", { className: "flex flex-col", children: children.map((child, i) => /* @__PURE__ */ jsxs("div", { className: cn("relative", i > 0 && "pt-1.5"), children: [
|
|
162
|
+
/* @__PURE__ */ jsx2(
|
|
3082
163
|
"div",
|
|
3083
164
|
{
|
|
3084
165
|
className: "absolute left-0 w-px bg-[hsl(var(--border))]",
|
|
@@ -3093,7 +174,7 @@ function VerticalRail({ count, children }) {
|
|
|
3093
174
|
}
|
|
3094
175
|
|
|
3095
176
|
// src/react/components/plan/phases/PlanTree.tsx
|
|
3096
|
-
import { jsx as
|
|
177
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
3097
178
|
function collectIdsToDepth(node, maxDepth, out) {
|
|
3098
179
|
if (node.depth > maxDepth) return;
|
|
3099
180
|
out.add(node.id);
|
|
@@ -3105,16 +186,16 @@ function treeFinger(node) {
|
|
|
3105
186
|
return `${node.label}[${children.map(treeFinger).join(",")}]`;
|
|
3106
187
|
}
|
|
3107
188
|
function PlanTree({ root, notes, active }) {
|
|
3108
|
-
return /* @__PURE__ */
|
|
189
|
+
return /* @__PURE__ */ jsx3(PlanTreeContent, { root, notes: notes ?? [], active }, treeFinger(root));
|
|
3109
190
|
}
|
|
3110
191
|
function PlanTreeContent({ root, notes, active }) {
|
|
3111
|
-
const [viewMode, setViewMode] =
|
|
3112
|
-
const defaultExpanded =
|
|
192
|
+
const [viewMode, setViewMode] = useState("mindmap");
|
|
193
|
+
const defaultExpanded = useMemo(() => {
|
|
3113
194
|
const ids = /* @__PURE__ */ new Set();
|
|
3114
195
|
collectIdsToDepth(root, 1, ids);
|
|
3115
196
|
return ids;
|
|
3116
197
|
}, [root]);
|
|
3117
|
-
const [expandedIds, setExpandedIds] =
|
|
198
|
+
const [expandedIds, setExpandedIds] = useState(defaultExpanded);
|
|
3118
199
|
const toggle = useCallback((id) => {
|
|
3119
200
|
setExpandedIds((prev) => {
|
|
3120
201
|
const next = new Set(prev);
|
|
@@ -3123,9 +204,9 @@ function PlanTreeContent({ root, notes, active }) {
|
|
|
3123
204
|
return next;
|
|
3124
205
|
});
|
|
3125
206
|
}, []);
|
|
3126
|
-
return /* @__PURE__ */
|
|
3127
|
-
/* @__PURE__ */
|
|
3128
|
-
/* @__PURE__ */
|
|
207
|
+
return /* @__PURE__ */ jsx3("div", { className: cn(!active && "pointer-events-none opacity-0"), children: /* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-3", children: [
|
|
208
|
+
/* @__PURE__ */ jsx3("div", { className: "flex items-center justify-end", children: /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-0.5 rounded-lg border border-[hsl(var(--border))] p-0.5", children: [
|
|
209
|
+
/* @__PURE__ */ jsx3(
|
|
3129
210
|
"button",
|
|
3130
211
|
{
|
|
3131
212
|
type: "button",
|
|
@@ -3135,10 +216,10 @@ function PlanTreeContent({ root, notes, active }) {
|
|
|
3135
216
|
viewMode === "mindmap" ? "bg-[hsl(var(--accent))] text-[hsl(var(--foreground))]" : "text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
|
|
3136
217
|
),
|
|
3137
218
|
title: "\u601D\u7EF4\u5BFC\u56FE",
|
|
3138
|
-
children: /* @__PURE__ */
|
|
219
|
+
children: /* @__PURE__ */ jsx3(GitBranch, { size: 14 })
|
|
3139
220
|
}
|
|
3140
221
|
),
|
|
3141
|
-
/* @__PURE__ */
|
|
222
|
+
/* @__PURE__ */ jsx3(
|
|
3142
223
|
"button",
|
|
3143
224
|
{
|
|
3144
225
|
type: "button",
|
|
@@ -3148,13 +229,13 @@ function PlanTreeContent({ root, notes, active }) {
|
|
|
3148
229
|
viewMode === "tree" ? "bg-[hsl(var(--accent))] text-[hsl(var(--foreground))]" : "text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
|
|
3149
230
|
),
|
|
3150
231
|
title: "\u5217\u8868\u89C6\u56FE",
|
|
3151
|
-
children: /* @__PURE__ */
|
|
232
|
+
children: /* @__PURE__ */ jsx3(List, { size: 14 })
|
|
3152
233
|
}
|
|
3153
234
|
)
|
|
3154
235
|
] }) }),
|
|
3155
|
-
/* @__PURE__ */
|
|
3156
|
-
notes.length > 0 && /* @__PURE__ */
|
|
3157
|
-
/* @__PURE__ */
|
|
236
|
+
/* @__PURE__ */ jsx3("div", { className: "rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card)/0.5)] px-3 py-3", children: viewMode === "mindmap" ? /* @__PURE__ */ jsx3(PlanMindMap, { root, expandedIds, onToggle: toggle }) : /* @__PURE__ */ jsx3(TreeNode, { node: root }) }),
|
|
237
|
+
notes.length > 0 && /* @__PURE__ */ jsx3("div", { className: "rounded-xl border border-[hsl(var(--border)/0.5)] bg-[hsl(var(--card)/0.5)] px-4 py-3 text-sm text-[hsl(var(--muted-foreground))]", children: notes.map((note, i) => /* @__PURE__ */ jsxs2("p", { className: "py-0.5", children: [
|
|
238
|
+
/* @__PURE__ */ jsxs2("span", { className: "mr-1.5 text-[hsl(var(--primary)/0.6)]", children: [
|
|
3158
239
|
i + 1,
|
|
3159
240
|
"."
|
|
3160
241
|
] }),
|
|
@@ -3165,43 +246,43 @@ function PlanTreeContent({ root, notes, active }) {
|
|
|
3165
246
|
function TreeNode({ node }) {
|
|
3166
247
|
const children = node.children ?? [];
|
|
3167
248
|
const hasChildren = children.length > 0;
|
|
3168
|
-
return /* @__PURE__ */
|
|
3169
|
-
/* @__PURE__ */
|
|
249
|
+
return /* @__PURE__ */ jsxs2("div", { children: [
|
|
250
|
+
/* @__PURE__ */ jsxs2(
|
|
3170
251
|
"div",
|
|
3171
252
|
{
|
|
3172
253
|
className: "group flex min-w-0 items-center gap-2 rounded-md px-2 py-2 hover:bg-[hsl(var(--accent)/0.5)]",
|
|
3173
254
|
style: { paddingLeft: `${node.depth * 24 + 10}px` },
|
|
3174
255
|
children: [
|
|
3175
|
-
hasChildren ? /* @__PURE__ */
|
|
3176
|
-
|
|
256
|
+
hasChildren ? /* @__PURE__ */ jsx3(
|
|
257
|
+
ChevronRight2,
|
|
3177
258
|
{
|
|
3178
259
|
size: 16,
|
|
3179
260
|
className: "shrink-0 rotate-90 text-[hsl(var(--muted-foreground)/0.6)]"
|
|
3180
261
|
}
|
|
3181
|
-
) : /* @__PURE__ */
|
|
3182
|
-
/* @__PURE__ */
|
|
3183
|
-
node.skillRef ? /* @__PURE__ */
|
|
3184
|
-
/* @__PURE__ */
|
|
3185
|
-
node.skillRef && /* @__PURE__ */
|
|
262
|
+
) : /* @__PURE__ */ jsx3("span", { className: "w-4 shrink-0" }),
|
|
263
|
+
/* @__PURE__ */ jsx3(PlanStatusIcon, { status: node.status }),
|
|
264
|
+
node.skillRef ? /* @__PURE__ */ jsx3(Workflow2, { size: 15, className: "shrink-0 text-[hsl(var(--primary))]" }) : /* @__PURE__ */ jsx3(CheckSquare, { size: 15, className: "shrink-0 text-[hsl(var(--muted-foreground))]" }),
|
|
265
|
+
/* @__PURE__ */ jsx3("span", { className: "min-w-0 flex-1 truncate text-[15px] font-medium text-[hsl(var(--foreground))]", children: node.label }),
|
|
266
|
+
node.skillRef && /* @__PURE__ */ jsx3("span", { className: "ml-auto max-w-[160px] shrink-0 truncate rounded bg-[hsl(var(--primary)/0.1)] px-2 py-0.5 text-sm font-medium text-[hsl(var(--primary)/0.8)]", children: node.skillRef })
|
|
3186
267
|
]
|
|
3187
268
|
}
|
|
3188
269
|
),
|
|
3189
|
-
hasChildren && /* @__PURE__ */
|
|
3190
|
-
/* @__PURE__ */
|
|
270
|
+
hasChildren && /* @__PURE__ */ jsxs2("div", { className: "relative", children: [
|
|
271
|
+
/* @__PURE__ */ jsx3(
|
|
3191
272
|
"div",
|
|
3192
273
|
{
|
|
3193
274
|
className: "absolute bottom-1 top-0 w-px bg-[hsl(var(--border)/0.4)]",
|
|
3194
275
|
style: { left: `${node.depth * 24 + 18}px` }
|
|
3195
276
|
}
|
|
3196
277
|
),
|
|
3197
|
-
children.map((child) => /* @__PURE__ */
|
|
278
|
+
children.map((child) => /* @__PURE__ */ jsx3(TreeNode, { node: child }, child.id))
|
|
3198
279
|
] })
|
|
3199
280
|
] });
|
|
3200
281
|
}
|
|
3201
282
|
|
|
3202
283
|
// src/react/components/plan/phases/SkillAnalysis.tsx
|
|
3203
284
|
import { BookOpen, Link2 } from "lucide-react";
|
|
3204
|
-
import { jsx as
|
|
285
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
3205
286
|
function planSkillDisplayName(skill) {
|
|
3206
287
|
return skill.displayName?.trim() || skill.skillId;
|
|
3207
288
|
}
|
|
@@ -3210,38 +291,38 @@ function extractSteps(content) {
|
|
|
3210
291
|
}
|
|
3211
292
|
function SkillAnalysis({ skills: rawSkills, active }) {
|
|
3212
293
|
const skills = Array.isArray(rawSkills) ? rawSkills : [];
|
|
3213
|
-
return /* @__PURE__ */
|
|
294
|
+
return /* @__PURE__ */ jsx4(
|
|
3214
295
|
"div",
|
|
3215
296
|
{
|
|
3216
297
|
className: cn(
|
|
3217
298
|
"transition-opacity duration-500",
|
|
3218
299
|
active ? "opacity-100" : "pointer-events-none opacity-0"
|
|
3219
300
|
),
|
|
3220
|
-
children: /* @__PURE__ */
|
|
301
|
+
children: /* @__PURE__ */ jsx4("div", { className: "columns-2 gap-4", children: skills.map((skill) => {
|
|
3221
302
|
const steps = skill.content ? extractSteps(skill.content) : [];
|
|
3222
|
-
return /* @__PURE__ */
|
|
303
|
+
return /* @__PURE__ */ jsxs3(
|
|
3223
304
|
"div",
|
|
3224
305
|
{
|
|
3225
306
|
className: "mb-4 break-inside-avoid overflow-hidden rounded-xl border border-[hsl(var(--primary)/0.4)] bg-[hsl(var(--card))] shadow-lg shadow-[hsl(var(--primary)/0.05)] transition-all duration-500",
|
|
3226
307
|
children: [
|
|
3227
|
-
/* @__PURE__ */
|
|
3228
|
-
/* @__PURE__ */
|
|
3229
|
-
/* @__PURE__ */
|
|
308
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 px-4 py-3", children: [
|
|
309
|
+
/* @__PURE__ */ jsx4(BookOpen, { size: 15, className: "shrink-0 text-[hsl(var(--primary))]" }),
|
|
310
|
+
/* @__PURE__ */ jsx4("span", { className: "min-w-0 truncate text-[15px] font-semibold text-[hsl(var(--foreground))]", children: planSkillDisplayName(skill) })
|
|
3230
311
|
] }),
|
|
3231
|
-
/* @__PURE__ */
|
|
3232
|
-
/* @__PURE__ */
|
|
3233
|
-
skill.references && skill.references.length > 0 && /* @__PURE__ */
|
|
312
|
+
/* @__PURE__ */ jsx4("div", { className: "border-t border-[hsl(var(--border)/0.5)] px-4 py-2.5", children: /* @__PURE__ */ jsx4("p", { className: "text-sm leading-relaxed text-[hsl(var(--muted-foreground))]", children: skill.description }) }),
|
|
313
|
+
/* @__PURE__ */ jsxs3("div", { className: "border-t border-[hsl(var(--border)/0.5)] px-4 py-2.5", children: [
|
|
314
|
+
skill.references && skill.references.length > 0 && /* @__PURE__ */ jsx4("div", { className: "mb-2 flex flex-wrap gap-1.5", children: skill.references.map((ref, refIdx) => /* @__PURE__ */ jsxs3(
|
|
3234
315
|
"span",
|
|
3235
316
|
{
|
|
3236
317
|
className: "inline-flex items-center gap-1 rounded-md bg-[hsl(var(--primary)/0.1)] px-2 py-0.5 text-xs text-[hsl(var(--primary))]",
|
|
3237
318
|
children: [
|
|
3238
|
-
/* @__PURE__ */
|
|
319
|
+
/* @__PURE__ */ jsx4(Link2, { size: 10 }),
|
|
3239
320
|
ref
|
|
3240
321
|
]
|
|
3241
322
|
},
|
|
3242
323
|
`${refIdx}-${ref}`
|
|
3243
324
|
)) }),
|
|
3244
|
-
steps.length > 0 && /* @__PURE__ */
|
|
325
|
+
steps.length > 0 && /* @__PURE__ */ jsx4("ol", { className: "list-inside list-decimal space-y-0.5 text-sm leading-relaxed text-[hsl(var(--muted-foreground)/0.8)]", children: steps.map((step, idx) => /* @__PURE__ */ jsx4("li", { children: step }, idx)) })
|
|
3245
326
|
] })
|
|
3246
327
|
]
|
|
3247
328
|
},
|
|
@@ -3253,9 +334,9 @@ function SkillAnalysis({ skills: rawSkills, active }) {
|
|
|
3253
334
|
}
|
|
3254
335
|
|
|
3255
336
|
// src/react/components/plan/phases/SkillDiscovery.tsx
|
|
3256
|
-
import { CheckCircle2 as
|
|
3257
|
-
import { useMemo as
|
|
3258
|
-
import { jsx as
|
|
337
|
+
import { CheckCircle2 as CheckCircle22, Search, Sparkles } from "lucide-react";
|
|
338
|
+
import { useMemo as useMemo2 } from "react";
|
|
339
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
3259
340
|
function sizeClass(name) {
|
|
3260
341
|
const hash = name.split("").reduce((acc, c) => acc + c.charCodeAt(0), 0);
|
|
3261
342
|
const sizes = ["text-xs", "text-sm", "text-[15px]"];
|
|
@@ -3274,12 +355,12 @@ function SkillDiscovery({
|
|
|
3274
355
|
const searchResults = Array.isArray(rawSearchResults) ? rawSearchResults : [];
|
|
3275
356
|
const selectedSkills = Array.isArray(rawSelectedSkills) ? rawSelectedSkills : [];
|
|
3276
357
|
const allSkillNames = Array.isArray(rawAllSkillNames) ? rawAllSkillNames : [];
|
|
3277
|
-
const searchNames =
|
|
3278
|
-
const selectedNames =
|
|
358
|
+
const searchNames = useMemo2(() => new Set(searchResults.map((s) => s.skillId)), [searchResults]);
|
|
359
|
+
const selectedNames = useMemo2(
|
|
3279
360
|
() => new Set(selectedSkills.map((s) => s.skillId)),
|
|
3280
361
|
[selectedSkills]
|
|
3281
362
|
);
|
|
3282
|
-
const cloudItems =
|
|
363
|
+
const cloudItems = useMemo2(() => {
|
|
3283
364
|
const bgItems = allSkillNames.filter((skill) => !searchNames.has(skill.skillId)).map((skill) => ({
|
|
3284
365
|
name: planSkillDisplayName2(skill),
|
|
3285
366
|
key: `bg:${skill.skillId}`,
|
|
@@ -3299,7 +380,7 @@ function SkillDiscovery({
|
|
|
3299
380
|
}
|
|
3300
381
|
return result;
|
|
3301
382
|
}, [allSkillNames, searchNames, searchResults, selectedNames]);
|
|
3302
|
-
return /* @__PURE__ */
|
|
383
|
+
return /* @__PURE__ */ jsxs4(
|
|
3303
384
|
"div",
|
|
3304
385
|
{
|
|
3305
386
|
className: cn(
|
|
@@ -3307,18 +388,18 @@ function SkillDiscovery({
|
|
|
3307
388
|
active ? "opacity-100" : "pointer-events-none opacity-0"
|
|
3308
389
|
),
|
|
3309
390
|
children: [
|
|
3310
|
-
/* @__PURE__ */
|
|
3311
|
-
/* @__PURE__ */
|
|
3312
|
-
/* @__PURE__ */
|
|
391
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex shrink-0 max-w-md self-center flex-col items-center gap-2.5 rounded-2xl border border-[hsl(var(--primary)/0.4)] bg-[hsl(var(--primary)/0.08)] px-6 py-5 text-center shadow-lg shadow-[hsl(var(--primary)/0.1)]", children: [
|
|
392
|
+
/* @__PURE__ */ jsx5(Sparkles, { size: 20, className: "text-[hsl(var(--primary))]" }),
|
|
393
|
+
/* @__PURE__ */ jsx5("p", { className: "break-words text-[15px] leading-relaxed text-[hsl(var(--foreground))]", children: intent })
|
|
3313
394
|
] }),
|
|
3314
|
-
/* @__PURE__ */
|
|
395
|
+
/* @__PURE__ */ jsx5("div", { className: "flex min-h-0 flex-1 flex-wrap content-start items-start justify-center gap-x-4 gap-y-3 overflow-y-auto px-6 pb-6", children: cloudItems.map((item) => {
|
|
3315
396
|
if (item.isSearch && item.isSelected) {
|
|
3316
|
-
return /* @__PURE__ */
|
|
397
|
+
return /* @__PURE__ */ jsxs4(
|
|
3317
398
|
"span",
|
|
3318
399
|
{
|
|
3319
400
|
className: "inline-flex items-center gap-1.5 rounded-lg border border-[hsl(var(--primary)/0.5)] bg-[hsl(var(--primary)/0.12)] px-3 py-1.5 text-[15px] font-medium text-[hsl(var(--primary))] shadow-md shadow-[hsl(var(--primary)/0.15)] transition-all duration-500",
|
|
3320
401
|
children: [
|
|
3321
|
-
/* @__PURE__ */
|
|
402
|
+
/* @__PURE__ */ jsx5(CheckCircle22, { size: 14, className: "shrink-0" }),
|
|
3322
403
|
item.name
|
|
3323
404
|
]
|
|
3324
405
|
},
|
|
@@ -3326,19 +407,19 @@ function SkillDiscovery({
|
|
|
3326
407
|
);
|
|
3327
408
|
}
|
|
3328
409
|
if (item.isSearch) {
|
|
3329
|
-
return /* @__PURE__ */
|
|
410
|
+
return /* @__PURE__ */ jsxs4(
|
|
3330
411
|
"span",
|
|
3331
412
|
{
|
|
3332
413
|
className: "inline-flex items-center gap-1.5 rounded-lg border border-[hsl(var(--foreground)/0.3)] bg-[hsl(var(--foreground)/0.08)] px-3 py-1.5 text-[15px] font-medium text-[hsl(var(--foreground))] transition-all duration-500",
|
|
3333
414
|
children: [
|
|
3334
|
-
/* @__PURE__ */
|
|
415
|
+
/* @__PURE__ */ jsx5(Search, { size: 14, className: "shrink-0" }),
|
|
3335
416
|
item.name
|
|
3336
417
|
]
|
|
3337
418
|
},
|
|
3338
419
|
item.key
|
|
3339
420
|
);
|
|
3340
421
|
}
|
|
3341
|
-
return /* @__PURE__ */
|
|
422
|
+
return /* @__PURE__ */ jsx5(
|
|
3342
423
|
"span",
|
|
3343
424
|
{
|
|
3344
425
|
className: cn(
|
|
@@ -3356,7 +437,7 @@ function SkillDiscovery({
|
|
|
3356
437
|
}
|
|
3357
438
|
|
|
3358
439
|
// src/react/components/plan/PlanVisualization.tsx
|
|
3359
|
-
import { Fragment as
|
|
440
|
+
import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
3360
441
|
var PHASE_LABELS = [
|
|
3361
442
|
{ key: "discovering", label: "\u6280\u80FD\u53D1\u73B0", icon: Search2 },
|
|
3362
443
|
{ key: "analyzing", label: "\u6280\u80FD\u5206\u6790", icon: BookOpen2 },
|
|
@@ -3377,7 +458,7 @@ function PlanVisualization({
|
|
|
3377
458
|
}) {
|
|
3378
459
|
const messages = Array.isArray(rawMessages) ? rawMessages : [];
|
|
3379
460
|
const allSkillNames = Array.isArray(rawAllSkillNames) ? rawAllSkillNames : [];
|
|
3380
|
-
const data =
|
|
461
|
+
const data = useMemo3(() => parsePlanMessages(messages), [messages]);
|
|
3381
462
|
const latestIdx = getLatestPhaseIndex(data);
|
|
3382
463
|
const isWaitingForInput = sessionStatus === "waiting_for_input";
|
|
3383
464
|
const hasError = sessionStatus === "failed";
|
|
@@ -3387,26 +468,26 @@ function PlanVisualization({
|
|
|
3387
468
|
const latestAskQuestion = data.askQuestions[data.askQuestions.length - 1];
|
|
3388
469
|
const hasPhaseData = latestIdx >= 0;
|
|
3389
470
|
const showAskQuestion = latestAskQuestion && isWaitingForInput && data.lastPauseTool === "AskUserQuestion" && onAnswer;
|
|
3390
|
-
const [selectedTab, setSelectedTab] =
|
|
3391
|
-
const prevLatestRef =
|
|
471
|
+
const [selectedTab, setSelectedTab] = useState2(null);
|
|
472
|
+
const prevLatestRef = useRef(latestIdx);
|
|
3392
473
|
if (latestIdx < prevLatestRef.current) {
|
|
3393
474
|
setSelectedTab(null);
|
|
3394
475
|
}
|
|
3395
476
|
prevLatestRef.current = latestIdx;
|
|
3396
477
|
const activeSelectedTab = selectedTab != null && selectedTab <= latestIdx ? selectedTab : null;
|
|
3397
478
|
const viewIdx = activeSelectedTab ?? latestIdx;
|
|
3398
|
-
return /* @__PURE__ */
|
|
3399
|
-
hasPhaseData && /* @__PURE__ */
|
|
3400
|
-
/* @__PURE__ */
|
|
3401
|
-
/* @__PURE__ */
|
|
479
|
+
return /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-4 rounded-2xl border border-[hsl(var(--border))] bg-[hsl(var(--card)/0.4)] p-5", children: [
|
|
480
|
+
hasPhaseData && /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
481
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between", children: [
|
|
482
|
+
/* @__PURE__ */ jsx6("div", { className: "flex items-center gap-1", children: PHASE_LABELS.map((p, i) => {
|
|
3402
483
|
const Icon = p.icon;
|
|
3403
484
|
const isViewing = viewIdx === i;
|
|
3404
485
|
const isDone = latestIdx > i;
|
|
3405
486
|
const isStatusPhase = i === activeStatusIdx && (hasError || isInterrupted);
|
|
3406
487
|
const tabHasData = i === 0 && data.searchResults.length > 0 || i === 1 && data.selectedSkills.length > 0 || i === 2 && (data.plan != null || data.hasPlanContent);
|
|
3407
488
|
const isReachable = i <= latestIdx || tabHasData;
|
|
3408
|
-
return /* @__PURE__ */
|
|
3409
|
-
i > 0 && /* @__PURE__ */
|
|
489
|
+
return /* @__PURE__ */ jsxs5("div", { className: "flex items-center", children: [
|
|
490
|
+
i > 0 && /* @__PURE__ */ jsx6(
|
|
3410
491
|
"div",
|
|
3411
492
|
{
|
|
3412
493
|
className: cn(
|
|
@@ -3415,7 +496,7 @@ function PlanVisualization({
|
|
|
3415
496
|
)
|
|
3416
497
|
}
|
|
3417
498
|
),
|
|
3418
|
-
/* @__PURE__ */
|
|
499
|
+
/* @__PURE__ */ jsxs5(
|
|
3419
500
|
"button",
|
|
3420
501
|
{
|
|
3421
502
|
type: "button",
|
|
@@ -3430,9 +511,9 @@ function PlanVisualization({
|
|
|
3430
511
|
isReachable && !isViewing && !isDone && !tabHasData && "text-[hsl(var(--muted-foreground))]"
|
|
3431
512
|
),
|
|
3432
513
|
children: [
|
|
3433
|
-
/* @__PURE__ */
|
|
514
|
+
/* @__PURE__ */ jsx6(Icon, { size: 12 }),
|
|
3434
515
|
p.label,
|
|
3435
|
-
latestIdx === i && isSessionActive && activeSelectedTab === null && /* @__PURE__ */
|
|
516
|
+
latestIdx === i && isSessionActive && activeSelectedTab === null && /* @__PURE__ */ jsx6(
|
|
3436
517
|
"span",
|
|
3437
518
|
{
|
|
3438
519
|
className: cn(
|
|
@@ -3441,7 +522,7 @@ function PlanVisualization({
|
|
|
3441
522
|
)
|
|
3442
523
|
}
|
|
3443
524
|
),
|
|
3444
|
-
isStatusPhase && latestIdx !== i && /* @__PURE__ */
|
|
525
|
+
isStatusPhase && latestIdx !== i && /* @__PURE__ */ jsx6(
|
|
3445
526
|
"span",
|
|
3446
527
|
{
|
|
3447
528
|
className: cn(
|
|
@@ -3455,13 +536,13 @@ function PlanVisualization({
|
|
|
3455
536
|
)
|
|
3456
537
|
] }, p.key);
|
|
3457
538
|
}) }),
|
|
3458
|
-
/* @__PURE__ */
|
|
3459
|
-
hasError && /* @__PURE__ */
|
|
3460
|
-
isInterrupted && /* @__PURE__ */
|
|
539
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-1", children: [
|
|
540
|
+
hasError && /* @__PURE__ */ jsx6("span", { className: "rounded-full bg-[hsl(var(--destructive)/0.12)] px-2.5 py-1 text-xs font-medium text-[hsl(var(--destructive))]", children: "\u51FA\u9519" }),
|
|
541
|
+
isInterrupted && /* @__PURE__ */ jsx6("span", { className: "rounded-full bg-orange-500/10 px-2.5 py-1 text-xs font-medium text-orange-300", children: "\u5DF2\u4E2D\u65AD" })
|
|
3461
542
|
] })
|
|
3462
543
|
] }),
|
|
3463
|
-
/* @__PURE__ */
|
|
3464
|
-
viewIdx === 0 && /* @__PURE__ */
|
|
544
|
+
/* @__PURE__ */ jsxs5("div", { className: "min-h-0", children: [
|
|
545
|
+
viewIdx === 0 && /* @__PURE__ */ jsx6(
|
|
3465
546
|
SkillDiscovery,
|
|
3466
547
|
{
|
|
3467
548
|
intent: data.intent,
|
|
@@ -3471,12 +552,12 @@ function PlanVisualization({
|
|
|
3471
552
|
active: true
|
|
3472
553
|
}
|
|
3473
554
|
),
|
|
3474
|
-
viewIdx === 1 && /* @__PURE__ */
|
|
3475
|
-
viewIdx === 2 && (data.plan != null ? /* @__PURE__ */
|
|
555
|
+
viewIdx === 1 && /* @__PURE__ */ jsx6(SkillAnalysis, { skills: data.selectedSkills, active: true }),
|
|
556
|
+
viewIdx === 2 && (data.plan != null ? /* @__PURE__ */ jsx6(PlanTree, { root: data.plan.root, notes: data.plan.notes, active: true }) : data.parseError ? /* @__PURE__ */ jsx6("div", { className: "flex min-h-[240px] items-center justify-center rounded-xl border border-[hsl(var(--destructive)/0.35)] bg-[hsl(var(--destructive)/0.08)] px-6 text-sm text-[hsl(var(--destructive))]", children: data.parseError }) : data.hasPlanContent ? /* @__PURE__ */ jsx6("div", { className: "flex min-h-[240px] items-center justify-center rounded-xl border border-dashed border-[hsl(var(--border))] bg-[hsl(var(--card)/0.3)] px-6 text-sm text-[hsl(var(--muted-foreground))]", children: "\u8BA1\u5212\u89E3\u6790\u4E2D..." }) : null)
|
|
3476
557
|
] })
|
|
3477
558
|
] }),
|
|
3478
|
-
!hasPhaseData && showAskQuestion && /* @__PURE__ */
|
|
3479
|
-
hasPhaseData && showAskQuestion && /* @__PURE__ */
|
|
559
|
+
!hasPhaseData && showAskQuestion && /* @__PURE__ */ jsx6(AskUserQuestionBlock, { data: latestAskQuestion.data, answered: false, toolCallId: latestAskQuestion.toolCallId, sessionStatus: sessionStatus ?? "", onAnswer }),
|
|
560
|
+
hasPhaseData && showAskQuestion && /* @__PURE__ */ jsx6(AskUserQuestionBlock, { data: latestAskQuestion.data, answered: false, toolCallId: latestAskQuestion.toolCallId, sessionStatus: sessionStatus ?? "", onAnswer })
|
|
3480
561
|
] });
|
|
3481
562
|
}
|
|
3482
563
|
export {
|