@tutti-os/agent-gui 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -39,7 +39,7 @@ import {
39
39
  useOptionalAgentHostApi,
40
40
  user_avatar_placeholder_default,
41
41
  workspaceAgentActivityStatusLabel
42
- } from "./chunk-AF5CXBJN.js";
42
+ } from "./chunk-5Q36BEUM.js";
43
43
  import {
44
44
  resolveWorkspaceAgentSessionSortTimeUnixMs,
45
45
  workspaceAgentProviderLabel
@@ -5236,6 +5236,7 @@ function computeAgentToolGroups(sequence, {
5236
5236
  }) {
5237
5237
  const groups = /* @__PURE__ */ new Map();
5238
5238
  const groupedIndices = /* @__PURE__ */ new Set();
5239
+ const suppressedIndices = allowTrailingFinalization ? /* @__PURE__ */ new Set() : findActiveTailSuppressedToolIndices(sequence);
5239
5240
  let currentCalls = [];
5240
5241
  let currentEntries = [];
5241
5242
  let currentIndices = [];
@@ -5265,6 +5266,9 @@ function computeAgentToolGroups(sequence, {
5265
5266
  if (!item) {
5266
5267
  continue;
5267
5268
  }
5269
+ if (suppressedIndices.has(index)) {
5270
+ continue;
5271
+ }
5268
5272
  if (item.kind === "tool-call" && isGroupableToolCall2(item.call)) {
5269
5273
  if (avoidGroupingEdits && isEditBoundaryToolCall(item.call)) {
5270
5274
  finalizeGroup();
@@ -5301,7 +5305,8 @@ function computeAgentToolGroups(sequence, {
5301
5305
  }
5302
5306
  return {
5303
5307
  groups,
5304
- groupedIndices
5308
+ groupedIndices,
5309
+ suppressedIndices
5305
5310
  };
5306
5311
  }
5307
5312
  function projectAgentToolGroupRowFromGroup(turnId, group) {
@@ -5318,12 +5323,12 @@ function projectAgentToolGroupRowFromGroup(turnId, group) {
5318
5323
  occurredAtUnixMs: group.calls.map((call) => call.occurredAtUnixMs ?? 0).reduce((latest, value) => value > latest ? value : latest, 0) || null
5319
5324
  };
5320
5325
  }
5321
- function projectAgentSingleToolRow(call) {
5326
+ function projectAgentSingleToolRow(call, turnId = call.turnId) {
5322
5327
  return {
5323
5328
  kind: "tool-group",
5324
5329
  id: `tool-row:${call.id}`,
5325
- expansionKey: `tool-row:${call.id}`,
5326
- turnId: call.turnId,
5330
+ expansionKey: `tool-group:${turnId}:${call.id}`,
5331
+ turnId,
5327
5332
  grouped: false,
5328
5333
  calls: [call],
5329
5334
  summary: null,
@@ -5337,6 +5342,7 @@ function projectMessage(message, turnId) {
5337
5342
  id: message.id,
5338
5343
  turnId: message.turnId ?? turnId,
5339
5344
  body: message.body,
5345
+ statusKind: message.statusKind ?? null,
5340
5346
  occurredAtUnixMs: message.occurredAtUnixMs ?? null,
5341
5347
  visibleError: message.visibleError ?? null,
5342
5348
  systemNotice: message.systemNotice ?? null
@@ -5375,6 +5381,50 @@ function isGroupableToolCall2(call) {
5375
5381
  return true;
5376
5382
  }
5377
5383
  }
5384
+ function findActiveTailSuppressedToolIndices(sequence) {
5385
+ const suppressedIndices = /* @__PURE__ */ new Set();
5386
+ let latestTailToolIndex = -1;
5387
+ for (let index = sequence.length - 1; index >= 0; index -= 1) {
5388
+ const item = sequence[index];
5389
+ if (!item) {
5390
+ continue;
5391
+ }
5392
+ if (item.kind !== "tool-call") {
5393
+ break;
5394
+ }
5395
+ latestTailToolIndex = Math.max(latestTailToolIndex, index);
5396
+ }
5397
+ if (latestTailToolIndex < 0) {
5398
+ return suppressedIndices;
5399
+ }
5400
+ const latestTailTool = sequence[latestTailToolIndex];
5401
+ if (latestTailTool?.kind !== "tool-call" || !isSuppressingActiveTailTool(latestTailTool.call)) {
5402
+ return suppressedIndices;
5403
+ }
5404
+ for (let index = latestTailToolIndex - 1; index >= 0; index -= 1) {
5405
+ const item = sequence[index];
5406
+ if (!item) {
5407
+ continue;
5408
+ }
5409
+ if (item.kind !== "tool-call") {
5410
+ break;
5411
+ }
5412
+ suppressedIndices.add(index);
5413
+ }
5414
+ return suppressedIndices;
5415
+ }
5416
+ function isSuppressingActiveTailTool(call) {
5417
+ switch (call.rendererKind) {
5418
+ case "approval":
5419
+ case "ask-user":
5420
+ case "plan-enter":
5421
+ case "plan-exit":
5422
+ case "task":
5423
+ return false;
5424
+ default:
5425
+ return true;
5426
+ }
5427
+ }
5378
5428
  function isEditBoundaryToolCall(call) {
5379
5429
  return call.rendererKind === "edit" || call.rendererKind === "write";
5380
5430
  }
@@ -6605,6 +6655,7 @@ function mergeAdjacentAssistantMessageRows(rows) {
6605
6655
  const nextMessage = row.messages[0];
6606
6656
  if (lastMessage && nextMessage) {
6607
6657
  lastMessage.body += nextMessage.body;
6658
+ lastMessage.statusKind = nextMessage.statusKind ?? lastMessage.statusKind ?? null;
6608
6659
  lastMessage.occurredAtUnixMs = nextMessage.occurredAtUnixMs ?? lastMessage.occurredAtUnixMs;
6609
6660
  }
6610
6661
  if (row.messages.length > 1) {
@@ -6777,7 +6828,11 @@ function userPromptContentBlocks(message, fallbackWorkspaceId) {
6777
6828
  }
6778
6829
  function projectTurnAgentRows(turn, options) {
6779
6830
  const sequence = buildAgentTurnSequenceItems(turn);
6780
- const { groups, groupedIndices } = computeAgentToolGroups(sequence, options);
6831
+ const { groups, groupedIndices, suppressedIndices } = computeAgentToolGroups(
6832
+ sequence,
6833
+ options
6834
+ );
6835
+ const skippedIndices = /* @__PURE__ */ new Set([...groupedIndices, ...suppressedIndices]);
6781
6836
  const rows = [];
6782
6837
  let pendingThinking = [];
6783
6838
  const flushThinking = () => {
@@ -6807,13 +6862,13 @@ function projectTurnAgentRows(turn, options) {
6807
6862
  index = group.endIndex;
6808
6863
  continue;
6809
6864
  }
6810
- if (groupedIndices.has(index)) {
6865
+ if (skippedIndices.has(index)) {
6811
6866
  continue;
6812
6867
  }
6813
6868
  if (item.kind === "thinking") {
6814
6869
  const next = nextUngroupedSequenceItem(
6815
6870
  sequence,
6816
- groupedIndices,
6871
+ skippedIndices,
6817
6872
  index + 1
6818
6873
  );
6819
6874
  if (next?.kind === "assistant-message") {
@@ -6838,7 +6893,7 @@ function projectTurnAgentRows(turn, options) {
6838
6893
  continue;
6839
6894
  }
6840
6895
  flushThinking();
6841
- rows.push(projectAgentSingleToolRow(item.call));
6896
+ rows.push(projectAgentSingleToolRow(item.call, turn.id));
6842
6897
  }
6843
6898
  flushThinking();
6844
6899
  return rows;
@@ -7105,6 +7160,8 @@ function isUserTimelineItem(item) {
7105
7160
 
7106
7161
  // agent-gui/agentGuiNode/model/agentGuiConversationModel.ts
7107
7162
  var AGENT_GUI_RUNTIME_SESSION_ORIGIN = WORKSPACE_AGENT_ACTIVITY_RUNTIME_SESSION_ORIGIN;
7163
+ var AGENT_GUI_CONVERSATION_PROJECT_SUMMARY_CACHE_LIMIT = 512;
7164
+ var agentGUIConversationProjectSummaryCache = /* @__PURE__ */ new Map();
7108
7165
  function resolveAgentGUIConversationSortTimeUnixMs(conversation) {
7109
7166
  return conversation.sortTimeUnixMs ?? conversation.updatedAtUnixMs;
7110
7167
  }
@@ -7282,7 +7339,7 @@ function resolveAgentGUIConversationProject(cwd, userProjects = []) {
7282
7339
  if (matchedProject.lastUsedAtUnixMs !== void 0) {
7283
7340
  summary.lastUsedAtUnixMs = matchedProject.lastUsedAtUnixMs;
7284
7341
  }
7285
- return summary;
7342
+ return cachedAgentGUIConversationProjectSummary(summary);
7286
7343
  }
7287
7344
  function applyAgentGUIConversationProjects(conversations, userProjects = []) {
7288
7345
  let changed = false;
@@ -7386,6 +7443,28 @@ function normalizeAgentGUIProjectPath(path) {
7386
7443
  }
7387
7444
  return normalized.replace(/\/+$/, "") || "/";
7388
7445
  }
7446
+ function cachedAgentGUIConversationProjectSummary(summary) {
7447
+ const key = [
7448
+ summary.id,
7449
+ summary.path,
7450
+ summary.label,
7451
+ summary.createdAtUnixMs ?? "",
7452
+ summary.updatedAtUnixMs ?? "",
7453
+ summary.lastUsedAtUnixMs ?? ""
7454
+ ].join("");
7455
+ const cached = agentGUIConversationProjectSummaryCache.get(key);
7456
+ if (cached) {
7457
+ return cached;
7458
+ }
7459
+ if (agentGUIConversationProjectSummaryCache.size >= AGENT_GUI_CONVERSATION_PROJECT_SUMMARY_CACHE_LIMIT) {
7460
+ const oldestKey = agentGUIConversationProjectSummaryCache.keys().next().value;
7461
+ if (oldestKey) {
7462
+ agentGUIConversationProjectSummaryCache.delete(oldestKey);
7463
+ }
7464
+ }
7465
+ agentGUIConversationProjectSummaryCache.set(key, summary);
7466
+ return summary;
7467
+ }
7389
7468
  function isSameAgentGUIConversationProject(left, right) {
7390
7469
  if (!left && !right) {
7391
7470
  return true;
@@ -8387,6 +8466,7 @@ var STREAM_LINGER_MS = 15e3;
8387
8466
  var EMPTY_AGENT_SESSION_VIEW_STORE_SNAPSHOT = {
8388
8467
  sessionViewsBySessionKey: {}
8389
8468
  };
8469
+ var MESSAGE_UPDATE_BATCH_DELAY_MS = 33;
8390
8470
  var snapshot = EMPTY_AGENT_SESSION_VIEW_STORE_SNAPSHOT;
8391
8471
  var storeListeners = /* @__PURE__ */ new Set();
8392
8472
  var ignoreEmptyViewUpdatesAfterTestReset = false;
@@ -8427,6 +8507,7 @@ function watchAgentSession(payload, options = {}) {
8427
8507
  payload: normalizedPayload3,
8428
8508
  listeners: /* @__PURE__ */ new Set(),
8429
8509
  lingerTimer: null,
8510
+ pendingMessageBatch: null,
8430
8511
  releaseRuntimeEvents: null,
8431
8512
  retainPromise: null
8432
8513
  };
@@ -8597,10 +8678,7 @@ function ensureWorkspaceSessionEventListener(workspaceId) {
8597
8678
  return;
8598
8679
  }
8599
8680
  for (const entry of entries) {
8600
- recordAgentSessionStreamEvent(entry, event);
8601
- for (const listener of entry.listeners) {
8602
- listener(event);
8603
- }
8681
+ receiveAgentSessionStreamEvent(entry, event);
8604
8682
  }
8605
8683
  }
8606
8684
  );
@@ -8671,6 +8749,80 @@ function clearEntryLingerTimer(entry) {
8671
8749
  clearTimeout(entry.lingerTimer);
8672
8750
  entry.lingerTimer = null;
8673
8751
  }
8752
+ function receiveAgentSessionStreamEvent(entry, event) {
8753
+ if (event.eventType !== "message_update" || isImmediateMessageUpdateEvent(event)) {
8754
+ flushPendingMessageBatch(entry);
8755
+ dispatchAgentSessionStreamEvent(entry, event);
8756
+ return;
8757
+ }
8758
+ enqueueMessageUpdateEvent(entry, event);
8759
+ }
8760
+ function enqueueMessageUpdateEvent(entry, event) {
8761
+ let batch = entry.pendingMessageBatch;
8762
+ if (!batch) {
8763
+ batch = {
8764
+ events: [],
8765
+ incomingCount: 0,
8766
+ timer: null
8767
+ };
8768
+ entry.pendingMessageBatch = batch;
8769
+ }
8770
+ batch.incomingCount += 1;
8771
+ upsertCoalescedMessageUpdate(batch.events, event);
8772
+ if (batch.timer !== null) {
8773
+ return;
8774
+ }
8775
+ batch.timer = setTimeout(
8776
+ () => flushPendingMessageBatch(entry),
8777
+ MESSAGE_UPDATE_BATCH_DELAY_MS
8778
+ );
8779
+ }
8780
+ function flushPendingMessageBatch(entry) {
8781
+ const batch = entry.pendingMessageBatch;
8782
+ if (!batch) {
8783
+ return;
8784
+ }
8785
+ clearPendingMessageBatch(entry);
8786
+ if (batch.events.length === 0) {
8787
+ return;
8788
+ }
8789
+ recordAgentSessionStreamEvents(entry, batch.events);
8790
+ for (const event of batch.events) {
8791
+ for (const listener of entry.listeners) {
8792
+ listener(event);
8793
+ }
8794
+ }
8795
+ reportMessageBatchDiagnostics(entry, batch);
8796
+ }
8797
+ function clearPendingMessageBatch(entry) {
8798
+ const batch = entry.pendingMessageBatch;
8799
+ if (!batch) {
8800
+ return;
8801
+ }
8802
+ if (batch.timer !== null) {
8803
+ clearTimeout(batch.timer);
8804
+ batch.timer = null;
8805
+ }
8806
+ entry.pendingMessageBatch = null;
8807
+ }
8808
+ function dispatchAgentSessionStreamEvent(entry, event) {
8809
+ recordAgentSessionStreamEvent(entry, event);
8810
+ for (const listener of entry.listeners) {
8811
+ listener(event);
8812
+ }
8813
+ }
8814
+ function upsertCoalescedMessageUpdate(events, event) {
8815
+ const messageId = event.data.messageId.trim();
8816
+ const existingIndex = events.findIndex(
8817
+ (candidate) => candidate.eventType === "message_update" && candidate.data.messageId.trim() === messageId
8818
+ );
8819
+ if (existingIndex >= 0) {
8820
+ events.splice(existingIndex, 1);
8821
+ events.push(event);
8822
+ return;
8823
+ }
8824
+ events.push(event);
8825
+ }
8674
8826
  function normalizeSubscribePayload(payload) {
8675
8827
  const workspaceId = payload.workspaceId?.trim();
8676
8828
  const agentSessionId = payload.agentSessionId.trim();
@@ -8714,10 +8866,20 @@ function findEntriesForEvent(event) {
8714
8866
  );
8715
8867
  }
8716
8868
  function recordAgentSessionStreamEvent(entry, event) {
8717
- const occurredAtUnixMs = "occurredAtUnixMs" in event.data && typeof event.data.occurredAtUnixMs === "number" ? event.data.occurredAtUnixMs : Date.now();
8869
+ recordAgentSessionStreamEvents(entry, [event]);
8870
+ }
8871
+ function recordAgentSessionStreamEvents(entry, events) {
8872
+ const latestEvent = events.at(-1);
8873
+ if (!latestEvent) {
8874
+ return;
8875
+ }
8876
+ const occurredAtUnixMs = "occurredAtUnixMs" in latestEvent.data && typeof latestEvent.data.occurredAtUnixMs === "number" ? latestEvent.data.occurredAtUnixMs : Date.now();
8718
8877
  updateAgentSessionView(entry.payload, (current) => {
8719
8878
  let next = current;
8720
- if (event.eventType === "available_commands_update") {
8879
+ for (const event of events) {
8880
+ if (event.eventType !== "available_commands_update") {
8881
+ continue;
8882
+ }
8721
8883
  next = {
8722
8884
  ...next,
8723
8885
  controlCommands: [...event.data.commands]
@@ -8731,6 +8893,36 @@ function recordAgentSessionStreamEvent(entry, event) {
8731
8893
  };
8732
8894
  });
8733
8895
  }
8896
+ function isImmediateMessageUpdateEvent(event) {
8897
+ if (event.eventType !== "message_update") {
8898
+ return true;
8899
+ }
8900
+ if (typeof event.data.completedAtUnixMs === "number") {
8901
+ return true;
8902
+ }
8903
+ const status = event.data.status?.trim().toLowerCase() ?? "";
8904
+ return status === "completed" || status === "failed" || status === "canceled" || status === "cancelled" || status === "error" || status === "waiting";
8905
+ }
8906
+ function reportMessageBatchDiagnostics(entry, batch) {
8907
+ if (batch.incomingCount <= 1 && batch.events.length <= 1) {
8908
+ return;
8909
+ }
8910
+ const runtime = getOptionalAgentActivityRuntime();
8911
+ try {
8912
+ void runtime?.reportDiagnostic?.({
8913
+ details: {
8914
+ agentSessionId: entry.payload.agentSessionId,
8915
+ coalescedCount: batch.events.length,
8916
+ incomingCount: batch.incomingCount
8917
+ },
8918
+ event: "agent.session_view.message_update_batch_flushed",
8919
+ level: "debug",
8920
+ source: "agent-gui",
8921
+ workspaceId: entry.payload.workspaceId
8922
+ });
8923
+ } catch {
8924
+ }
8925
+ }
8734
8926
  function isAgentSessionActivityStreamEvent(value) {
8735
8927
  if (!value || typeof value !== "object") {
8736
8928
  return false;
@@ -11261,6 +11453,17 @@ function mergeVisibleConversations(conversations, transientConversation) {
11261
11453
  }
11262
11454
  return [transientConversation, ...conversations];
11263
11455
  }
11456
+ function stableConversationSummaryList(previous, next) {
11457
+ if (previous?.length !== next.length) {
11458
+ return next;
11459
+ }
11460
+ for (let index = 0; index < next.length; index += 1) {
11461
+ if (previous[index] !== next[index]) {
11462
+ return next;
11463
+ }
11464
+ }
11465
+ return previous;
11466
+ }
11264
11467
  function mergeConversationTitleUpdateFields(current, incomingTitle) {
11265
11468
  const title = incomingTitle.trim();
11266
11469
  if (!title) {
@@ -11556,6 +11759,14 @@ function reasoningSelectionFromComposerOptions(options, currentValue) {
11556
11759
  function providerSkillsFromComposerOptions(options) {
11557
11760
  return options?.skills.map((skill) => ({ ...skill })) ?? [];
11558
11761
  }
11762
+ function areProviderSkillOptionsEqual(left, right) {
11763
+ return left.name === right.name && left.trigger === right.trigger && left.sourceKind === right.sourceKind && left.description === right.description && left.pluginName === right.pluginName;
11764
+ }
11765
+ function areProviderSkillOptionListsEqual(left, right) {
11766
+ return left.length === right.length && left.every(
11767
+ (skill, index) => areProviderSkillOptionsEqual(skill, right[index])
11768
+ );
11769
+ }
11559
11770
  function permissionConfigFromComposerOptions(options) {
11560
11771
  const config = options?.permissionConfig;
11561
11772
  if (!config) {
@@ -11672,6 +11883,25 @@ function cloneComposerSettings(settings) {
11672
11883
  function sameComposerSettings(left, right) {
11673
11884
  return (left?.model ?? null) === (right?.model ?? null) && (left?.reasoningEffort ?? null) === (right?.reasoningEffort ?? null) && Boolean(left?.planMode) === Boolean(right?.planMode) && (left?.permissionModeId ?? null) === (right?.permissionModeId ?? null);
11674
11885
  }
11886
+ function useStableComposerSettings(settings) {
11887
+ const settingsRef = useRef4(null);
11888
+ if (settingsRef.current === null || !sameComposerSettings(settingsRef.current.value, settings)) {
11889
+ settingsRef.current = { value: settings };
11890
+ }
11891
+ return settingsRef.current.value;
11892
+ }
11893
+ function useStableProviderSkillOptions(skills) {
11894
+ const skillsRef = useRef4(null);
11895
+ if (skillsRef.current === null || !areProviderSkillOptionListsEqual(skillsRef.current, skills)) {
11896
+ skillsRef.current = skills;
11897
+ }
11898
+ return skillsRef.current;
11899
+ }
11900
+ function useStableControllerEventCallback(callback) {
11901
+ const callbackRef = useRef4(callback);
11902
+ callbackRef.current = callback;
11903
+ return useCallback4((...args) => callbackRef.current(...args), []);
11904
+ }
11675
11905
  function pendingApprovalFromState(state) {
11676
11906
  return state?.pendingInteractive?.kind === "approval" ? state.pendingInteractive : null;
11677
11907
  }
@@ -15523,23 +15753,32 @@ function useAgentGUINodeController({
15523
15753
  userProjects,
15524
15754
  workspacePath
15525
15755
  ]);
15526
- const visibleConversations = useMemo4(
15527
- () => (isLoadingConversations ? mergeVisibleConversations(
15756
+ const visibleConversationsRef = useRef4(
15757
+ null
15758
+ );
15759
+ const visibleConversations = useMemo4(() => {
15760
+ const source = isLoadingConversations ? mergeVisibleConversations(
15528
15761
  conversations,
15529
15762
  transientConversationRef.current
15530
- ) : conversations).map(
15763
+ ) : conversations;
15764
+ const next = source.map(
15531
15765
  (conversation2) => mergeConversationSummaryWithRuntimeSession({
15532
15766
  conversation: conversation2,
15533
15767
  runtimeSyncState: stableRuntimeSyncStateBySessionId[conversation2.id]
15534
15768
  })
15535
- ),
15536
- [
15537
- conversations,
15538
- isLoadingConversations,
15539
- stableRuntimeSyncStateBySessionId,
15540
- transientConversation
15541
- ]
15542
- );
15769
+ );
15770
+ const stableNext = stableConversationSummaryList(
15771
+ visibleConversationsRef.current,
15772
+ next
15773
+ );
15774
+ visibleConversationsRef.current = stableNext;
15775
+ return stableNext;
15776
+ }, [
15777
+ conversations,
15778
+ isLoadingConversations,
15779
+ stableRuntimeSyncStateBySessionId,
15780
+ transientConversation
15781
+ ]);
15543
15782
  const conversationUserIds = useMemo4(
15544
15783
  () => [
15545
15784
  ...new Set(
@@ -15605,9 +15844,11 @@ function useAgentGUINodeController({
15605
15844
  () => planModeStateFromSessionState(activeSessionState),
15606
15845
  [activeSessionState]
15607
15846
  );
15608
- const availableSkills = useMemo4(
15609
- () => providerSkillsFromComposerOptions(providerComposerOptions),
15610
- [providerComposerOptions]
15847
+ const availableSkills = useStableProviderSkillOptions(
15848
+ useMemo4(
15849
+ () => providerSkillsFromComposerOptions(providerComposerOptions),
15850
+ [providerComposerOptions]
15851
+ )
15611
15852
  );
15612
15853
  const conversationDetail = useMemo4(
15613
15854
  () => projectionConversation ? buildAgentGUIConversationDetail({
@@ -15691,24 +15932,28 @@ function useAgentGUINodeController({
15691
15932
  [activeConversationId, queuedPromptsBySessionId]
15692
15933
  );
15693
15934
  const drainingQueuedPromptId = drainingQueuedPromptSessionId === activeConversationId ? queuedPrompts[0]?.id ?? null : null;
15694
- const sessionSettings = cloneComposerSettings(
15695
- activeSessionState?.settings ?? null
15935
+ const sessionSettings = useStableComposerSettings(
15936
+ cloneComposerSettings(activeSessionState?.settings ?? null)
15937
+ );
15938
+ const storedNodeDefaultSettings = useStableComposerSettings(
15939
+ readNodeDefaultDraftSettings({
15940
+ data,
15941
+ defaultReasoningEffort,
15942
+ drafts: draftSettingsBySessionId
15943
+ })
15944
+ );
15945
+ const homeComposerSettings = useStableComposerSettings(
15946
+ resolveEffectiveComposerSettings({
15947
+ settings: storedNodeDefaultSettings
15948
+ })
15696
15949
  );
15697
- const storedNodeDefaultSettings = readNodeDefaultDraftSettings({
15698
- data,
15699
- defaultReasoningEffort,
15700
- drafts: draftSettingsBySessionId
15701
- });
15702
- const homeComposerSettings = resolveEffectiveComposerSettings({
15703
- settings: storedNodeDefaultSettings
15704
- });
15705
15950
  const activeConversationDraftSettings = activeConversationId ? draftSettingsBySessionId[activeConversationId] ?? null : null;
15706
- const defaultConversationDraftSettings = {
15951
+ const defaultConversationDraftSettings = useStableComposerSettings({
15707
15952
  ...activeConversationDraftSettings ?? homeComposerSettings,
15708
15953
  permissionModeId: normalizePermissionModeId(activeSessionState?.permissionModeId) ?? normalizePermissionModeId(
15709
15954
  (activeConversationDraftSettings ?? homeComposerSettings).permissionModeId
15710
15955
  )
15711
- };
15956
+ });
15712
15957
  const draftSettings = activeConversationId ? sessionSettings ?? defaultConversationDraftSettings : homeComposerSettings;
15713
15958
  const draftModel = normalizeOptionalText(draftSettings.model);
15714
15959
  const draftReasoningEffort = normalizeOptionalText(
@@ -15773,6 +16018,14 @@ function useAgentGUINodeController({
15773
16018
  () => modelSelectionFromComposerOptions(providerComposerOptions, draftModel),
15774
16019
  [draftModel, providerComposerOptions]
15775
16020
  );
16021
+ const effectivePlanMode = useMemo4(
16022
+ () => resolveEffectivePlanModeFromStates({
16023
+ sessionPlanModeState,
16024
+ timelinePlanModeState,
16025
+ fallbackPlanMode: Boolean(draftSettings.planMode)
16026
+ }),
16027
+ [draftSettings.planMode, sessionPlanModeState, timelinePlanModeState]
16028
+ );
15776
16029
  const composerSettings = useMemo4(() => {
15777
16030
  const permissionConfig = permissionConfigFromComposerOptions(
15778
16031
  providerComposerOptions
@@ -15786,11 +16039,6 @@ function useAgentGUINodeController({
15786
16039
  const selectedModelValue = draftModel;
15787
16040
  const selectedReasoningEffortValue = draftReasoningEffort;
15788
16041
  const selectedPermissionModeValue = normalizePermissionModeId(draftSettings.permissionModeId) ?? normalizePermissionModeId(permissionConfig?.defaultValue);
15789
- const effectivePlanMode = resolveEffectivePlanModeFromStates({
15790
- sessionPlanModeState,
15791
- timelinePlanModeState,
15792
- fallbackPlanMode: Boolean(draftSettings.planMode)
15793
- });
15794
16042
  return {
15795
16043
  sessionSettings,
15796
16044
  draftSettings: {
@@ -15828,18 +16076,115 @@ function useAgentGUINodeController({
15828
16076
  activeSessionReasoningSelection,
15829
16077
  draftSettings.permissionModeId,
15830
16078
  draftSettings.planMode,
16079
+ effectivePlanMode,
15831
16080
  providerComposerOptions,
15832
- sessionPlanModeState,
15833
16081
  sessionSettings,
15834
16082
  selectedProjectPath,
15835
16083
  supports.model,
15836
16084
  supports.permission,
15837
16085
  supports.plan,
15838
16086
  supports.reasoning,
15839
- timelinePlanModeState,
15840
16087
  draftModel,
15841
16088
  draftReasoningEffort
15842
16089
  ]);
16090
+ const stableCreateConversation = useStableControllerEventCallback(createConversation);
16091
+ const stableSelectConversation = useStableControllerEventCallback(selectConversation);
16092
+ const stableSubmitPrompt = useStableControllerEventCallback(submitPrompt);
16093
+ const stableShowPromptImagesUnsupported = useStableControllerEventCallback(
16094
+ showPromptImagesUnsupported
16095
+ );
16096
+ const stableSubmitApprovalOption = useStableControllerEventCallback(submitApprovalOption);
16097
+ const stableSubmitInteractivePrompt = useStableControllerEventCallback(
16098
+ submitInteractivePrompt
16099
+ );
16100
+ const stableInterruptCurrentTurn = useStableControllerEventCallback(interruptCurrentTurn);
16101
+ const stableUpdateDraftPrompt = useStableControllerEventCallback(updateDraftPrompt);
16102
+ const stableUpdateSelectedProjectPath = useStableControllerEventCallback(
16103
+ updateSelectedProjectPath
16104
+ );
16105
+ const stableUpdateComposerSettings = useStableControllerEventCallback(
16106
+ updateComposerSettings
16107
+ );
16108
+ const stableSendQueuedPromptNext = useStableControllerEventCallback(sendQueuedPromptNext);
16109
+ const stableRemoveQueuedPrompt = useStableControllerEventCallback(removeQueuedPrompt);
16110
+ const stableEditQueuedPrompt = useStableControllerEventCallback(editQueuedPrompt);
16111
+ const stableRemoveProject = useStableControllerEventCallback(removeProject);
16112
+ const stableRequestDeleteProjectConversations = useStableControllerEventCallback(requestDeleteProjectConversations);
16113
+ const stableCancelDeleteProjectConversations = useStableControllerEventCallback(cancelDeleteProjectConversations);
16114
+ const stableConfirmDeleteProjectConversations = useStableControllerEventCallback(confirmDeleteProjectConversations);
16115
+ const stableToggleConversationPinned = useStableControllerEventCallback(
16116
+ toggleConversationPinned
16117
+ );
16118
+ const stableRequestDeleteConversation = useStableControllerEventCallback(
16119
+ requestDeleteConversation
16120
+ );
16121
+ const stableRetryActivation = useStableControllerEventCallback(retryActivation);
16122
+ const stableContinueInNewConversation = useStableControllerEventCallback(
16123
+ continueInNewConversation
16124
+ );
16125
+ const stableCancelDeleteConversation = useStableControllerEventCallback(
16126
+ cancelDeleteConversation
16127
+ );
16128
+ const stableConfirmDeleteConversation = useStableControllerEventCallback(
16129
+ confirmDeleteConversation
16130
+ );
16131
+ const stableRetryOpenclawGateway = useStableControllerEventCallback(
16132
+ ensureOpenclawGateway
16133
+ );
16134
+ const controllerActions = useMemo4(
16135
+ () => ({
16136
+ createConversation: stableCreateConversation,
16137
+ selectConversation: stableSelectConversation,
16138
+ submitPrompt: stableSubmitPrompt,
16139
+ showPromptImagesUnsupported: stableShowPromptImagesUnsupported,
16140
+ submitApprovalOption: stableSubmitApprovalOption,
16141
+ submitInteractivePrompt: stableSubmitInteractivePrompt,
16142
+ interruptCurrentTurn: stableInterruptCurrentTurn,
16143
+ updateDraftPrompt: stableUpdateDraftPrompt,
16144
+ updateSelectedProjectPath: stableUpdateSelectedProjectPath,
16145
+ updateComposerSettings: stableUpdateComposerSettings,
16146
+ sendQueuedPromptNext: stableSendQueuedPromptNext,
16147
+ removeQueuedPrompt: stableRemoveQueuedPrompt,
16148
+ editQueuedPrompt: stableEditQueuedPrompt,
16149
+ removeProject: stableRemoveProject,
16150
+ requestDeleteProjectConversations: stableRequestDeleteProjectConversations,
16151
+ cancelDeleteProjectConversations: stableCancelDeleteProjectConversations,
16152
+ confirmDeleteProjectConversations: stableConfirmDeleteProjectConversations,
16153
+ toggleConversationPinned: stableToggleConversationPinned,
16154
+ requestDeleteConversation: stableRequestDeleteConversation,
16155
+ retryActivation: stableRetryActivation,
16156
+ continueInNewConversation: stableContinueInNewConversation,
16157
+ cancelDeleteConversation: stableCancelDeleteConversation,
16158
+ confirmDeleteConversation: stableConfirmDeleteConversation,
16159
+ retryOpenclawGateway: stableRetryOpenclawGateway
16160
+ }),
16161
+ [
16162
+ stableCancelDeleteConversation,
16163
+ stableCancelDeleteProjectConversations,
16164
+ stableConfirmDeleteConversation,
16165
+ stableConfirmDeleteProjectConversations,
16166
+ stableContinueInNewConversation,
16167
+ stableCreateConversation,
16168
+ stableEditQueuedPrompt,
16169
+ stableInterruptCurrentTurn,
16170
+ stableRemoveProject,
16171
+ stableRemoveQueuedPrompt,
16172
+ stableRequestDeleteConversation,
16173
+ stableRequestDeleteProjectConversations,
16174
+ stableRetryActivation,
16175
+ stableRetryOpenclawGateway,
16176
+ stableSelectConversation,
16177
+ stableSendQueuedPromptNext,
16178
+ stableShowPromptImagesUnsupported,
16179
+ stableSubmitApprovalOption,
16180
+ stableSubmitInteractivePrompt,
16181
+ stableSubmitPrompt,
16182
+ stableToggleConversationPinned,
16183
+ stableUpdateComposerSettings,
16184
+ stableUpdateDraftPrompt,
16185
+ stableUpdateSelectedProjectPath
16186
+ ]
16187
+ );
15843
16188
  return useMemo4(
15844
16189
  () => ({
15845
16190
  viewModel: {
@@ -15889,32 +16234,7 @@ function useAgentGUINodeController({
15889
16234
  } : null,
15890
16235
  detailError
15891
16236
  },
15892
- actions: {
15893
- createConversation,
15894
- selectConversation,
15895
- submitPrompt,
15896
- showPromptImagesUnsupported,
15897
- submitApprovalOption,
15898
- submitInteractivePrompt,
15899
- interruptCurrentTurn,
15900
- updateDraftPrompt,
15901
- updateSelectedProjectPath,
15902
- updateComposerSettings,
15903
- sendQueuedPromptNext,
15904
- removeQueuedPrompt,
15905
- editQueuedPrompt,
15906
- removeProject,
15907
- requestDeleteProjectConversations,
15908
- cancelDeleteProjectConversations,
15909
- confirmDeleteProjectConversations,
15910
- toggleConversationPinned,
15911
- requestDeleteConversation,
15912
- retryActivation,
15913
- continueInNewConversation,
15914
- cancelDeleteConversation,
15915
- confirmDeleteConversation,
15916
- retryOpenclawGateway: ensureOpenclawGateway
15917
- }
16237
+ actions: controllerActions
15918
16238
  }),
15919
16239
  [
15920
16240
  activeConversation,
@@ -15929,12 +16249,10 @@ function useAgentGUINodeController({
15929
16249
  composerSettings,
15930
16250
  conversation,
15931
16251
  conversationDetail,
15932
- conversations,
15933
- createConversation,
16252
+ controllerActions,
15934
16253
  data,
15935
16254
  detailError,
15936
16255
  draftPrompt,
15937
- continueInNewConversation,
15938
16256
  isCreatingConversation,
15939
16257
  openclawGateway,
15940
16258
  promptImagesSupported,
@@ -15953,34 +16271,12 @@ function useAgentGUINodeController({
15953
16271
  pendingInteractivePrompt,
15954
16272
  queuedPrompts,
15955
16273
  drainingQueuedPromptId,
15956
- sendQueuedPromptNext,
15957
- removeQueuedPrompt,
15958
- editQueuedPrompt,
15959
- removeProject,
15960
- requestDeleteProjectConversations,
15961
- cancelDeleteProjectConversations,
15962
- confirmDeleteProjectConversations,
15963
- submitApprovalOption,
15964
- submitInteractivePrompt,
15965
- interruptCurrentTurn,
15966
- toggleConversationPinned,
15967
- requestDeleteConversation,
15968
- retryActivation,
15969
16274
  currentUserId,
15970
16275
  workspaceId,
15971
16276
  workspacePath,
15972
16277
  sessionChrome,
15973
- selectConversation,
15974
- showPromptImagesUnsupported,
15975
- submitPrompt,
15976
16278
  userProjects,
15977
- visibleConversations,
15978
- updateComposerSettings,
15979
- updateDraftPrompt,
15980
- updateSelectedProjectPath,
15981
- cancelDeleteConversation,
15982
- confirmDeleteConversation,
15983
- ensureOpenclawGateway
16279
+ visibleConversations
15984
16280
  ]
15985
16281
  );
15986
16282
  }
@@ -16141,6 +16437,7 @@ import {
16141
16437
  useRef as useRef20,
16142
16438
  useState as useState26
16143
16439
  } from "react";
16440
+ import { useSnapshot } from "valtio";
16144
16441
  import { ChevronRight as ChevronRight9, Info as Info2 } from "lucide-react";
16145
16442
  import { WorkspaceFileReferencePicker } from "@tutti-os/workspace-file-reference/ui";
16146
16443
  import {
@@ -17672,7 +17969,8 @@ function AgentMessageBlock({
17672
17969
  },
17673
17970
  workspaceAppIcons,
17674
17971
  deferLongContentRender: true,
17675
- enableImageZoom: true
17972
+ enableImageZoom: true,
17973
+ streaming: message.statusKind === "working"
17676
17974
  }
17677
17975
  );
17678
17976
  if (rawTimelineJson2) {
@@ -22848,9 +23146,124 @@ var AgentTranscriptItemView = memo2(function AgentTranscriptItemView2({
22848
23146
  }
22849
23147
  });
22850
23148
 
23149
+ // shared/agentConversation/components/agentTranscriptComplexity.ts
23150
+ var AGENT_TRANSCRIPT_COMPLEXITY_VIRTUALIZATION_SCORE = 40;
23151
+ var AGENT_TRANSCRIPT_COMPLEXITY_SINGLE_TURN_SCORE = 24;
23152
+ var AGENT_TRANSCRIPT_COMPLEXITY_TURN_COUNT = 30;
23153
+ function assessAgentTranscriptComplexity(turnGroups) {
23154
+ const complexity = calculateAgentTranscriptComplexity(turnGroups);
23155
+ return {
23156
+ ...complexity,
23157
+ shouldVirtualize: shouldVirtualizeAgentTranscript({
23158
+ turnCount: turnGroups.length,
23159
+ complexity
23160
+ }),
23161
+ turnCount: turnGroups.length
23162
+ };
23163
+ }
23164
+ function calculateAgentTranscriptComplexity(turnGroups) {
23165
+ const turnScores = turnGroups.map((group) => calculateTurnScore(group.rows));
23166
+ const totalScore = turnScores.reduce((total, score) => total + score, 0);
23167
+ return {
23168
+ totalScore,
23169
+ maxTurnScore: Math.max(0, ...turnScores),
23170
+ turnScores
23171
+ };
23172
+ }
23173
+ function shouldVirtualizeAgentTranscript(input) {
23174
+ return input.turnCount >= AGENT_TRANSCRIPT_COMPLEXITY_TURN_COUNT || input.complexity.totalScore >= AGENT_TRANSCRIPT_COMPLEXITY_VIRTUALIZATION_SCORE || input.complexity.maxTurnScore >= AGENT_TRANSCRIPT_COMPLEXITY_SINGLE_TURN_SCORE;
23175
+ }
23176
+ function calculateTurnScore(rows) {
23177
+ let rowCount = 0;
23178
+ let charCount = 0;
23179
+ let codeFenceCount = 0;
23180
+ let tableCount = 0;
23181
+ let toolCallCount = 0;
23182
+ let thinkingBlockCount = 0;
23183
+ let imageCount = 0;
23184
+ for (const { row } of rows) {
23185
+ rowCount += 1;
23186
+ if (row.kind === "message") {
23187
+ for (const message of row.messages) {
23188
+ charCount += message.body.length;
23189
+ codeFenceCount += countCodeFences(message.body);
23190
+ tableCount += countMarkdownTables(message.body);
23191
+ imageCount += (message.images?.length ?? 0) + countMarkdownImages(message.body);
23192
+ }
23193
+ for (const thinking of row.thinking) {
23194
+ thinkingBlockCount += 1;
23195
+ charCount += thinking.body.length;
23196
+ codeFenceCount += countCodeFences(thinking.body);
23197
+ }
23198
+ continue;
23199
+ }
23200
+ if (row.kind === "tool-group") {
23201
+ toolCallCount += row.calls.length;
23202
+ thinkingBlockCount += row.entries.filter(
23203
+ (entry) => entry.kind === "thinking"
23204
+ ).length;
23205
+ charCount += (row.summary ?? "").length;
23206
+ continue;
23207
+ }
23208
+ if (row.kind === "turn-summary") {
23209
+ charCount += row.files.reduce(
23210
+ (total, file) => total + file.label.length + file.path.length + (file.unifiedDiff?.length ?? 0) + (file.content?.length ?? 0),
23211
+ 0
23212
+ );
23213
+ }
23214
+ }
23215
+ return rowCount + charCount / 1200 + codeFenceCount * 4 + tableCount * 5 + toolCallCount * 2 + thinkingBlockCount * 2 + imageCount * 4;
23216
+ }
23217
+ function countCodeFences(value) {
23218
+ let count = 0;
23219
+ for (const line of value.split("\n")) {
23220
+ const trimmed = line.trimStart();
23221
+ if (trimmed.startsWith("```") || trimmed.startsWith("~~~")) {
23222
+ count += 1;
23223
+ }
23224
+ }
23225
+ return Math.ceil(count / 2);
23226
+ }
23227
+ function countMarkdownTables(value) {
23228
+ const lines = value.split("\n");
23229
+ let count = 0;
23230
+ for (let index = 1; index < lines.length; index += 1) {
23231
+ if (looksLikeTableSeparator(lines[index] ?? "") && lines[index - 1]?.includes("|")) {
23232
+ count += 1;
23233
+ }
23234
+ }
23235
+ return count;
23236
+ }
23237
+ function countMarkdownImages(value) {
23238
+ let count = 0;
23239
+ let index = 0;
23240
+ while (index < value.length) {
23241
+ const imageStart = value.indexOf("![", index);
23242
+ if (imageStart < 0) {
23243
+ break;
23244
+ }
23245
+ const labelEnd = value.indexOf("]", imageStart + 2);
23246
+ const targetStart = labelEnd >= 0 ? value.indexOf("(", labelEnd) : -1;
23247
+ const targetEnd = targetStart >= 0 ? value.indexOf(")", targetStart) : -1;
23248
+ if (labelEnd >= 0 && targetStart === labelEnd + 1 && targetEnd > targetStart) {
23249
+ count += 1;
23250
+ index = targetEnd + 1;
23251
+ continue;
23252
+ }
23253
+ index = imageStart + 2;
23254
+ }
23255
+ return count;
23256
+ }
23257
+ function looksLikeTableSeparator(line) {
23258
+ const cells = line.split("|").map((cell) => cell.trim()).filter(Boolean);
23259
+ return cells.length > 0 && cells.every((cell) => {
23260
+ const normalized = cell.replace(/:/gu, "");
23261
+ return normalized.length >= 3 && [...normalized].every((char) => char === "-");
23262
+ });
23263
+ }
23264
+
22851
23265
  // shared/agentConversation/components/AgentTranscriptView.tsx
22852
23266
  import { Fragment as Fragment8, jsx as jsx71, jsxs as jsxs46 } from "react/jsx-runtime";
22853
- var AGENT_TRANSCRIPT_VIRTUALIZATION_TURN_THRESHOLD = 30;
22854
23267
  var AGENT_TRANSCRIPT_VIRTUALIZATION_OVERSCAN = 6;
22855
23268
  var AGENT_TRANSCRIPT_ESTIMATED_TURN_HEIGHT_PX = 280;
22856
23269
  var AGENT_TRANSCRIPT_TURN_GAP_PX = 12;
@@ -22921,7 +23334,10 @@ var AgentTranscriptView = memo3(function AgentTranscriptView2({
22921
23334
  );
22922
23335
  const basePath = conversation.sourceDetail.cwd;
22923
23336
  const workspaceRoot = conversation.workspaceRoot;
22924
- const shouldVirtualize = turnGroups.length >= AGENT_TRANSCRIPT_VIRTUALIZATION_TURN_THRESHOLD;
23337
+ const shouldVirtualize = useMemo10(
23338
+ () => assessAgentTranscriptComplexity(turnGroups).shouldVirtualize,
23339
+ [turnGroups]
23340
+ );
22925
23341
  const rowVirtualizer = useVirtualizer({
22926
23342
  count: turnGroups.length,
22927
23343
  estimateSize: () => AGENT_TRANSCRIPT_ESTIMATED_TURN_HEIGHT_PX,
@@ -30177,6 +30593,21 @@ function SendFilledIcon() {
30177
30593
  );
30178
30594
  }
30179
30595
 
30596
+ // agent-gui/agentGuiNode/AgentGUIBottomDockStore.ts
30597
+ import { proxy } from "valtio/vanilla";
30598
+ function createAgentGUIBottomDockStore(initialState) {
30599
+ return proxy(initialState);
30600
+ }
30601
+ function syncAgentGUIBottomDockStore(store, next) {
30602
+ if (agentGUIBottomDockStoreSnapshotsEqual(store, next)) {
30603
+ return;
30604
+ }
30605
+ Object.assign(store, next);
30606
+ }
30607
+ function agentGUIBottomDockStoreSnapshotsEqual(current, next) {
30608
+ return current.bottomDockActivePrompt === next.bottomDockActivePrompt && current.composerProps === next.composerProps && current.inlineNoticeChrome === next.inlineNoticeChrome && current.isRespondingApproval === next.isRespondingApproval && current.sessionChrome === next.sessionChrome;
30609
+ }
30610
+
30180
30611
  // agent-gui/agentGuiNode/AgentConversationListSkeleton.tsx
30181
30612
  import { jsx as jsx84, jsxs as jsxs55 } from "react/jsx-runtime";
30182
30613
  var SKELETON_ROWS = [
@@ -30855,6 +31286,7 @@ function AgentGUINodeView({
30855
31286
  labels,
30856
31287
  workspaceUserProjectI18n,
30857
31288
  uiLanguage,
31289
+ showProjectSelector,
30858
31290
  createConversationDisabled,
30859
31291
  openclawGateway,
30860
31292
  isCollapsed: conversationRailCollapsed,
@@ -31294,14 +31726,170 @@ var AgentGUIDetailPane = memo5(function AgentGUIDetailPane2({
31294
31726
  );
31295
31727
  const handleInterruptCurrentTurn = useCallback18(() => {
31296
31728
  actions.interruptCurrentTurn(labels.noRunningResponse);
31297
- }, [actions, labels.noRunningResponse]);
31729
+ }, [actions.interruptCurrentTurn, labels.noRunningResponse]);
31730
+ const submitApprovalOption = useStableEventCallback(
31731
+ actions.submitApprovalOption
31732
+ );
31733
+ const retryActivation = useStableEventCallback(actions.retryActivation);
31734
+ const continueInNewConversation = useStableEventCallback(
31735
+ actions.continueInNewConversation
31736
+ );
31737
+ const updateDraftPrompt = useStableEventCallback(actions.updateDraftPrompt);
31738
+ const updateSelectedProjectPath = useOptionalStableEventCallback(
31739
+ actions.updateSelectedProjectPath
31740
+ );
31741
+ const updateComposerSettings = useStableEventCallback(
31742
+ actions.updateComposerSettings
31743
+ );
31744
+ const submitPrompt = useStableEventCallback(actions.submitPrompt);
31745
+ const showPromptImagesUnsupported = useStableEventCallback(
31746
+ actions.showPromptImagesUnsupported
31747
+ );
31748
+ const sendQueuedPromptNext = useStableEventCallback(
31749
+ actions.sendQueuedPromptNext
31750
+ );
31751
+ const removeQueuedPrompt = useStableEventCallback(actions.removeQueuedPrompt);
31752
+ const editQueuedPrompt = useStableEventCallback(actions.editQueuedPrompt);
31753
+ const submitInteractivePrompt = useStableEventCallback(
31754
+ actions.submitInteractivePrompt
31755
+ );
31756
+ const stableLinkAction = useOptionalStableEventCallback(onLinkAction);
31757
+ const stableRequestWorkspaceReferences = useOptionalStableEventCallback(
31758
+ onRequestWorkspaceReferences
31759
+ );
31760
+ const authLogin = useOptionalStableEventCallback(onAgentProviderLogin);
31298
31761
  const submitBottomDockInteractivePrompt = useCallback18(
31299
31762
  (input) => {
31300
- actions.submitInteractivePrompt(input);
31763
+ submitInteractivePrompt(input);
31301
31764
  setBottomDockDismissedPromptRequestId(input.requestId);
31302
31765
  },
31303
- [actions]
31766
+ [submitInteractivePrompt]
31304
31767
  );
31768
+ const bottomDockComposerProps = useMemo15(
31769
+ () => ({
31770
+ workspaceId: viewModel.workspaceId,
31771
+ workspacePath: viewModel.workspacePath,
31772
+ currentUserId: viewModel.currentUserId,
31773
+ provider: viewModel.data.provider,
31774
+ slashStatus,
31775
+ draftPrompt: viewModel.draftPrompt,
31776
+ availableCommands: viewModel.availableCommands,
31777
+ hasCompactableContext: viewModel.hasSentUserMessage,
31778
+ availableSkills: viewModel.availableSkills,
31779
+ disabled: composerDisabled,
31780
+ disabledReason: composerDisabledReason,
31781
+ submitDisabled,
31782
+ composerSettings: viewModel.composerSettings,
31783
+ queuedPrompts: viewModel.queuedPrompts,
31784
+ drainingQueuedPromptId: viewModel.drainingQueuedPromptId,
31785
+ workspaceAppIcons,
31786
+ canQueueWhileBusy,
31787
+ placeholder: viewModel.hasSentUserMessage ? labels.followupPlaceholder : labels.initialPlaceholder,
31788
+ showStopButton,
31789
+ activePrompt: shouldLiftActivePromptAboveInlineNotice ? null : activePrompt,
31790
+ activePromptKeyboardShortcutsEnabled: isActive,
31791
+ promptTips: labels.promptTips,
31792
+ composerFocusRequestSequence,
31793
+ isActive,
31794
+ promptImagesSupported: viewModel.promptImagesSupported,
31795
+ showProjectSelector,
31796
+ isInterrupting: viewModel.isInterrupting,
31797
+ isSendingTurn: isComposerSending,
31798
+ isSubmittingPrompt: viewModel.isRespondingApproval,
31799
+ labels: composerLabels,
31800
+ workspaceUserProjectI18n,
31801
+ onDraftChange: updateDraftPrompt,
31802
+ onProjectPathChange: updateSelectedProjectPath,
31803
+ onSettingsChange: updateComposerSettings,
31804
+ onSubmit: submitPrompt,
31805
+ onPromptImagesUnsupported: showPromptImagesUnsupported,
31806
+ onSendQueuedPromptNext: sendQueuedPromptNext,
31807
+ onRemoveQueuedPrompt: removeQueuedPrompt,
31808
+ onEditQueuedPrompt: editQueuedPrompt,
31809
+ onInterruptCurrentTurn: handleInterruptCurrentTurn,
31810
+ onSubmitInteractivePrompt: submitInteractivePrompt,
31811
+ onLinkAction: stableLinkAction,
31812
+ onRequestWorkspaceReferences: stableRequestWorkspaceReferences,
31813
+ richTextAtProviders
31814
+ }),
31815
+ [
31816
+ activePrompt,
31817
+ canQueueWhileBusy,
31818
+ composerDisabled,
31819
+ composerDisabledReason,
31820
+ composerFocusRequestSequence,
31821
+ composerLabels,
31822
+ handleInterruptCurrentTurn,
31823
+ isActive,
31824
+ isComposerSending,
31825
+ labels.followupPlaceholder,
31826
+ labels.initialPlaceholder,
31827
+ labels.promptTips,
31828
+ editQueuedPrompt,
31829
+ richTextAtProviders,
31830
+ removeQueuedPrompt,
31831
+ sendQueuedPromptNext,
31832
+ shouldLiftActivePromptAboveInlineNotice,
31833
+ showPromptImagesUnsupported,
31834
+ showProjectSelector,
31835
+ showStopButton,
31836
+ slashStatus,
31837
+ submitDisabled,
31838
+ submitInteractivePrompt,
31839
+ submitPrompt,
31840
+ stableLinkAction,
31841
+ stableRequestWorkspaceReferences,
31842
+ updateComposerSettings,
31843
+ updateDraftPrompt,
31844
+ updateSelectedProjectPath,
31845
+ viewModel.availableCommands,
31846
+ viewModel.availableSkills,
31847
+ viewModel.composerSettings,
31848
+ viewModel.currentUserId,
31849
+ viewModel.data.provider,
31850
+ viewModel.draftPrompt,
31851
+ viewModel.drainingQueuedPromptId,
31852
+ viewModel.hasSentUserMessage,
31853
+ viewModel.isInterrupting,
31854
+ viewModel.isRespondingApproval,
31855
+ viewModel.promptImagesSupported,
31856
+ viewModel.queuedPrompts,
31857
+ viewModel.workspaceId,
31858
+ viewModel.workspacePath,
31859
+ workspaceUserProjectI18n,
31860
+ workspaceAppIcons
31861
+ ]
31862
+ );
31863
+ const bottomDockStoreState = useMemo15(
31864
+ () => ({
31865
+ bottomDockActivePrompt,
31866
+ composerProps: bottomDockComposerProps,
31867
+ inlineNoticeChrome,
31868
+ isRespondingApproval: viewModel.isRespondingApproval,
31869
+ sessionChrome
31870
+ }),
31871
+ [
31872
+ bottomDockActivePrompt,
31873
+ bottomDockComposerProps,
31874
+ inlineNoticeChrome,
31875
+ sessionChrome,
31876
+ viewModel.isRespondingApproval
31877
+ ]
31878
+ );
31879
+ const bottomDockStoreRef = useRef20(null);
31880
+ if (bottomDockStoreRef.current === null) {
31881
+ bottomDockStoreRef.current = createAgentGUIBottomDockStore(bottomDockStoreState);
31882
+ }
31883
+ const bottomDockStore = bottomDockStoreRef.current;
31884
+ syncAgentGUIBottomDockStore(bottomDockStore, bottomDockStoreState);
31885
+ const bottomDockStoreRevision = [
31886
+ bottomDockActivePrompt?.requestId ?? "",
31887
+ inlineNoticeChrome?.recovery?.message ?? "",
31888
+ sessionChrome.auth?.message ?? "",
31889
+ sessionChrome.recovery?.kind ?? "",
31890
+ sessionChrome.recovery?.message ?? "",
31891
+ viewModel.isRespondingApproval ? "1" : "0"
31892
+ ].join("|");
31305
31893
  useEffect21(() => {
31306
31894
  setBottomDockDismissedPromptRequestId(null);
31307
31895
  }, [activePromptRequestId]);
@@ -31432,6 +32020,7 @@ var AgentGUIDetailPane = memo5(function AgentGUIDetailPane2({
31432
32020
  /* @__PURE__ */ jsx86(
31433
32021
  ScrollArea,
31434
32022
  {
32023
+ scrollbarMode: "native",
31435
32024
  className: "min-h-0 flex-1 [&_[data-orientation=vertical][data-slot=scroll-area-scrollbar]]:opacity-100",
31436
32025
  viewportRef: timelineRef,
31437
32026
  viewportTestId: "agent-gui-timeline",
@@ -31445,10 +32034,10 @@ var AgentGUIDetailPane = memo5(function AgentGUIDetailPane2({
31445
32034
  emptyProvider: labels.emptyProvider ?? "",
31446
32035
  inlineNoticeChrome,
31447
32036
  isRespondingApproval: viewModel.isRespondingApproval,
31448
- onSubmitApprovalOption: actions.submitApprovalOption,
31449
- onRetryActivation: actions.retryActivation,
31450
- onAuthLogin: onAgentProviderLogin,
31451
- onContinueInNewConversation: actions.continueInNewConversation,
32037
+ onSubmitApprovalOption: submitApprovalOption,
32038
+ onRetryActivation: retryActivation,
32039
+ onAuthLogin: authLogin,
32040
+ onContinueInNewConversation: continueInNewConversation,
31452
32041
  chromeLabels,
31453
32042
  composerProps: {
31454
32043
  workspaceId: viewModel.workspaceId,
@@ -31482,18 +32071,18 @@ var AgentGUIDetailPane = memo5(function AgentGUIDetailPane2({
31482
32071
  isSubmittingPrompt: viewModel.isRespondingApproval,
31483
32072
  labels: composerLabels,
31484
32073
  workspaceUserProjectI18n,
31485
- onDraftChange: actions.updateDraftPrompt,
31486
- onProjectPathChange: actions.updateSelectedProjectPath,
31487
- onSettingsChange: actions.updateComposerSettings,
31488
- onSubmit: actions.submitPrompt,
31489
- onPromptImagesUnsupported: actions.showPromptImagesUnsupported,
31490
- onSendQueuedPromptNext: actions.sendQueuedPromptNext,
31491
- onRemoveQueuedPrompt: actions.removeQueuedPrompt,
31492
- onEditQueuedPrompt: actions.editQueuedPrompt,
32074
+ onDraftChange: updateDraftPrompt,
32075
+ onProjectPathChange: updateSelectedProjectPath,
32076
+ onSettingsChange: updateComposerSettings,
32077
+ onSubmit: submitPrompt,
32078
+ onPromptImagesUnsupported: showPromptImagesUnsupported,
32079
+ onSendQueuedPromptNext: sendQueuedPromptNext,
32080
+ onRemoveQueuedPrompt: removeQueuedPrompt,
32081
+ onEditQueuedPrompt: editQueuedPrompt,
31493
32082
  onInterruptCurrentTurn: handleInterruptCurrentTurn,
31494
- onSubmitInteractivePrompt: actions.submitInteractivePrompt,
31495
- onLinkAction,
31496
- onRequestWorkspaceReferences,
32083
+ onSubmitInteractivePrompt: submitInteractivePrompt,
32084
+ onLinkAction: stableLinkAction,
32085
+ onRequestWorkspaceReferences: stableRequestWorkspaceReferences,
31497
32086
  richTextAtProviders
31498
32087
  }
31499
32088
  }
@@ -31504,8 +32093,8 @@ var AgentGUIDetailPane = memo5(function AgentGUIDetailPane2({
31504
32093
  isLoading: showTimelineSkeleton,
31505
32094
  loadingLabel: labels.loadingConversation,
31506
32095
  empty: conversationFlowEmpty,
31507
- onLinkAction,
31508
- onAuthLogin: onAgentProviderLogin,
32096
+ onLinkAction: stableLinkAction,
32097
+ onAuthLogin: authLogin,
31509
32098
  availableSkills: viewModel.availableSkills,
31510
32099
  workspaceAppIcons,
31511
32100
  labels: conversationFlowLabels
@@ -31517,63 +32106,15 @@ var AgentGUIDetailPane = memo5(function AgentGUIDetailPane2({
31517
32106
  AgentGUIBottomDockPane,
31518
32107
  {
31519
32108
  bottomDockRef,
31520
- activePrompt,
31521
- bottomDockActivePrompt,
32109
+ store: bottomDockStore,
32110
+ storeRevision: bottomDockStoreRevision,
31522
32111
  keyboardShortcutsEnabled: isActive,
31523
- inlineNoticeChrome,
31524
- sessionChrome,
31525
- isRespondingApproval: viewModel.isRespondingApproval,
31526
- composerProps: {
31527
- workspaceId: viewModel.workspaceId,
31528
- workspacePath: viewModel.workspacePath,
31529
- currentUserId: viewModel.currentUserId,
31530
- provider: viewModel.data.provider,
31531
- slashStatus,
31532
- draftPrompt: viewModel.draftPrompt,
31533
- availableCommands: viewModel.availableCommands,
31534
- hasCompactableContext: viewModel.hasSentUserMessage,
31535
- availableSkills: viewModel.availableSkills,
31536
- disabled: composerDisabled,
31537
- disabledReason: composerDisabledReason,
31538
- submitDisabled,
31539
- composerSettings: viewModel.composerSettings,
31540
- queuedPrompts: viewModel.queuedPrompts,
31541
- drainingQueuedPromptId: viewModel.drainingQueuedPromptId,
31542
- workspaceAppIcons,
31543
- canQueueWhileBusy,
31544
- placeholder: viewModel.hasSentUserMessage ? labels.followupPlaceholder : labels.initialPlaceholder,
31545
- showStopButton,
31546
- activePrompt: shouldLiftActivePromptAboveInlineNotice ? null : activePrompt,
31547
- activePromptKeyboardShortcutsEnabled: isActive,
31548
- composerFocusRequestSequence,
31549
- isActive,
31550
- promptImagesSupported: viewModel.promptImagesSupported,
31551
- showProjectSelector,
31552
- isInterrupting: viewModel.isInterrupting,
31553
- isSendingTurn: isComposerSending,
31554
- isSubmittingPrompt: viewModel.isRespondingApproval,
31555
- labels: composerLabels,
31556
- workspaceUserProjectI18n,
31557
- onDraftChange: actions.updateDraftPrompt,
31558
- onProjectPathChange: actions.updateSelectedProjectPath,
31559
- onSettingsChange: actions.updateComposerSettings,
31560
- onSubmit: actions.submitPrompt,
31561
- onPromptImagesUnsupported: actions.showPromptImagesUnsupported,
31562
- onSendQueuedPromptNext: actions.sendQueuedPromptNext,
31563
- onRemoveQueuedPrompt: actions.removeQueuedPrompt,
31564
- onEditQueuedPrompt: actions.editQueuedPrompt,
31565
- onInterruptCurrentTurn: handleInterruptCurrentTurn,
31566
- onSubmitInteractivePrompt: actions.submitInteractivePrompt,
31567
- onLinkAction,
31568
- onRequestWorkspaceReferences,
31569
- richTextAtProviders
31570
- },
31571
32112
  chromeLabels,
31572
32113
  promptLabels: interactivePromptLabels,
31573
- onSubmitApprovalOption: actions.submitApprovalOption,
31574
- onRetryActivation: actions.retryActivation,
31575
- onAuthLogin: onAgentProviderLogin,
31576
- onContinueInNewConversation: actions.continueInNewConversation,
32114
+ onSubmitApprovalOption: submitApprovalOption,
32115
+ onRetryActivation: retryActivation,
32116
+ onAuthLogin: authLogin,
32117
+ onContinueInNewConversation: continueInNewConversation,
31577
32118
  onSubmitBottomDockInteractivePrompt: submitBottomDockInteractivePrompt
31578
32119
  }
31579
32120
  ) : null
@@ -31657,6 +32198,25 @@ function AgentRunPathInfo({ path }) {
31657
32198
  )
31658
32199
  ] });
31659
32200
  }
32201
+ function useStableEventCallback(callback) {
32202
+ const callbackRef = useRef20(callback);
32203
+ useLayoutEffect6(() => {
32204
+ callbackRef.current = callback;
32205
+ }, [callback]);
32206
+ return useCallback18((...args) => callbackRef.current(...args), []);
32207
+ }
32208
+ function useOptionalStableEventCallback(callback) {
32209
+ const callbackRef = useRef20(callback);
32210
+ useLayoutEffect6(() => {
32211
+ callbackRef.current = callback;
32212
+ }, [callback]);
32213
+ return useMemo15(() => {
32214
+ if (callback == null) {
32215
+ return void 0;
32216
+ }
32217
+ return (...args) => callbackRef.current?.(...args);
32218
+ }, [callback != null]);
32219
+ }
31660
32220
  var AgentGUIEmptyHeroPane = memo5(function AgentGUIEmptyHeroPane2({
31661
32221
  provider,
31662
32222
  emptyLabel,
@@ -31716,12 +32276,9 @@ function EmptyHeroTitle({
31716
32276
  }
31717
32277
  var AgentGUIBottomDockPane = memo5(function AgentGUIBottomDockPane2({
31718
32278
  bottomDockRef,
31719
- bottomDockActivePrompt,
32279
+ store,
32280
+ storeRevision: _storeRevision,
31720
32281
  keyboardShortcutsEnabled,
31721
- inlineNoticeChrome,
31722
- sessionChrome,
31723
- isRespondingApproval,
31724
- composerProps,
31725
32282
  chromeLabels,
31726
32283
  promptLabels,
31727
32284
  onSubmitApprovalOption,
@@ -31731,6 +32288,14 @@ var AgentGUIBottomDockPane = memo5(function AgentGUIBottomDockPane2({
31731
32288
  onSubmitBottomDockInteractivePrompt
31732
32289
  }) {
31733
32290
  "use memo";
32291
+ const state = useSnapshot(store);
32292
+ const {
32293
+ bottomDockActivePrompt,
32294
+ composerProps,
32295
+ inlineNoticeChrome,
32296
+ isRespondingApproval,
32297
+ sessionChrome
32298
+ } = state;
31734
32299
  return /* @__PURE__ */ jsxs56(
31735
32300
  "div",
31736
32301
  {
@@ -31805,6 +32370,7 @@ var AgentGUIConversationRailPane = memo5(
31805
32370
  labels,
31806
32371
  workspaceUserProjectI18n,
31807
32372
  uiLanguage,
32373
+ showProjectSelector,
31808
32374
  createConversationDisabled,
31809
32375
  openclawGateway,
31810
32376
  isCollapsed,
@@ -31942,6 +32508,7 @@ var AgentGUIConversationRailPane = memo5(
31942
32508
  /* @__PURE__ */ jsx86(
31943
32509
  ScrollArea,
31944
32510
  {
32511
+ scrollbarMode: "native",
31945
32512
  className: "min-h-0 flex-1 [&_[data-orientation=vertical][data-slot=scroll-area-scrollbar]]:opacity-100",
31946
32513
  viewportRef: conversationListRef,
31947
32514
  viewportClassName: AgentGUINode_styles_default.conversationList,
@@ -31954,7 +32521,7 @@ var AgentGUIConversationRailPane = memo5(
31954
32521
  const projectPath = section.kind === "project" ? section.project?.path ?? "" : "";
31955
32522
  const projectLabel = section.kind === "project" ? section.label : "";
31956
32523
  const isProjectSection = section.kind === "project";
31957
- const showProjectRailHeader = !conversationQuery.trim() && section.kind !== "pinned" && (sectionIndex === 0 || groupedConversations[sectionIndex - 1]?.kind === "pinned");
32524
+ const showProjectRailHeader = showProjectSelector && !conversationQuery.trim() && section.kind !== "pinned" && (sectionIndex === 0 || groupedConversations[sectionIndex - 1]?.kind === "pinned");
31958
32525
  const isSectionCollapsed = isProjectSection && collapsedProjectSectionIds.has(section.id);
31959
32526
  const projectConversationCount = projectPath ? conversations.filter(
31960
32527
  (conversation) => normalizeConversationRailProjectPath(
@@ -33091,6 +33658,15 @@ var AgentGUINode = memo6(function AgentGUINode2({
33091
33658
  },
33092
33659
  [onAgentProviderLogin, state.provider]
33093
33660
  );
33661
+ const handleWorkspaceFileReferencesAdded = useCallback20(
33662
+ (references) => {
33663
+ onWorkspaceFileReferencesAdded?.({
33664
+ provider: state.provider,
33665
+ references
33666
+ });
33667
+ },
33668
+ [onWorkspaceFileReferencesAdded, state.provider]
33669
+ );
33094
33670
  const handleDataChange = useCallback20(
33095
33671
  (updater) => {
33096
33672
  onUpdateNode(updater);
@@ -33630,10 +34206,7 @@ var AgentGUINode = memo6(function AgentGUINode2({
33630
34206
  ),
33631
34207
  detailMinWidthPx: AGENT_GUI_DETAIL_MIN_WIDTH_PX,
33632
34208
  uiLanguage: locale,
33633
- onWorkspaceFileReferencesAdded: onWorkspaceFileReferencesAdded ? (references) => onWorkspaceFileReferencesAdded({
33634
- provider: state.provider,
33635
- references
33636
- }) : void 0,
34209
+ onWorkspaceFileReferencesAdded: onWorkspaceFileReferencesAdded ? handleWorkspaceFileReferencesAdded : void 0,
33637
34210
  onConversationRailWidthChanged: handleConversationRailWidthChanged,
33638
34211
  labels,
33639
34212
  workspaceUserProjectI18n,