@tutti-os/agent-gui 0.0.20 → 0.0.22

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 (34) hide show
  1. package/dist/agent-conversation/index.d.ts +4 -2
  2. package/dist/agent-conversation/index.js +3 -3
  3. package/dist/agent-message-center/index.d.ts +3 -2
  4. package/dist/agent-message-center/index.js +176 -158
  5. package/dist/agent-message-center/index.js.map +1 -1
  6. package/dist/{agentConversationVM-D09Aw-Ei.d.ts → agentConversationVM-BVe64P2E.d.ts} +0 -4
  7. package/dist/app/renderer/agentactivity.css +40 -8
  8. package/dist/{chunk-IEBWFP7O.js → chunk-222YHU5Z.js} +1 -1
  9. package/dist/chunk-222YHU5Z.js.map +1 -0
  10. package/dist/{chunk-5H4ISRJQ.js → chunk-64FJGJ3F.js} +31 -17
  11. package/dist/chunk-64FJGJ3F.js.map +1 -0
  12. package/dist/{chunk-7THDJWRR.js → chunk-6TRWLMRE.js} +76 -66
  13. package/dist/chunk-6TRWLMRE.js.map +1 -0
  14. package/dist/{chunk-DIBNU26G.js → chunk-KRKC3XXB.js} +253 -125
  15. package/dist/chunk-KRKC3XXB.js.map +1 -0
  16. package/dist/{chunk-7IFIA3DC.js → chunk-UUGCUPYW.js} +2 -10
  17. package/dist/chunk-UUGCUPYW.js.map +1 -0
  18. package/dist/{chunk-XHJQNUH4.js → chunk-ZWYHKGQ7.js} +4 -6
  19. package/dist/chunk-ZWYHKGQ7.js.map +1 -0
  20. package/dist/context-mention-palette/index.js +2 -2
  21. package/dist/index.d.ts +1 -25
  22. package/dist/index.js +967 -467
  23. package/dist/index.js.map +1 -1
  24. package/dist/plan-decision-ops.d.ts +1 -1
  25. package/dist/workbench/contribution.d.ts +1 -1
  26. package/dist/workbench/contribution.js +1 -1
  27. package/dist/workbench/index.js +1 -1
  28. package/package.json +12 -12
  29. package/dist/chunk-5H4ISRJQ.js.map +0 -1
  30. package/dist/chunk-7IFIA3DC.js.map +0 -1
  31. package/dist/chunk-7THDJWRR.js.map +0 -1
  32. package/dist/chunk-DIBNU26G.js.map +0 -1
  33. package/dist/chunk-IEBWFP7O.js.map +0 -1
  34. package/dist/chunk-XHJQNUH4.js.map +0 -1
package/dist/index.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  import {
14
14
  AgentInteractivePromptSurface,
15
15
  approvalOptionDisplayLabel
16
- } from "./chunk-5H4ISRJQ.js";
16
+ } from "./chunk-64FJGJ3F.js";
17
17
  import {
18
18
  PLAN_IMPLEMENTATION_ACTION_FEEDBACK,
19
19
  PLAN_IMPLEMENTATION_ACTION_IMPLEMENT,
@@ -25,7 +25,6 @@ import {
25
25
  import {
26
26
  AgentConversationFlow,
27
27
  Button,
28
- buildCanonicalWorkspaceAgentDetailView,
29
28
  buildWorkspaceAgentSessionDetailViewModel,
30
29
  createAgentRichTextInputExtensions,
31
30
  editorToPromptText,
@@ -37,13 +36,13 @@ import {
37
36
  skillDescriptionForDisplay,
38
37
  skillTriggerForPrefix,
39
38
  useProjectedAgentConversation
40
- } from "./chunk-DIBNU26G.js";
39
+ } from "./chunk-KRKC3XXB.js";
41
40
  import {
42
41
  AgentMessageMarkdown,
43
42
  ZoomableImage,
44
43
  cn,
45
44
  resolveWorkspaceLinkAction
46
- } from "./chunk-7THDJWRR.js";
45
+ } from "./chunk-6TRWLMRE.js";
47
46
  import {
48
47
  AGENT_MENTION_FILTER_TAB_ORDER,
49
48
  AgentFileMentionPalette,
@@ -51,7 +50,7 @@ import {
51
50
  DEFAULT_AGENT_MENTION_FILTER,
52
51
  flattenAgentMentionPaletteEntries,
53
52
  preloadAgentMentionBrowse
54
- } from "./chunk-7IFIA3DC.js";
53
+ } from "./chunk-UUGCUPYW.js";
55
54
  import {
56
55
  WORKSPACE_AGENT_ACTIVITY_RUNTIME_SESSION_ORIGIN,
57
56
  buildWorkspaceAgentActivityListViewModel,
@@ -91,7 +90,7 @@ import {
91
90
  useAgentActivitySnapshot,
92
91
  useAgentHostApi,
93
92
  useOptionalAgentActivityRuntime
94
- } from "./chunk-IEBWFP7O.js";
93
+ } from "./chunk-222YHU5Z.js";
95
94
  import "./chunk-TYGL25EL.js";
96
95
  import {
97
96
  AGENT_CONTEXT_MENTION_PROVIDER_IDS
@@ -2347,16 +2346,6 @@ var AGENT_PROVIDER_LABEL = {
2347
2346
  hermes: "Hermes Agent"
2348
2347
  };
2349
2348
 
2350
- // shared/agentConversation/projection/workspaceAgentTimelineProjection.ts
2351
- function projectWorkspaceAgentTimelineToConversationVM(input, options = {}) {
2352
- const detail = buildCanonicalWorkspaceAgentDetailView(input);
2353
- const conversation = projectAgentConversationVM(detail, options);
2354
- return conversation;
2355
- }
2356
-
2357
- // agent-gui/agentGuiNode/model/agentGuiConversationModel.ts
2358
- import { resolveWorkspaceUserProjectDisplayLabel } from "@tutti-os/workspace-user-project/core";
2359
-
2360
2349
  // agent-gui/agentGuiNode/model/agentGuiProviderIdentity.ts
2361
2350
  function normalizeAgentGUIProviderIdentity(provider) {
2362
2351
  switch (provider?.trim().toLowerCase() ?? "") {
@@ -2481,10 +2470,127 @@ function isUserTimelineItem(item) {
2481
2470
  return item.itemType.trim().toLowerCase() === "message.user";
2482
2471
  }
2483
2472
 
2484
- // agent-gui/agentGuiNode/model/agentGuiConversationModel.ts
2485
- var AGENT_GUI_RUNTIME_SESSION_ORIGIN = WORKSPACE_AGENT_ACTIVITY_RUNTIME_SESSION_ORIGIN;
2473
+ // agent-gui/agentGuiNode/model/agentGuiConversationProjectResolver.ts
2474
+ import { resolveWorkspaceUserProjectDisplayLabel } from "@tutti-os/workspace-user-project/core";
2486
2475
  var AGENT_GUI_CONVERSATION_PROJECT_SUMMARY_CACHE_LIMIT = 512;
2487
2476
  var agentGUIConversationProjectSummaryCache = /* @__PURE__ */ new Map();
2477
+ function createAgentGUIConversationProjectResolver(userProjects = [], options = {}) {
2478
+ const projectByNormalizedPath = buildAgentGUIConversationProjectIndex(userProjects);
2479
+ const resolvedByNormalizedCwd = /* @__PURE__ */ new Map();
2480
+ return {
2481
+ resolve: (cwd) => {
2482
+ const normalizedCwd = normalizeAgentGUIProjectPath(cwd);
2483
+ if (!normalizedCwd) {
2484
+ return null;
2485
+ }
2486
+ const cached = resolvedByNormalizedCwd.get(normalizedCwd);
2487
+ if (cached !== void 0) {
2488
+ return cached;
2489
+ }
2490
+ const resolved = resolveAgentGUIConversationProjectFromIndex(
2491
+ normalizedCwd,
2492
+ projectByNormalizedPath,
2493
+ options
2494
+ );
2495
+ resolvedByNormalizedCwd.set(normalizedCwd, resolved);
2496
+ return resolved;
2497
+ }
2498
+ };
2499
+ }
2500
+ function resolveAgentGUIConversationProject(cwd, userProjects = [], options = {}) {
2501
+ return createAgentGUIConversationProjectResolver(
2502
+ userProjects,
2503
+ options
2504
+ ).resolve(cwd);
2505
+ }
2506
+ function buildAgentGUIConversationProjectIndex(userProjects) {
2507
+ const projectByNormalizedPath = /* @__PURE__ */ new Map();
2508
+ for (const project of userProjects) {
2509
+ const projectPath = normalizeAgentGUIProjectPath(project.path);
2510
+ if (!projectPath || projectByNormalizedPath.has(projectPath)) {
2511
+ continue;
2512
+ }
2513
+ projectByNormalizedPath.set(projectPath, project);
2514
+ }
2515
+ return projectByNormalizedPath;
2516
+ }
2517
+ function resolveAgentGUIConversationProjectFromIndex(normalizedCwd, projectByNormalizedPath, options) {
2518
+ if (options.isNoProjectPath?.({ path: normalizedCwd })) {
2519
+ return null;
2520
+ }
2521
+ const matchedProject = lookupAgentGUIConversationProject(
2522
+ normalizedCwd,
2523
+ projectByNormalizedPath
2524
+ );
2525
+ if (!matchedProject) {
2526
+ return null;
2527
+ }
2528
+ const summary = {
2529
+ id: matchedProject.id,
2530
+ path: matchedProject.path,
2531
+ label: resolveWorkspaceUserProjectDisplayLabel(matchedProject)
2532
+ };
2533
+ if (matchedProject.createdAtUnixMs !== void 0) {
2534
+ summary.createdAtUnixMs = matchedProject.createdAtUnixMs;
2535
+ }
2536
+ if (matchedProject.updatedAtUnixMs !== void 0) {
2537
+ summary.updatedAtUnixMs = matchedProject.updatedAtUnixMs;
2538
+ }
2539
+ if (matchedProject.lastUsedAtUnixMs !== void 0) {
2540
+ summary.lastUsedAtUnixMs = matchedProject.lastUsedAtUnixMs;
2541
+ }
2542
+ return cachedAgentGUIConversationProjectSummary(summary);
2543
+ }
2544
+ function lookupAgentGUIConversationProject(normalizedCwd, projectByNormalizedPath) {
2545
+ let currentPath = normalizedCwd;
2546
+ while (currentPath) {
2547
+ const project = projectByNormalizedPath.get(currentPath);
2548
+ if (project) {
2549
+ return project;
2550
+ }
2551
+ const slashIndex = currentPath.lastIndexOf("/");
2552
+ if (slashIndex <= 0) {
2553
+ break;
2554
+ }
2555
+ currentPath = currentPath.slice(0, slashIndex);
2556
+ }
2557
+ if (normalizedCwd === "/") {
2558
+ return projectByNormalizedPath.get("/") ?? null;
2559
+ }
2560
+ return null;
2561
+ }
2562
+ function normalizeAgentGUIProjectPath(path) {
2563
+ const normalized = path?.trim().replaceAll("\\", "/") ?? "";
2564
+ if (!normalized) {
2565
+ return "";
2566
+ }
2567
+ return normalized.replace(/\/+$/, "") || "/";
2568
+ }
2569
+ function cachedAgentGUIConversationProjectSummary(summary) {
2570
+ const key = [
2571
+ summary.id,
2572
+ summary.path,
2573
+ summary.label,
2574
+ summary.createdAtUnixMs ?? "",
2575
+ summary.updatedAtUnixMs ?? "",
2576
+ summary.lastUsedAtUnixMs ?? ""
2577
+ ].join("");
2578
+ const cached = agentGUIConversationProjectSummaryCache.get(key);
2579
+ if (cached) {
2580
+ return cached;
2581
+ }
2582
+ if (agentGUIConversationProjectSummaryCache.size >= AGENT_GUI_CONVERSATION_PROJECT_SUMMARY_CACHE_LIMIT) {
2583
+ const oldestKey = agentGUIConversationProjectSummaryCache.keys().next().value;
2584
+ if (oldestKey) {
2585
+ agentGUIConversationProjectSummaryCache.delete(oldestKey);
2586
+ }
2587
+ }
2588
+ agentGUIConversationProjectSummaryCache.set(key, summary);
2589
+ return summary;
2590
+ }
2591
+
2592
+ // agent-gui/agentGuiNode/model/agentGuiConversationModel.ts
2593
+ var AGENT_GUI_RUNTIME_SESSION_ORIGIN = WORKSPACE_AGENT_ACTIVITY_RUNTIME_SESSION_ORIGIN;
2488
2594
  function resolveAgentGUIConversationSortTimeUnixMs(conversation) {
2489
2595
  return conversation.sortTimeUnixMs ?? conversation.updatedAtUnixMs;
2490
2596
  }
@@ -2499,13 +2605,17 @@ function buildAgentGUIConversationSummaries({
2499
2605
  const sessionsById = new Map(
2500
2606
  runtimeSnapshot.sessions.map((session) => [session.agentSessionId, session])
2501
2607
  );
2608
+ const projectResolver = createAgentGUIConversationProjectResolver(
2609
+ userProjects,
2610
+ { isNoProjectPath }
2611
+ );
2502
2612
  return buildWorkspaceAgentActivityListViewModel(runtimeSnapshot, {
2503
2613
  sessionMessagesById
2504
2614
  }).activities.map(
2505
2615
  (activity) => conversationSummaryFromActivity(
2506
2616
  activity,
2507
2617
  sessionsById.get(activity.sessionId),
2508
- { isNoProjectPath, userProjects }
2618
+ { projectResolver }
2509
2619
  )
2510
2620
  ).filter(
2511
2621
  (conversation) => conversation.provider === provider || conversation.provider === "unknown"
@@ -2553,52 +2663,38 @@ function buildAgentGUIConversationDetail({
2553
2663
  });
2554
2664
  return detail;
2555
2665
  }
2556
- function buildAgentGUIConversationVM({
2666
+ function buildAgentGUIConversationModels({
2557
2667
  timelineItems,
2558
2668
  conversation,
2559
2669
  workspaceRoot = null,
2560
2670
  avoidGroupingEdits = false
2561
2671
  }) {
2562
- const session = timelineSessionFromItems(timelineItems, conversation);
2563
- const activity = buildWorkspaceAgentActivityListViewModel(
2564
- {
2565
- presences: [],
2566
- sessions: [session]
2567
- },
2568
- {
2569
- sessionMessagesById: {
2570
- [session.agentSessionId]: workspaceAgentMessagesFromTimelineItems(timelineItems)
2571
- }
2572
- }
2573
- ).activities[0] ?? null;
2574
- if (!activity) {
2575
- return null;
2672
+ const detail = buildAgentGUIConversationDetail({
2673
+ timelineItems,
2674
+ conversation,
2675
+ workspaceRoot
2676
+ });
2677
+ if (!detail) {
2678
+ return { conversation: null, detail: null };
2576
2679
  }
2577
- const resolvedActivity = activityWithExplicitConversationTitle(
2578
- activity,
2579
- conversation
2580
- );
2581
- const conversationVM = projectWorkspaceAgentTimelineToConversationVM(
2582
- {
2583
- activity: resolvedActivity,
2584
- session,
2585
- timelineItems: [...timelineItems],
2586
- workspaceRoot
2587
- },
2588
- { avoidGroupingEdits }
2589
- );
2590
- return conversationVM;
2680
+ return {
2681
+ conversation: projectAgentConversationVM(detail, { avoidGroupingEdits }),
2682
+ detail
2683
+ };
2591
2684
  }
2592
2685
  function conversationSummaryFromAgentSession(session, options = {}) {
2593
2686
  const workspaceAgentSession = agentSessionToWorkspaceAgentSession(session);
2687
+ const projectResolver = createAgentGUIConversationProjectResolver(
2688
+ options.userProjects ?? [],
2689
+ { isNoProjectPath: options.isNoProjectPath }
2690
+ );
2594
2691
  const activity = buildWorkspaceAgentActivityListViewModel({
2595
2692
  presences: [],
2596
2693
  sessions: [workspaceAgentSession]
2597
2694
  }).activities[0] ?? null;
2598
2695
  if (activity) {
2599
2696
  return conversationSummaryFromActivity(activity, workspaceAgentSession, {
2600
- isNoProjectPath: options.isNoProjectPath,
2601
- userProjects: options.userProjects ?? []
2697
+ projectResolver
2602
2698
  });
2603
2699
  }
2604
2700
  const provider = resolveAgentGUIProviderIdentity({
@@ -2617,11 +2713,7 @@ function conversationSummaryFromAgentSession(session, options = {}) {
2617
2713
  titleFallback,
2618
2714
  status: conversationStatusFromActivity("idle"),
2619
2715
  cwd: session.cwd?.trim() ?? "",
2620
- project: resolveAgentGUIConversationProject(
2621
- session.cwd,
2622
- options.userProjects ?? [],
2623
- { isNoProjectPath: options.isNoProjectPath }
2624
- ),
2716
+ project: projectResolver.resolve(session.cwd),
2625
2717
  pinnedAtUnixMs: session.pinnedAtUnixMs ?? null,
2626
2718
  sortTimeUnixMs: resolveWorkspaceAgentSessionSortTimeUnixMs(
2627
2719
  workspaceAgentSession
@@ -2630,54 +2722,14 @@ function conversationSummaryFromAgentSession(session, options = {}) {
2630
2722
  syncState: void 0
2631
2723
  };
2632
2724
  }
2633
- function resolveAgentGUIConversationProject(cwd, userProjects = [], options = {}) {
2634
- const normalizedCwd = normalizeAgentGUIProjectPath(cwd);
2635
- if (!normalizedCwd) {
2636
- return null;
2637
- }
2638
- if (options.isNoProjectPath?.({ path: normalizedCwd })) {
2639
- return null;
2640
- }
2641
- let matchedProject = null;
2642
- let matchedPath = "";
2643
- for (const project of userProjects) {
2644
- const projectPath = normalizeAgentGUIProjectPath(project.path);
2645
- if (!projectPath || normalizedCwd !== projectPath && !normalizedCwd.startsWith(`${projectPath}/`)) {
2646
- continue;
2647
- }
2648
- if (projectPath.length <= matchedPath.length) {
2649
- continue;
2650
- }
2651
- matchedProject = project;
2652
- matchedPath = projectPath;
2653
- }
2654
- if (!matchedProject) {
2655
- return null;
2656
- }
2657
- const summary = {
2658
- id: matchedProject.id,
2659
- path: matchedProject.path,
2660
- label: resolveWorkspaceUserProjectDisplayLabel(matchedProject)
2661
- };
2662
- if (matchedProject.createdAtUnixMs !== void 0) {
2663
- summary.createdAtUnixMs = matchedProject.createdAtUnixMs;
2664
- }
2665
- if (matchedProject.updatedAtUnixMs !== void 0) {
2666
- summary.updatedAtUnixMs = matchedProject.updatedAtUnixMs;
2667
- }
2668
- if (matchedProject.lastUsedAtUnixMs !== void 0) {
2669
- summary.lastUsedAtUnixMs = matchedProject.lastUsedAtUnixMs;
2670
- }
2671
- return cachedAgentGUIConversationProjectSummary(summary);
2672
- }
2673
2725
  function applyAgentGUIConversationProjects(conversations, userProjects = [], options = {}) {
2674
2726
  let changed = false;
2727
+ const projectResolver = createAgentGUIConversationProjectResolver(
2728
+ userProjects,
2729
+ options
2730
+ );
2675
2731
  const next = conversations.map((conversation) => {
2676
- const project = resolveAgentGUIConversationProject(
2677
- conversation.cwd,
2678
- userProjects,
2679
- options
2680
- );
2732
+ const project = projectResolver.resolve(conversation.cwd);
2681
2733
  if (isSameAgentGUIConversationProject(conversation.project, project)) {
2682
2734
  return conversation;
2683
2735
  }
@@ -2732,7 +2784,7 @@ function filterAgentGUIRuntimeSnapshot(snapshot3) {
2732
2784
  function isAgentGUIRuntimeSession(session) {
2733
2785
  return session.agentSessionId.trim().length > 0 && isWorkspaceAgentActivityRuntimeSessionOrigin(session.sessionOrigin);
2734
2786
  }
2735
- function conversationSummaryFromActivity(activity, session, options = {}) {
2787
+ function conversationSummaryFromActivity(activity, session, options) {
2736
2788
  const status = conversationStatusFromActivity(activity.status);
2737
2789
  const provider = resolveAgentGUIProviderIdentity({
2738
2790
  workspaceSessionProvider: session?.provider,
@@ -2756,46 +2808,13 @@ function conversationSummaryFromActivity(activity, session, options = {}) {
2756
2808
  titleFallback,
2757
2809
  status,
2758
2810
  cwd: session?.cwd.trim() ?? "",
2759
- project: resolveAgentGUIConversationProject(
2760
- session?.cwd,
2761
- options.userProjects ?? [],
2762
- { isNoProjectPath: options.isNoProjectPath }
2763
- ),
2811
+ project: options.projectResolver.resolve(session?.cwd),
2764
2812
  pinnedAtUnixMs: session?.pinnedAtUnixMs ?? null,
2765
2813
  sortTimeUnixMs: activity.sortTimeUnixMs,
2766
2814
  updatedAtUnixMs: session?.updatedAtUnixMs || activity.sortTimeUnixMs || 0,
2767
2815
  syncState: session?.syncState
2768
2816
  };
2769
2817
  }
2770
- function normalizeAgentGUIProjectPath(path) {
2771
- const normalized = path?.trim().replaceAll("\\", "/") ?? "";
2772
- if (!normalized) {
2773
- return "";
2774
- }
2775
- return normalized.replace(/\/+$/, "") || "/";
2776
- }
2777
- function cachedAgentGUIConversationProjectSummary(summary) {
2778
- const key = [
2779
- summary.id,
2780
- summary.path,
2781
- summary.label,
2782
- summary.createdAtUnixMs ?? "",
2783
- summary.updatedAtUnixMs ?? "",
2784
- summary.lastUsedAtUnixMs ?? ""
2785
- ].join("");
2786
- const cached = agentGUIConversationProjectSummaryCache.get(key);
2787
- if (cached) {
2788
- return cached;
2789
- }
2790
- if (agentGUIConversationProjectSummaryCache.size >= AGENT_GUI_CONVERSATION_PROJECT_SUMMARY_CACHE_LIMIT) {
2791
- const oldestKey = agentGUIConversationProjectSummaryCache.keys().next().value;
2792
- if (oldestKey) {
2793
- agentGUIConversationProjectSummaryCache.delete(oldestKey);
2794
- }
2795
- }
2796
- agentGUIConversationProjectSummaryCache.set(key, summary);
2797
- return summary;
2798
- }
2799
2818
  function isSameAgentGUIConversationProject(left, right) {
2800
2819
  if (!left && !right) {
2801
2820
  return true;
@@ -3170,17 +3189,14 @@ function normalizeAgentPromptContentBlocks(content) {
3170
3189
  if (block.type === "image") {
3171
3190
  const mimeType = block.mimeType?.trim();
3172
3191
  const data = block.data?.trim();
3173
- const imagePath = block.path?.trim();
3174
- if (!data && !imagePath || mimeType !== "image/png" && mimeType !== "image/jpeg" && mimeType !== "image/webp") {
3192
+ const url = block.url?.trim();
3193
+ if (!data && !url || mimeType !== "image/png" && mimeType !== "image/jpeg" && mimeType !== "image/webp") {
3175
3194
  continue;
3176
3195
  }
3177
3196
  result.push({
3178
3197
  type: "image",
3179
3198
  mimeType,
3180
- ...imagePath ? { path: imagePath } : { data },
3181
- ...block.uri?.trim() ? { uri: block.uri.trim() } : {},
3182
- ...block.uploadStatus?.trim() ? { uploadStatus: block.uploadStatus.trim() } : {},
3183
- ...block.assetId?.trim() ? { assetId: block.assetId.trim() } : {},
3199
+ ...url ? { url } : { data },
3184
3200
  ...block.name?.trim() ? { name: block.name.trim() } : {}
3185
3201
  });
3186
3202
  continue;
@@ -3203,7 +3219,7 @@ function agentPromptContentHasImage(content) {
3203
3219
  }
3204
3220
  function agentPromptContentImageBlocks(content) {
3205
3221
  return normalizeAgentPromptContentBlocks(content).filter(
3206
- (block) => block.type === "image" && (typeof block.data === "string" || typeof block.path === "string") && typeof block.mimeType === "string"
3222
+ (block) => block.type === "image" && (typeof block.data === "string" || typeof block.url === "string") && typeof block.mimeType === "string"
3207
3223
  );
3208
3224
  }
3209
3225
  function agentPromptContentToComposerDraft(content, idPrefix) {
@@ -3231,10 +3247,7 @@ function agentComposerDraftToPromptContent(input) {
3231
3247
  ...input.draft.images.slice(0, MAX_AGENT_COMPOSER_DRAFT_IMAGES).filter((image) => !image.uploading && !image.uploadError).map((image) => ({
3232
3248
  type: "image",
3233
3249
  mimeType: image.mimeType,
3234
- ...image.path ? { path: image.path } : { data: image.data },
3235
- ...image.uri ? { uri: image.uri } : {},
3236
- ...image.uploadStatus ? { uploadStatus: image.uploadStatus } : {},
3237
- ...image.assetId ? { assetId: image.assetId } : {},
3250
+ ...image.url ? { url: image.url } : { data: image.data },
3238
3251
  name: image.name
3239
3252
  }))
3240
3253
  ]);
@@ -3277,11 +3290,8 @@ function agentPromptImageBlockToDraftImage(image, idPrefix, index) {
3277
3290
  name: image.name?.trim() || `image-${index + 1}`,
3278
3291
  mimeType: image.mimeType,
3279
3292
  data: image.data,
3280
- path: image.path,
3281
- uri: image.uri,
3282
- uploadStatus: image.uploadStatus,
3283
- assetId: image.assetId,
3284
- previewUrl: typeof image.data === "string" && image.data ? `data:${image.mimeType};base64,${image.data}` : image.path ?? ""
3293
+ url: image.url,
3294
+ previewUrl: typeof image.data === "string" && image.data ? `data:${image.mimeType};base64,${image.data}` : image.url ?? ""
3285
3295
  };
3286
3296
  }
3287
3297
 
@@ -3975,7 +3985,7 @@ function watchAgentSession(payload, options = {}) {
3975
3985
  entry = {
3976
3986
  key,
3977
3987
  payload: normalizedPayload2,
3978
- listeners: /* @__PURE__ */ new Set(),
3988
+ batchListeners: /* @__PURE__ */ new Set(),
3979
3989
  lingerTimer: null,
3980
3990
  pendingMessageBatch: null,
3981
3991
  releaseRuntimeEvents: null,
@@ -3984,8 +3994,8 @@ function watchAgentSession(payload, options = {}) {
3984
3994
  activityStreamEntries.set(key, entry);
3985
3995
  }
3986
3996
  clearEntryLingerTimer(entry);
3987
- if (options.onEvent) {
3988
- entry.listeners.add(options.onEvent);
3997
+ if (options.onEvents) {
3998
+ entry.batchListeners.add(options.onEvents);
3989
3999
  }
3990
4000
  ignoreEmptyViewUpdatesAfterTestReset = false;
3991
4001
  updateAgentSessionView(normalizedPayload2, (current) => ({
@@ -4000,8 +4010,8 @@ function watchAgentSession(payload, options = {}) {
4000
4010
  if (!currentEntry) {
4001
4011
  return;
4002
4012
  }
4003
- if (options.onEvent) {
4004
- currentEntry.listeners.delete(options.onEvent);
4013
+ if (options.onEvents) {
4014
+ currentEntry.batchListeners.delete(options.onEvents);
4005
4015
  }
4006
4016
  updateAgentSessionView(normalizedPayload2, (current) => ({
4007
4017
  ...current,
@@ -4266,11 +4276,7 @@ function flushPendingMessageBatch(entry) {
4266
4276
  return;
4267
4277
  }
4268
4278
  recordAgentSessionStreamEvents(entry, batch.events);
4269
- for (const event of batch.events) {
4270
- for (const listener of entry.listeners) {
4271
- listener(event);
4272
- }
4273
- }
4279
+ dispatchAgentSessionStreamEvents(entry, batch.events);
4274
4280
  reportMessageBatchDiagnostics(entry, batch);
4275
4281
  }
4276
4282
  function clearPendingMessageBatch(entry) {
@@ -4286,8 +4292,14 @@ function clearPendingMessageBatch(entry) {
4286
4292
  }
4287
4293
  function dispatchAgentSessionStreamEvent(entry, event) {
4288
4294
  recordAgentSessionStreamEvent(entry, event);
4289
- for (const listener of entry.listeners) {
4290
- listener(event);
4295
+ dispatchAgentSessionStreamEvents(entry, [event]);
4296
+ }
4297
+ function dispatchAgentSessionStreamEvents(entry, events) {
4298
+ if (events.length === 0) {
4299
+ return;
4300
+ }
4301
+ for (const listener of entry.batchListeners) {
4302
+ listener(events);
4291
4303
  }
4292
4304
  }
4293
4305
  function upsertCoalescedMessageUpdate(events, event) {
@@ -4475,14 +4487,15 @@ function useAgentSessionView2(ref) {
4475
4487
  return useAgentSessionView(ref);
4476
4488
  }
4477
4489
  function useWatchAgentSession(input) {
4478
- const onEventRef = useRef3(input.onEvent);
4490
+ const onEventsRef = useRef3(input.onEvents);
4479
4491
  const onSubscribeRef = useRef3(input.onSubscribe);
4480
4492
  const onCleanupRef = useRef3(input.onCleanup);
4493
+ const hasBatchEventListener = input.onEvents !== void 0;
4481
4494
  useEffect3(() => {
4482
- onEventRef.current = input.onEvent;
4495
+ onEventsRef.current = input.onEvents;
4483
4496
  onSubscribeRef.current = input.onSubscribe;
4484
4497
  onCleanupRef.current = input.onCleanup;
4485
- }, [input.onCleanup, input.onEvent, input.onSubscribe]);
4498
+ }, [input.onCleanup, input.onEvents, input.onSubscribe]);
4486
4499
  useEffect3(() => {
4487
4500
  const workspaceId = input.workspaceId.trim();
4488
4501
  const agentSessionId = input.agentSessionId?.trim();
@@ -4493,25 +4506,33 @@ function useWatchAgentSession(input) {
4493
4506
  const unsubscribe = watchAgentSession(
4494
4507
  { workspaceId, agentSessionId },
4495
4508
  {
4496
- onEvent: (event) => {
4497
- onEventRef.current?.(event);
4498
- }
4509
+ ...hasBatchEventListener ? {
4510
+ onEvents: (events) => {
4511
+ onEventsRef.current?.(events);
4512
+ }
4513
+ } : {}
4499
4514
  }
4500
4515
  );
4501
4516
  return () => {
4502
4517
  onCleanupRef.current?.();
4503
4518
  unsubscribe();
4504
4519
  };
4505
- }, [input.agentSessionId, input.enabled, input.workspaceId]);
4520
+ }, [
4521
+ hasBatchEventListener,
4522
+ input.agentSessionId,
4523
+ input.enabled,
4524
+ input.workspaceId
4525
+ ]);
4506
4526
  }
4507
4527
  function useWatchAgentSessions(input) {
4508
- const onEventRef = useRef3(input.onEvent);
4528
+ const onEventsRef = useRef3(input.onEvents);
4529
+ const hasBatchEventListener = input.onEvents !== void 0;
4509
4530
  const agentSessionIdsKey = JSON.stringify(
4510
4531
  [...new Set(input.agentSessionIds.map((id) => id.trim()))].filter(Boolean).sort()
4511
4532
  );
4512
4533
  useEffect3(() => {
4513
- onEventRef.current = input.onEvent;
4514
- }, [input.onEvent]);
4534
+ onEventsRef.current = input.onEvents;
4535
+ }, [input.onEvents]);
4515
4536
  useEffect3(() => {
4516
4537
  const workspaceId = input.workspaceId.trim();
4517
4538
  if (!input.enabled || !workspaceId) {
@@ -4525,9 +4546,11 @@ function useWatchAgentSessions(input) {
4525
4546
  (agentSessionId) => watchAgentSession(
4526
4547
  { workspaceId, agentSessionId },
4527
4548
  {
4528
- onEvent: (event) => {
4529
- onEventRef.current?.(event);
4530
- }
4549
+ ...hasBatchEventListener ? {
4550
+ onEvents: (events) => {
4551
+ onEventsRef.current?.(events);
4552
+ }
4553
+ } : {}
4531
4554
  }
4532
4555
  )
4533
4556
  );
@@ -4536,7 +4559,12 @@ function useWatchAgentSessions(input) {
4536
4559
  unsubscribe();
4537
4560
  }
4538
4561
  };
4539
- }, [agentSessionIdsKey, input.enabled, input.workspaceId]);
4562
+ }, [
4563
+ agentSessionIdsKey,
4564
+ hasBatchEventListener,
4565
+ input.enabled,
4566
+ input.workspaceId
4567
+ ]);
4540
4568
  }
4541
4569
 
4542
4570
  // contexts/workspace/presentation/renderer/agentGuiConversationList/agentGuiConversationListStore.ts
@@ -4643,6 +4671,7 @@ var listeners = /* @__PURE__ */ new Set();
4643
4671
  var refreshTimers = /* @__PURE__ */ new Map();
4644
4672
  var inflightRefreshByQueryKey = /* @__PURE__ */ new Map();
4645
4673
  var needsRefreshAfterInflight = /* @__PURE__ */ new Set();
4674
+ var pendingDirtySessionIdsByQueryKey = /* @__PURE__ */ new Map();
4646
4675
  var requestIdByQueryKey = /* @__PURE__ */ new Map();
4647
4676
  var localCreatedConversationIdsByQueryKey = /* @__PURE__ */ new Map();
4648
4677
  var deletedConversationIdsByQueryKey = /* @__PURE__ */ new Map();
@@ -5152,6 +5181,53 @@ function mergeSessionMessagesById(left, right) {
5152
5181
  }
5153
5182
  return next;
5154
5183
  }
5184
+ function normalizeDirtySessionIds(sessionIds) {
5185
+ if (!sessionIds || sessionIds.length === 0) {
5186
+ return [];
5187
+ }
5188
+ const seen = /* @__PURE__ */ new Set();
5189
+ const normalized = [];
5190
+ for (const sessionId of sessionIds) {
5191
+ const value = sessionId.trim();
5192
+ if (!value || seen.has(value)) {
5193
+ continue;
5194
+ }
5195
+ seen.add(value);
5196
+ normalized.push(value);
5197
+ }
5198
+ return normalized;
5199
+ }
5200
+ function addPendingDirtySessionIds(queryKey, sessionIds) {
5201
+ const normalized = normalizeDirtySessionIds(sessionIds);
5202
+ if (normalized.length === 0) {
5203
+ return;
5204
+ }
5205
+ const pending = pendingDirtySessionIdsByQueryKey.get(queryKey) ?? /* @__PURE__ */ new Set();
5206
+ for (const sessionId of normalized) {
5207
+ pending.add(sessionId);
5208
+ }
5209
+ pendingDirtySessionIdsByQueryKey.set(queryKey, pending);
5210
+ }
5211
+ function consumePendingDirtySessionIds(queryKey) {
5212
+ const pending = pendingDirtySessionIdsByQueryKey.get(queryKey);
5213
+ pendingDirtySessionIdsByQueryKey.delete(queryKey);
5214
+ return pending ?? /* @__PURE__ */ new Set();
5215
+ }
5216
+ function decorateConversationForRefresh(input) {
5217
+ const title = resolveAgentGUIConversationTitleFromMessages({
5218
+ messages: input.mergedMessages,
5219
+ conversation: input.conversation
5220
+ });
5221
+ const nextConversation = {
5222
+ ...input.conversation,
5223
+ ...title ? { title: title.title, titleFallback: title.titleFallback } : {},
5224
+ hasUnreadCompletion: input.conversation.status === "completed" ? input.currentConversation?.status !== "completed" && !hasActiveConversationOwner(input.queryKey, input.conversation.id) ? true : input.conversation.hasUnreadCompletion ?? false : false,
5225
+ status: input.conversation.status,
5226
+ updatedAtUnixMs: input.conversation.updatedAtUnixMs,
5227
+ pinnedAtUnixMs: input.conversation.pinnedAtUnixMs
5228
+ };
5229
+ return areConversationsEqual(input.conversation, nextConversation) ? input.conversation : nextConversation;
5230
+ }
5155
5231
  async function loadWorkspaceAgentSnapshotForConversations(input) {
5156
5232
  const snapshot3 = await getAgentActivityRuntime().load(input.workspaceId);
5157
5233
  return workspaceAgentSnapshotForConversations(snapshot3);
@@ -5160,7 +5236,7 @@ function getWorkspaceAgentSnapshotForConversations(input) {
5160
5236
  const snapshot3 = getAgentActivityRuntime().getSnapshot(input.workspaceId);
5161
5237
  return workspaceAgentSnapshotForConversations(snapshot3);
5162
5238
  }
5163
- async function refreshAgentGUIConversationListQuery(query, reason) {
5239
+ async function refreshAgentGUIConversationListQuery(query, reason, options = {}) {
5164
5240
  const state = ensureQueryState(query);
5165
5241
  if (!state) {
5166
5242
  return;
@@ -5187,12 +5263,16 @@ async function refreshAgentGUIConversationListQuery(query, reason) {
5187
5263
  sessionOrigin: state.query.sessionOrigin,
5188
5264
  userId: state.query.userId
5189
5265
  };
5190
- const workspaceAgentSnapshot = reason === "workspace-agent-update" ? getWorkspaceAgentSnapshotForConversations(workspaceAgentsInput) : await loadWorkspaceAgentSnapshotForConversations(
5266
+ const workspaceAgentSnapshot = reason === "workspace-agent-update" || reason === "session-overlay-update" ? getWorkspaceAgentSnapshotForConversations(workspaceAgentsInput) : await loadWorkspaceAgentSnapshotForConversations(
5191
5267
  workspaceAgentsInput
5192
5268
  );
5193
5269
  if (requestId !== requestIdByQueryKey.get(queryKey)) {
5194
5270
  return;
5195
5271
  }
5272
+ const dirtySessionIds = consumePendingDirtySessionIds(queryKey);
5273
+ for (const sessionId of normalizeDirtySessionIds(options.dirtySessionIds)) {
5274
+ dirtySessionIds.add(sessionId);
5275
+ }
5196
5276
  const sessionViewDataById = workspaceSessionViewDataBySessionId(
5197
5277
  state.query.workspaceId
5198
5278
  );
@@ -5203,11 +5283,6 @@ async function refreshAgentGUIConversationListQuery(query, reason) {
5203
5283
  sessionViewDataById
5204
5284
  )
5205
5285
  );
5206
- const baseConversations = buildAgentGUIConversationSummaries({
5207
- snapshot: workspaceAgentSnapshot,
5208
- provider: state.query.provider,
5209
- sessionMessagesById: sessionMessagesByIdForSummaries
5210
- });
5211
5286
  const deletedConversationIds = deletedConversationIdsByQueryKey.get(queryKey) ?? /* @__PURE__ */ new Set();
5212
5287
  const localCreatedConversationIds = localCreatedConversationIdsByQueryKey.get(queryKey) ?? /* @__PURE__ */ new Set();
5213
5288
  const snapshotSessionIds = new Set(
@@ -5238,6 +5313,24 @@ async function refreshAgentGUIConversationListQuery(query, reason) {
5238
5313
  conversation
5239
5314
  ])
5240
5315
  );
5316
+ const canApplyDirtySessionProjection = reason === "session-overlay-update" && state.initialized && dirtySessionIds.size > 0;
5317
+ const projectionSessions = canApplyDirtySessionProjection ? workspaceAgentSnapshot.sessions.filter((session) => {
5318
+ const agentSessionId = session.agentSessionId.trim();
5319
+ const syncSessionId = session.syncState?.agentSessionId?.trim() ?? "";
5320
+ return agentSessionId && dirtySessionIds.has(agentSessionId) || syncSessionId && dirtySessionIds.has(syncSessionId);
5321
+ }) : workspaceAgentSnapshot.sessions;
5322
+ const baseConversations = buildAgentGUIConversationSummaries({
5323
+ snapshot: canApplyDirtySessionProjection ? {
5324
+ ...workspaceAgentSnapshot,
5325
+ sessions: projectionSessions
5326
+ } : workspaceAgentSnapshot,
5327
+ provider: state.query.provider,
5328
+ sessionMessagesById: sessionMessagesByIdForSummaries
5329
+ });
5330
+ const conversationsToDecorate = canApplyDirtySessionProjection ? /* @__PURE__ */ new Set([
5331
+ ...dirtySessionIds,
5332
+ ...baseConversations.map((conversation) => conversation.id)
5333
+ ]) : null;
5241
5334
  const retainedSessionIds = new Set(retainedSnapshotSessionIds);
5242
5335
  if (reason === "workspace-agent-update") {
5243
5336
  for (const conversation of currentConversations) {
@@ -5253,6 +5346,13 @@ async function refreshAgentGUIConversationListQuery(query, reason) {
5253
5346
  }
5254
5347
  }
5255
5348
  }
5349
+ if (canApplyDirtySessionProjection) {
5350
+ for (const conversation of currentConversations) {
5351
+ if (!nextDeletedConversationIds.has(conversation.id)) {
5352
+ retainedSessionIds.add(conversation.id);
5353
+ }
5354
+ }
5355
+ }
5256
5356
  const nextConversations = sortConversationsByRecency(
5257
5357
  mergeLoadedConversations(
5258
5358
  currentConversations,
@@ -5263,24 +5363,21 @@ async function refreshAgentGUIConversationListQuery(query, reason) {
5263
5363
  ),
5264
5364
  createConversationOrderIndex(currentConversations)
5265
5365
  ).map((conversation) => {
5366
+ if (conversationsToDecorate !== null && !conversationsToDecorate.has(conversation.id)) {
5367
+ return conversation;
5368
+ }
5266
5369
  const currentConversation = currentConversationById.get(conversation.id);
5267
5370
  const sessionViewData = sessionViewDataById[conversation.id];
5268
5371
  const mergedMessages = sessionMessagesByIdForSummaries[conversation.id] ?? mergeWorkspaceAgentActivityDurableAndOverlayMessages({
5269
5372
  durableMessages: workspaceAgentSnapshot.sessionMessagesById?.[conversation.id],
5270
5373
  localMessages: sessionViewData?.overlayMessages
5271
5374
  });
5272
- const title = resolveAgentGUIConversationTitleFromMessages({
5273
- messages: mergedMessages,
5274
- conversation
5375
+ return decorateConversationForRefresh({
5376
+ conversation,
5377
+ currentConversation,
5378
+ mergedMessages,
5379
+ queryKey
5275
5380
  });
5276
- return {
5277
- ...conversation,
5278
- ...title ? { title: title.title, titleFallback: title.titleFallback } : {},
5279
- hasUnreadCompletion: conversation.status === "completed" ? currentConversation?.status !== "completed" && !hasActiveConversationOwner(queryKey, conversation.id) ? true : conversation.hasUnreadCompletion ?? false : false,
5280
- status: conversation.status,
5281
- updatedAtUnixMs: conversation.updatedAtUnixMs,
5282
- pinnedAtUnixMs: conversation.pinnedAtUnixMs
5283
- };
5284
5381
  });
5285
5382
  updateQueryState(query, (current) => {
5286
5383
  const conversations = areConversationListsEqual(
@@ -5317,12 +5414,13 @@ async function refreshAgentGUIConversationListQuery(query, reason) {
5317
5414
  throw error;
5318
5415
  }
5319
5416
  }
5320
- function scheduleQueryRefresh(query, _reason) {
5417
+ function scheduleQueryRefresh(query, _reason, options = {}) {
5321
5418
  const state = ensureQueryState(query);
5322
5419
  if (!state) {
5323
5420
  return;
5324
5421
  }
5325
5422
  const queryKey = state.queryKey;
5423
+ addPendingDirtySessionIds(queryKey, options.dirtySessionIds);
5326
5424
  const delayMs = _reason === "projection-sync" && !state.initialized ? 0 : REFRESH_DEBOUNCE_MS;
5327
5425
  const runRefresh = () => {
5328
5426
  refreshTimers.delete(queryKey);
@@ -5330,7 +5428,11 @@ function scheduleQueryRefresh(query, _reason) {
5330
5428
  needsRefreshAfterInflight.add(queryKey);
5331
5429
  return;
5332
5430
  }
5333
- const promise = refreshAgentGUIConversationListQuery(state.query, _reason).catch(() => void 0).finally(() => {
5431
+ const promise = refreshAgentGUIConversationListQuery(
5432
+ state.query,
5433
+ _reason,
5434
+ options
5435
+ ).catch(() => void 0).finally(() => {
5334
5436
  inflightRefreshByQueryKey.delete(queryKey);
5335
5437
  if (needsRefreshAfterInflight.has(queryKey)) {
5336
5438
  needsRefreshAfterInflight.delete(queryKey);
@@ -5388,8 +5490,8 @@ function isAgentGUIConversationListRefreshing(query) {
5388
5490
  const key = createAgentGUIConversationListQueryKey(query);
5389
5491
  return key !== null && inflightRefreshByQueryKey.has(key);
5390
5492
  }
5391
- function scheduleAgentGUIConversationListProjection(query, reason) {
5392
- scheduleQueryRefresh(query, reason);
5493
+ function scheduleAgentGUIConversationListProjection(query, reason, options = {}) {
5494
+ scheduleQueryRefresh(query, reason, options);
5393
5495
  }
5394
5496
  function stripProjectFromConversations(conversations) {
5395
5497
  let changed = false;
@@ -6410,14 +6512,83 @@ function mergeVisibleConversations(conversations, transientConversation) {
6410
6512
  }
6411
6513
  function stableConversationSummaryList(previous, next) {
6412
6514
  if (previous?.length !== next.length) {
6413
- return next;
6515
+ const previousById = new Map(
6516
+ (previous ?? []).map((conversation) => [conversation.id, conversation])
6517
+ );
6518
+ return next.map((conversation) => {
6519
+ const previousConversation = previousById.get(conversation.id);
6520
+ return previousConversation && conversationSummariesRenderEqual(previousConversation, conversation) ? previousConversation : conversation;
6521
+ });
6414
6522
  }
6415
- for (let index = 0; index < next.length; index += 1) {
6416
- if (previous[index] !== next[index]) {
6417
- return next;
6523
+ let hasRenderChange = false;
6524
+ const stable = next.map((conversation, index) => {
6525
+ const previousConversation = previous[index];
6526
+ if (previousConversation && conversationSummariesRenderEqual(previousConversation, conversation)) {
6527
+ return previousConversation;
6418
6528
  }
6529
+ hasRenderChange = true;
6530
+ return conversation;
6531
+ });
6532
+ return hasRenderChange ? stable : previous;
6533
+ }
6534
+ function useStableConversationDetail(detail) {
6535
+ const detailRef = useRef4(null);
6536
+ detailRef.current = stabilizeConversationDetail(detailRef.current, detail);
6537
+ return detailRef.current;
6538
+ }
6539
+ function stabilizeConversationDetail(previous, next) {
6540
+ if (!previous || !next) {
6541
+ return next;
6419
6542
  }
6420
- return previous;
6543
+ const session = conversationDetailSessionsEqual(
6544
+ previous.session,
6545
+ next.session
6546
+ ) ? previous.session : next.session;
6547
+ const activity = stabilizeConversationDetailActivity(
6548
+ previous.activity,
6549
+ next.activity
6550
+ );
6551
+ if (previous.cwd === next.cwd && previous.workspaceRoot === next.workspaceRoot && previous.showProcessingIndicator === next.showProcessingIndicator && previous.turns === next.turns && previous.session === session && previous.activity === activity) {
6552
+ return previous;
6553
+ }
6554
+ return {
6555
+ ...next,
6556
+ activity,
6557
+ session
6558
+ };
6559
+ }
6560
+ function stabilizeConversationDetailActivity(previous, next) {
6561
+ const changedFiles = conversationDetailChangedFilesEqual(
6562
+ previous.changedFiles,
6563
+ next.changedFiles
6564
+ ) ? previous.changedFiles : next.changedFiles;
6565
+ if (previous.id === next.id && previous.sessionId === next.sessionId && previous.userId === next.userId && previous.userName === next.userName && previous.userAvatarUrl === next.userAvatarUrl && previous.agentProvider === next.agentProvider && previous.agentName === next.agentName && previous.title === next.title && previous.status === next.status && previous.latestActivitySummary === next.latestActivitySummary && previous.conversationPreview === next.conversationPreview && previous.latestActivityActorName === next.latestActivityActorName && previous.toolCalls === next.toolCalls && previous.changedFiles === changedFiles && previous.sortTimeUnixMs === next.sortTimeUnixMs && previous.readTimeUnixMs === next.readTimeUnixMs) {
6566
+ return previous;
6567
+ }
6568
+ return {
6569
+ ...next,
6570
+ changedFiles
6571
+ };
6572
+ }
6573
+ function conversationDetailChangedFilesEqual(left, right) {
6574
+ return left.length === right.length && left.every(
6575
+ (file, index) => file.path === right[index]?.path && file.label === right[index]?.label
6576
+ );
6577
+ }
6578
+ function conversationDetailSessionsEqual(left, right) {
6579
+ return left.id === right.id && left.workspaceId === right.workspaceId && left.agentSessionId === right.agentSessionId && left.presenceId === right.presenceId && left.userId === right.userId && left.provider === right.provider && left.providerSessionId === right.providerSessionId && left.resumable === right.resumable && left.sessionOrigin === right.sessionOrigin && left.lifecycleStatus === right.lifecycleStatus && left.turnPhase === right.turnPhase && left.endedAtUnixMs === right.endedAtUnixMs && left.effectiveStatus === right.effectiveStatus && left.status === right.status && left.title === right.title && left.pinnedAtUnixMs === right.pinnedAtUnixMs && left.createdAtUnixMs === right.createdAtUnixMs && left.updatedAtUnixMs === right.updatedAtUnixMs && left.cwd === right.cwd && conversationSyncStatesEqual(left.syncState, right.syncState);
6580
+ }
6581
+ function conversationSummariesRenderEqual(left, right) {
6582
+ return left.id === right.id && left.userId === right.userId && left.provider === right.provider && left.title === right.title && conversationTitleFallbacksRenderEqual(
6583
+ left.titleFallback,
6584
+ right.titleFallback
6585
+ ) && left.status === right.status && left.cwd === right.cwd && left.pinnedAtUnixMs === right.pinnedAtUnixMs && left.sortTimeUnixMs === right.sortTimeUnixMs && left.updatedAtUnixMs === right.updatedAtUnixMs && left.hasUnreadCompletion === right.hasUnreadCompletion && conversationProjectsRenderEqual(left.project, right.project) && conversationSyncStatesEqual(left.syncState, right.syncState);
6586
+ }
6587
+ function conversationTitleFallbacksRenderEqual(left, right) {
6588
+ return left === right || JSON.stringify(left ?? null) === JSON.stringify(right ?? null);
6589
+ }
6590
+ function conversationProjectsRenderEqual(left, right) {
6591
+ return left === right || (!left || !right ? !left && !right : left.id === right.id && left.path === right.path && left.label === right.label && left.createdAtUnixMs === right.createdAtUnixMs && left.updatedAtUnixMs === right.updatedAtUnixMs && left.lastUsedAtUnixMs === right.lastUsedAtUnixMs);
6421
6592
  }
6422
6593
  function mergeConversationTitleUpdateFields(current, incomingTitle, provider) {
6423
6594
  const title = incomingTitle.trim();
@@ -6835,7 +7006,7 @@ function useStableProviderSkillOptions(skills) {
6835
7006
  return skillsRef.current;
6836
7007
  }
6837
7008
  function areComposerSettingsDraftsEqual(left, right) {
6838
- return left.model === right.model && left.reasoningEffort === right.reasoningEffort && left.speed === right.speed && left.planMode === right.planMode && (left.permissionModeId ?? null) === (right.permissionModeId ?? null);
7009
+ return left.model === right.model && left.reasoningEffort === right.reasoningEffort && left.speed === right.speed && left.planMode === right.planMode && (left.browserUse ?? true) === (right.browserUse ?? true) && (left.computerUse ?? true) === (right.computerUse ?? true) && (left.permissionModeId ?? null) === (right.permissionModeId ?? null);
6839
7010
  }
6840
7011
  function arePermissionModeOptionsEqual(left, right) {
6841
7012
  return left.id === right.id && left.label === right.label && left.description === right.description && left.semantic === right.semantic;
@@ -6849,7 +7020,7 @@ function arePermissionConfigsEqual(left, right) {
6849
7020
  );
6850
7021
  }
6851
7022
  function areComposerSettingsVMsEqual(left, right) {
6852
- return left.sessionSettings === right.sessionSettings && areComposerSettingsDraftsEqual(left.draftSettings, right.draftSettings) && left.supportsModel === right.supportsModel && left.supportsReasoningEffort === right.supportsReasoningEffort && left.supportsSpeed === right.supportsSpeed && (left.supportsPermissionMode ?? false) === (right.supportsPermissionMode ?? false) && left.supportsPlanMode === right.supportsPlanMode && left.isSettingsLoading === right.isSettingsLoading && left.modelUnavailable === right.modelUnavailable && left.reasoningUnavailable === right.reasoningUnavailable && left.speedUnavailable === right.speedUnavailable && (left.permissionModeUnavailable ?? false) === (right.permissionModeUnavailable ?? false) && (left.planExclusiveWithPermissionMode ?? false) === (right.planExclusiveWithPermissionMode ?? false) && (left.selectedModelValue ?? null) === (right.selectedModelValue ?? null) && (left.selectedReasoningEffortValue ?? null) === (right.selectedReasoningEffortValue ?? null) && (left.selectedSpeedValue ?? null) === (right.selectedSpeedValue ?? null) && (left.selectedPermissionModeValue ?? null) === (right.selectedPermissionModeValue ?? null) && arePermissionConfigsEqual(left.permissionConfig, right.permissionConfig) && (left.selectedProjectPath ?? null) === (right.selectedProjectPath ?? null) && Boolean(left.projectLocked) === Boolean(right.projectLocked) && areComposerSettingOptionListsEqual(
7023
+ return sameComposerSettings(left.sessionSettings, right.sessionSettings) && areComposerSettingsDraftsEqual(left.draftSettings, right.draftSettings) && left.supportsModel === right.supportsModel && left.supportsReasoningEffort === right.supportsReasoningEffort && left.supportsSpeed === right.supportsSpeed && (left.supportsPermissionMode ?? false) === (right.supportsPermissionMode ?? false) && left.supportsPlanMode === right.supportsPlanMode && (left.supportsBrowser ?? false) === (right.supportsBrowser ?? false) && (left.supportsComputerUse ?? false) === (right.supportsComputerUse ?? false) && left.isSettingsLoading === right.isSettingsLoading && left.modelUnavailable === right.modelUnavailable && left.reasoningUnavailable === right.reasoningUnavailable && left.speedUnavailable === right.speedUnavailable && (left.permissionModeUnavailable ?? false) === (right.permissionModeUnavailable ?? false) && (left.planExclusiveWithPermissionMode ?? false) === (right.planExclusiveWithPermissionMode ?? false) && (left.selectedModelValue ?? null) === (right.selectedModelValue ?? null) && (left.selectedReasoningEffortValue ?? null) === (right.selectedReasoningEffortValue ?? null) && (left.selectedSpeedValue ?? null) === (right.selectedSpeedValue ?? null) && (left.selectedPermissionModeValue ?? null) === (right.selectedPermissionModeValue ?? null) && arePermissionConfigsEqual(left.permissionConfig, right.permissionConfig) && (left.selectedProjectPath ?? null) === (right.selectedProjectPath ?? null) && Boolean(left.projectLocked) === Boolean(right.projectLocked) && areComposerSettingOptionListsEqual(
6853
7024
  left.availableModels,
6854
7025
  right.availableModels
6855
7026
  ) && areComposerSettingOptionListsEqual(
@@ -6865,11 +7036,58 @@ function areComposerSettingsVMsEqual(left, right) {
6865
7036
  }
6866
7037
  function useStableComposerSettingsVM(settings) {
6867
7038
  const settingsRef = useRef4(null);
6868
- if (settingsRef.current === null || !areComposerSettingsVMsEqual(settingsRef.current, settings)) {
6869
- settingsRef.current = settings;
6870
- }
7039
+ settingsRef.current = stabilizeComposerSettingsVM(
7040
+ settingsRef.current,
7041
+ settings
7042
+ );
6871
7043
  return settingsRef.current;
6872
7044
  }
7045
+ function stabilizeComposerSettingsVM(previous, next) {
7046
+ if (!previous) {
7047
+ return next;
7048
+ }
7049
+ if (areComposerSettingsVMsEqual(previous, next)) {
7050
+ return previous;
7051
+ }
7052
+ const sessionSettings = sameComposerSettings(
7053
+ previous.sessionSettings,
7054
+ next.sessionSettings
7055
+ ) ? previous.sessionSettings : next.sessionSettings;
7056
+ const draftSettings = areComposerSettingsDraftsEqual(
7057
+ previous.draftSettings,
7058
+ next.draftSettings
7059
+ ) ? previous.draftSettings : next.draftSettings;
7060
+ const permissionConfig = arePermissionConfigsEqual(
7061
+ previous.permissionConfig,
7062
+ next.permissionConfig
7063
+ ) ? previous.permissionConfig : next.permissionConfig;
7064
+ const availableModels = areComposerSettingOptionListsEqual(
7065
+ previous.availableModels,
7066
+ next.availableModels
7067
+ ) ? previous.availableModels : next.availableModels;
7068
+ const availableReasoningEfforts = areComposerSettingOptionListsEqual(
7069
+ previous.availableReasoningEfforts,
7070
+ next.availableReasoningEfforts
7071
+ ) ? previous.availableReasoningEfforts : next.availableReasoningEfforts;
7072
+ const availableSpeeds = areComposerSettingOptionListsEqual(
7073
+ previous.availableSpeeds,
7074
+ next.availableSpeeds
7075
+ ) ? previous.availableSpeeds : next.availableSpeeds;
7076
+ const availablePermissionModes = areComposerSettingOptionListsEqual(
7077
+ previous.availablePermissionModes ?? [],
7078
+ next.availablePermissionModes ?? []
7079
+ ) ? previous.availablePermissionModes : next.availablePermissionModes;
7080
+ return {
7081
+ ...next,
7082
+ sessionSettings,
7083
+ draftSettings,
7084
+ permissionConfig,
7085
+ availableModels,
7086
+ availableReasoningEfforts,
7087
+ availableSpeeds,
7088
+ availablePermissionModes
7089
+ };
7090
+ }
6873
7091
  function useStableControllerEventCallback(callback) {
6874
7092
  const callbackRef = useRef4(callback);
6875
7093
  callbackRef.current = callback;
@@ -7219,7 +7437,9 @@ function useAgentGUINodeController({
7219
7437
  );
7220
7438
  const [pendingCreateConversationId, setPendingCreateConversationId] = useState4(resolvePendingCreateConversationId);
7221
7439
  const conversations = conversationListState?.conversations ?? [];
7222
- const [userProjects, setUserProjects] = useState4([]);
7440
+ const [userProjects, setUserProjects] = useState4(
7441
+ () => readAgentGUIUserProjectSnapshot(agentHostApi.userProjects)
7442
+ );
7223
7443
  const isNoProjectPath = agentHostApi.userProjects?.isNoProjectPath;
7224
7444
  const [activeConversationId, setActiveConversationId] = useState4(data.lastActiveAgentSessionId);
7225
7445
  const [intent, setIntent] = useState4(
@@ -7420,7 +7640,16 @@ function useAgentGUINodeController({
7420
7640
  [agentActivitySnapshot.sessions]
7421
7641
  );
7422
7642
  const stableRuntimeSyncStateBySessionId = useMemo4(() => {
7423
- const next = { ...stableRuntimeSyncStateBySessionIdRef.current };
7643
+ const current = stableRuntimeSyncStateBySessionIdRef.current;
7644
+ let next = current;
7645
+ let changed = false;
7646
+ const mutableNext = () => {
7647
+ if (!changed) {
7648
+ next = { ...current };
7649
+ changed = true;
7650
+ }
7651
+ return next;
7652
+ };
7424
7653
  const activeSessionIds = /* @__PURE__ */ new Set();
7425
7654
  for (const session of agentActivitySnapshot.sessions) {
7426
7655
  const agentSessionId = session.agentSessionId.trim();
@@ -7430,7 +7659,9 @@ function useAgentGUINodeController({
7430
7659
  activeSessionIds.add(agentSessionId);
7431
7660
  const nextSyncState = runtimeSessionSyncState(session);
7432
7661
  if (!nextSyncState) {
7433
- delete next[agentSessionId];
7662
+ if (current[agentSessionId] !== void 0) {
7663
+ delete mutableNext()[agentSessionId];
7664
+ }
7434
7665
  delete latestRuntimeSyncStateBySessionIdRef.current[agentSessionId];
7435
7666
  continue;
7436
7667
  }
@@ -7444,16 +7675,18 @@ function useAgentGUINodeController({
7444
7675
  };
7445
7676
  const currentSyncState = next[agentSessionId];
7446
7677
  if (!currentSyncState || !syncStateRenderFieldsEqual(currentSyncState, nextSyncState)) {
7447
- next[agentSessionId] = nextSyncState;
7678
+ mutableNext()[agentSessionId] = nextSyncState;
7448
7679
  }
7449
7680
  }
7450
7681
  for (const agentSessionId of Object.keys(next)) {
7451
7682
  if (!activeSessionIds.has(agentSessionId)) {
7452
- delete next[agentSessionId];
7683
+ delete mutableNext()[agentSessionId];
7453
7684
  delete latestRuntimeSyncStateBySessionIdRef.current[agentSessionId];
7454
7685
  }
7455
7686
  }
7456
- stableRuntimeSyncStateBySessionIdRef.current = next;
7687
+ if (changed) {
7688
+ stableRuntimeSyncStateBySessionIdRef.current = next;
7689
+ }
7457
7690
  return next;
7458
7691
  }, [agentActivitySnapshot.sessions]);
7459
7692
  const markSessionSettingsRequestState = useCallback4(
@@ -7642,11 +7875,9 @@ function useAgentGUINodeController({
7642
7875
  []
7643
7876
  );
7644
7877
  useEffect4(() => {
7645
- if (previewMode) {
7646
- return void 0;
7647
- }
7648
7878
  const api = agentHostApi.userProjects;
7649
7879
  let disposed = false;
7880
+ setUserProjectsSnapshot(readAgentGUIUserProjectSnapshot(api));
7650
7881
  const loadUserProjects = async () => {
7651
7882
  const requestSeq = ++userProjectsLoadSeqRef.current;
7652
7883
  if (!api) {
@@ -7667,7 +7898,7 @@ function useAgentGUINodeController({
7667
7898
  }
7668
7899
  };
7669
7900
  void loadUserProjects();
7670
- const unsubscribe = api?.subscribe?.(() => {
7901
+ const unsubscribe = previewMode ? void 0 : api?.subscribe?.(() => {
7671
7902
  void loadUserProjects();
7672
7903
  });
7673
7904
  return () => {
@@ -8893,11 +9124,19 @@ function useAgentGUINodeController({
8893
9124
  sessionViewRef(agentSessionId),
8894
9125
  overlayMessages
8895
9126
  );
9127
+ if (conversationListQuery) {
9128
+ scheduleAgentGUIConversationListProjection(
9129
+ conversationListQuery,
9130
+ "session-overlay-update",
9131
+ { dirtySessionIds: [agentSessionId] }
9132
+ );
9133
+ }
8896
9134
  applyTimelineProjectionUpdate(agentSessionId, nextItems, mergedItems);
8897
9135
  },
8898
9136
  [
8899
9137
  agentActivitySnapshot.sessionMessagesById,
8900
9138
  applyTimelineProjectionUpdate,
9139
+ conversationListQuery,
8901
9140
  sessionViewRef
8902
9141
  ]
8903
9142
  );
@@ -9011,39 +9250,50 @@ function useAgentGUINodeController({
9011
9250
  workspaceId
9012
9251
  ]
9013
9252
  );
9014
- const handleActivityStreamEvent = useCallback4(
9015
- (event) => {
9016
- if (event.eventType === "available_commands_update") {
9017
- return;
9018
- }
9019
- if (event.eventType === "message_update") {
9020
- const message = messageFromMessageUpdate(event.data);
9021
- const agentSessionId = message.agentSessionId.trim();
9022
- if (agentSessionId) {
9253
+ const handleActivityStreamEvents = useCallback4(
9254
+ (events) => {
9255
+ const pendingMessagesBySessionId = /* @__PURE__ */ new Map();
9256
+ const flushPendingMessages = () => {
9257
+ for (const [agentSessionId, messages] of pendingMessagesBySessionId) {
9023
9258
  applyBackgroundTimelineStatusUpdate(
9024
9259
  agentSessionId,
9025
- projectAgentGUIMessagesToTimelineItems([message])
9260
+ projectAgentGUIMessagesToTimelineItems(messages)
9026
9261
  );
9027
9262
  }
9028
- return;
9029
- }
9030
- if (event.eventType === "state_patch") {
9031
- applyStatePatch(event.data);
9263
+ pendingMessagesBySessionId.clear();
9264
+ };
9265
+ for (const event of events) {
9266
+ if (event.eventType === "available_commands_update") {
9267
+ continue;
9268
+ }
9269
+ if (event.eventType === "message_update") {
9270
+ const message = messageFromMessageUpdate(event.data);
9271
+ const agentSessionId = message.agentSessionId.trim();
9272
+ if (!agentSessionId) {
9273
+ continue;
9274
+ }
9275
+ const messages = pendingMessagesBySessionId.get(agentSessionId);
9276
+ if (messages) {
9277
+ messages.push(message);
9278
+ } else {
9279
+ pendingMessagesBySessionId.set(agentSessionId, [message]);
9280
+ }
9281
+ continue;
9282
+ }
9283
+ flushPendingMessages();
9284
+ if (event.eventType === "state_patch") {
9285
+ applyStatePatch(event.data);
9286
+ }
9032
9287
  }
9288
+ flushPendingMessages();
9033
9289
  },
9034
9290
  [applyStatePatch, applyBackgroundTimelineStatusUpdate]
9035
9291
  );
9036
- const handleBackgroundActivityStreamEvent = useCallback4(
9037
- (event) => {
9038
- if (event.eventType === "message_update") {
9039
- handleActivityStreamEvent(event);
9040
- return;
9041
- }
9042
- if (event.eventType === "state_patch") {
9043
- applyStatePatch(event.data);
9044
- }
9292
+ const handleBackgroundActivityStreamEvents = useCallback4(
9293
+ (events) => {
9294
+ handleActivityStreamEvents(events);
9045
9295
  },
9046
- [applyStatePatch, handleActivityStreamEvent]
9296
+ [handleActivityStreamEvents]
9047
9297
  );
9048
9298
  useEffect4(() => {
9049
9299
  if (previewMode) {
@@ -9086,16 +9336,19 @@ function useAgentGUINodeController({
9086
9336
  return;
9087
9337
  }
9088
9338
  },
9089
- onEvent: (event) => {
9339
+ onEvents: (events) => {
9090
9340
  if (!activeConversationId) {
9091
9341
  return;
9092
9342
  }
9093
- handleActivityStreamEvent(event);
9094
- const eventSessionId = event.data.agentSessionId?.trim() || activeConversationId;
9095
- if (activeConversationIdRef.current !== activeConversationId || activeConversationIdRef.current !== eventSessionId) {
9096
- return;
9097
- }
9098
- if (event.eventType !== "message_update") {
9343
+ handleActivityStreamEvents(events);
9344
+ for (const event of events) {
9345
+ const eventSessionId = event.data.agentSessionId?.trim() || activeConversationId;
9346
+ if (activeConversationIdRef.current !== activeConversationId || activeConversationIdRef.current !== eventSessionId) {
9347
+ continue;
9348
+ }
9349
+ if (event.eventType === "message_update" || event.eventType === "available_commands_update") {
9350
+ continue;
9351
+ }
9099
9352
  scheduleActivityStreamStateReload(activeConversationId, {
9100
9353
  source: "activity-stream",
9101
9354
  eventType: event.eventType
@@ -9112,8 +9365,8 @@ function useAgentGUINodeController({
9112
9365
  workspaceId,
9113
9366
  agentSessionIds: backgroundWatchedConversationIds,
9114
9367
  enabled: backgroundWatchedConversationIds.length > 0,
9115
- onEvent: (event) => {
9116
- handleBackgroundActivityStreamEvent(event);
9368
+ onEvents: (events) => {
9369
+ handleBackgroundActivityStreamEvents(events);
9117
9370
  }
9118
9371
  });
9119
9372
  useEffect4(() => {
@@ -11317,21 +11570,13 @@ function useAgentGUINodeController({
11317
11570
  [providerComposerOptions]
11318
11571
  )
11319
11572
  );
11320
- const conversationDetail = useMemo4(
11321
- () => projectionConversation ? buildAgentGUIConversationDetail({
11322
- timelineItems: activeTimelineItems,
11323
- conversation: projectionConversation,
11324
- workspaceRoot: workspacePath
11325
- }) : null,
11326
- [activeTimelineItems, projectionConversation, workspacePath]
11327
- );
11328
- const conversation = useMemo4(
11329
- () => projectionConversation ? buildAgentGUIConversationVM({
11573
+ const conversationModels = useMemo4(
11574
+ () => projectionConversation ? buildAgentGUIConversationModels({
11330
11575
  timelineItems: activeTimelineItems,
11331
11576
  conversation: projectionConversation,
11332
11577
  workspaceRoot: workspacePath,
11333
11578
  avoidGroupingEdits
11334
- }) : null,
11579
+ }) : { conversation: null, detail: null },
11335
11580
  [
11336
11581
  activeTimelineItems,
11337
11582
  avoidGroupingEdits,
@@ -11339,6 +11584,22 @@ function useAgentGUINodeController({
11339
11584
  workspacePath
11340
11585
  ]
11341
11586
  );
11587
+ const conversationDetail = useStableConversationDetail(
11588
+ conversationModels.detail
11589
+ );
11590
+ const conversation = useMemo4(() => {
11591
+ if (!conversationModels.conversation) {
11592
+ return null;
11593
+ }
11594
+ if (conversationDetail && (conversationModels.conversation.sourceDetail !== conversationDetail || conversationModels.conversation.activity !== conversationDetail.activity)) {
11595
+ return {
11596
+ ...conversationModels.conversation,
11597
+ activity: conversationDetail.activity,
11598
+ sourceDetail: conversationDetail
11599
+ };
11600
+ }
11601
+ return conversationModels.conversation;
11602
+ }, [conversationDetail, conversationModels.conversation]);
11342
11603
  const activeLiveState = activeConversationLiveState;
11343
11604
  const activationError = activation.errorFor(activeConversationId);
11344
11605
  const activationErrorCode = activation.codeFor(activeConversationId);
@@ -11920,6 +12181,17 @@ function areAgentGUIUserProjectsEqual(left, right) {
11920
12181
  return candidate !== void 0 && project.id === candidate.id && project.path === candidate.path && project.label === candidate.label;
11921
12182
  });
11922
12183
  }
12184
+ function readAgentGUIUserProjectSnapshot(api) {
12185
+ const projects = api?.service?.getSnapshot?.().projects ?? [];
12186
+ return projects.map((project) => ({
12187
+ ...project.createdAtUnixMs === void 0 ? {} : { createdAtUnixMs: project.createdAtUnixMs },
12188
+ id: project.id,
12189
+ ...project.lastUsedAtUnixMs === void 0 || project.lastUsedAtUnixMs === null ? {} : { lastUsedAtUnixMs: project.lastUsedAtUnixMs },
12190
+ label: project.label,
12191
+ path: project.path,
12192
+ ...project.updatedAtUnixMs === void 0 ? {} : { updatedAtUnixMs: project.updatedAtUnixMs }
12193
+ }));
12194
+ }
11923
12195
  function normalizeInteractiveQuestions(value) {
11924
12196
  if (!Array.isArray(value)) {
11925
12197
  return [];
@@ -11993,7 +12265,7 @@ import { WorkspaceUserProjectSelect as WorkspaceUserProjectSelect2 } from "@tutt
11993
12265
  import { BareIconButton, ScrollArea } from "@tutti-os/ui-system/components";
11994
12266
  import {
11995
12267
  EditIcon,
11996
- FolderIcon,
12268
+ FolderIcon as FolderIcon2,
11997
12269
  MoreHorizontalIcon
11998
12270
  } from "@tutti-os/ui-system/icons";
11999
12271
 
@@ -14219,7 +14491,8 @@ import {
14219
14491
  } from "react";
14220
14492
  import { ChevronDown as ChevronDown2, ZapIcon } from "lucide-react";
14221
14493
  import {
14222
- WorkspaceUserProjectSelect
14494
+ WorkspaceUserProjectSelect,
14495
+ resolveWorkspaceUserProjectSelectLabels
14223
14496
  } from "@tutti-os/workspace-user-project/ui";
14224
14497
  import { prepareWorkspaceUserProjectSelection } from "@tutti-os/workspace-user-project/core";
14225
14498
  import {
@@ -14233,7 +14506,9 @@ import {
14233
14506
  DropdownMenuSubContent as DropdownMenuSubContent2,
14234
14507
  DropdownMenuSubTrigger as DropdownMenuSubTrigger2,
14235
14508
  DropdownMenuTrigger as DropdownMenuTrigger2,
14509
+ FolderIcon,
14236
14510
  NewWorkspaceLinedIcon,
14511
+ NoWorkspaceLinedIcon,
14237
14512
  RoomsHintIcon,
14238
14513
  Select,
14239
14514
  SelectContent,
@@ -14573,22 +14848,73 @@ function optionsWithSelectedValue(options, selectedValue) {
14573
14848
 
14574
14849
  // agent-gui/agentGuiNode/AgentComposerSettingsMenus.tsx
14575
14850
  import { Fragment as Fragment4, jsx as jsx21, jsxs as jsxs10 } from "react/jsx-runtime";
14851
+ function basenameProjectPath(path) {
14852
+ const normalized = path.trim().replaceAll("\\", "/").replace(/\/+$/, "");
14853
+ return normalized.split("/").filter(Boolean).at(-1) ?? path;
14854
+ }
14576
14855
  function AgentProjectDropdown({
14577
14856
  composerSettings,
14578
14857
  labels,
14579
14858
  i18n,
14859
+ previewMode = false,
14580
14860
  onProjectMissingChange,
14581
14861
  onProjectPathChange
14582
14862
  }) {
14583
14863
  "use memo";
14584
14864
  const agentHostApi = useAgentHostApi();
14865
+ const resolvedLabels = useMemo5(
14866
+ () => resolveWorkspaceUserProjectSelectLabels(i18n, labels),
14867
+ [i18n, labels]
14868
+ );
14585
14869
  const userProjectApi = useMemo5(
14586
- () => agentHostApi.userProjects ? {
14870
+ () => !previewMode && agentHostApi.userProjects ? {
14587
14871
  ...agentHostApi.userProjects,
14588
14872
  selectDirectory: agentHostApi.workspace.selectDirectory
14589
14873
  } : null,
14590
- [agentHostApi.userProjects, agentHostApi.workspace.selectDirectory]
14874
+ [
14875
+ agentHostApi.userProjects,
14876
+ agentHostApi.workspace.selectDirectory,
14877
+ previewMode
14878
+ ]
14591
14879
  );
14880
+ if (previewMode) {
14881
+ const selectedPath = composerSettings.selectedProjectPath?.trim() ?? "";
14882
+ const triggerLabel = selectedPath ? basenameProjectPath(selectedPath) : resolvedLabels.noProject;
14883
+ return /* @__PURE__ */ jsxs10(
14884
+ "button",
14885
+ {
14886
+ type: "button",
14887
+ "aria-label": composerSettings.projectLocked ? resolvedLabels.projectLocked : resolvedLabels.projectLabel,
14888
+ className: cn2(
14889
+ "w-auto max-w-full",
14890
+ AgentGUINode_styles_default.composerMenuTrigger,
14891
+ "text-[var(--agent-gui-text-tertiary)]"
14892
+ ),
14893
+ children: [
14894
+ /* @__PURE__ */ jsxs10(
14895
+ "span",
14896
+ {
14897
+ className: "workspace-user-project-trigger-label",
14898
+ "data-workspace-user-project-trigger-label": "true",
14899
+ children: [
14900
+ selectedPath ? /* @__PURE__ */ jsx21(FolderIcon, { "aria-hidden": true, className: "shrink-0", size: 15 }) : /* @__PURE__ */ jsx21(
14901
+ NoWorkspaceLinedIcon,
14902
+ {
14903
+ "aria-hidden": true,
14904
+ className: "shrink-0",
14905
+ "data-agent-project-trigger-no-workspace-icon": "true",
14906
+ size: 15
14907
+ }
14908
+ ),
14909
+ /* @__PURE__ */ jsx21("span", { className: "min-w-0 truncate", children: triggerLabel })
14910
+ ]
14911
+ }
14912
+ ),
14913
+ /* @__PURE__ */ jsx21(ChevronDown2, { "aria-hidden": "true", className: "shrink-0", size: 16 })
14914
+ ]
14915
+ }
14916
+ );
14917
+ }
14592
14918
  return /* @__PURE__ */ jsx21(
14593
14919
  WorkspaceUserProjectSelect,
14594
14920
  {
@@ -14628,6 +14954,7 @@ function AgentProjectDropdown({
14628
14954
  function AgentPermissionModeDropdown({
14629
14955
  composerSettings,
14630
14956
  disabled = false,
14957
+ previewMode = false,
14631
14958
  labels,
14632
14959
  onSettingsChange
14633
14960
  }) {
@@ -14659,6 +14986,27 @@ function AgentPermissionModeDropdown({
14659
14986
  }
14660
14987
  applyPermissionModeId(permissionModeId);
14661
14988
  };
14989
+ const trigger = /* @__PURE__ */ jsxs10(
14990
+ "button",
14991
+ {
14992
+ type: "button",
14993
+ className: cn2(
14994
+ "w-auto max-w-full",
14995
+ AgentGUINode_styles_default.composerMenuTrigger,
14996
+ selectDisabled && "cursor-not-allowed text-[var(--agent-gui-text-tertiary)] opacity-60 hover:text-[var(--agent-gui-text-tertiary)]",
14997
+ composerSettings.isSettingsLoading && "animate-pulse"
14998
+ ),
14999
+ "aria-label": labels.permissionLabel,
15000
+ "data-permission-tone": triggerTone,
15001
+ children: [
15002
+ /* @__PURE__ */ jsx21("span", { className: "flex min-w-0 flex-1 items-center", children: /* @__PURE__ */ jsx21("span", { className: "truncate", children: triggerLabel }) }),
15003
+ /* @__PURE__ */ jsx21(ChevronDown2, { "aria-hidden": "true", className: "shrink-0", size: 16 })
15004
+ ]
15005
+ }
15006
+ );
15007
+ if (previewMode) {
15008
+ return trigger;
15009
+ }
14662
15010
  return /* @__PURE__ */ jsxs10(
14663
15011
  Select,
14664
15012
  {
@@ -14702,7 +15050,13 @@ function AgentPermissionModeDropdown({
14702
15050
  onPointerDown: (event) => handleSelectedItemPointerDown(event, option.value),
14703
15051
  children: /* @__PURE__ */ jsxs10("span", { className: "flex min-w-0 items-center gap-1.5", children: [
14704
15052
  /* @__PURE__ */ jsx21("span", { className: "min-w-0 truncate", children: option.label }),
14705
- option.description ? /* @__PURE__ */ jsx21(ComposerOptionInfoTooltip, { description: option.description }) : null
15053
+ option.description ? /* @__PURE__ */ jsx21(
15054
+ ComposerOptionInfoTooltip,
15055
+ {
15056
+ description: option.description,
15057
+ tooltipsEnabled: !previewMode
15058
+ }
15059
+ ) : null
14706
15060
  ] })
14707
15061
  },
14708
15062
  option.value
@@ -14770,23 +15124,28 @@ function AgentProjectMissingStatusProbe({
14770
15124
  return null;
14771
15125
  }
14772
15126
  function ComposerOptionInfoTooltip({
14773
- description
15127
+ description,
15128
+ tooltipsEnabled = true
14774
15129
  }) {
14775
15130
  const stopSelect = (event) => {
14776
15131
  event.preventDefault();
14777
15132
  event.stopPropagation();
14778
15133
  };
15134
+ const trigger = /* @__PURE__ */ jsx21(
15135
+ "span",
15136
+ {
15137
+ className: "pointer-events-none inline-flex shrink-0 cursor-help text-[var(--agent-gui-text-tertiary)] opacity-0 transition-opacity group-hover/composer-option:pointer-events-auto group-hover/composer-option:opacity-100 group-data-[highlighted]/composer-option:pointer-events-auto group-data-[highlighted]/composer-option:opacity-100",
15138
+ "data-agent-composer-option-info-trigger": "true",
15139
+ onClick: stopSelect,
15140
+ onPointerDown: stopSelect,
15141
+ children: /* @__PURE__ */ jsx21(RoomsHintIcon, { "aria-hidden": true, className: "size-3" })
15142
+ }
15143
+ );
15144
+ if (!tooltipsEnabled) {
15145
+ return trigger;
15146
+ }
14779
15147
  return /* @__PURE__ */ jsxs10(Tooltip2, { children: [
14780
- /* @__PURE__ */ jsx21(TooltipTrigger2, { asChild: true, children: /* @__PURE__ */ jsx21(
14781
- "span",
14782
- {
14783
- className: "pointer-events-none inline-flex shrink-0 cursor-help text-[var(--agent-gui-text-tertiary)] opacity-0 transition-opacity group-hover/composer-option:pointer-events-auto group-hover/composer-option:opacity-100 group-data-[highlighted]/composer-option:pointer-events-auto group-data-[highlighted]/composer-option:opacity-100",
14784
- "data-agent-composer-option-info-trigger": "true",
14785
- onClick: stopSelect,
14786
- onPointerDown: stopSelect,
14787
- children: /* @__PURE__ */ jsx21(RoomsHintIcon, { "aria-hidden": true, className: "size-3" })
14788
- }
14789
- ) }),
15148
+ /* @__PURE__ */ jsx21(TooltipTrigger2, { asChild: true, children: trigger }),
14790
15149
  /* @__PURE__ */ jsx21(TooltipContent2, { side: "right", className: "max-w-[240px] whitespace-normal", children: description })
14791
15150
  ] });
14792
15151
  }
@@ -14815,6 +15174,7 @@ function normalizePermissionModeValue(value) {
14815
15174
  function AgentModelReasoningDropdown({
14816
15175
  composerSettings,
14817
15176
  disabled = false,
15177
+ previewMode = false,
14818
15178
  labels,
14819
15179
  onSettingsChange
14820
15180
  }) {
@@ -14826,39 +15186,43 @@ function AgentModelReasoningDropdown({
14826
15186
  onSettingsChange(patch);
14827
15187
  setMenuOpen(false);
14828
15188
  };
15189
+ const trigger = /* @__PURE__ */ jsxs10(
15190
+ "button",
15191
+ {
15192
+ type: "button",
15193
+ className: cn2(
15194
+ "w-auto",
15195
+ AgentGUINode_styles_default.composerMenuTrigger,
15196
+ menuDisabled && "cursor-not-allowed text-[var(--agent-gui-text-tertiary)] opacity-60 hover:text-[var(--agent-gui-text-tertiary)]",
15197
+ composerSettings.isSettingsLoading && "animate-pulse"
15198
+ ),
15199
+ "aria-label": `${labels.modelLabel} / ${labels.reasoningLabel}`,
15200
+ "data-agent-model-reasoning-trigger": "true",
15201
+ children: [
15202
+ /* @__PURE__ */ jsxs10("span", { className: "flex min-w-0 flex-1 items-center gap-2 overflow-hidden", children: [
15203
+ menu.speed.show && menu.trigger.isFast ? /* @__PURE__ */ jsx21(
15204
+ ZapIcon,
15205
+ {
15206
+ "aria-hidden": true,
15207
+ className: "size-3.5 shrink-0",
15208
+ "data-agent-speed-indicator": "fast",
15209
+ strokeWidth: 2.5
15210
+ }
15211
+ ) : null,
15212
+ menu.trigger.showCombined ? /* @__PURE__ */ jsx21("span", { className: "min-w-0 truncate", children: menu.trigger.combinedLabel }) : /* @__PURE__ */ jsxs10(Fragment4, { children: [
15213
+ /* @__PURE__ */ jsx21("span", { className: "min-w-0 truncate", children: menu.trigger.modelLabel }),
15214
+ /* @__PURE__ */ jsx21("span", { className: "shrink-0", children: menu.trigger.reasoningLabel })
15215
+ ] })
15216
+ ] }),
15217
+ /* @__PURE__ */ jsx21(ChevronDown2, { "aria-hidden": "true", className: "shrink-0", size: 16 })
15218
+ ]
15219
+ }
15220
+ );
15221
+ if (previewMode) {
15222
+ return trigger;
15223
+ }
14829
15224
  return /* @__PURE__ */ jsxs10(DropdownMenu2, { open: menuOpen, onOpenChange: setMenuOpen, children: [
14830
- /* @__PURE__ */ jsx21(DropdownMenuTrigger2, { asChild: true, disabled: menuDisabled, children: /* @__PURE__ */ jsxs10(
14831
- "button",
14832
- {
14833
- type: "button",
14834
- className: cn2(
14835
- "w-auto",
14836
- AgentGUINode_styles_default.composerMenuTrigger,
14837
- menuDisabled && "cursor-not-allowed text-[var(--agent-gui-text-tertiary)] opacity-60 hover:text-[var(--agent-gui-text-tertiary)]",
14838
- composerSettings.isSettingsLoading && "animate-pulse"
14839
- ),
14840
- "aria-label": `${labels.modelLabel} / ${labels.reasoningLabel}`,
14841
- "data-agent-model-reasoning-trigger": "true",
14842
- children: [
14843
- /* @__PURE__ */ jsxs10("span", { className: "flex min-w-0 flex-1 items-center gap-2 overflow-hidden", children: [
14844
- menu.speed.show && menu.trigger.isFast ? /* @__PURE__ */ jsx21(
14845
- ZapIcon,
14846
- {
14847
- "aria-hidden": true,
14848
- className: "size-3.5 shrink-0",
14849
- "data-agent-speed-indicator": "fast",
14850
- strokeWidth: 2.5
14851
- }
14852
- ) : null,
14853
- menu.trigger.showCombined ? /* @__PURE__ */ jsx21("span", { className: "min-w-0 truncate", children: menu.trigger.combinedLabel }) : /* @__PURE__ */ jsxs10(Fragment4, { children: [
14854
- /* @__PURE__ */ jsx21("span", { className: "min-w-0 truncate", children: menu.trigger.modelLabel }),
14855
- /* @__PURE__ */ jsx21("span", { className: "shrink-0", children: menu.trigger.reasoningLabel })
14856
- ] })
14857
- ] }),
14858
- /* @__PURE__ */ jsx21(ChevronDown2, { "aria-hidden": "true", className: "shrink-0", size: 16 })
14859
- ]
14860
- }
14861
- ) }),
15225
+ /* @__PURE__ */ jsx21(DropdownMenuTrigger2, { asChild: true, disabled: menuDisabled, children: trigger }),
14862
15226
  /* @__PURE__ */ jsxs10(
14863
15227
  DropdownMenuContent2,
14864
15228
  {
@@ -14880,6 +15244,7 @@ function AgentModelReasoningDropdown({
14880
15244
  options: menu.model.options,
14881
15245
  selectedValue: menu.model.selectedValue,
14882
15246
  descriptionPresentation: "model-tooltip",
15247
+ tooltipsEnabled: !previewMode,
14883
15248
  onSelect: (value) => applySettingsChange({ model: value })
14884
15249
  }
14885
15250
  )
@@ -14907,6 +15272,7 @@ function AgentModelReasoningDropdown({
14907
15272
  {
14908
15273
  options: menu.reasoning.options,
14909
15274
  selectedValue: menu.reasoning.selectedValue,
15275
+ tooltipsEnabled: !previewMode,
14910
15276
  onSelect: (value) => applySettingsChange({ reasoningEffort: value })
14911
15277
  }
14912
15278
  )
@@ -14936,6 +15302,7 @@ function AgentModelReasoningDropdown({
14936
15302
  options: menu.speed.options,
14937
15303
  selectedValue: menu.speed.selectedValue,
14938
15304
  descriptionPresentation: "inline",
15305
+ tooltipsEnabled: !previewMode,
14939
15306
  onSelect: (value) => applySettingsChange({ speed: value })
14940
15307
  }
14941
15308
  )
@@ -14951,6 +15318,7 @@ function ComposerMenuOptionItems({
14951
15318
  options,
14952
15319
  selectedValue,
14953
15320
  descriptionPresentation = "none",
15321
+ tooltipsEnabled = true,
14954
15322
  onSelect
14955
15323
  }) {
14956
15324
  return /* @__PURE__ */ jsx21(Fragment4, { children: options.map((option) => {
@@ -15001,7 +15369,8 @@ function ComposerMenuOptionItems({
15001
15369
  showTooltipDescription && option.description ? /* @__PURE__ */ jsx21(
15002
15370
  ComposerOptionInfoTooltip,
15003
15371
  {
15004
- description: option.description
15372
+ description: option.description,
15373
+ tooltipsEnabled
15005
15374
  }
15006
15375
  ) : null
15007
15376
  ] }),
@@ -15023,14 +15392,23 @@ function ComposerMenuOptionItems({
15023
15392
  },
15024
15393
  option.value
15025
15394
  );
15026
- return showModelTooltip ? /* @__PURE__ */ jsx21(ComposerModelOptionTooltip, { option, children: item }, option.value) : item;
15395
+ return showModelTooltip ? /* @__PURE__ */ jsx21(
15396
+ ComposerModelOptionTooltip,
15397
+ {
15398
+ option,
15399
+ tooltipsEnabled,
15400
+ children: item
15401
+ },
15402
+ option.value
15403
+ ) : item;
15027
15404
  }) });
15028
15405
  }
15029
15406
  function ComposerModelOptionTooltip({
15030
15407
  children,
15031
- option
15408
+ option,
15409
+ tooltipsEnabled = true
15032
15410
  }) {
15033
- if (!option.tooltip) {
15411
+ if (!tooltipsEnabled || !option.tooltip) {
15034
15412
  return children;
15035
15413
  }
15036
15414
  return /* @__PURE__ */ jsxs10(Tooltip2, { children: [
@@ -16514,7 +16892,8 @@ function AgentUsageChip({
16514
16892
  usedTokens,
16515
16893
  totalTokens,
16516
16894
  limits,
16517
- labels
16895
+ labels,
16896
+ tooltipsEnabled = true
16518
16897
  }) {
16519
16898
  "use memo";
16520
16899
  const clampedPercent = Math.max(0, Math.min(100, percentUsed));
@@ -16522,29 +16901,33 @@ function AgentUsageChip({
16522
16901
  const showTokens = usedTokens !== null && totalTokens !== null;
16523
16902
  const usageLevel = agentUsageChipLevel(clampedPercent);
16524
16903
  const ringColor = agentUsageRingColor(usageLevel);
16525
- return /* @__PURE__ */ jsxs13(Popover, { children: [
16526
- /* @__PURE__ */ jsx26(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsxs13(Tooltip, { children: [
16527
- /* @__PURE__ */ jsx26(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx26(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx26(
16528
- "button",
16904
+ const trigger = /* @__PURE__ */ jsx26(
16905
+ "button",
16906
+ {
16907
+ type: "button",
16908
+ "aria-label": chipLabel,
16909
+ className: "nodrag relative mr-2 inline-flex size-4 shrink-0 cursor-default items-center justify-center rounded-full p-0 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[color:color-mix(in_srgb,var(--text-primary)_34%,transparent)] [-webkit-app-region:no-drag]",
16910
+ "data-testid": "agent-gui-usage-chip",
16911
+ "data-usage-level": usageLevel,
16912
+ title: chipLabel,
16913
+ style: {
16914
+ background: `conic-gradient(${ringColor} ${clampedPercent}%, color-mix(in srgb, ${ringColor} 16%, transparent) 0)`
16915
+ },
16916
+ children: /* @__PURE__ */ jsx26(
16917
+ "span",
16529
16918
  {
16530
- type: "button",
16531
- "aria-label": chipLabel,
16532
- className: "nodrag relative mr-2 inline-flex size-4 shrink-0 cursor-default items-center justify-center rounded-full p-0 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[color:color-mix(in_srgb,var(--text-primary)_34%,transparent)] [-webkit-app-region:no-drag]",
16533
- "data-testid": "agent-gui-usage-chip",
16534
- "data-usage-level": usageLevel,
16535
- title: chipLabel,
16536
- style: {
16537
- background: `conic-gradient(${ringColor} ${clampedPercent}%, color-mix(in srgb, ${ringColor} 16%, transparent) 0)`
16538
- },
16539
- children: /* @__PURE__ */ jsx26(
16540
- "span",
16541
- {
16542
- "aria-hidden": "true",
16543
- className: "absolute inset-0.5 rounded-full bg-[var(--agent-gui-surface-raised,var(--background-fronted))]"
16544
- }
16545
- )
16919
+ "aria-hidden": "true",
16920
+ className: "absolute inset-0.5 rounded-full bg-[var(--agent-gui-surface-raised,var(--background-fronted))]"
16546
16921
  }
16547
- ) }) }),
16922
+ )
16923
+ }
16924
+ );
16925
+ if (!tooltipsEnabled) {
16926
+ return trigger;
16927
+ }
16928
+ return /* @__PURE__ */ jsxs13(Popover, { children: [
16929
+ /* @__PURE__ */ jsx26(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsxs13(Tooltip, { children: [
16930
+ /* @__PURE__ */ jsx26(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx26(TooltipTrigger, { asChild: true, children: trigger }) }),
16548
16931
  /* @__PURE__ */ jsx26(TooltipContent, { side: "top", children: labels.usageTooltipLabel })
16549
16932
  ] }) }),
16550
16933
  /* @__PURE__ */ jsx26(
@@ -16693,6 +17076,7 @@ function AgentComposer({
16693
17076
  isSendingTurn,
16694
17077
  isSubmittingPrompt,
16695
17078
  isActive = true,
17079
+ previewMode = false,
16696
17080
  promptImagesSupported = true,
16697
17081
  composerFocusRequestSequence = null,
16698
17082
  layoutMode = "dock",
@@ -17555,20 +17939,17 @@ function AgentComposer({
17555
17939
  const uploadedImage = result.content.find(
17556
17940
  (block) => block.type === "image"
17557
17941
  );
17558
- const uploadedPath = uploadedImage?.path?.trim() ?? "";
17559
- if (!uploadedPath) {
17560
- throw new Error("Prompt image upload completed without path.");
17942
+ const uploadedUrl = uploadedImage?.url?.trim() ?? "";
17943
+ if (!uploadedUrl) {
17944
+ throw new Error("Prompt image upload completed without url.");
17561
17945
  }
17562
17946
  const uploadedDraftImages = draftImagesRef.current.map(
17563
17947
  (image) => image.id === draftImage.id ? {
17564
17948
  id: image.id,
17565
17949
  name: image.name,
17566
17950
  mimeType: image.mimeType,
17567
- path: uploadedPath,
17568
- ...uploadedImage?.uri ? { uri: uploadedImage.uri } : {},
17569
- ...uploadedImage?.uploadStatus ? { uploadStatus: uploadedImage.uploadStatus } : {},
17570
- ...uploadedImage?.assetId ? { assetId: uploadedImage.assetId } : {},
17571
- previewUrl: image.previewUrl,
17951
+ url: uploadedUrl,
17952
+ previewUrl: uploadedUrl,
17572
17953
  uploading: false
17573
17954
  } : image
17574
17955
  );
@@ -17846,6 +18227,10 @@ function AgentComposer({
17846
18227
  "--agent-gui-prompt-tip-cycle-duration": `${promptTips.length * PROMPT_TIP_CYCLE_STEP_MS}ms`
17847
18228
  } : void 0;
17848
18229
  useLayoutEffect4(() => {
18230
+ if (previewMode) {
18231
+ setIsPromptTipOverflowing(false);
18232
+ return;
18233
+ }
17849
18234
  if (!activePromptTipId) {
17850
18235
  setIsPromptTipOverflowing(false);
17851
18236
  return;
@@ -17869,7 +18254,12 @@ function AgentComposer({
17869
18254
  resizeObserver?.disconnect();
17870
18255
  window.removeEventListener("resize", measure);
17871
18256
  };
17872
- }, [activePromptTipId, activePromptTipText, isPromptTipOverflowing]);
18257
+ }, [
18258
+ activePromptTipId,
18259
+ activePromptTipText,
18260
+ isPromptTipOverflowing,
18261
+ previewMode
18262
+ ]);
17873
18263
  const inputShellStyle = useMemo9(
17874
18264
  () => showFileMentionPalette || showFloatingCommandMenu ? { zIndex: composerPaletteZIndex } : void 0,
17875
18265
  [showFileMentionPalette, showFloatingCommandMenu]
@@ -17969,6 +18359,7 @@ function AgentComposer({
17969
18359
  embedded: true,
17970
18360
  edgeGlow: true,
17971
18361
  keyboardShortcuts: activePromptKeyboardShortcutsEnabled,
18362
+ previewMode,
17972
18363
  isSubmitting: isSubmittingPrompt,
17973
18364
  onSubmit: submitInteractivePromptAndDismiss,
17974
18365
  labels: {
@@ -18283,7 +18674,27 @@ function AgentComposer({
18283
18674
  ),
18284
18675
  /* @__PURE__ */ jsxs13("div", { className: AgentGUINode_styles_default.composerFooter, children: [
18285
18676
  /* @__PURE__ */ jsxs13("div", { className: composerStyles.footerGroup, children: [
18286
- /* @__PURE__ */ jsx26(
18677
+ previewMode ? /* @__PURE__ */ jsx26(
18678
+ "button",
18679
+ {
18680
+ type: "button",
18681
+ "aria-label": labels.referenceWorkspaceFiles,
18682
+ title: labels.referenceWorkspaceFiles,
18683
+ className: cn(
18684
+ AgentGUINode_styles_default.composerMenuTrigger,
18685
+ AgentGUINode_styles_default.composerReferenceTrigger,
18686
+ "w-auto justify-center px-1 text-[var(--agent-gui-text-secondary)] [&_svg]:shrink-0"
18687
+ ),
18688
+ children: /* @__PURE__ */ jsx26(
18689
+ AddIcon,
18690
+ {
18691
+ "aria-hidden": true,
18692
+ className: "size-3.5",
18693
+ "data-agent-reference-add-icon": "true"
18694
+ }
18695
+ )
18696
+ }
18697
+ ) : /* @__PURE__ */ jsx26(
18287
18698
  Select2,
18288
18699
  {
18289
18700
  open: false,
@@ -18351,6 +18762,7 @@ function AgentComposer({
18351
18762
  usedTokens: usage.usedTokens,
18352
18763
  totalTokens: usage.totalTokens,
18353
18764
  limits: slashStatus?.limits ?? [],
18765
+ tooltipsEnabled: !previewMode,
18354
18766
  labels: {
18355
18767
  usageChipLabel: labels.usageChipLabel,
18356
18768
  usageTooltipLabel: labels.usageTooltipLabel,
@@ -18365,6 +18777,7 @@ function AgentComposer({
18365
18777
  {
18366
18778
  composerSettings,
18367
18779
  disabled: settingsControlsDisabled,
18780
+ previewMode,
18368
18781
  labels: {
18369
18782
  permissionLabel: labels.permissionLabel
18370
18783
  },
@@ -18376,6 +18789,7 @@ function AgentComposer({
18376
18789
  {
18377
18790
  composerSettings,
18378
18791
  disabled: settingsControlsDisabled,
18792
+ previewMode,
18379
18793
  labels: {
18380
18794
  modelLabel: labels.modelLabel,
18381
18795
  modelSelectionLabel: labels.modelSelectionLabel,
@@ -18474,6 +18888,7 @@ function AgentComposer({
18474
18888
  {
18475
18889
  composerSettings,
18476
18890
  i18n: workspaceUserProjectI18n,
18891
+ previewMode,
18477
18892
  labels: {
18478
18893
  projectLocked: labels.projectLocked,
18479
18894
  projectMissingDescription: labels.projectMissingDescription
@@ -18487,7 +18902,7 @@ function AgentComposer({
18487
18902
  {
18488
18903
  className: AgentGUINode_styles_default.composerPromptTips,
18489
18904
  "data-testid": "agent-gui-prompt-tips",
18490
- children: isPromptTipOverflowing && promptTipNode ? /* @__PURE__ */ jsx26(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsxs13(Tooltip, { children: [
18905
+ children: !previewMode && isPromptTipOverflowing && promptTipNode ? /* @__PURE__ */ jsx26(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsxs13(Tooltip, { children: [
18491
18906
  /* @__PURE__ */ jsx26(TooltipTrigger, { asChild: true, children: promptTipNode }),
18492
18907
  /* @__PURE__ */ jsx26(
18493
18908
  TooltipContent,
@@ -18680,13 +19095,16 @@ function groupConversations(conversations, labels, userProjects = [], options =
18680
19095
  });
18681
19096
  }
18682
19097
  const projectGroups = /* @__PURE__ */ new Map();
19098
+ const normalizedProjectPathByPath = /* @__PURE__ */ new Map();
19099
+ const normalizeProjectPath = (path) => normalizeConversationProjectPathCached(path, normalizedProjectPathByPath);
18683
19100
  userProjects.forEach((project, projectOrder) => {
18684
- const normalizedPath = normalizeConversationProjectPath(project.path);
18685
- if (!normalizedPath || projectGroups.has(`project:${normalizedPath}`)) {
19101
+ const normalizedPath = normalizeProjectPath(project.path);
19102
+ const sectionId = `project:${normalizedPath}`;
19103
+ if (projectGroups.has(sectionId)) {
18686
19104
  return;
18687
19105
  }
18688
- projectGroups.set(`project:${normalizedPath}`, {
18689
- id: `project:${normalizedPath}`,
19106
+ projectGroups.set(sectionId, {
19107
+ id: sectionId,
18690
19108
  kind: "project",
18691
19109
  label: project.label,
18692
19110
  project,
@@ -18712,19 +19130,41 @@ function groupConversations(conversations, labels, userProjects = [], options =
18712
19130
  if ((conversation.pinnedAtUnixMs ?? 0) > 0) {
18713
19131
  continue;
18714
19132
  }
18715
- const section = resolveConversationProjectSection(conversation, labels);
18716
- const existing = projectGroups.get(section.id);
19133
+ if (!conversation.project) {
19134
+ const existing2 = projectGroups.get("conversations");
19135
+ if (existing2) {
19136
+ existing2.items.push(conversation);
19137
+ continue;
19138
+ }
19139
+ projectGroups.set("conversations", {
19140
+ id: "conversations",
19141
+ kind: "conversations",
19142
+ label: labels.sectionConversations,
19143
+ project: null,
19144
+ items: [conversation],
19145
+ projectOrder: Number.MAX_SAFE_INTEGER,
19146
+ sectionOrder: 1,
19147
+ projectUpdatedAtUnixMs: 0
19148
+ });
19149
+ continue;
19150
+ }
19151
+ const normalizedPath = normalizeProjectPath(conversation.project.path);
19152
+ const sectionId = `project:${normalizedPath}`;
19153
+ const existing = projectGroups.get(sectionId);
18717
19154
  if (existing) {
18718
19155
  existing.items.push(conversation);
18719
19156
  continue;
18720
19157
  }
18721
- projectGroups.set(section.id, {
18722
- ...section,
19158
+ projectGroups.set(sectionId, {
19159
+ id: sectionId,
19160
+ kind: "project",
19161
+ label: conversation.project.label,
19162
+ project: conversation.project,
18723
19163
  items: [conversation],
18724
- projectOrder: section.kind === "project" ? Number.MAX_SAFE_INTEGER - 1 : Number.MAX_SAFE_INTEGER,
18725
- sectionOrder: section.kind === "project" ? 0 : 1,
19164
+ projectOrder: Number.MAX_SAFE_INTEGER - 1,
19165
+ sectionOrder: 0,
18726
19166
  projectUpdatedAtUnixMs: resolveConversationProjectUpdatedAtUnixMs(
18727
- section.project
19167
+ conversation.project
18728
19168
  )
18729
19169
  });
18730
19170
  }
@@ -18748,27 +19188,18 @@ function resolveConversationProjectUpdatedAtUnixMs(project) {
18748
19188
  }
18749
19189
  return project.updatedAtUnixMs ?? project.lastUsedAtUnixMs ?? project.createdAtUnixMs ?? 0;
18750
19190
  }
18751
- function resolveConversationProjectSection(conversation, labels) {
18752
- if (conversation.project) {
18753
- return {
18754
- id: `project:${normalizeConversationProjectPath(
18755
- conversation.project.path
18756
- )}`,
18757
- kind: "project",
18758
- label: conversation.project.label,
18759
- project: conversation.project
18760
- };
18761
- }
18762
- return {
18763
- id: "conversations",
18764
- kind: "conversations",
18765
- label: labels.sectionConversations,
18766
- project: null
18767
- };
18768
- }
18769
19191
  function normalizeConversationProjectPath(path) {
18770
19192
  return path.trim().replaceAll("\\", "/").replace(/\/+$/, "") || "/";
18771
19193
  }
19194
+ function normalizeConversationProjectPathCached(path, normalizedPathByPath) {
19195
+ const cached = normalizedPathByPath.get(path);
19196
+ if (cached !== void 0) {
19197
+ return cached;
19198
+ }
19199
+ const normalized = normalizeConversationProjectPath(path);
19200
+ normalizedPathByPath.set(path, normalized);
19201
+ return normalized;
19202
+ }
18772
19203
  function conversationMetaKind(conversation) {
18773
19204
  if (conversation.status === "working") {
18774
19205
  return "loading";
@@ -19368,8 +19799,6 @@ function AgentGUINodeView({
19368
19799
  };
19369
19800
  const conversationRailStoreState = useMemo10(
19370
19801
  () => ({
19371
- conversations: viewModel.conversations,
19372
- userProjects: viewModel.userProjects,
19373
19802
  activeConversationId: viewModel.activeConversationId,
19374
19803
  pendingDeleteConversationId: viewModel.pendingDeleteConversation?.id ?? null,
19375
19804
  isLoadingConversations: viewModel.isLoadingConversations,
@@ -19379,6 +19808,7 @@ function AgentGUINodeView({
19379
19808
  workspaceUserProjectI18n,
19380
19809
  uiLanguage,
19381
19810
  showProjectSelector,
19811
+ previewMode,
19382
19812
  createConversationDisabled,
19383
19813
  openclawGateway,
19384
19814
  isCollapsed: conversationRailCollapsed,
@@ -19404,6 +19834,7 @@ function AgentGUINodeView({
19404
19834
  openConversationWindow,
19405
19835
  openProjectFiles,
19406
19836
  openclawGateway,
19837
+ previewMode,
19407
19838
  removeProject,
19408
19839
  requestCreateConversation,
19409
19840
  requestDeleteConversation,
@@ -19413,12 +19844,10 @@ function AgentGUINodeView({
19413
19844
  toggleConversationPinned,
19414
19845
  uiLanguage,
19415
19846
  viewModel.activeConversationId,
19416
- viewModel.conversations,
19417
19847
  viewModel.isDeletingConversation,
19418
19848
  viewModel.isDeletingProjectConversations,
19419
19849
  viewModel.isLoadingConversations,
19420
19850
  viewModel.pendingDeleteConversation?.id,
19421
- viewModel.userProjects,
19422
19851
  workspaceUserProjectI18n
19423
19852
  ]
19424
19853
  );
@@ -19435,7 +19864,7 @@ function AgentGUINodeView({
19435
19864
  conversationRailStore,
19436
19865
  conversationRailStoreState
19437
19866
  );
19438
- return /* @__PURE__ */ jsxs15(TooltipProvider2, { children: [
19867
+ const content = /* @__PURE__ */ jsxs15(Fragment8, { children: [
19439
19868
  /* @__PURE__ */ jsxs15(
19440
19869
  "div",
19441
19870
  {
@@ -19456,8 +19885,10 @@ function AgentGUINodeView({
19456
19885
  children: /* @__PURE__ */ jsx29(
19457
19886
  AgentGUIConversationRailStorePane,
19458
19887
  {
19888
+ conversations: viewModel.conversations,
19459
19889
  store: conversationRailStore,
19460
- storeState: conversationRailStoreState
19890
+ storeState: conversationRailStoreState,
19891
+ userProjects: viewModel.userProjects
19461
19892
  }
19462
19893
  )
19463
19894
  }
@@ -19508,7 +19939,8 @@ function AgentGUINodeView({
19508
19939
  onRequestGitBranches,
19509
19940
  contextMentionProviders,
19510
19941
  workspaceAppIcons: effectiveWorkspaceAppIcons,
19511
- workspaceUserProjectI18n
19942
+ workspaceUserProjectI18n,
19943
+ previewMode
19512
19944
  }
19513
19945
  ) })
19514
19946
  ]
@@ -19540,6 +19972,7 @@ function AgentGUINodeView({
19540
19972
  }
19541
19973
  )
19542
19974
  ] });
19975
+ return previewMode ? content : /* @__PURE__ */ jsx29(TooltipProvider2, { children: content });
19543
19976
  }
19544
19977
  function mergeWorkspaceAppIconsFromCommands(input) {
19545
19978
  const seen = new Set(
@@ -19607,6 +20040,7 @@ var AgentGUIDetailPane = memo(function AgentGUIDetailPane2({
19607
20040
  uiLanguage,
19608
20041
  hideDetailHeader,
19609
20042
  isActive,
20043
+ previewMode,
19610
20044
  composerFocusRequestSequence,
19611
20045
  isAgentProviderReady,
19612
20046
  slashStatusLimits,
@@ -20085,6 +20519,7 @@ var AgentGUIDetailPane = memo(function AgentGUIDetailPane2({
20085
20519
  canQueueWhileBusy,
20086
20520
  placeholder: viewModel.hasSentUserMessage ? labels.followupPlaceholder : labels.initialPlaceholder,
20087
20521
  showStopButton,
20522
+ previewMode,
20088
20523
  // Plan decisions replace the composer via bottomDockReplacementPrompt;
20089
20524
  // approval / ask-user embed here (composerActivePrompt encodes that).
20090
20525
  activePrompt: composerActivePrompt,
@@ -20129,6 +20564,7 @@ var AgentGUIDetailPane = memo(function AgentGUIDetailPane2({
20129
20564
  labels.followupPlaceholder,
20130
20565
  labels.initialPlaceholder,
20131
20566
  labels.promptTips,
20567
+ previewMode,
20132
20568
  composerActivePrompt,
20133
20569
  editQueuedPrompt,
20134
20570
  onCapabilitySettingsRequest,
@@ -20168,6 +20604,14 @@ var AgentGUIDetailPane = memo(function AgentGUIDetailPane2({
20168
20604
  workspaceAppIcons
20169
20605
  ]
20170
20606
  );
20607
+ const emptyHeroComposerProps = useMemo10(
20608
+ () => ({
20609
+ ...bottomDockComposerProps,
20610
+ compactSupported: viewModel.compactSupported,
20611
+ layoutMode: "hero"
20612
+ }),
20613
+ [bottomDockComposerProps, viewModel.compactSupported]
20614
+ );
20171
20615
  const bottomDockStoreState = useMemo10(
20172
20616
  () => ({
20173
20617
  // The lifted prompt is rendered from props on the pane; the store still
@@ -20323,7 +20767,8 @@ var AgentGUIDetailPane = memo(function AgentGUIDetailPane2({
20323
20767
  showSyncIndicator,
20324
20768
  syncStatus,
20325
20769
  syncLabel,
20326
- showFailedSyncLabel
20770
+ showFailedSyncLabel,
20771
+ previewMode
20327
20772
  }
20328
20773
  ),
20329
20774
  showProviderSetupNotice ? /* @__PURE__ */ jsxs15(
@@ -20368,57 +20813,7 @@ var AgentGUIDetailPane = memo(function AgentGUIDetailPane2({
20368
20813
  onAuthLogin: authLogin,
20369
20814
  onContinueInNewConversation: continueInNewConversation,
20370
20815
  chromeLabels,
20371
- composerProps: {
20372
- workspaceId: viewModel.workspaceId,
20373
- workspacePath: viewModel.workspacePath,
20374
- currentUserId: viewModel.currentUserId,
20375
- provider: viewModel.data.provider,
20376
- slashStatus,
20377
- usage: viewModel.usage,
20378
- draftContent: viewModel.draftContent,
20379
- availableCommands: viewModel.availableCommands,
20380
- hasCompactableContext: viewModel.hasSentUserMessage,
20381
- compactSupported: viewModel.compactSupported,
20382
- availableSkills: viewModel.availableSkills,
20383
- disabled: composerDisabled,
20384
- disabledReason: composerDisabledReason,
20385
- submitDisabled,
20386
- composerSettings: viewModel.composerSettings,
20387
- queuedPrompts: viewModel.queuedPrompts,
20388
- drainingQueuedPromptId: viewModel.drainingQueuedPromptId,
20389
- workspaceAppIcons,
20390
- canQueueWhileBusy,
20391
- placeholder: viewModel.hasSentUserMessage ? labels.followupPlaceholder : labels.initialPlaceholder,
20392
- showStopButton,
20393
- activePrompt: composerActivePrompt,
20394
- activePromptKeyboardShortcutsEnabled: isActive,
20395
- composerFocusRequestSequence,
20396
- isActive,
20397
- promptImagesSupported: viewModel.promptImagesSupported,
20398
- promptTips: labels.promptTips,
20399
- showProjectSelector,
20400
- isInterrupting: viewModel.isInterrupting,
20401
- isSendingTurn: isComposerSending,
20402
- isSubmittingPrompt: viewModel.isRespondingApproval,
20403
- labels: composerLabels,
20404
- workspaceUserProjectI18n,
20405
- capabilityMenuState,
20406
- onDraftContentChange: updateDraftContent,
20407
- onProjectPathChange: updateSelectedProjectPath,
20408
- onSettingsChange: updateComposerSettings,
20409
- onSubmit: submitPrompt,
20410
- onPromptImagesUnsupported: showPromptImagesUnsupported,
20411
- onSendQueuedPromptNext: sendQueuedPromptNext,
20412
- onRemoveQueuedPrompt: removeQueuedPrompt,
20413
- onEditQueuedPrompt: editQueuedPrompt,
20414
- onInterruptCurrentTurn: handleInterruptCurrentTurn,
20415
- onSubmitInteractivePrompt: submitInteractivePrompt,
20416
- onCapabilitySettingsRequest,
20417
- onLinkAction: stableLinkAction,
20418
- onRequestWorkspaceReferences: stableRequestWorkspaceReferences,
20419
- onRequestGitBranches: stableRequestGitBranches,
20420
- contextMentionProviders
20421
- }
20816
+ composerProps: emptyHeroComposerProps
20422
20817
  }
20423
20818
  ) : /* @__PURE__ */ jsx29(
20424
20819
  AgentGUIConversationTimelinePane,
@@ -20431,6 +20826,7 @@ var AgentGUIDetailPane = memo(function AgentGUIDetailPane2({
20431
20826
  onAuthLogin: authLogin,
20432
20827
  availableSkills: viewModel.availableSkills,
20433
20828
  workspaceAppIcons,
20829
+ previewMode,
20434
20830
  labels: conversationFlowLabels
20435
20831
  }
20436
20832
  )
@@ -20471,7 +20867,8 @@ var AgentGUIDetailHeader = memo(function AgentGUIDetailHeader2({
20471
20867
  showSyncIndicator,
20472
20868
  syncStatus,
20473
20869
  syncLabel,
20474
- showFailedSyncLabel
20870
+ showFailedSyncLabel,
20871
+ previewMode
20475
20872
  }) {
20476
20873
  "use memo";
20477
20874
  if (hidden || !activeConversation) {
@@ -20482,7 +20879,7 @@ var AgentGUIDetailHeader = memo(function AgentGUIDetailHeader2({
20482
20879
  return /* @__PURE__ */ jsxs15("div", { className: AgentGUINode_styles_default.detailHeader, children: [
20483
20880
  /* @__PURE__ */ jsxs15("span", { className: AgentGUINode_styles_default.detailHeaderTitleGroup, children: [
20484
20881
  /* @__PURE__ */ jsx29("span", { className: AgentGUINode_styles_default.detailHeaderTitle, children: conversationPlainTitle(activeConversation, labels, uiLanguage) }),
20485
- runPath ? /* @__PURE__ */ jsx29(AgentRunPathInfo, { path: runPath }) : null
20882
+ runPath ? /* @__PURE__ */ jsx29(AgentRunPathInfo, { path: runPath, previewMode }) : null
20486
20883
  ] }),
20487
20884
  /* @__PURE__ */ jsxs15(
20488
20885
  "span",
@@ -20506,18 +20903,25 @@ var AgentGUIDetailHeader = memo(function AgentGUIDetailHeader2({
20506
20903
  )
20507
20904
  ] });
20508
20905
  });
20509
- function AgentRunPathInfo({ path }) {
20906
+ function AgentRunPathInfo({
20907
+ path,
20908
+ previewMode
20909
+ }) {
20510
20910
  "use memo";
20911
+ const trigger = /* @__PURE__ */ jsx29(
20912
+ "button",
20913
+ {
20914
+ type: "button",
20915
+ className: AgentGUINode_styles_default.detailHeaderPathInfo,
20916
+ "aria-label": path,
20917
+ children: /* @__PURE__ */ jsx29(Info, { size: 14, strokeWidth: 2, "aria-hidden": "true" })
20918
+ }
20919
+ );
20920
+ if (previewMode) {
20921
+ return trigger;
20922
+ }
20511
20923
  return /* @__PURE__ */ jsxs15(Tooltip3, { children: [
20512
- /* @__PURE__ */ jsx29(TooltipTrigger3, { asChild: true, children: /* @__PURE__ */ jsx29(
20513
- "button",
20514
- {
20515
- type: "button",
20516
- className: AgentGUINode_styles_default.detailHeaderPathInfo,
20517
- "aria-label": path,
20518
- children: /* @__PURE__ */ jsx29(Info, { size: 14, strokeWidth: 2, "aria-hidden": "true" })
20519
- }
20520
- ) }),
20924
+ /* @__PURE__ */ jsx29(TooltipTrigger3, { asChild: true, children: trigger }),
20521
20925
  /* @__PURE__ */ jsx29(
20522
20926
  TooltipContent3,
20523
20927
  {
@@ -20587,7 +20991,7 @@ var AgentGUIEmptyHeroPane = memo(function AgentGUIEmptyHeroPane2({
20587
20991
  labels: chromeLabels
20588
20992
  }
20589
20993
  ) : null,
20590
- /* @__PURE__ */ jsx29(AgentComposer, { ...composerProps, layoutMode: "hero" })
20994
+ /* @__PURE__ */ jsx29(AgentComposer, { ...composerProps })
20591
20995
  ] }) });
20592
20996
  });
20593
20997
  function EmptyHeroTitle({
@@ -20686,6 +21090,7 @@ var AgentGUIBottomDockPane = memo(function AgentGUIBottomDockPane2({
20686
21090
  isRespondingApproval,
20687
21091
  sessionChrome
20688
21092
  } = state;
21093
+ const previewMode = composerProps.previewMode === true;
20689
21094
  const goal = objectRecord(sessionChrome.rawState?.runtimeContext?.goal);
20690
21095
  const goalObjective = goal ? stringValue2(goal.objective) : "";
20691
21096
  const goalStatus = goal ? stringValue2(goal.status) : "";
@@ -20711,6 +21116,7 @@ var AgentGUIBottomDockPane = memo(function AgentGUIBottomDockPane2({
20711
21116
  embedded: true,
20712
21117
  edgeGlow: true,
20713
21118
  keyboardShortcuts: keyboardShortcutsEnabled,
21119
+ previewMode,
20714
21120
  isSubmitting: isRespondingApproval,
20715
21121
  onSubmit: onSubmitBottomDockInteractivePrompt,
20716
21122
  labels: promptLabels
@@ -20775,6 +21181,7 @@ var AgentGUIBottomDockPane = memo(function AgentGUIBottomDockPane2({
20775
21181
  embedded: true,
20776
21182
  edgeGlow: true,
20777
21183
  keyboardShortcuts: keyboardShortcutsEnabled,
21184
+ previewMode,
20778
21185
  isSubmitting: isRespondingApproval,
20779
21186
  onSubmit: onSubmitBottomDockInteractivePrompt,
20780
21187
  labels: promptLabels
@@ -20796,16 +21203,25 @@ function syncAgentGUIConversationRailStore(store, next) {
20796
21203
  Object.assign(store, next);
20797
21204
  }
20798
21205
  function agentGUIConversationRailStoreSnapshotsEqual(current, next) {
20799
- return current.conversations === next.conversations && current.userProjects === next.userProjects && current.activeConversationId === next.activeConversationId && current.pendingDeleteConversationId === next.pendingDeleteConversationId && current.isLoadingConversations === next.isLoadingConversations && current.isDeletingConversation === next.isDeletingConversation && current.isDeletingProjectConversations === next.isDeletingProjectConversations && current.labels === next.labels && current.workspaceUserProjectI18n === next.workspaceUserProjectI18n && current.uiLanguage === next.uiLanguage && current.showProjectSelector === next.showProjectSelector && current.createConversationDisabled === next.createConversationDisabled && current.openclawGateway === next.openclawGateway && current.isCollapsed === next.isCollapsed && current.onCreateConversation === next.onCreateConversation && current.onRetryOpenclawGateway === next.onRetryOpenclawGateway && current.onSelectConversation === next.onSelectConversation && current.onToggleConversationPinned === next.onToggleConversationPinned && current.onOpenProjectFiles === next.onOpenProjectFiles && current.onOpenConversationWindow === next.onOpenConversationWindow && current.onRemoveProject === next.onRemoveProject && current.onConfirmDeleteProjectConversations === next.onConfirmDeleteProjectConversations && current.onRequestDeleteConversation === next.onRequestDeleteConversation && current.onCancelDeleteConversation === next.onCancelDeleteConversation && current.onConfirmDeleteConversation === next.onConfirmDeleteConversation;
21206
+ return current.activeConversationId === next.activeConversationId && current.pendingDeleteConversationId === next.pendingDeleteConversationId && current.isLoadingConversations === next.isLoadingConversations && current.isDeletingConversation === next.isDeletingConversation && current.isDeletingProjectConversations === next.isDeletingProjectConversations && current.labels === next.labels && current.workspaceUserProjectI18n === next.workspaceUserProjectI18n && current.uiLanguage === next.uiLanguage && current.showProjectSelector === next.showProjectSelector && current.previewMode === next.previewMode && current.createConversationDisabled === next.createConversationDisabled && current.openclawGateway === next.openclawGateway && current.isCollapsed === next.isCollapsed && current.onCreateConversation === next.onCreateConversation && current.onRetryOpenclawGateway === next.onRetryOpenclawGateway && current.onSelectConversation === next.onSelectConversation && current.onToggleConversationPinned === next.onToggleConversationPinned && current.onOpenProjectFiles === next.onOpenProjectFiles && current.onOpenConversationWindow === next.onOpenConversationWindow && current.onRemoveProject === next.onRemoveProject && current.onConfirmDeleteProjectConversations === next.onConfirmDeleteProjectConversations && current.onRequestDeleteConversation === next.onRequestDeleteConversation && current.onCancelDeleteConversation === next.onCancelDeleteConversation && current.onConfirmDeleteConversation === next.onConfirmDeleteConversation;
20800
21207
  }
20801
21208
  var AgentGUIConversationRailStorePane = memo(
20802
21209
  function AgentGUIConversationRailStorePane2({
21210
+ conversations,
20803
21211
  store,
20804
- storeState: _storeState
21212
+ storeState: _storeState,
21213
+ userProjects
20805
21214
  }) {
20806
21215
  "use memo";
20807
21216
  const state = useSnapshot(store);
20808
- return /* @__PURE__ */ jsx29(AgentGUIConversationRailPane, { ...state });
21217
+ return /* @__PURE__ */ jsx29(
21218
+ AgentGUIConversationRailPane,
21219
+ {
21220
+ ...state,
21221
+ conversations,
21222
+ userProjects
21223
+ }
21224
+ );
20809
21225
  }
20810
21226
  );
20811
21227
  function normalizeConversationRailProjectPath(path) {
@@ -20815,6 +21231,75 @@ function normalizeConversationRailProjectPath(path) {
20815
21231
  }
20816
21232
  return normalized.replace(/\/+$/, "") || "/";
20817
21233
  }
21234
+ function stabilizeConversationSections(previous, next) {
21235
+ if (!previous) {
21236
+ return [...next];
21237
+ }
21238
+ const previousById = new Map(
21239
+ previous.map((section) => [section.id, section])
21240
+ );
21241
+ let changed = previous.length !== next.length;
21242
+ const stable = next.map((section, index) => {
21243
+ const previousSection = previousById.get(section.id) ?? null;
21244
+ if (!previousSection) {
21245
+ changed = true;
21246
+ return section;
21247
+ }
21248
+ const items = stabilizeConversationSectionItems(
21249
+ previousSection.items,
21250
+ section.items
21251
+ );
21252
+ const canReuseSection = previousSection.kind === section.kind && previousSection.label === section.label && conversationProjectsRenderEqual2(
21253
+ previousSection.project,
21254
+ section.project
21255
+ ) && items === previousSection.items;
21256
+ if (canReuseSection) {
21257
+ if (previous[index] !== previousSection) {
21258
+ changed = true;
21259
+ }
21260
+ return previousSection;
21261
+ }
21262
+ changed = true;
21263
+ return { ...section, items };
21264
+ });
21265
+ return changed ? stable : previous;
21266
+ }
21267
+ function stabilizeConversationSectionItems(previous, next) {
21268
+ if (previous.length !== next.length) {
21269
+ const previousById = /* @__PURE__ */ new Map();
21270
+ for (const item of previous) {
21271
+ if (!previousById.has(item.id)) {
21272
+ previousById.set(item.id, item);
21273
+ }
21274
+ }
21275
+ return next.map((item) => {
21276
+ const previousItem = previousById.get(item.id);
21277
+ return previousItem && conversationSummariesRenderEqual2(previousItem, item) ? previousItem : item;
21278
+ });
21279
+ }
21280
+ let changed = false;
21281
+ const stable = next.map((item, index) => {
21282
+ const previousItem = previous[index];
21283
+ if (previousItem && conversationSummariesRenderEqual2(previousItem, item)) {
21284
+ if (previousItem !== item) {
21285
+ changed = true;
21286
+ }
21287
+ return previousItem;
21288
+ }
21289
+ changed = true;
21290
+ return item;
21291
+ });
21292
+ return changed ? stable : previous;
21293
+ }
21294
+ function conversationSummariesRenderEqual2(left, right) {
21295
+ return left.id === right.id && left.provider === right.provider && left.title === right.title && left.titleFallback === right.titleFallback && left.status === right.status && left.cwd === right.cwd && left.pinnedAtUnixMs === right.pinnedAtUnixMs && left.sortTimeUnixMs === right.sortTimeUnixMs && left.updatedAtUnixMs === right.updatedAtUnixMs && left.hasUnreadCompletion === right.hasUnreadCompletion && conversationProjectsRenderEqual2(left.project, right.project) && conversationSyncStatesRenderEqual(left.syncState, right.syncState);
21296
+ }
21297
+ function conversationSyncStatesRenderEqual(left, right) {
21298
+ return left === right || (!left || !right ? (left ?? null) === (right ?? null) : left.workspaceId === right.workspaceId && left.agentSessionId === right.agentSessionId && left.status === right.status && left.pendingTimelineItemCount === right.pendingTimelineItemCount && left.pendingStatePatchCount === right.pendingStatePatchCount && left.attemptCount === right.attemptCount && left.failedReportCount === right.failedReportCount && left.lastError === right.lastError && left.lastAttemptAtUnixMs === right.lastAttemptAtUnixMs && left.lastSyncedAtUnixMs === right.lastSyncedAtUnixMs && left.updatedAtUnixMs === right.updatedAtUnixMs);
21299
+ }
21300
+ function conversationProjectsRenderEqual2(left, right) {
21301
+ return left === right || (!left || !right ? !left && !right : left.id === right.id && left.path === right.path && left.label === right.label && left.createdAtUnixMs === right.createdAtUnixMs && left.updatedAtUnixMs === right.updatedAtUnixMs && left.lastUsedAtUnixMs === right.lastUsedAtUnixMs);
21302
+ }
20818
21303
  var AgentGUIConversationRailPane = memo(
20819
21304
  function AgentGUIConversationRailPane2({
20820
21305
  conversations,
@@ -20828,6 +21313,7 @@ var AgentGUIConversationRailPane = memo(
20828
21313
  workspaceUserProjectI18n,
20829
21314
  uiLanguage,
20830
21315
  showProjectSelector,
21316
+ previewMode,
20831
21317
  createConversationDisabled,
20832
21318
  openclawGateway,
20833
21319
  isCollapsed,
@@ -20853,6 +21339,7 @@ var AgentGUIConversationRailPane = memo(
20853
21339
  const conversationItemElementsRef = useRef13(
20854
21340
  /* @__PURE__ */ new Map()
20855
21341
  );
21342
+ const groupedConversationsRef = useRef13(null);
20856
21343
  useEffect12(() => {
20857
21344
  const timer = window.setInterval(() => {
20858
21345
  setCurrentTimeMs(Date.now());
@@ -20875,13 +21362,19 @@ var AgentGUIConversationRailPane = memo(
20875
21362
  const filteredConversations = filteredConversationResult.items;
20876
21363
  const groupedConversationResult = useMemo10(() => {
20877
21364
  const startedAtMs = agentGuiPerfNowMs();
21365
+ const rawGroups = groupConversations(
21366
+ filteredConversations,
21367
+ labels,
21368
+ conversationQuery.trim() ? [] : userProjects,
21369
+ { includeEmptyConversations: !conversationQuery.trim() }
21370
+ );
21371
+ const groups = stabilizeConversationSections(
21372
+ groupedConversationsRef.current,
21373
+ rawGroups
21374
+ );
21375
+ groupedConversationsRef.current = groups;
20878
21376
  return {
20879
- groups: groupConversations(
20880
- filteredConversations,
20881
- labels,
20882
- conversationQuery.trim() ? [] : userProjects,
20883
- { includeEmptyConversations: !conversationQuery.trim() }
20884
- ),
21377
+ groups,
20885
21378
  groupMs: roundAgentGuiPerfMs(agentGuiPerfNowMs() - startedAtMs)
20886
21379
  };
20887
21380
  }, [conversationQuery, filteredConversations, labels, userProjects]);
@@ -21025,6 +21518,7 @@ var AgentGUIConversationRailPane = memo(
21025
21518
  isSectionCollapsed,
21026
21519
  labels,
21027
21520
  pendingDeleteConversationId,
21521
+ previewMode,
21028
21522
  projectConversationCount,
21029
21523
  projectLabel,
21030
21524
  projectPath,
@@ -21098,6 +21592,7 @@ var AgentGUIConversationRailSection = memo(
21098
21592
  isSectionCollapsed,
21099
21593
  activeConversationId,
21100
21594
  pendingDeleteConversationId,
21595
+ previewMode,
21101
21596
  isDeletingConversation,
21102
21597
  createConversationDisabled,
21103
21598
  currentTimeMs,
@@ -21120,10 +21615,10 @@ var AgentGUIConversationRailSection = memo(
21120
21615
  const [visibleItemLimit, setVisibleItemLimit] = useState11(
21121
21616
  AGENT_GUI_CONVERSATION_RAIL_SECTION_PAGE_SIZE
21122
21617
  );
21123
- const visibleItemCount = Math.min(visibleItemLimit, section.items.length);
21124
- const visibleItems = section.items.slice(0, visibleItemCount);
21125
- const canShowMore = visibleItemCount < section.items.length;
21126
- const canShowLess = visibleItemCount > AGENT_GUI_CONVERSATION_RAIL_SECTION_PAGE_SIZE;
21618
+ const visibleItemCount = isSectionCollapsed ? 0 : Math.min(visibleItemLimit, section.items.length);
21619
+ const visibleItems = isSectionCollapsed ? [] : section.items.slice(0, visibleItemCount);
21620
+ const canShowMore = !isSectionCollapsed && visibleItemCount < section.items.length;
21621
+ const canShowLess = !isSectionCollapsed && visibleItemCount > AGENT_GUI_CONVERSATION_RAIL_SECTION_PAGE_SIZE;
21127
21622
  const showMoreConversations = useCallback10(() => {
21128
21623
  setVisibleItemLimit(
21129
21624
  (current) => Math.min(
@@ -21160,7 +21655,7 @@ var AgentGUIConversationRailSection = memo(
21160
21655
  ),
21161
21656
  /* @__PURE__ */ jsxs15("span", { className: AgentGUINode_styles_default.conversationSectionLabel, children: [
21162
21657
  /* @__PURE__ */ jsx29(
21163
- FolderIcon,
21658
+ FolderIcon2,
21164
21659
  {
21165
21660
  "aria-hidden": "true",
21166
21661
  className: AgentGUINode_styles_default.conversationSectionLabelIcon
@@ -21281,7 +21776,7 @@ var AgentGUIConversationRailSection = memo(
21281
21776
  className: AgentGUINode_styles_default.conversationSectionItems,
21282
21777
  "aria-hidden": isSectionCollapsed ? "true" : void 0,
21283
21778
  children: /* @__PURE__ */ jsxs15("div", { className: AgentGUINode_styles_default.conversationSectionItemsInner, children: [
21284
- section.items.length === 0 ? /* @__PURE__ */ jsx29("div", { className: AgentGUINode_styles_default.conversationSectionEmpty, children: labels.emptyProjectConversations }) : null,
21779
+ !isSectionCollapsed && section.items.length === 0 ? /* @__PURE__ */ jsx29("div", { className: AgentGUINode_styles_default.conversationSectionEmpty, children: labels.emptyProjectConversations }) : null,
21285
21780
  visibleItems.map((item) => /* @__PURE__ */ jsx29(
21286
21781
  AgentGUIConversationRailItem,
21287
21782
  {
@@ -21291,6 +21786,7 @@ var AgentGUIConversationRailSection = memo(
21291
21786
  isPendingDeleteConversation: pendingDeleteConversationId === item.id,
21292
21787
  item,
21293
21788
  labels,
21789
+ previewMode,
21294
21790
  registerItemElement,
21295
21791
  uiLanguage,
21296
21792
  onCancelDeleteConversation,
@@ -21338,6 +21834,7 @@ var AgentGUIConversationRailItem = memo(
21338
21834
  isDeletingConversation,
21339
21835
  currentTimeMs,
21340
21836
  labels,
21837
+ previewMode,
21341
21838
  uiLanguage,
21342
21839
  registerItemElement,
21343
21840
  onSelectConversation,
@@ -21395,7 +21892,7 @@ var AgentGUIConversationRailItem = memo(
21395
21892
  ]
21396
21893
  }
21397
21894
  ),
21398
- /* @__PURE__ */ jsx29("div", { className: AgentGUINode_styles_default.conversationActions, children: isPendingDeleteConversation ? /* @__PURE__ */ jsx29(
21895
+ previewMode ? null : /* @__PURE__ */ jsx29("div", { className: AgentGUINode_styles_default.conversationActions, children: isPendingDeleteConversation ? /* @__PURE__ */ jsx29(
21399
21896
  "button",
21400
21897
  {
21401
21898
  type: "button",
@@ -21560,6 +22057,7 @@ var AgentGUIConversationTimelinePane = memo(
21560
22057
  onAuthLogin,
21561
22058
  availableSkills,
21562
22059
  workspaceAppIcons = EMPTY_WORKSPACE_APP_ICONS3,
22060
+ previewMode = false,
21563
22061
  labels
21564
22062
  }) {
21565
22063
  "use memo";
@@ -21574,6 +22072,7 @@ var AgentGUIConversationTimelinePane = memo(
21574
22072
  onAuthLogin,
21575
22073
  availableSkills,
21576
22074
  workspaceAppIcons,
22075
+ previewMode,
21577
22076
  labels
21578
22077
  }
21579
22078
  );
@@ -23073,14 +23572,15 @@ var AgentGUI = memo3(function AgentGUI2({
23073
23572
  locale,
23074
23573
  ...props
23075
23574
  }) {
23076
- return /* @__PURE__ */ jsx32(TooltipProvider3, { delayDuration: 120, skipDelayDuration: 0, children: /* @__PURE__ */ jsx32(AgentGuiI18nProvider, { runtime: i18n, locale, children: /* @__PURE__ */ jsx32(
23575
+ const content = /* @__PURE__ */ jsx32(AgentGuiI18nProvider, { runtime: i18n, locale, children: /* @__PURE__ */ jsx32(
23077
23576
  AgentActivityHostProvider,
23078
23577
  {
23079
23578
  agentActivityRuntime,
23080
23579
  agentHostApi,
23081
23580
  children: /* @__PURE__ */ jsx32(AgentGUINode, { ...props })
23082
23581
  }
23083
- ) }) });
23582
+ ) });
23583
+ return props.previewMode ? content : /* @__PURE__ */ jsx32(TooltipProvider3, { delayDuration: 120, skipDelayDuration: 0, children: content });
23084
23584
  });
23085
23585
 
23086
23586
  // index.ts