@blade-hq/agent-kit 0.0.0-placeholder.0 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +66 -3
  2. package/dist/AskUserQuestionBlock-CjvG_pUY.d.ts +116 -0
  3. package/dist/SkillStatusBar-DItrW2vv.d.ts +203 -0
  4. package/dist/blade-client-nOsdVlb1.d.ts +1498 -0
  5. package/dist/client/index.d.ts +8036 -0
  6. package/dist/client/index.js +1057 -0
  7. package/dist/client/index.js.map +1 -0
  8. package/dist/licenses-Cxl1xGVy.d.ts +16 -0
  9. package/dist/projection-DIfyh6RK.d.ts +85 -0
  10. package/dist/react/api/licenses.d.ts +7 -0
  11. package/dist/react/api/licenses.js +1477 -0
  12. package/dist/react/api/licenses.js.map +1 -0
  13. package/dist/react/api/vibe-coding.d.ts +55 -0
  14. package/dist/react/api/vibe-coding.js +1503 -0
  15. package/dist/react/api/vibe-coding.js.map +1 -0
  16. package/dist/react/cards/register.d.ts +2 -0
  17. package/dist/react/cards/register.js +4367 -0
  18. package/dist/react/cards/register.js.map +1 -0
  19. package/dist/react/components/chat/index.d.ts +128 -0
  20. package/dist/react/components/chat/index.js +11389 -0
  21. package/dist/react/components/chat/index.js.map +1 -0
  22. package/dist/react/components/plan/index.d.ts +111 -0
  23. package/dist/react/components/plan/index.js +3490 -0
  24. package/dist/react/components/plan/index.js.map +1 -0
  25. package/dist/react/components/session/index.d.ts +53 -0
  26. package/dist/react/components/session/index.js +2175 -0
  27. package/dist/react/components/session/index.js.map +1 -0
  28. package/dist/react/components/workspace/index.d.ts +35 -0
  29. package/dist/react/components/workspace/index.js +2886 -0
  30. package/dist/react/components/workspace/index.js.map +1 -0
  31. package/dist/react/devtools/bridge-devtools/index.d.ts +36 -0
  32. package/dist/react/devtools/bridge-devtools/index.js +692 -0
  33. package/dist/react/devtools/bridge-devtools/index.js.map +1 -0
  34. package/dist/react/index.d.ts +1283 -0
  35. package/dist/react/index.js +14299 -0
  36. package/dist/react/index.js.map +1 -0
  37. package/dist/session-CDeiO81j.d.ts +128 -0
  38. package/package.json +73 -7
  39. package/index.js +0 -1
@@ -0,0 +1,2175 @@
1
+ // src/client/resources/models.ts
2
+ import { type } from "arktype";
3
+ var ModelOption = type({
4
+ id: "string",
5
+ label: "string"
6
+ });
7
+ var ModelsConfig = type({
8
+ default: "string",
9
+ models: ModelOption.array()
10
+ });
11
+
12
+ // src/client/socket.ts
13
+ import { io } from "socket.io-client";
14
+
15
+ // src/react/schemas/partner-skill.ts
16
+ import { type as type2 } from "arktype";
17
+ var PartnerSkillName = type2("/^[a-z0-9-]+\\/[a-z0-9-]+$/");
18
+ var PartnerSkillFile = type2({
19
+ path: "string > 0",
20
+ content: "string"
21
+ });
22
+ var PartnerSkillInstallPayload = type2({
23
+ name: PartnerSkillName,
24
+ files: PartnerSkillFile.array().atLeastLength(1)
25
+ });
26
+ var PartnerSkillInstallResult = type2({
27
+ name: PartnerSkillName,
28
+ skill_dir: "string",
29
+ file_count: "number.integer >= 0",
30
+ overwritten: "boolean"
31
+ });
32
+
33
+ // src/react/stores/ui-bridge-store.ts
34
+ import { create } from "zustand";
35
+
36
+ // src/react/stores/client-aware.ts
37
+ function createClientActions(set) {
38
+ return {
39
+ _client: null,
40
+ setClient: (client) => set({ _client: client })
41
+ };
42
+ }
43
+
44
+ // src/react/stores/ui-bridge-store.ts
45
+ function buildSignalId() {
46
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
47
+ return crypto.randomUUID();
48
+ }
49
+ return `bridge-${Date.now()}-${Math.random().toString(36).slice(2)}`;
50
+ }
51
+ function clearSignalRecord(record, sessionId) {
52
+ if (!(sessionId in record)) {
53
+ return record;
54
+ }
55
+ const next = { ...record };
56
+ delete next[sessionId];
57
+ return next;
58
+ }
59
+ var useUiBridgeStore = create()((set, get) => ({
60
+ ...createClientActions(set),
61
+ pendingContexts: {},
62
+ draftAppends: {},
63
+ sendRequests: {},
64
+ addPendingContext: (sessionId, context) => set((state) => ({
65
+ pendingContexts: {
66
+ ...state.pendingContexts,
67
+ [sessionId]: [
68
+ ...state.pendingContexts[sessionId] ?? [],
69
+ {
70
+ id: buildSignalId(),
71
+ label: context.label,
72
+ content: context.content
73
+ }
74
+ ]
75
+ }
76
+ })),
77
+ removePendingContext: (sessionId, contextId) => set((state) => {
78
+ const contexts = state.pendingContexts[sessionId];
79
+ if (!contexts?.length) {
80
+ return state;
81
+ }
82
+ const nextContexts = contexts.filter((context) => context.id !== contextId);
83
+ if (nextContexts.length === contexts.length) {
84
+ return state;
85
+ }
86
+ return {
87
+ pendingContexts: nextContexts.length > 0 ? {
88
+ ...state.pendingContexts,
89
+ [sessionId]: nextContexts
90
+ } : clearSignalRecord(state.pendingContexts, sessionId)
91
+ };
92
+ }),
93
+ consumePendingContexts: (sessionId) => {
94
+ const signals = get().pendingContexts[sessionId] ?? [];
95
+ if (signals.length === 0) return [];
96
+ set((state) => ({
97
+ pendingContexts: clearSignalRecord(state.pendingContexts, sessionId)
98
+ }));
99
+ return signals;
100
+ },
101
+ clearPendingContexts: (sessionId) => set((state) => ({
102
+ pendingContexts: clearSignalRecord(state.pendingContexts, sessionId)
103
+ })),
104
+ addDraftAppend: (sessionId, text) => set((state) => ({
105
+ draftAppends: {
106
+ ...state.draftAppends,
107
+ [sessionId]: [
108
+ ...state.draftAppends[sessionId] ?? [],
109
+ {
110
+ id: buildSignalId(),
111
+ text
112
+ }
113
+ ]
114
+ }
115
+ })),
116
+ consumeDraftAppends: (sessionId) => {
117
+ const signals = get().draftAppends[sessionId] ?? [];
118
+ if (signals.length === 0) return [];
119
+ set((state) => ({
120
+ draftAppends: clearSignalRecord(state.draftAppends, sessionId)
121
+ }));
122
+ return signals;
123
+ },
124
+ clearDraftAppends: (sessionId) => set((state) => ({
125
+ draftAppends: clearSignalRecord(state.draftAppends, sessionId)
126
+ })),
127
+ addSendRequest: (sessionId) => set((state) => ({
128
+ sendRequests: {
129
+ ...state.sendRequests,
130
+ [sessionId]: [
131
+ ...state.sendRequests[sessionId] ?? [],
132
+ {
133
+ id: buildSignalId()
134
+ }
135
+ ]
136
+ }
137
+ })),
138
+ consumeSendRequests: (sessionId) => {
139
+ const signals = get().sendRequests[sessionId] ?? [];
140
+ if (signals.length === 0) return [];
141
+ set((state) => ({
142
+ sendRequests: clearSignalRecord(state.sendRequests, sessionId)
143
+ }));
144
+ return signals;
145
+ },
146
+ clearSendRequests: (sessionId) => set((state) => ({
147
+ sendRequests: clearSignalRecord(state.sendRequests, sessionId)
148
+ })),
149
+ clearSession: (sessionId) => set((state) => ({
150
+ pendingContexts: clearSignalRecord(state.pendingContexts, sessionId),
151
+ draftAppends: clearSignalRecord(state.draftAppends, sessionId),
152
+ sendRequests: clearSignalRecord(state.sendRequests, sessionId)
153
+ }))
154
+ }));
155
+
156
+ // src/react/lib/chat.ts
157
+ function normalizeMessageContent(content) {
158
+ if (typeof content === "string") return content;
159
+ if (Array.isArray(content)) return content;
160
+ return String(content ?? "");
161
+ }
162
+
163
+ // src/react/stores/auth-store.ts
164
+ import { create as create6 } from "zustand";
165
+ import { createJSONStorage, persist } from "zustand/middleware";
166
+
167
+ // src/react/api/auth.ts
168
+ var r = () => getClient().auth;
169
+ var getMe = (...args) => r().getMe(...args);
170
+ var logout = (...args) => r().logout(...args);
171
+
172
+ // src/react/sockets/socket-state.ts
173
+ var agentSocket = null;
174
+
175
+ // src/react/stores/session-store.ts
176
+ import { create as create5 } from "zustand";
177
+
178
+ // src/react/stores/chat-store.ts
179
+ import { create as create3 } from "zustand";
180
+
181
+ // src/react/components/chat/display-utils.ts
182
+ var TOOL_NAME_ALIASES = {
183
+ agent: "Agent",
184
+ ask_user_question: "AskUserQuestion",
185
+ bash: "Bash",
186
+ bg_bash: "BgBash",
187
+ edit: "Edit",
188
+ exit_plan_mode: "ExitPlanMode",
189
+ file_edit: "Edit",
190
+ file_read: "Read",
191
+ file_write: "Write",
192
+ finish_task: "FinishTask",
193
+ get_skill_content: "GetSkillContent",
194
+ glob: "Glob",
195
+ grep: "Grep",
196
+ list_skill_tools: "ListSkillTools",
197
+ load_skill_tools: "LoadSkillTools",
198
+ ls: "Ls",
199
+ read: "Read",
200
+ run_skill_tool: "RunSkillTool",
201
+ search_skills: "SearchSkills",
202
+ web_fetch: "WebFetch",
203
+ web_search: "WebSearch",
204
+ write: "Write"
205
+ };
206
+ var TOOL_DISPLAY_LABELS = {
207
+ Bash: "\u6267\u884C\u547D\u4EE4",
208
+ BgBash: "\u540E\u53F0\u6267\u884C\u547D\u4EE4",
209
+ Read: "\u8BFB\u53D6\u6587\u4EF6",
210
+ Write: "\u5199\u5165\u6587\u4EF6",
211
+ Edit: "\u7F16\u8F91\u6587\u4EF6",
212
+ Ls: "\u5217\u51FA\u76EE\u5F55",
213
+ Glob: "\u5339\u914D\u6587\u4EF6",
214
+ Grep: "\u641C\u7D22\u6587\u672C",
215
+ WebSearch: "\u641C\u7D22\u7F51\u9875",
216
+ WebFetch: "\u6574\u7406\u7F51\u9875\u5185\u5BB9",
217
+ Agent: "\u6D3E\u751F\u5B50\u667A\u80FD\u4F53",
218
+ AskUserQuestion: "\u5411\u7528\u6237\u63D0\u95EE",
219
+ SearchSkills: "\u641C\u7D22\u6280\u80FD",
220
+ GetSkillContent: "\u8BFB\u53D6\u6280\u80FD",
221
+ ListSkillTools: "\u67E5\u770B\u5DE5\u5177\u5217\u8868",
222
+ LoadSkillTools: "\u52A0\u8F7D\u6280\u80FD",
223
+ RunSkillTool: "\u6267\u884C\u6280\u80FD\u5DE5\u5177",
224
+ FinishTask: "\u4EFB\u52A1\u5B8C\u6210",
225
+ ExitPlanMode: "\u63D0\u4EA4\u8BA1\u5212",
226
+ ListSessions: "\u5217\u51FA\u5386\u53F2\u4F1A\u8BDD",
227
+ GetSessionHistory: "\u8BFB\u53D6\u4F1A\u8BDD\u5386\u53F2"
228
+ };
229
+ var GENERIC_DISPLAY_NAMES = new Set(Object.values(TOOL_DISPLAY_LABELS));
230
+ function formatToolName(name) {
231
+ const trimmed = name.trim();
232
+ if (!trimmed) return name;
233
+ const stripped = trimmed.split(":").pop()?.split("/").pop()?.split(".").pop()?.trim() || trimmed;
234
+ const normalized = stripped.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
235
+ return TOOL_NAME_ALIASES[normalized] ?? stripped;
236
+ }
237
+
238
+ // src/react/stores/ui-store.ts
239
+ import { create as create2 } from "zustand";
240
+ var isBrowser = typeof window !== "undefined" && typeof document !== "undefined";
241
+ function resolveEffectiveTheme(theme) {
242
+ if (theme !== "system") return theme;
243
+ return isBrowser && window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
244
+ }
245
+ function applyTheme(theme) {
246
+ if (!isBrowser) return;
247
+ const effective = resolveEffectiveTheme(theme);
248
+ document.documentElement.setAttribute("data-theme", effective);
249
+ }
250
+ var storedTheme = isBrowser ? localStorage.getItem("blade-theme") ?? "light" : "light";
251
+ applyTheme(storedTheme);
252
+ if (isBrowser) {
253
+ window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => {
254
+ const current = useUiStore?.getState?.()?.theme;
255
+ if (current === "system") applyTheme("system");
256
+ });
257
+ }
258
+ function removeArtifactAtIndex(state, index) {
259
+ const next = state.artifacts.filter((_, i) => i !== index);
260
+ let nextActive = state.activeArtifactIndex;
261
+ if (next.length === 0) {
262
+ return { artifacts: [], activeArtifactIndex: -1, rightPanelCollapsed: true };
263
+ }
264
+ if (index < nextActive) {
265
+ nextActive -= 1;
266
+ } else if (index === nextActive) {
267
+ nextActive = Math.min(index, next.length - 1);
268
+ }
269
+ return { artifacts: next, activeArtifactIndex: nextActive };
270
+ }
271
+ function upsertArtifactState(state, target, options) {
272
+ const reveal = options?.reveal ?? true;
273
+ const activate = options?.activate ?? true;
274
+ const targetKey = target.key ?? target.title;
275
+ const applyUiState = (partial) => ({
276
+ ...partial,
277
+ ...reveal ? { rightPanelCollapsed: false, activeRightTab: "preview" } : {}
278
+ });
279
+ const existing = state.artifacts.findIndex((artifact) => targetKey && (artifact.key ?? artifact.title) === targetKey);
280
+ if (existing >= 0) {
281
+ const updated = [...state.artifacts];
282
+ updated[existing] = target;
283
+ return applyUiState({
284
+ artifacts: updated,
285
+ activeArtifactIndex: activate ? existing : state.activeArtifactIndex
286
+ });
287
+ }
288
+ const next = [...state.artifacts, target];
289
+ return applyUiState({
290
+ artifacts: next,
291
+ activeArtifactIndex: activate ? next.length - 1 : state.activeArtifactIndex >= 0 ? state.activeArtifactIndex : 0
292
+ });
293
+ }
294
+ var useUiStore = create2()((set) => ({
295
+ ...createClientActions(set),
296
+ leftPanelSize: 20,
297
+ rightPanelSize: 25,
298
+ leftPanelCollapsed: false,
299
+ rightPanelCollapsed: false,
300
+ activeRightTab: "situation",
301
+ artifacts: [],
302
+ activeArtifactIndex: -1,
303
+ theme: storedTheme,
304
+ setLeftPanelSize: (size) => set({ leftPanelSize: size }),
305
+ setRightPanelSize: (size) => set({ rightPanelSize: size }),
306
+ setLeftPanelCollapsed: (collapsed) => set({ leftPanelCollapsed: collapsed }),
307
+ setRightPanelCollapsed: (collapsed) => set({ rightPanelCollapsed: collapsed }),
308
+ toggleLeftPanel: () => set((s) => ({ leftPanelCollapsed: !s.leftPanelCollapsed })),
309
+ toggleRightPanel: () => set((s) => ({ rightPanelCollapsed: !s.rightPanelCollapsed })),
310
+ setActiveRightTab: (tab) => set({ activeRightTab: tab }),
311
+ pushArtifact: (target) => set((state) => upsertArtifactState(state, target)),
312
+ upsertArtifact: (target, options) => set((state) => upsertArtifactState(state, target, options)),
313
+ setActiveArtifact: (index) => set({ activeArtifactIndex: index }),
314
+ closeArtifact: (index) => set((state) => removeArtifactAtIndex(state, index)),
315
+ removeArtifactByKey: (key) => set((state) => {
316
+ const index = state.artifacts.findIndex((artifact) => (artifact.key ?? artifact.title) === key);
317
+ if (index < 0) return state;
318
+ return removeArtifactAtIndex(state, index);
319
+ }),
320
+ clearArtifacts: () => set({ artifacts: [], activeArtifactIndex: -1 }),
321
+ setPreviewTarget: (target) => set(() => {
322
+ if (target === null) {
323
+ return { artifacts: [], activeArtifactIndex: -1 };
324
+ }
325
+ return {
326
+ artifacts: [target],
327
+ activeArtifactIndex: 0,
328
+ rightPanelCollapsed: false,
329
+ activeRightTab: "preview"
330
+ };
331
+ }),
332
+ setTheme: (theme) => {
333
+ localStorage.setItem("blade-theme", theme);
334
+ applyTheme(theme);
335
+ set({ theme });
336
+ }
337
+ }));
338
+
339
+ // src/react/stores/chat-store.ts
340
+ var _getActiveSessionId = null;
341
+ function setChatStoreSessionAccessor(fn) {
342
+ _getActiveSessionId = fn;
343
+ }
344
+ function parseAgentDescription(argumentsJson) {
345
+ try {
346
+ return JSON.parse(argumentsJson)?.description ?? "\u5B50\u667A\u80FD\u4F53";
347
+ } catch {
348
+ return "\u5B50\u667A\u80FD\u4F53";
349
+ }
350
+ }
351
+ function inferLoopStatusFromMessages(messages) {
352
+ const toolCalls = messages.flatMap((message) => message.tool_calls ?? []);
353
+ if (messages.some((message) => message.status === "streaming")) return "running";
354
+ if (toolCalls.some((toolCall) => toolCall.status === "awaiting_answer")) return "awaiting_answer";
355
+ if (toolCalls.some((toolCall) => toolCall.status === "error")) return "error";
356
+ if (toolCalls.some((toolCall) => toolCall.status === "cancelled")) return "cancelled";
357
+ if (toolCalls.some((toolCall) => toolCall.status === "pending")) return "running";
358
+ return "done";
359
+ }
360
+ function inferLoopStatusFromTurns(turns, messages) {
361
+ const latestAgentNotification = [...turns].reverse().flatMap((turn) => turn.blocks).find((block) => {
362
+ if (block.type !== "system_notification" || !isRecord(block.content)) return false;
363
+ return block.content.notification_type === "agent:start" || block.content.notification_type === "agent:end";
364
+ });
365
+ if (latestAgentNotification?.type === "system_notification" && isRecord(latestAgentNotification.content)) {
366
+ const notificationType = latestAgentNotification.content.notification_type;
367
+ const status = latestAgentNotification.content.status;
368
+ if (notificationType === "agent:start" || status === "running") return "running";
369
+ if (status === "error") return "error";
370
+ if (status === "cancelled") return "cancelled";
371
+ }
372
+ return inferLoopStatusFromMessages(messages);
373
+ }
374
+ function isRecord(value) {
375
+ return typeof value === "object" && value !== null && !Array.isArray(value);
376
+ }
377
+ function parentForkToolCallIdFromTurn(turn) {
378
+ if (typeof turn.parent_fork_tool_call_id === "string" && turn.parent_fork_tool_call_id.length > 0) {
379
+ return turn.parent_fork_tool_call_id;
380
+ }
381
+ for (const block of turn.blocks) {
382
+ if (block.type !== "system_notification" || !isRecord(block.content)) continue;
383
+ const metadata = block.content.metadata;
384
+ if (!isRecord(metadata)) continue;
385
+ const parentId = metadata.parent_fork_tool_call_id;
386
+ if (typeof parentId === "string" && parentId.length > 0) return parentId;
387
+ }
388
+ return null;
389
+ }
390
+ function buildMessageContent(turn) {
391
+ const textBlocks = turn.blocks.filter((block) => block.type === "text");
392
+ if (textBlocks.length === 0) return "";
393
+ if (textBlocks.length === 1) return normalizeMessageContent(textBlocks[0].content);
394
+ return textBlocks.map((block) => {
395
+ if (typeof block.content === "string") return block.content;
396
+ return JSON.stringify(block.content);
397
+ }).join("");
398
+ }
399
+ function buildReasoning(turn) {
400
+ const thinking = turn.blocks.filter((block) => block.type === "thinking").map((block) => typeof block.content === "string" ? block.content : JSON.stringify(block.content)).filter(Boolean);
401
+ if (thinking.length === 0) return void 0;
402
+ return thinking.join("\n\n");
403
+ }
404
+ function isModeChangeContent(value) {
405
+ return typeof value === "object" && value !== null && typeof value.from === "string" && typeof value.to === "string";
406
+ }
407
+ function extractModeFromBlocks(blocks) {
408
+ const modeBlock = blocks.find((block) => block.type === "mode_change");
409
+ if (modeBlock && isModeChangeContent(modeBlock.content)) {
410
+ return modeBlock.content.to === "planning" || modeBlock.content.to === "executing" ? modeBlock.content.to : null;
411
+ }
412
+ if (blocks.some((block) => block.type === "planning_enter")) return "planning";
413
+ if (blocks.some((block) => block.type === "planning_exit")) return "executing";
414
+ return null;
415
+ }
416
+ function projectionToMessage(turn) {
417
+ if (turn.kind === "compaction" && turn.compaction_id) {
418
+ return {
419
+ role: "assistant",
420
+ content: turn.summary_preview ?? "",
421
+ kind: "compaction",
422
+ loop_name: turn.loop_id,
423
+ entry_id: turn.turn_id,
424
+ status: turn.status,
425
+ compaction: {
426
+ compaction_id: turn.compaction_id,
427
+ summary_preview: turn.summary_preview,
428
+ summary_full: turn.summary_full,
429
+ archived_count: turn.archived_count,
430
+ archived_files: turn.archived_files,
431
+ archived_tool_calls: turn.archived_tool_calls,
432
+ tokens_before: turn.tokens_before,
433
+ tokens_after: turn.tokens_after,
434
+ saved_ratio: turn.saved_ratio,
435
+ trigger: turn.trigger,
436
+ failure_reason: turn.failure_reason,
437
+ fallback_applied: turn.fallback_applied
438
+ }
439
+ };
440
+ }
441
+ const planningBlock = turn.blocks.find(
442
+ (block) => block.type === "mode_change" || block.type === "planning_enter" || block.type === "planning_exit" || block.type === "plan_status"
443
+ );
444
+ if (planningBlock) {
445
+ if (planningBlock.type === "plan_status") {
446
+ return {
447
+ role: "tool",
448
+ content: typeof planningBlock.content === "string" ? planningBlock.content : JSON.stringify(planningBlock.content ?? {}, null, 2),
449
+ kind: "plan_status",
450
+ loop_name: turn.loop_id,
451
+ entry_id: turn.turn_id,
452
+ status: turn.status
453
+ };
454
+ }
455
+ return {
456
+ role: "assistant",
457
+ content: planningBlock.type === "mode_change" ? typeof planningBlock.content === "string" ? planningBlock.content : JSON.stringify(planningBlock.content ?? {}) : "",
458
+ kind: planningBlock.type,
459
+ loop_name: turn.loop_id,
460
+ entry_id: turn.turn_id,
461
+ status: turn.status
462
+ };
463
+ }
464
+ if (turn.blocks.some((block) => block.type === "ask_user_answer")) {
465
+ return null;
466
+ }
467
+ const content = buildMessageContent(turn);
468
+ const reasoning = buildReasoning(turn);
469
+ const toolCalls = turn.tool_calls.length > 0 ? turn.tool_calls.map((toolCall) => ({
470
+ id: toolCall.id,
471
+ name: toolCall.tool_name,
472
+ display_name: toolCall.display_name,
473
+ arguments: toolCall.arguments,
474
+ result: toolCall.result ?? void 0,
475
+ pending_question_ref: toolCall.pending_question_ref ?? void 0,
476
+ status: toolCall.status === "pending" || toolCall.status === "awaiting_answer" || toolCall.status === "done" || toolCall.status === "error" || toolCall.status === "cancelled" ? toolCall.status : "pending",
477
+ ...typeof toolCall.duration_ms === "number" ? { duration_ms: toolCall.duration_ms } : {}
478
+ })) : void 0;
479
+ if (turn.role === "system" && !content && !toolCalls?.length) {
480
+ return null;
481
+ }
482
+ return {
483
+ role: turn.role === "system" ? "assistant" : turn.role,
484
+ content,
485
+ blocks: turn.blocks,
486
+ ...reasoning ? { reasoning } : {},
487
+ ...toolCalls ? { tool_calls: toolCalls } : {},
488
+ loop_name: turn.loop_id,
489
+ entry_id: turn.turn_id,
490
+ status: turn.status,
491
+ ...typeof turn.duration_ms === "number" ? { duration_ms: turn.duration_ms } : {},
492
+ ...turn.started_at ? { timestamp: turn.started_at } : {},
493
+ ...turn.memory_refs?.length ? { memory_refs: turn.memory_refs } : {}
494
+ };
495
+ }
496
+ function rebuildAgentLoops(turns) {
497
+ const messages = turns.map(projectionToMessage).filter(Boolean);
498
+ const childLoopNames = [...new Set(turns.map((turn) => turn.loop_id).filter((name) => name !== "root"))];
499
+ if (childLoopNames.length === 0) return {};
500
+ const agentToolCalls = messages.filter((message) => message.role === "assistant" && (message.loop_name ?? "root") === "root").flatMap((message) => message.tool_calls ?? []).filter((toolCall) => formatToolName(toolCall.name) === "Agent");
501
+ const loops = {};
502
+ const agentToolCallsById = new Map(agentToolCalls.map((toolCall) => [toolCall.id, toolCall]));
503
+ const explicitParentToolCallIds = new Set(
504
+ turns.map(parentForkToolCallIdFromTurn).filter((id) => id !== null)
505
+ );
506
+ const usedToolCallIds = /* @__PURE__ */ new Set();
507
+ for (const loopName of childLoopNames) {
508
+ const loopMessages = messages.filter((message) => (message.loop_name ?? "root") === loopName);
509
+ const loopTurns = turns.filter((turn) => turn.loop_id === loopName);
510
+ const parentToolCallId = loopTurns.map(parentForkToolCallIdFromTurn).find(Boolean);
511
+ const explicitToolCall = parentToolCallId && !usedToolCallIds.has(parentToolCallId) ? agentToolCallsById.get(parentToolCallId) ?? null : null;
512
+ const fallbackToolCall = explicitToolCall === null ? agentToolCalls.find(
513
+ (toolCall2) => !usedToolCallIds.has(toolCall2.id) && !explicitParentToolCallIds.has(toolCall2.id)
514
+ ) : null;
515
+ const toolCall = explicitToolCall ?? fallbackToolCall;
516
+ if (!toolCall) continue;
517
+ usedToolCallIds.add(toolCall.id);
518
+ loops[loopName] = {
519
+ toolCallId: toolCall.id,
520
+ description: parseAgentDescription(toolCall.arguments),
521
+ status: inferLoopStatusFromTurns(loopTurns, loopMessages)
522
+ };
523
+ }
524
+ return loops;
525
+ }
526
+ function applyPlanningSideEffects(sessionId, turns) {
527
+ const latestMode = [...turns].reverse().map((turn) => extractModeFromBlocks(turn.blocks)).find((mode) => mode !== null);
528
+ if (latestMode !== "planning") return;
529
+ if (_getActiveSessionId?.() !== sessionId) return;
530
+ const ui = useUiStore.getState();
531
+ ui.setActiveRightTab("situation");
532
+ if (ui.rightPanelCollapsed) {
533
+ ui.toggleRightPanel();
534
+ }
535
+ }
536
+ function materialize(turns) {
537
+ const messages = turns.map(projectionToMessage).filter((message) => message !== null);
538
+ const activeCompaction = [...turns].reverse().find(
539
+ (turn) => turn.kind === "compaction" && turn.status === "streaming" && typeof turn.compaction_id === "string"
540
+ );
541
+ return {
542
+ messages,
543
+ agentLoops: rebuildAgentLoops(turns),
544
+ activeCompaction: activeCompaction ? {
545
+ turn_id: activeCompaction.turn_id,
546
+ status: activeCompaction.status,
547
+ compaction_id: activeCompaction.compaction_id,
548
+ summary_preview: activeCompaction.summary_preview,
549
+ summary_full: activeCompaction.summary_full,
550
+ archived_count: activeCompaction.archived_count,
551
+ archived_files: activeCompaction.archived_files,
552
+ archived_tool_calls: activeCompaction.archived_tool_calls,
553
+ tokens_before: activeCompaction.tokens_before,
554
+ tokens_after: activeCompaction.tokens_after,
555
+ saved_ratio: activeCompaction.saved_ratio,
556
+ trigger: activeCompaction.trigger,
557
+ failure_reason: activeCompaction.failure_reason,
558
+ fallback_applied: activeCompaction.fallback_applied
559
+ } : null
560
+ };
561
+ }
562
+ var ERROR_ANCHOR_PREFIX = "error-anchor:";
563
+ function updateSessionState(state, sessionId, turns) {
564
+ const orderedTurns = [...turns].sort((left, right) => left.sequence - right.sequence);
565
+ const { messages, agentLoops, activeCompaction } = materialize(orderedTurns);
566
+ applyPlanningSideEffects(sessionId, orderedTurns);
567
+ const lastTurnId = orderedTurns[orderedTurns.length - 1]?.turn_id ?? null;
568
+ const preservedErrors = lastTurnId ? (state.messages[sessionId] ?? []).filter(
569
+ (m) => m.role === "error" && typeof m.entry_id === "string" && m.entry_id.startsWith(`${ERROR_ANCHOR_PREFIX}${lastTurnId}:`)
570
+ ) : [];
571
+ const mergedMessages = preservedErrors.length > 0 ? [...messages, ...preservedErrors] : messages;
572
+ return {
573
+ turns: { ...state.turns, [sessionId]: orderedTurns },
574
+ messages: { ...state.messages, [sessionId]: mergedMessages },
575
+ agentLoops: { ...state.agentLoops, [sessionId]: agentLoops },
576
+ activeCompactions: { ...state.activeCompactions, [sessionId]: activeCompaction }
577
+ };
578
+ }
579
+ var useChatStore = create3()((set) => ({
580
+ ...createClientActions(set),
581
+ turns: {},
582
+ messages: {},
583
+ askAnswers: {},
584
+ isStreaming: {},
585
+ agentLoops: {},
586
+ activeCompactions: {},
587
+ addUserMessage: (sessionId, content) => {
588
+ set((state) => {
589
+ const existing = state.turns[sessionId] ?? [];
590
+ const turnId = `local-user-${Date.now()}`;
591
+ const optimisticTurn = {
592
+ id: turnId,
593
+ sequence: Math.max(0, ...existing.map((turn) => turn.sequence)) + 1,
594
+ turn_id: turnId,
595
+ loop_id: "root",
596
+ role: "user",
597
+ status: "completed",
598
+ blocks: [{ type: "text", content }],
599
+ tool_calls: [],
600
+ model: null,
601
+ usage: null,
602
+ duration_ms: 0
603
+ };
604
+ return updateSessionState(state, sessionId, [...existing, optimisticTurn]);
605
+ });
606
+ },
607
+ setTurns: (sessionId, turns) => {
608
+ set((state) => updateSessionState(state, sessionId, [...turns]));
609
+ },
610
+ upsertTurn: (sessionId, turn) => {
611
+ set((state) => {
612
+ const existing = [...state.turns[sessionId] ?? []];
613
+ const index = existing.findIndex((item) => item.turn_id === turn.turn_id);
614
+ if (index >= 0) {
615
+ existing[index] = turn;
616
+ } else {
617
+ existing.push(turn);
618
+ }
619
+ return {
620
+ ...updateSessionState(state, sessionId, existing)
621
+ };
622
+ });
623
+ },
624
+ applyTurnPatch: (sessionId, patch) => {
625
+ set((state) => {
626
+ const turn = patch.data.turn;
627
+ if (!turn) return state;
628
+ const existing = [...state.turns[sessionId] ?? []];
629
+ const index = existing.findIndex((item) => item.turn_id === turn.turn_id);
630
+ const lastSequence = index >= 0 ? existing[index].sequence : null;
631
+ if (lastSequence !== null && patch.sequence <= lastSequence) {
632
+ return state;
633
+ }
634
+ const nextTurn = {
635
+ ...turn,
636
+ sequence: patch.sequence
637
+ };
638
+ if (index >= 0) {
639
+ existing[index] = nextTurn;
640
+ } else {
641
+ existing.push(nextTurn);
642
+ }
643
+ return {
644
+ ...updateSessionState(state, sessionId, existing)
645
+ };
646
+ });
647
+ },
648
+ addErrorMessage: (sessionId, content) => {
649
+ set((state) => {
650
+ const turns = state.turns[sessionId] ?? [];
651
+ const anchorTurnId = turns[turns.length - 1]?.turn_id ?? null;
652
+ const entry_id = anchorTurnId ? `${ERROR_ANCHOR_PREFIX}${anchorTurnId}:${Date.now()}` : void 0;
653
+ return {
654
+ messages: {
655
+ ...state.messages,
656
+ [sessionId]: [
657
+ ...state.messages[sessionId] ?? [],
658
+ { role: "error", content, loop_name: "root", entry_id }
659
+ ]
660
+ }
661
+ };
662
+ });
663
+ },
664
+ markInterrupted: (sessionId) => {
665
+ set((state) => {
666
+ const turns = (state.turns[sessionId] ?? []).map((turn) => {
667
+ if (turn.status !== "streaming") return turn;
668
+ return {
669
+ ...turn,
670
+ status: "interrupted",
671
+ tool_calls: turn.tool_calls.map(
672
+ (toolCall) => toolCall.status === "pending" || toolCall.status === "awaiting_answer" ? { ...toolCall, status: "cancelled" } : toolCall
673
+ )
674
+ };
675
+ });
676
+ return {
677
+ ...updateSessionState(state, sessionId, turns)
678
+ };
679
+ });
680
+ },
681
+ markFailed: (sessionId) => {
682
+ set((state) => {
683
+ const turns = (state.turns[sessionId] ?? []).map((turn) => {
684
+ if (turn.status !== "streaming") return turn;
685
+ return {
686
+ ...turn,
687
+ status: "failed",
688
+ tool_calls: turn.tool_calls.map(
689
+ (toolCall) => toolCall.status === "pending" || toolCall.status === "awaiting_answer" ? { ...toolCall, status: "error" } : toolCall
690
+ )
691
+ };
692
+ });
693
+ return {
694
+ ...updateSessionState(state, sessionId, turns)
695
+ };
696
+ });
697
+ },
698
+ setStreaming: (sessionId, streaming) => {
699
+ set((state) => ({
700
+ isStreaming: { ...state.isStreaming, [sessionId]: streaming }
701
+ }));
702
+ },
703
+ setAskAnswers: (sessionId, answers) => {
704
+ set((state) => ({
705
+ askAnswers: {
706
+ ...state.askAnswers,
707
+ [sessionId]: answers
708
+ }
709
+ }));
710
+ },
711
+ clearMessages: (sessionId) => {
712
+ set((state) => {
713
+ const { [sessionId]: _turns, ...restTurns } = state.turns;
714
+ const { [sessionId]: _messages, ...restMessages } = state.messages;
715
+ const { [sessionId]: _answers, ...restAnswers } = state.askAnswers;
716
+ const { [sessionId]: _loops, ...restLoops } = state.agentLoops;
717
+ const { [sessionId]: _compaction, ...restCompactions } = state.activeCompactions;
718
+ return {
719
+ turns: restTurns,
720
+ messages: restMessages,
721
+ askAnswers: restAnswers,
722
+ agentLoops: restLoops,
723
+ activeCompactions: restCompactions
724
+ };
725
+ });
726
+ }
727
+ }));
728
+
729
+ // src/react/stores/task-store.ts
730
+ import { create as create4 } from "zustand";
731
+ var EMPTY_TASKS = [];
732
+ var useTaskStore = create4()((set, get) => ({
733
+ ...createClientActions(set),
734
+ tasks: {},
735
+ setTasks: (sessionId, tasks) => {
736
+ set((state) => ({
737
+ tasks: { ...state.tasks, [sessionId]: tasks }
738
+ }));
739
+ },
740
+ getTasks: (sessionId) => {
741
+ return get().tasks[sessionId] ?? EMPTY_TASKS;
742
+ }
743
+ }));
744
+
745
+ // src/react/stores/session-store.ts
746
+ var DEFAULT_SESSION_MODE = "executing";
747
+ var onSessionChange = null;
748
+ function invalidateHomeSidebarSessions() {
749
+ const queryClient = globalThis.__agentQueryClient;
750
+ if (!queryClient) return;
751
+ void queryClient.invalidateQueries({ queryKey: ["sessions", "home-sidebar"] });
752
+ }
753
+ function isSessionAccessRevoked(error) {
754
+ if (error instanceof Error) {
755
+ const msg = error.message;
756
+ return msg.includes("API 403") || msg.includes("API 404");
757
+ }
758
+ return false;
759
+ }
760
+ function removeSessionArtifacts(sessionId) {
761
+ useChatStore.getState().clearMessages(sessionId);
762
+ useTaskStore.getState().setTasks(sessionId, []);
763
+ }
764
+ function pruneSessionState(state, sessionId) {
765
+ const nextFresh = new Set(state._freshSessions);
766
+ nextFresh.delete(sessionId);
767
+ const { [sessionId]: _mode, ...modes } = state.modes;
768
+ const { [sessionId]: _rewindDraft, ...rewindDrafts } = state.rewindDrafts;
769
+ return {
770
+ sessions: state.sessions.filter((session) => session.id !== sessionId),
771
+ activeSessionId: state.activeSessionId === sessionId ? null : state.activeSessionId,
772
+ modes,
773
+ rewindDrafts,
774
+ _freshSessions: nextFresh
775
+ };
776
+ }
777
+ function navigateAwayFromSession(sessionId) {
778
+ if (typeof window === "undefined") return;
779
+ if (window.location.pathname !== `/chat/${sessionId}`) return;
780
+ window.history.replaceState(window.history.state, "", "/chat");
781
+ window.dispatchEvent(new PopStateEvent("popstate"));
782
+ }
783
+ function handleUnreadableSession(set, get, sessionId) {
784
+ const wasActive = get().activeSessionId === sessionId;
785
+ removeSessionArtifacts(sessionId);
786
+ set((state) => pruneSessionState(state, sessionId));
787
+ if (wasActive) {
788
+ onSessionChange?.(null);
789
+ navigateAwayFromSession(sessionId);
790
+ }
791
+ invalidateHomeSidebarSessions();
792
+ }
793
+ function isPlainRecord(value) {
794
+ return typeof value === "object" && value !== null && !Array.isArray(value);
795
+ }
796
+ function extractModeFromBlocks2(blocks) {
797
+ const modeBlock = blocks.find((block) => block.type === "mode_change");
798
+ if (modeBlock && isPlainRecord(modeBlock.content) && (modeBlock.content.to === "planning" || modeBlock.content.to === "executing")) {
799
+ return modeBlock.content.to;
800
+ }
801
+ if (blocks.some((block) => block.type === "planning_enter")) return "planning";
802
+ if (blocks.some((block) => block.type === "planning_exit")) return "executing";
803
+ return null;
804
+ }
805
+ function toSelectionMap(value) {
806
+ if (!isPlainRecord(value)) return {};
807
+ const entries = Object.entries(value).map(([questionKey, optionIndexes]) => {
808
+ if (!Array.isArray(optionIndexes)) return null;
809
+ const parsedIndexes = optionIndexes.map((item) => typeof item === "number" ? item : Number(item)).filter((item) => Number.isInteger(item));
810
+ return [Number(questionKey), parsedIndexes];
811
+ }).filter((entry) => entry !== null);
812
+ return Object.fromEntries(entries);
813
+ }
814
+ function toCustomMap(value) {
815
+ if (!isPlainRecord(value)) return {};
816
+ const entries = Object.entries(value).filter(([, text]) => typeof text === "string").map(([questionKey, text]) => [Number(questionKey), text]);
817
+ return Object.fromEntries(entries);
818
+ }
819
+ function extractAskAnswers(turns) {
820
+ const answers = {};
821
+ for (const turn of turns) {
822
+ for (const block of turn.blocks) {
823
+ if (block.type !== "ask_user_answer" || typeof block.tool_call_id !== "string") continue;
824
+ answers[block.tool_call_id] = {
825
+ selections: toSelectionMap(isPlainRecord(block.content) ? block.content.selections : void 0),
826
+ custom: toCustomMap(isPlainRecord(block.content) ? block.content.custom : void 0)
827
+ };
828
+ }
829
+ }
830
+ return answers;
831
+ }
832
+ function registerCreatedSessionState(set, session, mode = DEFAULT_SESSION_MODE) {
833
+ useChatStore.getState().setTurns(session.id, []);
834
+ useTaskStore.getState().setTasks(session.id, []);
835
+ set((state) => ({
836
+ sessions: [session, ...state.sessions.filter((item) => item.id !== session.id)],
837
+ modes: { ...state.modes, [session.id]: state.modes[session.id] ?? mode },
838
+ _freshSessions: new Set(state._freshSessions).add(session.id)
839
+ }));
840
+ }
841
+ async function revalidateViewerSessions(existingSessions) {
842
+ const viewerSessions = existingSessions.filter((session) => session.viewer_role === "viewer");
843
+ if (viewerSessions.length === 0) {
844
+ return [];
845
+ }
846
+ const refreshed = await Promise.all(
847
+ viewerSessions.map(async (session) => {
848
+ try {
849
+ return await getSession(session.id);
850
+ } catch (error) {
851
+ if (isSessionAccessRevoked(error)) {
852
+ return null;
853
+ }
854
+ return session;
855
+ }
856
+ })
857
+ );
858
+ return refreshed.filter((session) => session !== null);
859
+ }
860
+ var useSessionStore = create5()((set, get) => ({
861
+ ...createClientActions(set),
862
+ sessions: [],
863
+ activeSessionId: null,
864
+ loading: false,
865
+ modes: {},
866
+ rewindDrafts: {},
867
+ _freshSessions: /* @__PURE__ */ new Set(),
868
+ fetchSessions: async () => {
869
+ set({ loading: true });
870
+ try {
871
+ const currentState = get();
872
+ const [sessions, viewerSessions] = await Promise.all([
873
+ listSessions(),
874
+ revalidateViewerSessions(currentState.sessions)
875
+ ]);
876
+ const knownSessionIds = new Set(sessions.map((session) => session.id));
877
+ const mergedSessions = [
878
+ ...sessions,
879
+ ...viewerSessions.filter((session) => !knownSessionIds.has(session.id))
880
+ ];
881
+ const removedSessionIds = currentState.sessions.filter(
882
+ (session) => session.viewer_role === "viewer" && !mergedSessions.some((candidate) => candidate.id === session.id)
883
+ ).map((session) => session.id);
884
+ const activeSessionId = currentState.activeSessionId;
885
+ for (const sessionId of removedSessionIds) {
886
+ removeSessionArtifacts(sessionId);
887
+ }
888
+ let nextState = {
889
+ sessions: mergedSessions,
890
+ activeSessionId: currentState.activeSessionId,
891
+ loading: false,
892
+ modes: currentState.modes,
893
+ rewindDrafts: currentState.rewindDrafts,
894
+ _freshSessions: currentState._freshSessions
895
+ };
896
+ for (const sessionId of removedSessionIds) {
897
+ nextState = {
898
+ ...nextState,
899
+ ...pruneSessionState(
900
+ {
901
+ ...currentState,
902
+ sessions: nextState.sessions ?? currentState.sessions,
903
+ activeSessionId: nextState.activeSessionId ?? currentState.activeSessionId,
904
+ loading: nextState.loading ?? currentState.loading,
905
+ modes: nextState.modes ?? currentState.modes,
906
+ rewindDrafts: nextState.rewindDrafts ?? currentState.rewindDrafts,
907
+ _freshSessions: nextState._freshSessions ?? currentState._freshSessions
908
+ },
909
+ sessionId
910
+ )
911
+ };
912
+ }
913
+ set(nextState);
914
+ if (activeSessionId && removedSessionIds.includes(activeSessionId)) {
915
+ onSessionChange?.(null);
916
+ navigateAwayFromSession(activeSessionId);
917
+ }
918
+ invalidateHomeSidebarSessions();
919
+ } catch (error) {
920
+ set({ loading: false });
921
+ throw error;
922
+ }
923
+ },
924
+ createSession: async (intent) => {
925
+ const { session_id } = await createSession(intent);
926
+ const now = (/* @__PURE__ */ new Date()).toISOString();
927
+ get().registerCreatedSession({
928
+ id: session_id,
929
+ intent: intent ?? "",
930
+ status: "created",
931
+ created_at: now,
932
+ updated_at: now
933
+ });
934
+ get().setActiveSession(session_id);
935
+ invalidateHomeSidebarSessions();
936
+ get().fetchSessions().catch(() => {
937
+ });
938
+ return session_id;
939
+ },
940
+ registerCreatedSession: (session, mode = DEFAULT_SESSION_MODE) => {
941
+ registerCreatedSessionState(set, session, mode);
942
+ },
943
+ upsertSession: (session) => {
944
+ set((state) => ({
945
+ sessions: state.sessions.some((item) => item.id === session.id) ? state.sessions.map((item) => item.id === session.id ? { ...item, ...session } : item) : [session, ...state.sessions]
946
+ }));
947
+ },
948
+ isFreshSession: (sessionId) => get()._freshSessions.has(sessionId),
949
+ setActiveSession: (id) => {
950
+ set({ activeSessionId: id });
951
+ onSessionChange?.(id);
952
+ getSession(id).then((detail) => {
953
+ set((state) => ({
954
+ sessions: state.sessions.some((s) => s.id === id) ? state.sessions.map(
955
+ (s) => s.id === id ? { ...s, status: detail.status, updated_at: detail.updated_at } : s
956
+ ) : [detail, ...state.sessions]
957
+ }));
958
+ }).catch(() => {
959
+ });
960
+ const tasksPromise = getSessionTasks(id).catch(() => null);
961
+ Promise.all([getSessionTurns(id), tasksPromise]).then(([turns, tasks]) => {
962
+ if (tasks) useTaskStore.getState().setTasks(id, tasks);
963
+ useChatStore.getState().setAskAnswers(id, extractAskAnswers(turns));
964
+ let inferredMode = DEFAULT_SESSION_MODE;
965
+ for (const turn of turns) {
966
+ const nextMode = extractModeFromBlocks2(turn.blocks);
967
+ if (nextMode) {
968
+ inferredMode = nextMode;
969
+ }
970
+ }
971
+ set((state) => {
972
+ if (state.modes[id] == null) {
973
+ return { modes: { ...state.modes, [id]: inferredMode } };
974
+ }
975
+ return state;
976
+ });
977
+ const isFresh = get()._freshSessions.has(id);
978
+ if (isFresh) {
979
+ set((state) => {
980
+ const next = new Set(state._freshSessions);
981
+ next.delete(id);
982
+ return { _freshSessions: next };
983
+ });
984
+ if (turns.length > 0) {
985
+ useChatStore.getState().setTurns(id, turns);
986
+ }
987
+ } else {
988
+ useChatStore.getState().setTurns(id, turns);
989
+ }
990
+ }).catch((error) => {
991
+ if (isSessionAccessRevoked(error)) {
992
+ handleUnreadableSession(set, get, id);
993
+ return;
994
+ }
995
+ console.error("Failed to load session data", error);
996
+ });
997
+ },
998
+ clearActiveSession: () => {
999
+ set({ activeSessionId: null });
1000
+ onSessionChange?.(null);
1001
+ },
1002
+ deleteSession: async (id) => {
1003
+ await deleteSession(id);
1004
+ invalidateHomeSidebarSessions();
1005
+ const wasActive = get().activeSessionId === id;
1006
+ await get().fetchSessions();
1007
+ if (!wasActive) {
1008
+ return;
1009
+ }
1010
+ const nextId = get().sessions[0]?.id ?? null;
1011
+ if (nextId) {
1012
+ get().setActiveSession(nextId);
1013
+ return;
1014
+ }
1015
+ get().clearActiveSession();
1016
+ },
1017
+ updateSessionStatus: (sessionId, status) => {
1018
+ set((state) => ({
1019
+ sessions: state.sessions.map((s) => s.id === sessionId ? { ...s, status } : s)
1020
+ }));
1021
+ if (status === "failed" || status === "interrupted") {
1022
+ useChatStore.getState().setStreaming(sessionId, false);
1023
+ if (status === "interrupted") {
1024
+ useChatStore.getState().markInterrupted(sessionId);
1025
+ } else {
1026
+ useChatStore.getState().markFailed(sessionId);
1027
+ }
1028
+ }
1029
+ },
1030
+ updateSessionIntent: (sessionId, intent) => {
1031
+ set((state) => ({
1032
+ sessions: state.sessions.map((s) => s.id === sessionId ? { ...s, intent } : s)
1033
+ }));
1034
+ },
1035
+ patchSession: (sessionId, patch) => {
1036
+ set((state) => ({
1037
+ sessions: state.sessions.map(
1038
+ (s) => s.id === sessionId ? { ...s, ...patch } : s
1039
+ )
1040
+ }));
1041
+ },
1042
+ pinSession: async (sessionId, pinned) => {
1043
+ const updated = await pinSession(sessionId, pinned);
1044
+ set((state) => ({
1045
+ sessions: state.sessions.map(
1046
+ (s) => s.id === sessionId ? { ...s, is_pinned: updated.is_pinned, pinned_at: updated.pinned_at } : s
1047
+ )
1048
+ }));
1049
+ invalidateHomeSidebarSessions();
1050
+ },
1051
+ setRewindDraft: (sessionId, text) => {
1052
+ set((state) => {
1053
+ if (text == null) {
1054
+ const { [sessionId]: _, ...rest } = state.rewindDrafts;
1055
+ return { rewindDrafts: rest };
1056
+ }
1057
+ return {
1058
+ rewindDrafts: {
1059
+ ...state.rewindDrafts,
1060
+ [sessionId]: text
1061
+ }
1062
+ };
1063
+ });
1064
+ },
1065
+ setMode: (sessionId, mode) => {
1066
+ set((state) => ({ modes: { ...state.modes, [sessionId]: mode } }));
1067
+ },
1068
+ togglePlanningMode: (sessionId) => {
1069
+ const current = get().modes[sessionId] ?? DEFAULT_SESSION_MODE;
1070
+ const next = current === "executing" ? "planning" : "executing";
1071
+ set((state) => ({ modes: { ...state.modes, [sessionId]: next } }));
1072
+ },
1073
+ toggleSharing: async (sessionId) => {
1074
+ const session = get().sessions.find((s) => s.id === sessionId);
1075
+ if (!session) return;
1076
+ const newShared = !session.shared;
1077
+ const result = await updateSharing(sessionId, newShared);
1078
+ set((state) => ({
1079
+ sessions: state.sessions.map(
1080
+ (s) => s.id === sessionId ? { ...s, shared: result.shared } : s
1081
+ )
1082
+ }));
1083
+ },
1084
+ reset: () => {
1085
+ set({
1086
+ sessions: [],
1087
+ activeSessionId: null,
1088
+ loading: false,
1089
+ modes: {},
1090
+ rewindDrafts: {},
1091
+ _freshSessions: /* @__PURE__ */ new Set()
1092
+ });
1093
+ invalidateHomeSidebarSessions();
1094
+ }
1095
+ }));
1096
+ setChatStoreSessionAccessor(() => useSessionStore.getState().activeSessionId);
1097
+
1098
+ // src/react/stores/auth-store.ts
1099
+ var noopStorage = {
1100
+ getItem: () => null,
1101
+ setItem: () => {
1102
+ },
1103
+ removeItem: () => {
1104
+ }
1105
+ };
1106
+ function shouldClearPersistedAuthState(value) {
1107
+ try {
1108
+ const parsed = JSON.parse(value);
1109
+ return parsed.state?.token == null && parsed.state?.user == null;
1110
+ } catch {
1111
+ return false;
1112
+ }
1113
+ }
1114
+ var authStorage = createJSONStorage(() => {
1115
+ if (typeof localStorage === "undefined") {
1116
+ return noopStorage;
1117
+ }
1118
+ return {
1119
+ getItem: (name) => localStorage.getItem(name),
1120
+ setItem: (name, value) => {
1121
+ if (shouldClearPersistedAuthState(value)) {
1122
+ localStorage.removeItem(name);
1123
+ return;
1124
+ }
1125
+ localStorage.setItem(name, value);
1126
+ },
1127
+ removeItem: (name) => localStorage.removeItem(name)
1128
+ };
1129
+ });
1130
+ function finishAuth(set, payload) {
1131
+ set({
1132
+ token: payload.token,
1133
+ socketAuthToken: null,
1134
+ user: payload.user,
1135
+ loading: false,
1136
+ error: null
1137
+ });
1138
+ agentSocket?.reconnect();
1139
+ useSessionStore.getState().fetchSessions().catch(() => {
1140
+ });
1141
+ }
1142
+ function finishCookieHydration(set, payload) {
1143
+ set({
1144
+ token: null,
1145
+ socketAuthToken: payload.token,
1146
+ user: payload.user,
1147
+ loading: false,
1148
+ error: null
1149
+ });
1150
+ agentSocket?.reconnect();
1151
+ useSessionStore.getState().fetchSessions().catch(() => {
1152
+ });
1153
+ }
1154
+ function toUser(info) {
1155
+ return {
1156
+ id: info.id,
1157
+ username: info.username,
1158
+ display_name: info.display_name,
1159
+ avatar_url: info.avatar_url,
1160
+ is_admin: info.is_admin
1161
+ };
1162
+ }
1163
+ var useAuthStore = create6()(
1164
+ persist(
1165
+ (set, get) => ({
1166
+ ...createClientActions(set),
1167
+ token: null,
1168
+ socketAuthToken: null,
1169
+ user: null,
1170
+ loading: false,
1171
+ error: null,
1172
+ logout: () => {
1173
+ void logout().catch(() => {
1174
+ });
1175
+ set({ token: null, socketAuthToken: null, user: null, error: null });
1176
+ agentSocket?.disconnect();
1177
+ useSessionStore.getState().reset();
1178
+ },
1179
+ checkAuth: async () => {
1180
+ const token = get().token;
1181
+ if (!token) return;
1182
+ try {
1183
+ const auth = await getMe();
1184
+ finishAuth(set, { token: auth.token, user: toUser(auth) });
1185
+ } catch {
1186
+ set({ token: null, socketAuthToken: null, user: null, error: null });
1187
+ useSessionStore.getState().reset();
1188
+ }
1189
+ },
1190
+ hydrateFromCookie: async () => {
1191
+ set({ loading: true, error: null });
1192
+ const previousToken = get().token;
1193
+ if (previousToken) {
1194
+ try {
1195
+ const auth = await getMe();
1196
+ finishAuth(set, { token: auth.token, user: toUser(auth) });
1197
+ return;
1198
+ } catch {
1199
+ set({ token: null, socketAuthToken: null, user: null, error: null });
1200
+ useSessionStore.getState().reset();
1201
+ }
1202
+ }
1203
+ try {
1204
+ const auth = await getMe();
1205
+ finishCookieHydration(set, { token: auth.token, user: toUser(auth) });
1206
+ } catch {
1207
+ set({
1208
+ token: null,
1209
+ socketAuthToken: null,
1210
+ user: null,
1211
+ loading: false,
1212
+ error: null
1213
+ });
1214
+ useSessionStore.getState().reset();
1215
+ }
1216
+ }
1217
+ }),
1218
+ {
1219
+ name: "agent-auth",
1220
+ storage: authStorage,
1221
+ partialize: (state) => {
1222
+ if (state.socketAuthToken) {
1223
+ return { token: null, user: null };
1224
+ }
1225
+ return { token: state.token, user: state.user };
1226
+ }
1227
+ }
1228
+ )
1229
+ );
1230
+
1231
+ // src/react/stores/background-store.ts
1232
+ import { create as create7 } from "zustand";
1233
+ var useBackgroundStore = create7()((set) => ({
1234
+ ...createClientActions(set),
1235
+ tasks: {},
1236
+ selectedTaskId: {},
1237
+ setTasks: (sessionId, tasks) => set((state) => ({
1238
+ tasks: { ...state.tasks, [sessionId]: tasks },
1239
+ selectedTaskId: {
1240
+ ...state.selectedTaskId,
1241
+ [sessionId]: state.selectedTaskId[sessionId] ?? tasks[0]?.id ?? null
1242
+ }
1243
+ })),
1244
+ upsertTask: (sessionId, task) => set((state) => {
1245
+ const existing = state.tasks[sessionId] ?? [];
1246
+ const index = existing.findIndex((item) => item.id === task.id);
1247
+ const next = [...existing];
1248
+ if (index >= 0) {
1249
+ next[index] = { ...next[index], ...task };
1250
+ } else {
1251
+ next.unshift(task);
1252
+ }
1253
+ return {
1254
+ tasks: { ...state.tasks, [sessionId]: next },
1255
+ selectedTaskId: {
1256
+ ...state.selectedTaskId,
1257
+ [sessionId]: state.selectedTaskId[sessionId] ?? task.id
1258
+ }
1259
+ };
1260
+ }),
1261
+ selectTask: (sessionId, taskId) => set((state) => ({
1262
+ selectedTaskId: { ...state.selectedTaskId, [sessionId]: taskId }
1263
+ }))
1264
+ }));
1265
+
1266
+ // src/react/stores/card-state-store.ts
1267
+ import { create as create8 } from "zustand";
1268
+ var useCardStateStore = create8((set, get) => ({
1269
+ ...createClientActions(set),
1270
+ states: {},
1271
+ getCardState: (cardId) => {
1272
+ return get().states[cardId];
1273
+ },
1274
+ setCardState: (cardId, state) => {
1275
+ set((prev) => ({
1276
+ states: { ...prev.states, [cardId]: state }
1277
+ }));
1278
+ },
1279
+ removeCardState: (cardId) => {
1280
+ set((prev) => {
1281
+ const { [cardId]: _, ...rest } = prev.states;
1282
+ return { states: rest };
1283
+ });
1284
+ },
1285
+ clearAllStates: () => {
1286
+ set({ states: {} });
1287
+ }
1288
+ }));
1289
+
1290
+ // src/react/stores/connection-store.ts
1291
+ import { create as create9 } from "zustand";
1292
+ var initialConnectionState = {
1293
+ status: "disconnected",
1294
+ reconnectAttempt: 0,
1295
+ lastConnectedAt: null,
1296
+ lastDisconnectedAt: null,
1297
+ hasEverConnected: false
1298
+ };
1299
+ var useConnectionStore = create9()((set) => ({
1300
+ ...createClientActions(set),
1301
+ ...initialConnectionState,
1302
+ markConnecting: (attempt = 0) => set({
1303
+ status: "connecting",
1304
+ reconnectAttempt: attempt
1305
+ }),
1306
+ markConnected: () => set({
1307
+ status: "connected",
1308
+ reconnectAttempt: 0,
1309
+ lastConnectedAt: Date.now(),
1310
+ hasEverConnected: true
1311
+ }),
1312
+ markDisconnected: () => set({
1313
+ status: "disconnected",
1314
+ reconnectAttempt: 0,
1315
+ lastDisconnectedAt: Date.now()
1316
+ }),
1317
+ reset: () => set(initialConnectionState)
1318
+ }));
1319
+
1320
+ // src/react/stores/runtime-store.ts
1321
+ import { create as create10 } from "zustand";
1322
+ var useRuntimeStore = create10()((set) => ({
1323
+ ...createClientActions(set),
1324
+ events: {},
1325
+ addEvent: (sessionId, event) => set((state) => {
1326
+ const current = state.events[sessionId] ?? [];
1327
+ const nextEvent = {
1328
+ ...event,
1329
+ id: `${Date.now()}-${current.length}`,
1330
+ sessionId,
1331
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1332
+ };
1333
+ return {
1334
+ events: {
1335
+ ...state.events,
1336
+ [sessionId]: [...current.slice(-59), nextEvent]
1337
+ }
1338
+ };
1339
+ }),
1340
+ clearSession: (sessionId) => set((state) => ({
1341
+ events: { ...state.events, [sessionId]: [] }
1342
+ }))
1343
+ }));
1344
+
1345
+ // src/react/stores/gis-store.ts
1346
+ import { create as create11 } from "zustand";
1347
+ var EMPTY_GOALS = [];
1348
+ var EMPTY_RESOURCES = [];
1349
+ var EMPTY_TARGETS = [];
1350
+ var EMPTY_COMMANDS = [];
1351
+ function newCommandId() {
1352
+ if (typeof globalThis !== "undefined" && "crypto" in globalThis) {
1353
+ return globalThis.crypto?.randomUUID?.() ?? `gis-map-${Date.now()}-${Math.random()}`;
1354
+ }
1355
+ return `gis-map-${Date.now()}-${Math.random()}`;
1356
+ }
1357
+ var useGisStore = create11()((set, get) => ({
1358
+ ...createClientActions(set),
1359
+ goalsBySession: {},
1360
+ resourcesBySession: {},
1361
+ targetsBySession: {},
1362
+ pendingMapCommandsBySession: {},
1363
+ setGoals: (sessionId, goals) => {
1364
+ set((state) => ({
1365
+ goalsBySession: { ...state.goalsBySession, [sessionId]: goals }
1366
+ }));
1367
+ },
1368
+ setResources: (sessionId, resources) => {
1369
+ set((state) => ({
1370
+ resourcesBySession: { ...state.resourcesBySession, [sessionId]: resources }
1371
+ }));
1372
+ },
1373
+ setTargets: (sessionId, targets) => {
1374
+ set((state) => ({
1375
+ targetsBySession: { ...state.targetsBySession, [sessionId]: targets }
1376
+ }));
1377
+ },
1378
+ pushMapCommand: (sessionId, command) => {
1379
+ set((state) => ({
1380
+ pendingMapCommandsBySession: {
1381
+ ...state.pendingMapCommandsBySession,
1382
+ [sessionId]: [
1383
+ ...state.pendingMapCommandsBySession[sessionId] ?? EMPTY_COMMANDS,
1384
+ {
1385
+ ...command,
1386
+ id: newCommandId(),
1387
+ createdAt: Date.now()
1388
+ }
1389
+ ]
1390
+ }
1391
+ }));
1392
+ },
1393
+ consumeMapCommand: (sessionId, commandId) => {
1394
+ set((state) => ({
1395
+ pendingMapCommandsBySession: {
1396
+ ...state.pendingMapCommandsBySession,
1397
+ [sessionId]: (state.pendingMapCommandsBySession[sessionId] ?? EMPTY_COMMANDS).filter(
1398
+ (command) => command.id !== commandId
1399
+ )
1400
+ }
1401
+ }));
1402
+ },
1403
+ getGoals: (sessionId) => get().goalsBySession[sessionId] ?? EMPTY_GOALS,
1404
+ getResources: (sessionId) => get().resourcesBySession[sessionId] ?? EMPTY_RESOURCES,
1405
+ getTargets: (sessionId) => get().targetsBySession[sessionId] ?? EMPTY_TARGETS,
1406
+ getPendingMapCommands: (sessionId) => get().pendingMapCommandsBySession[sessionId] ?? EMPTY_COMMANDS
1407
+ }));
1408
+
1409
+ // src/react/stores/answer-callback-store.ts
1410
+ import { create as create12 } from "zustand";
1411
+ var useAnswerCallbackStore = create12()((set) => ({
1412
+ ...createClientActions(set),
1413
+ callbacks: {},
1414
+ setAnswerCallback: (sessionId, callback) => {
1415
+ set((state) => ({
1416
+ callbacks: {
1417
+ ...state.callbacks,
1418
+ [sessionId]: callback
1419
+ }
1420
+ }));
1421
+ }
1422
+ }));
1423
+
1424
+ // src/react/stores/runtime-features-store.ts
1425
+ import { create as create13 } from "zustand";
1426
+ var useRuntimeFeaturesStore = create13((set) => ({
1427
+ ...createClientActions(set),
1428
+ asrEnabled: false,
1429
+ asrProvider: "volcengine",
1430
+ publicSharingEnabled: false,
1431
+ memoryEnabled: false,
1432
+ setFeatures: (features) => set((prev) => ({
1433
+ asrEnabled: features.asrEnabled ?? prev.asrEnabled,
1434
+ asrProvider: features.asrProvider ?? prev.asrProvider,
1435
+ publicSharingEnabled: features.publicSharingEnabled ?? prev.publicSharingEnabled,
1436
+ memoryEnabled: features.memoryEnabled ?? prev.memoryEnabled
1437
+ }))
1438
+ }));
1439
+
1440
+ // src/react/bootstrap.ts
1441
+ var bootstrappedClient = null;
1442
+ function getBootstrappedClient() {
1443
+ if (!bootstrappedClient) {
1444
+ throw new Error("bootstrapBladeClient() must be called before any SDK usage");
1445
+ }
1446
+ return bootstrappedClient;
1447
+ }
1448
+
1449
+ // src/react/api/client.ts
1450
+ function getClient() {
1451
+ return getBootstrappedClient();
1452
+ }
1453
+
1454
+ // src/react/api/sessions.ts
1455
+ var r2 = () => getClient().sessions;
1456
+ var listSessions = (...args) => r2().listSessions(...args);
1457
+ var createSession = (...args) => r2().createSession(...args);
1458
+ var getSession = (...args) => r2().getSession(...args);
1459
+ var pinSession = (...args) => r2().pinSession(...args);
1460
+ var updateSharing = (...args) => r2().updateSharing(...args);
1461
+ var getSessionTasks = (...args) => r2().getSessionTasks(...args);
1462
+ var getSessionTurns = (...args) => r2().getSessionTurns(...args);
1463
+ var getSessionCheckpoints = (...args) => r2().getSessionCheckpoints(...args);
1464
+ var checkoutSession = (...args) => r2().checkoutSession(...args);
1465
+ var deleteSession = (...args) => r2().deleteSession(...args);
1466
+
1467
+ // src/react/lib/utils.ts
1468
+ import { clsx } from "clsx";
1469
+ import { twMerge } from "tailwind-merge";
1470
+ function cn(...inputs) {
1471
+ return twMerge(clsx(inputs));
1472
+ }
1473
+
1474
+ // src/react/components/session/SessionDetail.tsx
1475
+ import * as Tooltip from "@radix-ui/react-tooltip";
1476
+ import { useQuery, useQueryClient } from "@tanstack/react-query";
1477
+ import { HelpCircle } from "lucide-react";
1478
+ import { useEffect, useMemo, useRef, useState } from "react";
1479
+ import { jsx, jsxs } from "react/jsx-runtime";
1480
+ var ROW_HEIGHT = 32;
1481
+ var SVG_WIDTH = 44;
1482
+ function SessionDetailTrigger({
1483
+ onClick,
1484
+ showReplayDot = false
1485
+ }) {
1486
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
1487
+ /* @__PURE__ */ jsxs(
1488
+ "button",
1489
+ {
1490
+ type: "button",
1491
+ onClick,
1492
+ className: "flex items-center gap-1.5 text-[10px] font-medium tracking-[0.18em] text-[hsl(var(--muted-foreground))] uppercase transition-colors hover:text-[hsl(var(--foreground))]",
1493
+ children: [
1494
+ /* @__PURE__ */ jsx("span", { children: "\u5386\u53F2\u56DE\u6EAF" }),
1495
+ /* @__PURE__ */ jsx(HelpCircle, { size: 12 })
1496
+ ]
1497
+ }
1498
+ ),
1499
+ showReplayDot ? /* @__PURE__ */ jsx(Tooltip.Provider, { delayDuration: 150, children: /* @__PURE__ */ jsxs(Tooltip.Root, { children: [
1500
+ /* @__PURE__ */ jsx(Tooltip.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(
1501
+ "span",
1502
+ {
1503
+ className: "h-[5px] w-[5px] shrink-0 rounded-full bg-amber-400",
1504
+ "aria-label": "\u5F53\u524D\u5C5E\u4E8E\u56DE\u653E\u6A21\u5F0F"
1505
+ }
1506
+ ) }),
1507
+ /* @__PURE__ */ jsx(Tooltip.Portal, { children: /* @__PURE__ */ jsxs(
1508
+ Tooltip.Content,
1509
+ {
1510
+ side: "top",
1511
+ className: "z-[60] rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--popover))] px-2 py-1 text-[11px] text-[hsl(var(--popover-foreground))] shadow-lg",
1512
+ children: [
1513
+ "\u5F53\u524D\u5C5E\u4E8E\u56DE\u653E\u6A21\u5F0F",
1514
+ /* @__PURE__ */ jsx(Tooltip.Arrow, { className: "fill-[hsl(var(--popover))]" })
1515
+ ]
1516
+ }
1517
+ ) })
1518
+ ] }) }) : null
1519
+ ] });
1520
+ }
1521
+ function SessionDetail({ session }) {
1522
+ const [pendingNodeId, setPendingNodeId] = useState(null);
1523
+ const isStreaming = useChatStore((state) => state.isStreaming[session.id] ?? false);
1524
+ const setRewindDraft = useSessionStore((state) => state.setRewindDraft);
1525
+ const setActiveSession = useSessionStore((state) => state.setActiveSession);
1526
+ const queryClient = useQueryClient();
1527
+ const { data: tree } = useQuery({
1528
+ queryKey: ["checkpoints", session.id],
1529
+ queryFn: ({ signal }) => getSessionCheckpoints(session.id, { signal })
1530
+ });
1531
+ const prevStreamingRef = useRef(isStreaming);
1532
+ useEffect(() => {
1533
+ if (prevStreamingRef.current && !isStreaming) {
1534
+ void queryClient.invalidateQueries({ queryKey: ["checkpoints", session.id] });
1535
+ }
1536
+ prevStreamingRef.current = isStreaming;
1537
+ }, [isStreaming, queryClient, session.id]);
1538
+ const hasHistory = (tree?.nodes.length ?? 0) > 0;
1539
+ const layout = useMemo(() => buildLayoutNodes(tree), [tree]);
1540
+ const nodeMap = useMemo(
1541
+ () => new Map((tree?.nodes ?? []).map((node) => [node.id, node])),
1542
+ [tree]
1543
+ );
1544
+ const isBusy = isStreaming || pendingNodeId !== null;
1545
+ const graphHeight = Math.max((layout.maxRow + 1) * ROW_HEIGHT, ROW_HEIGHT);
1546
+ const graphWidth = Math.max(SVG_WIDTH, getColumnX(layout.maxColumn) + 12);
1547
+ async function checkoutNode(node, position) {
1548
+ if (isBusy) {
1549
+ return;
1550
+ }
1551
+ setPendingNodeId(node.id);
1552
+ try {
1553
+ const result = await checkoutSession(session.id, node.id, position);
1554
+ if (result.position === "before" && result.content.trim()) {
1555
+ setRewindDraft(session.id, result.content);
1556
+ } else {
1557
+ setRewindDraft(session.id, "");
1558
+ }
1559
+ setActiveSession(session.id);
1560
+ await queryClient.invalidateQueries({ queryKey: ["checkpoints", session.id] });
1561
+ } finally {
1562
+ setPendingNodeId(null);
1563
+ }
1564
+ }
1565
+ async function handleVirtualClick(node) {
1566
+ if (isBusy || !node.isVirtual) return;
1567
+ const leafId = node.content;
1568
+ if (!leafId) return;
1569
+ if (node.isActive) return;
1570
+ const sourceNode = nodeMap.get(leafId);
1571
+ if (!sourceNode) return;
1572
+ await checkoutNode(sourceNode, "turn_end");
1573
+ }
1574
+ async function handleNodeSelect(node) {
1575
+ if (isBusy) {
1576
+ return;
1577
+ }
1578
+ const sourceNode = nodeMap.get(node.id);
1579
+ if (!sourceNode) {
1580
+ return;
1581
+ }
1582
+ await checkoutNode(sourceNode, "before");
1583
+ }
1584
+ if (!hasHistory) return null;
1585
+ return /* @__PURE__ */ jsx(Tooltip.Provider, { delayDuration: 200, children: /* @__PURE__ */ jsx("div", { className: "flex max-h-52 justify-center overflow-x-auto overflow-y-auto", children: /* @__PURE__ */ jsxs("div", { className: "relative", style: { height: graphHeight, width: graphWidth }, children: [
1586
+ /* @__PURE__ */ jsx(
1587
+ "svg",
1588
+ {
1589
+ "aria-hidden": "true",
1590
+ className: "pointer-events-none absolute left-0 top-0 z-10 overflow-visible",
1591
+ width: graphWidth,
1592
+ height: graphHeight,
1593
+ viewBox: `0 0 ${graphWidth} ${graphHeight}`,
1594
+ fill: "none",
1595
+ children: layout.nodes.map((node) => {
1596
+ if (node.parentRow == null || node.parentColumn == null) {
1597
+ return null;
1598
+ }
1599
+ const childStroke = node.isActive ? "hsl(var(--primary))" : "hsl(var(--muted-foreground))";
1600
+ return /* @__PURE__ */ jsx(
1601
+ "path",
1602
+ {
1603
+ d: buildEdgePath(
1604
+ getColumnX(node.parentColumn),
1605
+ getRowY(node.parentRow),
1606
+ getColumnX(node.column),
1607
+ getRowY(node.row)
1608
+ ),
1609
+ stroke: childStroke,
1610
+ strokeOpacity: node.isActive ? 0.95 : 0.45,
1611
+ strokeWidth: node.isActive ? 1.8 : 1.4,
1612
+ strokeLinecap: "round",
1613
+ strokeLinejoin: "round",
1614
+ strokeDasharray: node.isVirtual ? "3 3" : void 0
1615
+ },
1616
+ `path-${node.id}`
1617
+ );
1618
+ })
1619
+ }
1620
+ ),
1621
+ /* @__PURE__ */ jsx("div", { className: "relative z-0 h-full", children: layout.nodes.map((node) => {
1622
+ const isPendingNode = pendingNodeId === node.id;
1623
+ const color = node.isActive ? "hsl(var(--primary))" : "hsl(var(--muted-foreground))";
1624
+ const previewText = node.isVirtual ? isPendingNode ? "\u5207\u6362\u4E2D..." : "" : isPendingNode ? "\u5207\u6362\u4E2D..." : node.preview;
1625
+ const shouldShowTooltip = !node.isVirtual || Boolean(previewText);
1626
+ const button = /* @__PURE__ */ jsx(
1627
+ "button",
1628
+ {
1629
+ type: "button",
1630
+ onClick: () => void (node.isVirtual ? handleVirtualClick(node) : handleNodeSelect(node)),
1631
+ disabled: isBusy,
1632
+ "aria-current": !node.isVirtual && node.isCurrent ? "step" : void 0,
1633
+ "aria-label": node.isVirtual ? "\u5207\u6362\u5230\u8BE5\u5206\u652F" : node.preview,
1634
+ className: cn(
1635
+ "absolute z-20 h-8 w-6 overflow-visible",
1636
+ isBusy ? "cursor-not-allowed opacity-60" : "cursor-pointer"
1637
+ ),
1638
+ style: {
1639
+ top: node.row * ROW_HEIGHT,
1640
+ left: getColumnX(node.column) - 12
1641
+ },
1642
+ children: /* @__PURE__ */ jsx(
1643
+ "span",
1644
+ {
1645
+ "aria-hidden": "true",
1646
+ className: "absolute left-0 top-0 h-8 w-6",
1647
+ children: /* @__PURE__ */ jsx(
1648
+ "svg",
1649
+ {
1650
+ "aria-hidden": "true",
1651
+ role: "img",
1652
+ className: "pointer-events-none absolute left-0 top-0 h-full w-full overflow-visible",
1653
+ width: 24,
1654
+ height: ROW_HEIGHT,
1655
+ viewBox: `0 0 24 ${ROW_HEIGHT}`,
1656
+ fill: "none",
1657
+ children: node.isVirtual ? /* @__PURE__ */ jsx(
1658
+ "circle",
1659
+ {
1660
+ cx: 12,
1661
+ cy: ROW_HEIGHT / 2,
1662
+ r: 3.5,
1663
+ fill: "none",
1664
+ stroke: color,
1665
+ strokeWidth: 1.2,
1666
+ strokeDasharray: "2 2",
1667
+ opacity: 0.7
1668
+ }
1669
+ ) : /* @__PURE__ */ jsxs("g", { children: [
1670
+ node.isCurrent ? /* @__PURE__ */ jsx(
1671
+ "circle",
1672
+ {
1673
+ cx: 12,
1674
+ cy: ROW_HEIGHT / 2,
1675
+ r: 9,
1676
+ fill: color,
1677
+ opacity: 0.18
1678
+ }
1679
+ ) : null,
1680
+ /* @__PURE__ */ jsx(
1681
+ "circle",
1682
+ {
1683
+ cx: 12,
1684
+ cy: ROW_HEIGHT / 2,
1685
+ r: node.isCurrent ? 5.5 : 4,
1686
+ fill: color
1687
+ }
1688
+ )
1689
+ ] })
1690
+ }
1691
+ )
1692
+ }
1693
+ )
1694
+ }
1695
+ );
1696
+ if (!shouldShowTooltip) {
1697
+ return /* @__PURE__ */ jsx("div", { children: button }, node.id);
1698
+ }
1699
+ return /* @__PURE__ */ jsxs(Tooltip.Root, { children: [
1700
+ /* @__PURE__ */ jsx(Tooltip.Trigger, { asChild: true, children: button }),
1701
+ /* @__PURE__ */ jsx(Tooltip.Portal, { children: /* @__PURE__ */ jsx(
1702
+ Tooltip.Content,
1703
+ {
1704
+ side: "right",
1705
+ sideOffset: 8,
1706
+ className: cn(
1707
+ "rounded-md border border-border bg-popover px-2 py-1 text-xs text-foreground shadow-md",
1708
+ !node.isVirtual && node.isCurrent && "font-semibold"
1709
+ ),
1710
+ children: previewText
1711
+ }
1712
+ ) })
1713
+ ] }, node.id);
1714
+ }) })
1715
+ ] }) }) });
1716
+ }
1717
+ function buildLayoutNodes(tree) {
1718
+ if (!tree || tree.nodes.length === 0) {
1719
+ return { nodes: [], hasCycle: false, maxColumn: 0, maxRow: 0 };
1720
+ }
1721
+ const nodeMap = new Map(tree.nodes.map((node) => [node.id, node]));
1722
+ const order = new Map(tree.nodes.map((node, index) => [node.id, index]));
1723
+ const childrenMap = /* @__PURE__ */ new Map();
1724
+ for (const node of tree.nodes) {
1725
+ const parentId = node.parent_id && nodeMap.has(node.parent_id) ? node.parent_id : null;
1726
+ const siblings = childrenMap.get(parentId) ?? [];
1727
+ siblings.push(node);
1728
+ childrenMap.set(parentId, siblings);
1729
+ }
1730
+ const activeIds = /* @__PURE__ */ new Set();
1731
+ const activeVisited = /* @__PURE__ */ new Set();
1732
+ const leafId = tree.leaf_id;
1733
+ let current = tree.leaf_id;
1734
+ while (current && nodeMap.has(current) && !activeVisited.has(current)) {
1735
+ activeIds.add(current);
1736
+ activeVisited.add(current);
1737
+ current = nodeMap.get(current)?.parent_id ?? null;
1738
+ }
1739
+ const layoutNodes = [];
1740
+ const placed = /* @__PURE__ */ new Set();
1741
+ const visiting = /* @__PURE__ */ new Set();
1742
+ let nextColumn = 1;
1743
+ let maxColumn = 0;
1744
+ let maxRow = 0;
1745
+ let hasCycle = false;
1746
+ const rootSort = (a, b) => (order.get(a.id) ?? 0) - (order.get(b.id) ?? 0);
1747
+ const childSort = (a, b) => (order.get(a.id) ?? 0) - (order.get(b.id) ?? 0);
1748
+ const allocateColumn = () => {
1749
+ const fresh = nextColumn;
1750
+ nextColumn += 1;
1751
+ return fresh;
1752
+ };
1753
+ function walk(node, parent, column, row) {
1754
+ if (visiting.has(node.id)) {
1755
+ hasCycle = true;
1756
+ return;
1757
+ }
1758
+ if (placed.has(node.id)) {
1759
+ return;
1760
+ }
1761
+ visiting.add(node.id);
1762
+ maxColumn = Math.max(maxColumn, column);
1763
+ maxRow = Math.max(maxRow, row);
1764
+ const layoutNode = {
1765
+ id: node.id,
1766
+ column,
1767
+ row,
1768
+ parentRow: parent?.row ?? null,
1769
+ parentColumn: parent?.column ?? null,
1770
+ isActive: activeIds.has(node.id),
1771
+ isCurrent: node.id === leafId,
1772
+ isVirtual: false,
1773
+ preview: formatPreview(node.content_preview),
1774
+ content: node.content
1775
+ };
1776
+ layoutNodes.push(layoutNode);
1777
+ placed.add(node.id);
1778
+ const children = [...childrenMap.get(node.id) ?? []].sort(childSort);
1779
+ if (children.length === 0) {
1780
+ const virtualRow = row + 1;
1781
+ maxRow = Math.max(maxRow, virtualRow);
1782
+ layoutNodes.push({
1783
+ id: `__virtual__${node.id}`,
1784
+ column,
1785
+ row: virtualRow,
1786
+ parentRow: layoutNode.row,
1787
+ parentColumn: layoutNode.column,
1788
+ isActive: activeIds.has(node.id),
1789
+ isCurrent: false,
1790
+ isVirtual: true,
1791
+ preview: "",
1792
+ content: node.id
1793
+ });
1794
+ } else {
1795
+ for (const [index, child] of children.entries()) {
1796
+ if (visiting.has(child.id)) {
1797
+ hasCycle = true;
1798
+ continue;
1799
+ }
1800
+ const childColumn = index === 0 ? column : allocateColumn();
1801
+ walk(child, layoutNode, childColumn, row + 1);
1802
+ }
1803
+ }
1804
+ visiting.delete(node.id);
1805
+ }
1806
+ const roots = [...childrenMap.get(null) ?? []].sort(rootSort);
1807
+ for (const [index, root] of roots.entries()) {
1808
+ const rootColumn = index === 0 ? 0 : allocateColumn();
1809
+ walk(root, null, rootColumn, 0);
1810
+ }
1811
+ for (const node of [...tree.nodes].sort(rootSort)) {
1812
+ if (!placed.has(node.id)) {
1813
+ hasCycle = true;
1814
+ const rootColumn = layoutNodes.length === 0 ? 0 : allocateColumn();
1815
+ walk(node, null, rootColumn, 0);
1816
+ }
1817
+ }
1818
+ return {
1819
+ nodes: layoutNodes,
1820
+ hasCycle,
1821
+ maxColumn,
1822
+ maxRow
1823
+ };
1824
+ }
1825
+ function formatPreview(preview) {
1826
+ const normalized = preview.replace(/\s+/g, " ").trim();
1827
+ return normalized.length > 0 ? normalized : "(\u7A7A\u6D88\u606F)";
1828
+ }
1829
+ function getColumnX(column) {
1830
+ return 12 + column * 24;
1831
+ }
1832
+ function getRowY(row) {
1833
+ return row * ROW_HEIGHT + ROW_HEIGHT / 2;
1834
+ }
1835
+ function buildEdgePath(fromX, fromY, toX, toY) {
1836
+ if (fromX === toX || fromY === toY) {
1837
+ return `M ${fromX} ${fromY} L ${toX} ${toY}`;
1838
+ }
1839
+ const r3 = Math.min(5, Math.abs(toX - fromX), Math.abs(toY - fromY));
1840
+ const dx = toX > fromX ? 1 : -1;
1841
+ return [
1842
+ `M ${fromX} ${fromY}`,
1843
+ `H ${toX - dx * r3}`,
1844
+ `A ${r3} ${r3} 0 0 ${dx > 0 ? 1 : 0} ${toX} ${fromY + r3}`,
1845
+ `V ${toY}`
1846
+ ].join(" ");
1847
+ }
1848
+
1849
+ // src/react/components/session/SessionList.tsx
1850
+ import { Plus } from "lucide-react";
1851
+ import { useState as useState2 } from "react";
1852
+
1853
+ // src/react/hooks/use-session.ts
1854
+ function useSession() {
1855
+ const sessions = useSessionStore((s) => s.sessions);
1856
+ const activeSessionId = useSessionStore((s) => s.activeSessionId);
1857
+ const loading = useSessionStore((s) => s.loading);
1858
+ const fetchSessions = useSessionStore((s) => s.fetchSessions);
1859
+ const createSession2 = useSessionStore((s) => s.createSession);
1860
+ const setActiveSession = useSessionStore((s) => s.setActiveSession);
1861
+ const deleteSession2 = useSessionStore((s) => s.deleteSession);
1862
+ const activeSession = sessions.find((s) => s.id === activeSessionId) ?? null;
1863
+ return {
1864
+ sessions,
1865
+ activeSessionId,
1866
+ activeSession,
1867
+ loading,
1868
+ fetchSessions,
1869
+ createSession: createSession2,
1870
+ setActiveSession,
1871
+ deleteSession: deleteSession2
1872
+ };
1873
+ }
1874
+
1875
+ // src/react/components/session/SessionListItem.tsx
1876
+ import { Loader2 } from "lucide-react";
1877
+
1878
+ // src/react/components/session/StatusBadge.tsx
1879
+ import { jsx as jsx2 } from "react/jsx-runtime";
1880
+ var statusConfig = {
1881
+ created: {
1882
+ className: "bg-[hsl(var(--muted))] text-[hsl(var(--muted-foreground))]",
1883
+ label: "\u5DF2\u521B\u5EFA"
1884
+ },
1885
+ running: { className: "bg-blue-500/15 text-blue-400", label: "\u8FD0\u884C\u4E2D" },
1886
+ completed: { className: "bg-emerald-500/15 text-emerald-400", label: "\u5DF2\u5B8C\u6210" },
1887
+ failed: {
1888
+ className: "bg-[hsl(var(--destructive)/0.15)] text-[hsl(var(--destructive))]",
1889
+ label: "\u5931\u8D25"
1890
+ },
1891
+ interrupted: { className: "bg-amber-500/15 text-amber-400", label: "\u5DF2\u4E2D\u65AD" },
1892
+ waiting_for_input: { className: "bg-violet-500/15 text-violet-400", label: "\u7B49\u5F85\u8F93\u5165" },
1893
+ pending: {
1894
+ className: "bg-[hsl(var(--muted))] text-[hsl(var(--muted-foreground))]",
1895
+ label: "\u5F85\u5904\u7406"
1896
+ },
1897
+ in_progress: { className: "bg-blue-500/15 text-blue-400", label: "\u8FDB\u884C\u4E2D" },
1898
+ done: { className: "bg-emerald-500/15 text-emerald-400", label: "\u5B8C\u6210" },
1899
+ info: {
1900
+ className: "bg-[hsl(var(--muted))] text-[hsl(var(--muted-foreground))]",
1901
+ label: "\u4FE1\u606F"
1902
+ },
1903
+ error: {
1904
+ className: "bg-[hsl(var(--destructive)/0.15)] text-[hsl(var(--destructive))]",
1905
+ label: "\u9519\u8BEF"
1906
+ },
1907
+ skipped: {
1908
+ className: "bg-[hsl(var(--muted)/0.5)] text-[hsl(var(--muted-foreground)/0.6)]",
1909
+ label: "\u5DF2\u8DF3\u8FC7"
1910
+ },
1911
+ example: {
1912
+ className: "bg-[hsl(var(--muted))] text-[hsl(var(--muted-foreground))]",
1913
+ label: "\u793A\u4F8B"
1914
+ }
1915
+ };
1916
+ var defaultConfig = {
1917
+ className: "bg-[hsl(var(--muted))] text-[hsl(var(--muted-foreground))]",
1918
+ label: ""
1919
+ };
1920
+ function StatusBadge({ status }) {
1921
+ const config = statusConfig[status] ?? defaultConfig;
1922
+ return /* @__PURE__ */ jsx2(
1923
+ "span",
1924
+ {
1925
+ className: cn(
1926
+ "inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-medium tracking-wide",
1927
+ config.className
1928
+ ),
1929
+ children: config.label || status
1930
+ }
1931
+ );
1932
+ }
1933
+
1934
+ // src/react/components/session/session-utils.ts
1935
+ var SESSION_GROUPS = ["today", "yesterday", "week", "older"];
1936
+ var SESSION_GROUP_LABELS = {
1937
+ today: "\u4ECA\u5929",
1938
+ yesterday: "\u6628\u5929",
1939
+ week: "\u672C\u5468",
1940
+ older: "\u66F4\u65E9"
1941
+ };
1942
+ var RELATIVE_TIME_FORMATTER = new Intl.RelativeTimeFormat("zh-CN", {
1943
+ numeric: "auto"
1944
+ });
1945
+ function getSessionTimestamp(session) {
1946
+ const value = Date.parse(session.updated_at || session.created_at);
1947
+ return Number.isNaN(value) ? 0 : value;
1948
+ }
1949
+ function getSessionLabel(session, fallback = "\u672A\u547D\u540D\u5BF9\u8BDD") {
1950
+ const normalized = session.intent?.trim();
1951
+ return normalized ? normalized : fallback;
1952
+ }
1953
+ function shouldShowSessionInHistory(session) {
1954
+ return !(session.status === "created" && !session.intent.trim());
1955
+ }
1956
+ function isReplaySession(session) {
1957
+ return session.replay_state?.status === "replay";
1958
+ }
1959
+ function getDisplaySessionStatus(session) {
1960
+ if (session.replay_state?.status === "autonomous" && session.replay_state.autonomous_reason === "completed") {
1961
+ return "completed";
1962
+ }
1963
+ return session.status;
1964
+ }
1965
+ function startOfDay(date) {
1966
+ const copy = new Date(date);
1967
+ copy.setHours(0, 0, 0, 0);
1968
+ return copy;
1969
+ }
1970
+ function startOfWeek(date) {
1971
+ const copy = startOfDay(date);
1972
+ const day = copy.getDay();
1973
+ const diff = day === 0 ? 6 : day - 1;
1974
+ copy.setDate(copy.getDate() - diff);
1975
+ return copy;
1976
+ }
1977
+ function getSessionGroup(timestamp, now = /* @__PURE__ */ new Date()) {
1978
+ if (!timestamp) {
1979
+ return "older";
1980
+ }
1981
+ const sessionDate = new Date(timestamp);
1982
+ const today = startOfDay(now);
1983
+ const yesterday = new Date(today);
1984
+ yesterday.setDate(yesterday.getDate() - 1);
1985
+ const thisWeek = startOfWeek(now);
1986
+ if (sessionDate >= today) {
1987
+ return "today";
1988
+ }
1989
+ if (sessionDate >= yesterday) {
1990
+ return "yesterday";
1991
+ }
1992
+ if (sessionDate >= thisWeek) {
1993
+ return "week";
1994
+ }
1995
+ return "older";
1996
+ }
1997
+ function formatRelativeTime(timestamp, now = Date.now()) {
1998
+ if (!timestamp) {
1999
+ return "\u65F6\u95F4\u672A\u77E5";
2000
+ }
2001
+ const diffSeconds = Math.round((timestamp - now) / 1e3);
2002
+ const absSeconds = Math.abs(diffSeconds);
2003
+ if (absSeconds < 60) {
2004
+ return "\u521A\u521A";
2005
+ }
2006
+ const units = [
2007
+ ["minute", 60],
2008
+ ["hour", 60 * 60],
2009
+ ["day", 60 * 60 * 24],
2010
+ ["week", 60 * 60 * 24 * 7],
2011
+ ["month", 60 * 60 * 24 * 30],
2012
+ ["year", 60 * 60 * 24 * 365]
2013
+ ];
2014
+ for (let index = 0; index < units.length; index += 1) {
2015
+ const [unit, threshold] = units[index];
2016
+ const nextThreshold = units[index + 1]?.[1];
2017
+ if (!nextThreshold || absSeconds < nextThreshold) {
2018
+ return RELATIVE_TIME_FORMATTER.format(Math.round(diffSeconds / threshold), unit);
2019
+ }
2020
+ }
2021
+ return RELATIVE_TIME_FORMATTER.format(Math.round(diffSeconds / (60 * 60 * 24 * 365)), "year");
2022
+ }
2023
+ function getStatusMeta(status) {
2024
+ switch (status) {
2025
+ case "running":
2026
+ return {
2027
+ dotClass: "bg-emerald-400",
2028
+ toneClass: "border-emerald-500/20 bg-emerald-500/8 text-emerald-300",
2029
+ label: "\u8FD0\u884C\u4E2D"
2030
+ };
2031
+ case "failed":
2032
+ return {
2033
+ dotClass: "bg-rose-400",
2034
+ toneClass: "border-rose-500/20 bg-rose-500/8 text-rose-300",
2035
+ label: "\u5F02\u5E38"
2036
+ };
2037
+ case "waiting_for_input":
2038
+ return {
2039
+ dotClass: "bg-violet-400",
2040
+ toneClass: "border-violet-500/20 bg-violet-500/8 text-violet-300",
2041
+ label: "\u5F85\u8865\u5145"
2042
+ };
2043
+ case "completed":
2044
+ return {
2045
+ dotClass: "bg-sky-400",
2046
+ toneClass: "border-sky-500/20 bg-sky-500/8 text-sky-300",
2047
+ label: "\u5DF2\u5B8C\u6210"
2048
+ };
2049
+ case "interrupted":
2050
+ return {
2051
+ dotClass: "bg-amber-400",
2052
+ toneClass: "border-amber-500/20 bg-amber-500/8 text-amber-300",
2053
+ label: "\u5DF2\u4E2D\u65AD"
2054
+ };
2055
+ default:
2056
+ return {
2057
+ dotClass: "bg-[hsl(var(--muted-foreground))]",
2058
+ toneClass: "border-[hsl(var(--border))] bg-[hsl(var(--accent))] text-[hsl(var(--muted-foreground))]",
2059
+ label: "\u51C6\u5907\u4E2D"
2060
+ };
2061
+ }
2062
+ }
2063
+
2064
+ // src/react/components/session/SessionListItem.tsx
2065
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
2066
+ function SessionListItem({ session, active, onClick }) {
2067
+ const isReplay = isReplaySession(session);
2068
+ return /* @__PURE__ */ jsxs2(
2069
+ "button",
2070
+ {
2071
+ type: "button",
2072
+ onClick,
2073
+ className: cn(
2074
+ "flex w-full flex-col gap-1 rounded-md border-l-2 px-3 py-2 text-left text-sm transition-all hover:bg-[hsl(var(--accent))]",
2075
+ active ? "border-l-[hsl(var(--primary))] bg-[hsl(var(--accent))]" : "border-l-transparent"
2076
+ ),
2077
+ children: [
2078
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
2079
+ session.status === "running" && /* @__PURE__ */ jsx3(Loader2, { size: 11, className: "shrink-0 animate-spin text-blue-400" }),
2080
+ /* @__PURE__ */ jsx3("span", { className: "truncate text-[13px] font-medium leading-snug", children: getSessionLabel(session) }),
2081
+ isReplay ? /* @__PURE__ */ jsx3(
2082
+ "span",
2083
+ {
2084
+ className: "h-[5px] w-[5px] shrink-0 rounded-full bg-amber-400",
2085
+ title: "\u5F53\u524D\u5C5E\u4E8E\u56DE\u653E\u6A21\u5F0F",
2086
+ "aria-label": "\u5F53\u524D\u5C5E\u4E8E\u56DE\u653E\u6A21\u5F0F"
2087
+ }
2088
+ ) : null
2089
+ ] }),
2090
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
2091
+ /* @__PURE__ */ jsx3(StatusBadge, { status: getDisplaySessionStatus(session) }),
2092
+ /* @__PURE__ */ jsx3("span", { className: "font-mono text-[10px] text-[hsl(var(--muted-foreground))]", children: session.id.slice(0, 8) })
2093
+ ] })
2094
+ ]
2095
+ }
2096
+ );
2097
+ }
2098
+
2099
+ // src/react/components/session/SessionList.tsx
2100
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
2101
+ function SessionList({ sessions, onSessionCreated, onSessionSelect }) {
2102
+ const { createSession: createSession2, activeSessionId } = useSession();
2103
+ const [error, setError] = useState2(null);
2104
+ const handleCreateSession = async () => {
2105
+ setError(null);
2106
+ try {
2107
+ const sessionId = await createSession2();
2108
+ onSessionCreated?.(sessionId);
2109
+ onSessionSelect(sessionId);
2110
+ } catch (err) {
2111
+ const message = err instanceof Error ? err.message : String(err);
2112
+ setError(`\u521B\u5EFA\u5BF9\u8BDD\u5931\u8D25: ${message}`);
2113
+ setTimeout(() => setError(null), 5e3);
2114
+ }
2115
+ };
2116
+ return /* @__PURE__ */ jsxs3("div", { className: "flex flex-col pt-1", children: [
2117
+ /* @__PURE__ */ jsxs3(
2118
+ "button",
2119
+ {
2120
+ type: "button",
2121
+ onClick: handleCreateSession,
2122
+ className: "mx-2 mt-1.5 flex items-center gap-2 rounded-md border border-dashed border-[hsl(var(--border))] px-3 py-2 text-sm text-[hsl(var(--muted-foreground))] transition-all hover:border-[hsl(var(--ring))] hover:text-[hsl(var(--primary))]",
2123
+ children: [
2124
+ /* @__PURE__ */ jsx4(Plus, { size: 13 }),
2125
+ "\u65B0\u5EFA\u4F1A\u8BDD"
2126
+ ]
2127
+ }
2128
+ ),
2129
+ error ? /* @__PURE__ */ jsx4("p", { className: "mx-2 mt-2 rounded-lg bg-[hsl(var(--destructive)/0.1)] px-3 py-2 text-sm text-[hsl(var(--destructive))]", children: error }) : null,
2130
+ /* @__PURE__ */ jsx4(
2131
+ SessionListEntries,
2132
+ {
2133
+ sessions,
2134
+ activeSessionId,
2135
+ onSessionSelect
2136
+ }
2137
+ )
2138
+ ] });
2139
+ }
2140
+ function SessionListEntries({
2141
+ sessions,
2142
+ activeSessionId,
2143
+ onSessionSelect
2144
+ }) {
2145
+ return /* @__PURE__ */ jsx4("div", { className: "mt-2 flex flex-col gap-0.5 px-1.5", children: sessions.filter(shouldShowSessionInHistory).map((session) => /* @__PURE__ */ jsx4(
2146
+ SessionListItem,
2147
+ {
2148
+ session,
2149
+ active: session.id === activeSessionId,
2150
+ onClick: () => onSessionSelect(session.id)
2151
+ },
2152
+ session.id
2153
+ )) });
2154
+ }
2155
+ export {
2156
+ RELATIVE_TIME_FORMATTER,
2157
+ SESSION_GROUPS,
2158
+ SESSION_GROUP_LABELS,
2159
+ SessionDetail,
2160
+ SessionDetailTrigger,
2161
+ SessionList,
2162
+ SessionListItem,
2163
+ StatusBadge,
2164
+ formatRelativeTime,
2165
+ getDisplaySessionStatus,
2166
+ getSessionGroup,
2167
+ getSessionLabel,
2168
+ getSessionTimestamp,
2169
+ getStatusMeta,
2170
+ isReplaySession,
2171
+ shouldShowSessionInHistory,
2172
+ startOfDay,
2173
+ startOfWeek
2174
+ };
2175
+ //# sourceMappingURL=index.js.map