@tutti-os/agent-gui 0.0.33 → 0.0.35

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 (46) hide show
  1. package/README.md +5 -0
  2. package/dist/{AgentMessageMarkdown-Cp70W-Z2.d.ts → AgentMessageMarkdown-0sV8Biv3.d.ts} +53 -5
  3. package/dist/agent-conversation/index.d.ts +4 -4
  4. package/dist/agent-conversation/index.js +5 -5
  5. package/dist/agent-message-center/index.d.ts +2 -2
  6. package/dist/agent-message-center/index.js +29 -8
  7. package/dist/agent-message-center/index.js.map +1 -1
  8. package/dist/{agentConversationVM-BElW2LNR.d.ts → agentConversationVM-xuzmD5IL.d.ts} +2 -0
  9. package/dist/app/renderer/agentactivity.css +54 -128
  10. package/dist/{chunk-SGQ4C7JE.js → chunk-2ADVHCST.js} +2 -1
  11. package/dist/chunk-2ADVHCST.js.map +1 -0
  12. package/dist/{chunk-WPWZKKKV.js → chunk-3LEUGNCT.js} +2 -2
  13. package/dist/{chunk-AFTAFV2C.js → chunk-CMXRLF64.js} +85 -13
  14. package/dist/chunk-CMXRLF64.js.map +1 -0
  15. package/dist/{chunk-RDCBACWR.js → chunk-JYH2ITNL.js} +5 -5
  16. package/dist/{chunk-RQ3HEF42.js → chunk-KFMD33UD.js} +36 -12
  17. package/dist/chunk-KFMD33UD.js.map +1 -0
  18. package/dist/{chunk-ZWYHKGQ7.js → chunk-NCS7X3XY.js} +11 -9
  19. package/dist/{chunk-ZWYHKGQ7.js.map → chunk-NCS7X3XY.js.map} +1 -1
  20. package/dist/{chunk-GSZPMKFW.js → chunk-O56P2KSZ.js} +237 -26
  21. package/dist/chunk-O56P2KSZ.js.map +1 -0
  22. package/dist/{chunk-ERRANBGS.js → chunk-U3D7YI47.js} +7 -11
  23. package/dist/chunk-U3D7YI47.js.map +1 -0
  24. package/dist/{chunk-OOQIJXCA.js → chunk-UN4XGHLQ.js} +3 -2
  25. package/dist/{chunk-A4KUAXE5.js → chunk-VPC55T7P.js} +2 -2
  26. package/dist/context-mention-palette/index.js +6 -6
  27. package/dist/i18n/index.d.ts +34 -10
  28. package/dist/i18n/index.js +1 -1
  29. package/dist/index.d.ts +5 -4
  30. package/dist/index.js +1012 -477
  31. package/dist/index.js.map +1 -1
  32. package/dist/plan-decision-ops.d.ts +1 -1
  33. package/dist/workbench/contribution.js +1 -1
  34. package/dist/workbench/index.js +1 -1
  35. package/dist/workspace-agent-generated-files.js +2 -2
  36. package/dist/{workspaceLinkActions-CqWZaCY4.d.ts → workspaceLinkActions-C6POH38D.d.ts} +7 -1
  37. package/package.json +12 -12
  38. package/dist/chunk-AFTAFV2C.js.map +0 -1
  39. package/dist/chunk-ERRANBGS.js.map +0 -1
  40. package/dist/chunk-GSZPMKFW.js.map +0 -1
  41. package/dist/chunk-RQ3HEF42.js.map +0 -1
  42. package/dist/chunk-SGQ4C7JE.js.map +0 -1
  43. /package/dist/{chunk-WPWZKKKV.js.map → chunk-3LEUGNCT.js.map} +0 -0
  44. /package/dist/{chunk-RDCBACWR.js.map → chunk-JYH2ITNL.js.map} +0 -0
  45. /package/dist/{chunk-OOQIJXCA.js.map → chunk-UN4XGHLQ.js.map} +0 -0
  46. /package/dist/{chunk-A4KUAXE5.js.map → chunk-VPC55T7P.js.map} +0 -0
package/dist/index.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  import {
14
14
  AgentInteractivePromptSurface,
15
15
  approvalOptionDisplayLabel
16
- } from "./chunk-RDCBACWR.js";
16
+ } from "./chunk-JYH2ITNL.js";
17
17
  import {
18
18
  PLAN_IMPLEMENTATION_ACTION_FEEDBACK,
19
19
  PLAN_IMPLEMENTATION_ACTION_IMPLEMENT,
@@ -29,6 +29,7 @@ import {
29
29
  createAgentRichTextInputExtensions,
30
30
  editorToPromptText,
31
31
  labelForProviderSkill,
32
+ openAgentEnvPanel,
32
33
  plainTextToAgentRichTextDoc,
33
34
  plainTextToAgentRichTextInlineContent,
34
35
  projectAgentConversationVM,
@@ -36,13 +37,13 @@ import {
36
37
  skillDescriptionForDisplay,
37
38
  skillTriggerForPrefix,
38
39
  useProjectedAgentConversation
39
- } from "./chunk-GSZPMKFW.js";
40
+ } from "./chunk-O56P2KSZ.js";
40
41
  import {
41
42
  AgentMessageMarkdown,
42
43
  ZoomableImage,
43
44
  cn,
44
45
  resolveWorkspaceLinkAction
45
- } from "./chunk-AFTAFV2C.js";
46
+ } from "./chunk-CMXRLF64.js";
46
47
  import {
47
48
  AGENT_MENTION_FILTER_TAB_ORDER,
48
49
  AgentFileMentionPalette,
@@ -50,23 +51,24 @@ import {
50
51
  DEFAULT_AGENT_MENTION_FILTER,
51
52
  agentMentionItemKey,
52
53
  preloadAgentMentionBrowse
53
- } from "./chunk-ERRANBGS.js";
54
+ } from "./chunk-U3D7YI47.js";
54
55
  import {
55
56
  WORKSPACE_AGENT_ACTIVITY_RUNTIME_SESSION_ORIGIN,
56
57
  buildWorkspaceAgentActivityListViewModel,
57
58
  createWorkspaceAgentActivityUserMessageIdFromClientSubmitId,
59
+ isWorkspaceAgentActivityOptimisticMessage,
58
60
  isWorkspaceAgentActivityRuntimeSessionOrigin,
59
61
  isWorkspaceAgentUntitledTask,
60
62
  mergeWorkspaceAgentActivityDurableAndOverlayMessages,
61
63
  normalizeOptionalWorkspaceAgentStatus,
62
64
  selectWorkspaceAgentActivityOverlayMessages
63
- } from "./chunk-OOQIJXCA.js";
65
+ } from "./chunk-UN4XGHLQ.js";
64
66
  import {
65
67
  MANAGED_AGENT_ICON_FALLBACK_URL,
66
68
  MANAGED_AGENT_ICON_URLS,
67
69
  Spinner,
68
70
  normalizeManagedAgentProvider
69
- } from "./chunk-WPWZKKKV.js";
71
+ } from "./chunk-3LEUGNCT.js";
70
72
  import "./chunk-MTFSQWZ6.js";
71
73
  import {
72
74
  resolveWorkspaceAgentSessionSortTimeUnixMs
@@ -77,7 +79,7 @@ import {
77
79
  mentionItemToAttrs,
78
80
  normalizeAgentSessionMentionTitle,
79
81
  parseMentionItemFromHref
80
- } from "./chunk-A4KUAXE5.js";
82
+ } from "./chunk-VPC55T7P.js";
81
83
  import "./chunk-Y35GDLP2.js";
82
84
  import {
83
85
  AgentActivityHostProvider,
@@ -91,7 +93,7 @@ import {
91
93
  useAgentActivitySnapshot,
92
94
  useAgentHostApi,
93
95
  useOptionalAgentActivityRuntime
94
- } from "./chunk-SGQ4C7JE.js";
96
+ } from "./chunk-2ADVHCST.js";
95
97
  import "./chunk-TYGL25EL.js";
96
98
  import {
97
99
  AGENT_CONTEXT_MENTION_PROVIDER_IDS
@@ -106,7 +108,7 @@ import {
106
108
  getActiveUiLanguage,
107
109
  translate,
108
110
  useTranslation
109
- } from "./chunk-RQ3HEF42.js";
111
+ } from "./chunk-KFMD33UD.js";
110
112
  import "./chunk-LUGELG5V.js";
111
113
  import "./chunk-PJP5BUU6.js";
112
114
 
@@ -460,7 +462,7 @@ var CanvasNodeGhostIconButton = forwardRef(function CanvasNodeGhostIconButton2({
460
462
  ...rest,
461
463
  type: "button",
462
464
  className: cn(
463
- "nodrag inline-flex h-7 min-h-7 w-7 min-w-7 shrink-0 items-center justify-center rounded-[4px] border border-transparent bg-transparent p-0 text-[var(--cove-canvas-control-text)] transition-[background-color,color,border-color,opacity] duration-200 ease-in-out [-webkit-app-region:no-drag] hover:border-transparent hover:bg-[var(--transparency-block)] hover:text-[var(--cove-canvas-control-text-hover)] active:bg-[var(--transparency-block-active)] active:text-[var(--cove-canvas-control-text-hover)] disabled:cursor-not-allowed disabled:opacity-45 focus-visible:outline-2 focus-visible:outline-[color:color-mix(in_srgb,var(--tsh-shell-accent)_80%,white)] focus-visible:outline-offset-2 [&_svg]:block [&_svg]:h-4 [&_svg]:w-4 [&_svg]:shrink-0 [&_svg:not([data-nexight-chrome-glyph=fill]):not([data-tutti-chrome-glyph=fill])]:fill-none [&_svg:not([data-nexight-chrome-glyph=fill]):not([data-tutti-chrome-glyph=fill])]:stroke-current data-[menu-open=true]:border-transparent data-[menu-open=true]:bg-[var(--transparency-block)] data-[menu-open=true]:text-[var(--cove-canvas-control-text-hover)] data-[menu-open=true]:hover:bg-[var(--transparency-block-active)]",
465
+ "nodrag inline-flex h-7 min-h-7 w-7 min-w-7 shrink-0 items-center justify-center rounded-[4px] border border-transparent bg-transparent p-0 text-[var(--text-secondary)] transition-[background-color,color,border-color,opacity] duration-200 ease-in-out [-webkit-app-region:no-drag] hover:border-transparent hover:bg-[var(--transparency-block)] hover:text-[var(--text-primary)] active:bg-[var(--transparency-block-active)] active:text-[var(--text-primary)] disabled:cursor-not-allowed disabled:opacity-45 focus-visible:outline-2 focus-visible:outline-[color:color-mix(in_srgb,var(--tsh-shell-accent)_80%,white)] focus-visible:outline-offset-2 [&_svg]:block [&_svg]:h-3.5 [&_svg]:w-3.5 [&_svg]:shrink-0 [&_svg:not([data-nexight-chrome-glyph=fill]):not([data-tutti-chrome-glyph=fill])]:fill-none [&_svg:not([data-nexight-chrome-glyph=fill]):not([data-tutti-chrome-glyph=fill])]:stroke-current data-[menu-open=true]:border-transparent data-[menu-open=true]:bg-[var(--transparency-block)] data-[menu-open=true]:text-[var(--text-primary)] data-[menu-open=true]:hover:bg-[var(--transparency-block-active)]",
464
466
  className
465
467
  ),
466
468
  onPointerDown: (event) => {
@@ -2516,6 +2518,10 @@ function buildAgentGUIConversationProjectIndex(userProjects) {
2516
2518
  return projectByNormalizedPath;
2517
2519
  }
2518
2520
  function resolveAgentGUIConversationProjectFromIndex(normalizedCwd, projectByNormalizedPath, options) {
2521
+ const exactProject = projectByNormalizedPath.get(normalizedCwd);
2522
+ if (exactProject) {
2523
+ return agentGUIConversationProjectSummaryFromProject(exactProject);
2524
+ }
2519
2525
  if (options.isNoProjectPath?.({ path: normalizedCwd })) {
2520
2526
  return null;
2521
2527
  }
@@ -2526,6 +2532,9 @@ function resolveAgentGUIConversationProjectFromIndex(normalizedCwd, projectByNor
2526
2532
  if (!matchedProject) {
2527
2533
  return null;
2528
2534
  }
2535
+ return agentGUIConversationProjectSummaryFromProject(matchedProject);
2536
+ }
2537
+ function agentGUIConversationProjectSummaryFromProject(matchedProject) {
2529
2538
  const summary = {
2530
2539
  id: matchedProject.id,
2531
2540
  path: matchedProject.path,
@@ -3669,46 +3678,12 @@ function normalizeToken(value) {
3669
3678
  }
3670
3679
 
3671
3680
  // host/workspaceAgentSessionMessages.ts
3681
+ import {
3682
+ loadAllAgentSessionMessages,
3683
+ mergeAgentActivityMessages
3684
+ } from "@tutti-os/agent-activity-core";
3672
3685
  function mergeWorkspaceAgentMessages(previous, incoming) {
3673
- const byKey = /* @__PURE__ */ new Map();
3674
- for (const message of previous) {
3675
- byKey.set(workspaceAgentMessageMergeKey(message), message);
3676
- }
3677
- for (const message of incoming) {
3678
- const key = workspaceAgentMessageMergeKey(message);
3679
- const fallbackKey = workspaceAgentMessageFallbackMergeKey(message);
3680
- const fallbackMessage = (message.id ?? 0) > 0 && fallbackKey !== key ? byKey.get(fallbackKey) : void 0;
3681
- const previousMessage = byKey.get(key);
3682
- if (fallbackMessage) {
3683
- byKey.delete(fallbackKey);
3684
- }
3685
- byKey.set(key, {
3686
- ...fallbackMessage,
3687
- ...previousMessage,
3688
- ...message,
3689
- payload: {
3690
- ...fallbackMessage?.payload ?? {},
3691
- ...previousMessage?.payload ?? {},
3692
- ...message.payload ?? {}
3693
- }
3694
- });
3695
- }
3696
- return [...byKey.values()].sort(
3697
- (left, right) => (left.id ?? 0) - (right.id ?? 0)
3698
- );
3699
- }
3700
- function workspaceAgentMessageMergeKey(message) {
3701
- if (message.messageId?.trim()) {
3702
- return workspaceAgentMessageFallbackMergeKey(message);
3703
- }
3704
- if ((message.id ?? 0) > 0) {
3705
- return `id:${message.id}`;
3706
- }
3707
- return workspaceAgentMessageFallbackMergeKey(message);
3708
- }
3709
- function workspaceAgentMessageFallbackMergeKey(message) {
3710
- const messageId = message.messageId?.trim();
3711
- return `message:${message.agentSessionId}:${messageId}`;
3686
+ return mergeAgentActivityMessages(previous, incoming);
3712
3687
  }
3713
3688
 
3714
3689
  // host/agentHostAccountStore.ts
@@ -3991,7 +3966,7 @@ function subscribeCoalesced(type, options, callback) {
3991
3966
 
3992
3967
  // contexts/workspace/presentation/renderer/agentSessions/agentSessionViewStore.ts
3993
3968
  import { useSyncExternalStore } from "react";
3994
- import { mergeAgentActivityMessages } from "@tutti-os/agent-activity-core";
3969
+ import { mergeAgentActivityMessages as mergeAgentActivityMessages2 } from "@tutti-os/agent-activity-core";
3995
3970
  var STREAM_LINGER_MS = 15e3;
3996
3971
  var EMPTY_AGENT_SESSION_VIEW_STORE_SNAPSHOT = {
3997
3972
  sessionViewsBySessionKey: {}
@@ -4090,6 +4065,77 @@ function setAgentSessionViewOverlayMessages(ref, nextMessages) {
4090
4065
  };
4091
4066
  });
4092
4067
  }
4068
+ function setAgentSessionViewDetailMessages(ref, nextMessages, options = {}) {
4069
+ const normalized = normalizeAgentSessionViewRef(ref);
4070
+ if (!normalized) {
4071
+ return;
4072
+ }
4073
+ updateAgentSessionView(normalized, (current) => {
4074
+ const detailMessages = mergeMessages([], nextMessages);
4075
+ const oldestLoadedVersion = oldestMessageVersion(detailMessages);
4076
+ if (sameMessages(current.detailMessages, detailMessages) && current.oldestLoadedVersion === oldestLoadedVersion && current.hasOlderMessages === (options.hasOlderMessages ?? current.hasOlderMessages) && current.isLoadingOlderMessages === (options.isLoadingOlderMessages ?? current.isLoadingOlderMessages)) {
4077
+ return current;
4078
+ }
4079
+ return {
4080
+ ...current,
4081
+ detailMessages,
4082
+ oldestLoadedVersion,
4083
+ hasOlderMessages: options.hasOlderMessages ?? current.hasOlderMessages,
4084
+ isLoadingOlderMessages: options.isLoadingOlderMessages ?? current.isLoadingOlderMessages
4085
+ };
4086
+ });
4087
+ }
4088
+ function mergeAgentSessionViewDetailMessages(ref, nextMessages, options = {}) {
4089
+ const normalized = normalizeAgentSessionViewRef(ref);
4090
+ if (!normalized) {
4091
+ return;
4092
+ }
4093
+ updateAgentSessionView(normalized, (current) => {
4094
+ const detailMessages = mergeMessages(current.detailMessages, nextMessages);
4095
+ const oldestLoadedVersion = oldestMessageVersion(detailMessages);
4096
+ if (sameMessages(current.detailMessages, detailMessages) && current.oldestLoadedVersion === oldestLoadedVersion && current.hasOlderMessages === (options.hasOlderMessages ?? current.hasOlderMessages) && current.isLoadingOlderMessages === (options.isLoadingOlderMessages ?? current.isLoadingOlderMessages)) {
4097
+ return current;
4098
+ }
4099
+ return {
4100
+ ...current,
4101
+ detailMessages,
4102
+ oldestLoadedVersion,
4103
+ hasOlderMessages: options.hasOlderMessages ?? current.hasOlderMessages,
4104
+ isLoadingOlderMessages: options.isLoadingOlderMessages ?? current.isLoadingOlderMessages
4105
+ };
4106
+ });
4107
+ }
4108
+ function resetAgentSessionViewDetailMessages(ref) {
4109
+ const normalized = normalizeAgentSessionViewRef(ref);
4110
+ if (!normalized) {
4111
+ return;
4112
+ }
4113
+ updateAgentSessionView(normalized, (current) => {
4114
+ if (current.detailMessages.length === 0 && current.oldestLoadedVersion === null && current.hasOlderMessages === false && current.isLoadingOlderMessages === false) {
4115
+ return current;
4116
+ }
4117
+ return {
4118
+ ...current,
4119
+ detailMessages: [],
4120
+ oldestLoadedVersion: null,
4121
+ hasOlderMessages: false,
4122
+ isLoadingOlderMessages: false
4123
+ };
4124
+ });
4125
+ }
4126
+ function setAgentSessionViewOlderMessagesLoading(ref, isLoadingOlderMessages) {
4127
+ const normalized = normalizeAgentSessionViewRef(ref);
4128
+ if (!normalized) {
4129
+ return;
4130
+ }
4131
+ updateAgentSessionView(
4132
+ normalized,
4133
+ (current) => current.isLoadingOlderMessages === isLoadingOlderMessages ? current : {
4134
+ ...current,
4135
+ isLoadingOlderMessages
4136
+ }
4137
+ );
4138
+ }
4093
4139
  function setAgentSessionViewControlState(ref, controlState) {
4094
4140
  const normalized = normalizeAgentSessionViewRef(ref);
4095
4141
  if (!normalized) {
@@ -4481,18 +4527,22 @@ function createEmptySessionView(ref) {
4481
4527
  workspaceId: ref.workspaceId,
4482
4528
  agentSessionId: ref.agentSessionId,
4483
4529
  overlayMessages: [],
4530
+ detailMessages: [],
4484
4531
  controlCommands: [],
4485
4532
  controlState: null,
4486
4533
  lastEventAt: null,
4487
4534
  isLive: false,
4488
4535
  isLoadingControlState: false,
4489
4536
  isLoadingMessages: false,
4537
+ isLoadingOlderMessages: false,
4538
+ hasOlderMessages: false,
4539
+ oldestLoadedVersion: null,
4490
4540
  error: null,
4491
4541
  watcherCount: 0
4492
4542
  };
4493
4543
  }
4494
4544
  function mergeMessages(left, right) {
4495
- return mergeAgentActivityMessages(left, right);
4545
+ return mergeAgentActivityMessages2(left, right);
4496
4546
  }
4497
4547
  function sameMessages(left, right) {
4498
4548
  if (left.length !== right.length) {
@@ -4502,6 +4552,10 @@ function sameMessages(left, right) {
4502
4552
  (item, index) => equivalentMessageValue(item, right[index])
4503
4553
  );
4504
4554
  }
4555
+ function oldestMessageVersion(messages) {
4556
+ const versions = messages.map((message) => message.version).filter((version) => Number.isFinite(version));
4557
+ return versions.length === 0 ? null : Math.min(...versions);
4558
+ }
4505
4559
  function equivalentMessageValue(left, right) {
4506
4560
  if (Object.is(left, right)) {
4507
4561
  return true;
@@ -4618,7 +4672,7 @@ function useWatchAgentSessions(input) {
4618
4672
  }
4619
4673
 
4620
4674
  // contexts/workspace/presentation/renderer/agentGuiConversationList/agentGuiConversationListStore.ts
4621
- import { mergeAgentActivityMessages as mergeAgentActivityMessages2 } from "@tutti-os/agent-activity-core";
4675
+ import { mergeAgentActivityMessages as mergeAgentActivityMessages3 } from "@tutti-os/agent-activity-core";
4622
4676
 
4623
4677
  // contexts/workspace/presentation/renderer/agentGuiConversationList/agentGuiConversationListPendingState.ts
4624
4678
  var pendingCreateConversationIdsByQueryKey = /* @__PURE__ */ new Map();
@@ -4725,6 +4779,8 @@ var pendingDirtySessionIdsByQueryKey = /* @__PURE__ */ new Map();
4725
4779
  var requestIdByQueryKey = /* @__PURE__ */ new Map();
4726
4780
  var localCreatedConversationIdsByQueryKey = /* @__PURE__ */ new Map();
4727
4781
  var deletedConversationIdsByQueryKey = /* @__PURE__ */ new Map();
4782
+ var readStateByQueryKey = /* @__PURE__ */ new Map();
4783
+ var readStateLoadByQueryKey = /* @__PURE__ */ new Map();
4728
4784
  var runtimeRefreshUnsubscribeByWorkspaceId = /* @__PURE__ */ new Map();
4729
4785
  var activeConversationIdsByQueryKey = /* @__PURE__ */ new Map();
4730
4786
  var updateStormDiagnosticsByQueryKey = /* @__PURE__ */ new Map();
@@ -4835,6 +4891,107 @@ function hasActiveConversationOwner(queryKey, conversationId) {
4835
4891
  }
4836
4892
  return false;
4837
4893
  }
4894
+ function emptyWorkspaceAgentReadState() {
4895
+ return {
4896
+ completed: { readIds: [], unreadIds: [] },
4897
+ failed: { readIds: [], unreadIds: [] }
4898
+ };
4899
+ }
4900
+ function normalizeWorkspaceAgentReadState(value) {
4901
+ const record = value && typeof value === "object" ? value : {};
4902
+ return {
4903
+ completed: normalizeWorkspaceAgentReadStateBucket(
4904
+ record.completed
4905
+ ),
4906
+ failed: normalizeWorkspaceAgentReadStateBucket(
4907
+ record.failed
4908
+ )
4909
+ };
4910
+ }
4911
+ function normalizeWorkspaceAgentReadStateBucket(value) {
4912
+ const record = value && typeof value === "object" ? value : {};
4913
+ return {
4914
+ readIds: normalizeIdList(record.readIds),
4915
+ unreadIds: normalizeIdList(record.unreadIds)
4916
+ };
4917
+ }
4918
+ function normalizeIdList(value) {
4919
+ return Array.isArray(value) ? [...new Set(value.map((item) => `${item}`.trim()).filter(Boolean))] : [];
4920
+ }
4921
+ function normalizeCompletionKey(value) {
4922
+ return value?.trim() ?? "";
4923
+ }
4924
+ function isCompletedRead(readState, completionKey) {
4925
+ return readState.completed.readIds.includes(completionKey);
4926
+ }
4927
+ function updateCompletedReadState(queryState, completionKey) {
4928
+ const normalizedCompletionKey = normalizeCompletionKey(completionKey);
4929
+ if (!normalizedCompletionKey) {
4930
+ return;
4931
+ }
4932
+ const current = readStateByQueryKey.get(queryState.queryKey) ?? emptyWorkspaceAgentReadState();
4933
+ if (current.completed.readIds.includes(normalizedCompletionKey)) {
4934
+ return;
4935
+ }
4936
+ const next = {
4937
+ ...current,
4938
+ completed: {
4939
+ readIds: [...current.completed.readIds, normalizedCompletionKey],
4940
+ unreadIds: current.completed.unreadIds.filter(
4941
+ (id) => id !== normalizedCompletionKey
4942
+ )
4943
+ }
4944
+ };
4945
+ readStateByQueryKey.set(queryState.queryKey, next);
4946
+ void persistCompletedReadState(queryState, next.completed);
4947
+ }
4948
+ async function persistCompletedReadState(queryState, completed) {
4949
+ try {
4950
+ await getOptionalAgentHostApi()?.persistence?.writeWorkspaceAgentReadState({
4951
+ roomId: queryState.query.workspaceId,
4952
+ userId: queryState.query.userId,
4953
+ kind: "completed",
4954
+ readIds: completed.readIds,
4955
+ unreadIds: completed.unreadIds
4956
+ });
4957
+ } catch {
4958
+ }
4959
+ }
4960
+ async function loadWorkspaceAgentReadState(queryState) {
4961
+ const cached = readStateByQueryKey.get(queryState.queryKey);
4962
+ if (cached) {
4963
+ return cached;
4964
+ }
4965
+ const inflight = readStateLoadByQueryKey.get(queryState.queryKey);
4966
+ if (inflight) {
4967
+ return inflight;
4968
+ }
4969
+ const promise = (async () => {
4970
+ try {
4971
+ const loaded = await getOptionalAgentHostApi()?.persistence?.readWorkspaceAgentReadState(
4972
+ {
4973
+ roomId: queryState.query.workspaceId,
4974
+ userId: queryState.query.userId
4975
+ }
4976
+ );
4977
+ const normalized = normalizeWorkspaceAgentReadState(loaded);
4978
+ const current = readStateByQueryKey.get(queryState.queryKey);
4979
+ if (current) {
4980
+ return current;
4981
+ }
4982
+ readStateByQueryKey.set(queryState.queryKey, normalized);
4983
+ return normalized;
4984
+ } catch {
4985
+ const empty = emptyWorkspaceAgentReadState();
4986
+ readStateByQueryKey.set(queryState.queryKey, empty);
4987
+ return empty;
4988
+ } finally {
4989
+ readStateLoadByQueryKey.delete(queryState.queryKey);
4990
+ }
4991
+ })();
4992
+ readStateLoadByQueryKey.set(queryState.queryKey, promise);
4993
+ return promise;
4994
+ }
4838
4995
  function describeError(error) {
4839
4996
  if (error instanceof Error) {
4840
4997
  return error.message;
@@ -4920,6 +5077,7 @@ function mergeLoadedConversation(current, incoming) {
4920
5077
  ...incoming,
4921
5078
  ...mergeLoadedConversationTitleFields(current, incoming, preferCurrent),
4922
5079
  hasUnreadCompletion: incoming.hasUnreadCompletion || (preferCurrent ? current?.hasUnreadCompletion : false),
5080
+ unreadCompletionKey: incoming.unreadCompletionKey ?? (preferCurrent ? current?.unreadCompletionKey : null),
4923
5081
  pinnedAtUnixMs,
4924
5082
  sortTimeUnixMs: maxOptionalTimeUnixMs(
4925
5083
  current?.sortTimeUnixMs,
@@ -4956,7 +5114,7 @@ function areConversationProjectsEqual(left, right) {
4956
5114
  return left.id === right.id && left.path === right.path && left.label === right.label && left.createdAtUnixMs === right.createdAtUnixMs && left.updatedAtUnixMs === right.updatedAtUnixMs && left.lastUsedAtUnixMs === right.lastUsedAtUnixMs;
4957
5115
  }
4958
5116
  function areConversationsEqual(left, right) {
4959
- return left.id === right.id && left.userId === right.userId && left.provider === right.provider && left.title === right.title && left.status === right.status && left.cwd === right.cwd && areConversationProjectsEqual(left.project, right.project) && left.sortTimeUnixMs === right.sortTimeUnixMs && left.updatedAtUnixMs === right.updatedAtUnixMs && (left.pinnedAtUnixMs ?? 0) === (right.pinnedAtUnixMs ?? 0) && left.hasUnreadCompletion === right.hasUnreadCompletion && areConversationTitleFallbacksEqual(
5117
+ return left.id === right.id && left.userId === right.userId && left.provider === right.provider && left.title === right.title && left.status === right.status && left.cwd === right.cwd && areConversationProjectsEqual(left.project, right.project) && left.sortTimeUnixMs === right.sortTimeUnixMs && left.updatedAtUnixMs === right.updatedAtUnixMs && (left.pinnedAtUnixMs ?? 0) === (right.pinnedAtUnixMs ?? 0) && left.hasUnreadCompletion === right.hasUnreadCompletion && left.unreadCompletionKey === right.unreadCompletionKey && areConversationTitleFallbacksEqual(
4960
5118
  left.titleFallback,
4961
5119
  right.titleFallback
4962
5120
  ) && areConversationSyncStatesEqual(left.syncState, right.syncState);
@@ -5047,6 +5205,14 @@ function describeConversationListChange(previous, next) {
5047
5205
  "hasUnreadCompletion"
5048
5206
  );
5049
5207
  }
5208
+ if (left.unreadCompletionKey !== right.unreadCompletionKey) {
5209
+ addChangedConversationField(
5210
+ fields,
5211
+ changedIds,
5212
+ right.id,
5213
+ "unreadCompletionKey"
5214
+ );
5215
+ }
5050
5216
  if (!areConversationTitleFallbacksEqual(
5051
5217
  left.titleFallback,
5052
5218
  right.titleFallback
@@ -5224,7 +5390,7 @@ function nonEmptySessionMessagesById(durableMessagesBySessionId, sessionViewData
5224
5390
  function mergeSessionMessagesById(left, right) {
5225
5391
  const next = { ...left };
5226
5392
  for (const [agentSessionId, messages] of Object.entries(right)) {
5227
- next[agentSessionId] = mergeAgentActivityMessages2(
5393
+ next[agentSessionId] = mergeAgentActivityMessages3(
5228
5394
  next[agentSessionId] ?? [],
5229
5395
  messages
5230
5396
  );
@@ -5263,15 +5429,53 @@ function consumePendingDirtySessionIds(queryKey) {
5263
5429
  pendingDirtySessionIdsByQueryKey.delete(queryKey);
5264
5430
  return pending ?? /* @__PURE__ */ new Set();
5265
5431
  }
5432
+ function latestCompletionKeyForConversation(input) {
5433
+ const latestCompletedMessage = [...input.messages].filter(isCompletedAgentMessage).sort(compareMessagesByRecentTime)[0];
5434
+ if (latestCompletedMessage) {
5435
+ const subject = latestCompletedMessage.turnId?.trim() || latestCompletedMessage.messageId.trim();
5436
+ return subject ? `turn:${input.conversation.id}:${subject}:completed` : null;
5437
+ }
5438
+ return input.conversation.status === "completed" ? `session:${input.conversation.id}:completed` : null;
5439
+ }
5440
+ function isCompletedAgentMessage(message) {
5441
+ if ((message.role ?? "").trim().toLowerCase() !== "assistant") {
5442
+ return false;
5443
+ }
5444
+ const kind = (message.kind ?? "").trim().toLowerCase();
5445
+ if (kind !== "message" && kind !== "text") {
5446
+ return false;
5447
+ }
5448
+ const payload = message.payload && typeof message.payload === "object" ? message.payload : {};
5449
+ const status = message.status?.trim().toLowerCase() || stringPayloadValue(payload, "status").toLowerCase();
5450
+ return status === "completed";
5451
+ }
5452
+ function stringPayloadValue(payload, key) {
5453
+ const value = payload[key];
5454
+ return typeof value === "string" ? value.trim() : "";
5455
+ }
5456
+ function compareMessagesByRecentTime(left, right) {
5457
+ return messageTime(right) - messageTime(left) || right.messageId.localeCompare(left.messageId);
5458
+ }
5459
+ function messageTime(message) {
5460
+ return message.completedAtUnixMs ?? message.occurredAtUnixMs ?? message.startedAtUnixMs ?? message.version ?? 0;
5461
+ }
5266
5462
  function decorateConversationForRefresh(input) {
5267
5463
  const title = resolveAgentGUIConversationTitleFromMessages({
5268
5464
  messages: input.mergedMessages,
5269
5465
  conversation: input.conversation
5270
5466
  });
5467
+ const completionKey = latestCompletionKeyForConversation({
5468
+ conversation: input.conversation,
5469
+ messages: input.mergedMessages
5470
+ });
5471
+ const hasUnreadCompletion = Boolean(
5472
+ completionKey && !isCompletedRead(input.readState, completionKey) && !hasActiveConversationOwner(input.queryKey, input.conversation.id)
5473
+ );
5271
5474
  const nextConversation = {
5272
5475
  ...input.conversation,
5273
5476
  ...title ? { title: title.title, titleFallback: title.titleFallback } : {},
5274
- hasUnreadCompletion: input.conversation.status === "completed" ? input.currentConversation?.status !== "completed" && !hasActiveConversationOwner(input.queryKey, input.conversation.id) ? true : input.conversation.hasUnreadCompletion ?? false : false,
5477
+ hasUnreadCompletion,
5478
+ unreadCompletionKey: completionKey,
5275
5479
  status: input.conversation.status,
5276
5480
  updatedAtUnixMs: input.conversation.updatedAtUnixMs,
5277
5481
  pinnedAtUnixMs: input.conversation.pinnedAtUnixMs
@@ -5333,6 +5537,10 @@ async function refreshAgentGUIConversationListQuery(query, reason, options = {})
5333
5537
  sessionViewDataById
5334
5538
  )
5335
5539
  );
5540
+ const readState = await loadWorkspaceAgentReadState(state);
5541
+ if (requestId !== requestIdByQueryKey.get(queryKey)) {
5542
+ return;
5543
+ }
5336
5544
  const deletedConversationIds = deletedConversationIdsByQueryKey.get(queryKey) ?? /* @__PURE__ */ new Set();
5337
5545
  const localCreatedConversationIds = localCreatedConversationIdsByQueryKey.get(queryKey) ?? /* @__PURE__ */ new Set();
5338
5546
  const snapshotSessionIds = new Set(
@@ -5357,12 +5565,6 @@ async function refreshAgentGUIConversationListQuery(query, reason, options = {})
5357
5565
  localCreatedConversationIds
5358
5566
  );
5359
5567
  const currentConversations = getQueryState(query)?.conversations ?? [];
5360
- const currentConversationById = new Map(
5361
- currentConversations.map((conversation) => [
5362
- conversation.id,
5363
- conversation
5364
- ])
5365
- );
5366
5568
  const canApplyDirtySessionProjection = reason === "session-overlay-update" && state.initialized && dirtySessionIds.size > 0;
5367
5569
  const projectionSessions = canApplyDirtySessionProjection ? workspaceAgentSnapshot.sessions.filter((session) => {
5368
5570
  const agentSessionId = session.agentSessionId.trim();
@@ -5416,7 +5618,6 @@ async function refreshAgentGUIConversationListQuery(query, reason, options = {})
5416
5618
  if (conversationsToDecorate !== null && !conversationsToDecorate.has(conversation.id)) {
5417
5619
  return conversation;
5418
5620
  }
5419
- const currentConversation = currentConversationById.get(conversation.id);
5420
5621
  const sessionViewData = sessionViewDataById[conversation.id];
5421
5622
  const mergedMessages = sessionMessagesByIdForSummaries[conversation.id] ?? mergeWorkspaceAgentActivityDurableAndOverlayMessages({
5422
5623
  durableMessages: workspaceAgentSnapshot.sessionMessagesById?.[conversation.id],
@@ -5424,9 +5625,9 @@ async function refreshAgentGUIConversationListQuery(query, reason, options = {})
5424
5625
  });
5425
5626
  return decorateConversationForRefresh({
5426
5627
  conversation,
5427
- currentConversation,
5428
5628
  mergedMessages,
5429
- queryKey
5629
+ queryKey,
5630
+ readState
5430
5631
  });
5431
5632
  });
5432
5633
  updateQueryState(query, (current) => {
@@ -5620,6 +5821,16 @@ function clearAgentGUIConversationUnreadCompletion(input) {
5620
5821
  if (!conversationId) {
5621
5822
  return;
5622
5823
  }
5824
+ const queryState = ensureQueryState(input.query);
5825
+ if (!queryState) {
5826
+ return;
5827
+ }
5828
+ const completionKey = queryState.conversations.find(
5829
+ (conversation) => conversation.id === conversationId
5830
+ )?.unreadCompletionKey;
5831
+ if (completionKey) {
5832
+ updateCompletedReadState(queryState, completionKey);
5833
+ }
5623
5834
  updateAgentGUIConversationListConversations(
5624
5835
  input.query,
5625
5836
  (current) => {
@@ -5660,14 +5871,26 @@ function markAgentGUIConversationCompletionObserved(input) {
5660
5871
  if (conversation.id !== conversationId) {
5661
5872
  return conversation;
5662
5873
  }
5663
- const hasUnreadCompletion = conversation.status === "completed" && shouldMarkUnread;
5664
- if (conversation.hasUnreadCompletion === hasUnreadCompletion) {
5874
+ const completionKey = normalizeCompletionKey(input.completionKey) || (conversation.status === "completed" ? `session:${conversation.id}:completed` : "");
5875
+ if (!completionKey) {
5876
+ return conversation;
5877
+ }
5878
+ if (!shouldMarkUnread) {
5879
+ updateCompletedReadState(queryState, completionKey);
5880
+ }
5881
+ const canShowCompletion = conversation.status === "completed" || input.allowReadyStatus === true;
5882
+ const hasUnreadCompletion = canShowCompletion && shouldMarkUnread && !isCompletedRead(
5883
+ readStateByQueryKey.get(queryState.queryKey) ?? emptyWorkspaceAgentReadState(),
5884
+ completionKey
5885
+ );
5886
+ if (conversation.hasUnreadCompletion === hasUnreadCompletion && conversation.unreadCompletionKey === completionKey) {
5665
5887
  return conversation;
5666
5888
  }
5667
5889
  changed = true;
5668
5890
  return {
5669
5891
  ...conversation,
5670
- hasUnreadCompletion
5892
+ hasUnreadCompletion,
5893
+ unreadCompletionKey: completionKey
5671
5894
  };
5672
5895
  });
5673
5896
  return changed ? next : current;
@@ -6009,6 +6232,17 @@ function useAgentGUIActivation({
6009
6232
  );
6010
6233
  }
6011
6234
 
6235
+ // agent-gui/agentGuiNode/controller/pendingInterrupt.ts
6236
+ function pendingInterruptActionForDisplayStatus(status) {
6237
+ if (status === "working") {
6238
+ return "fire";
6239
+ }
6240
+ if (status === "completed" || status === "failed" || status === "canceled") {
6241
+ return "clear";
6242
+ }
6243
+ return "wait";
6244
+ }
6245
+
6012
6246
  // agent-gui/agentGuiNode/controller/useAgentGUINodeController.ts
6013
6247
  import { createRichTextMentionHref } from "@tutti-os/ui-rich-text/core";
6014
6248
 
@@ -6220,41 +6454,11 @@ function nodeDataFromComposerSettings(current, settings) {
6220
6454
  };
6221
6455
  }
6222
6456
 
6223
- // agent-gui/agentGuiNode/model/agentUsageThresholds.ts
6224
- var USAGE_WARN_PERCENT = 80;
6225
- var USAGE_CRITICAL_PERCENT = 95;
6226
-
6227
- // agent-gui/agentGuiNode/model/agentUsageAlerts.ts
6228
- var INITIAL_USAGE_ALERT_STATE = {
6229
- warned: false,
6230
- criticaled: false
6231
- };
6232
- function nextUsageAlert(percentUsed, state) {
6233
- if (percentUsed === null) {
6234
- return { fire: null, state };
6235
- }
6236
- if (percentUsed >= USAGE_CRITICAL_PERCENT) {
6237
- if (state.criticaled) {
6238
- return { fire: null, state };
6239
- }
6240
- return { fire: "critical", state: { warned: true, criticaled: true } };
6241
- }
6242
- if (percentUsed >= USAGE_WARN_PERCENT) {
6243
- if (state.warned) {
6244
- return { fire: null, state };
6245
- }
6246
- return { fire: "warn", state: { ...state, warned: true } };
6247
- }
6248
- if (state.warned || state.criticaled) {
6249
- return { fire: null, state: INITIAL_USAGE_ALERT_STATE };
6250
- }
6251
- return { fire: null, state };
6252
- }
6253
-
6254
6457
  // agent-gui/agentGuiNode/controller/useAgentGUINodeController.ts
6255
6458
  var EMPTY_AGENT_GUI_MESSAGES = [];
6256
6459
  var EMPTY_AGENT_GUI_AVAILABLE_COMMANDS = [];
6257
6460
  var ACTIVITY_STREAM_STATE_RELOAD_DEBOUNCE_MS = 150;
6461
+ var AGENT_GUI_DETAIL_MESSAGES_PAGE_SIZE = 100;
6258
6462
  function mergeAgentModelCatalogInvalidationEvents(events) {
6259
6463
  const providers = /* @__PURE__ */ new Set();
6260
6464
  let occurredAtUnixMs = 0;
@@ -6576,6 +6780,16 @@ function isNonRetryableResumeErrorCode(code) {
6576
6780
  function isSessionNotFoundErrorCode(code) {
6577
6781
  return code === AGENT_SESSION_NOT_FOUND_ERROR;
6578
6782
  }
6783
+ var WORKSPACE_AGENT_SESSION_NOT_READY_REASON = "workspace_agent_session_not_found";
6784
+ function isAgentSessionNotReadyError(error) {
6785
+ if (error && typeof error === "object") {
6786
+ const reason = error.reason;
6787
+ if (reason === WORKSPACE_AGENT_SESSION_NOT_READY_REASON) {
6788
+ return true;
6789
+ }
6790
+ }
6791
+ return getAgentGUIRawErrorMessage(error)?.trim() === "workspace agent session not found";
6792
+ }
6579
6793
  function isSettingsRequireNewSessionErrorCode(code) {
6580
6794
  return code === AGENT_SETTINGS_REQUIRE_NEW_SESSION_ERROR;
6581
6795
  }
@@ -6767,7 +6981,7 @@ function conversationSummariesRenderEqual(left, right) {
6767
6981
  return left.id === right.id && left.userId === right.userId && left.provider === right.provider && left.title === right.title && conversationTitleFallbacksRenderEqual(
6768
6982
  left.titleFallback,
6769
6983
  right.titleFallback
6770
- ) && 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);
6984
+ ) && left.status === right.status && left.cwd === right.cwd && left.pinnedAtUnixMs === right.pinnedAtUnixMs && left.sortTimeUnixMs === right.sortTimeUnixMs && left.updatedAtUnixMs === right.updatedAtUnixMs && left.hasUnreadCompletion === right.hasUnreadCompletion && left.unreadCompletionKey === right.unreadCompletionKey && conversationProjectsRenderEqual(left.project, right.project) && conversationSyncStatesEqual(left.syncState, right.syncState);
6771
6985
  }
6772
6986
  function conversationTitleFallbacksRenderEqual(left, right) {
6773
6987
  return left === right || JSON.stringify(left ?? null) === JSON.stringify(right ?? null);
@@ -6820,10 +7034,58 @@ function sessionHasRenderableMessages(input) {
6820
7034
  const sessionView = getAgentSessionView(
6821
7035
  input.sessionViewRef(normalizedAgentSessionId)
6822
7036
  );
6823
- if ((sessionView?.overlayMessages?.length ?? 0) > 0) {
6824
- return true;
7037
+ return (sessionView?.detailMessages?.length ?? 0) > 0 || (sessionView?.overlayMessages?.length ?? 0) > 0;
7038
+ }
7039
+ function filterMessagesForDetailWindowOverlay(input) {
7040
+ if (input.localMessages.length === 0) {
7041
+ return [];
7042
+ }
7043
+ if (input.detailMessages.length === 0) {
7044
+ if (input.durableMessages.length <= AGENT_GUI_DETAIL_MESSAGES_PAGE_SIZE) {
7045
+ return [...input.localMessages];
7046
+ }
7047
+ const newestDurableVersion = maxFiniteMessageVersion(input.durableMessages);
7048
+ return input.localMessages.filter((message) => {
7049
+ if (isWorkspaceAgentActivityOptimisticMessage(message)) {
7050
+ return true;
7051
+ }
7052
+ return newestDurableVersion !== null && Number.isFinite(message.version) && message.version >= newestDurableVersion;
7053
+ });
7054
+ }
7055
+ const oldestDetailVersion = minFiniteMessageVersion(input.detailMessages);
7056
+ const newestDetailVersion = maxFiniteMessageVersion(input.detailMessages);
7057
+ return input.localMessages.filter((message) => {
7058
+ if (isWorkspaceAgentActivityOptimisticMessage(message)) {
7059
+ return true;
7060
+ }
7061
+ if (!Number.isFinite(message.version)) {
7062
+ return true;
7063
+ }
7064
+ if (newestDetailVersion !== null && message.version > newestDetailVersion) {
7065
+ return true;
7066
+ }
7067
+ return oldestDetailVersion !== null && message.version >= oldestDetailVersion;
7068
+ });
7069
+ }
7070
+ function minFiniteMessageVersion(messages) {
7071
+ let result = null;
7072
+ for (const message of messages) {
7073
+ if (!Number.isFinite(message.version)) {
7074
+ continue;
7075
+ }
7076
+ result = result === null ? message.version : Math.min(result, message.version);
7077
+ }
7078
+ return result;
7079
+ }
7080
+ function maxFiniteMessageVersion(messages) {
7081
+ let result = null;
7082
+ for (const message of messages) {
7083
+ if (!Number.isFinite(message.version)) {
7084
+ continue;
7085
+ }
7086
+ result = result === null ? message.version : Math.max(result, message.version);
6825
7087
  }
6826
- return (input.snapshotMessagesById[normalizedAgentSessionId]?.length ?? 0) > 0;
7088
+ return result;
6827
7089
  }
6828
7090
  function hasPromptConversationTitle2(conversation) {
6829
7091
  return resolveAgentGUIExplicitConversationTitle(conversation) !== null;
@@ -6864,7 +7126,7 @@ function hasSettledTimelineEvidence(timelineItems) {
6864
7126
  }
6865
7127
  return relevantItems.some((item) => {
6866
7128
  const normalizedStatus = normalizeOptionalWorkspaceAgentStatus({
6867
- status: item.status ?? stringPayloadValue(item.payload, "status")
7129
+ status: item.status ?? stringPayloadValue2(item.payload, "status")
6868
7130
  })?.kind;
6869
7131
  return item.role === "assistant" || normalizedStatus === "completed" || normalizedStatus === "ready" || normalizedStatus === "failed" || normalizedStatus === "canceled";
6870
7132
  });
@@ -6894,13 +7156,13 @@ function resolveConversationStatusFromTimelineEvidence(input) {
6894
7156
  function hasRejectedApprovalDecision(timelineItems) {
6895
7157
  return timelineItems.some((item) => {
6896
7158
  const payload = item.payload ?? {};
6897
- const callType = (item.callType ?? stringPayloadValue(payload, "callType") ?? "").trim().toLowerCase();
6898
- const toolName = (stringPayloadValue(payload, "toolName") ?? "").trim().toLowerCase();
7159
+ const callType = (item.callType ?? stringPayloadValue2(payload, "callType") ?? "").trim().toLowerCase();
7160
+ const toolName = (stringPayloadValue2(payload, "toolName") ?? "").trim().toLowerCase();
6899
7161
  if (callType !== "approval" && toolName !== "approval") {
6900
7162
  return false;
6901
7163
  }
6902
7164
  const status = normalizeOptionalWorkspaceAgentStatus({
6903
- status: item.status ?? stringPayloadValue(payload, "status")
7165
+ status: item.status ?? stringPayloadValue2(payload, "status")
6904
7166
  })?.kind;
6905
7167
  if (status !== "completed") {
6906
7168
  return false;
@@ -6910,7 +7172,7 @@ function hasRejectedApprovalDecision(timelineItems) {
6910
7172
  });
6911
7173
  }
6912
7174
  function isRejectedApprovalDecision(output) {
6913
- const selectedId = stringPayloadValue(output ?? void 0, "selectedId");
7175
+ const selectedId = stringPayloadValue2(output ?? void 0, "selectedId");
6914
7176
  if (!selectedId) {
6915
7177
  return false;
6916
7178
  }
@@ -6984,7 +7246,7 @@ function runtimeSessionSyncState(session) {
6984
7246
  }
6985
7247
  return syncState;
6986
7248
  }
6987
- function stringPayloadValue(value, key) {
7249
+ function stringPayloadValue2(value, key) {
6988
7250
  const nested = value?.[key];
6989
7251
  return typeof nested === "string" ? nested : void 0;
6990
7252
  }
@@ -7386,6 +7648,23 @@ function removeQueuedPromptById(queue, queuedPromptId) {
7386
7648
  }
7387
7649
  var NODE_DEFAULT_DRAFT_KEY = "__agent_gui_node_defaults__";
7388
7650
  var EMPTY_AGENT_COMPOSER_DRAFT = emptyAgentComposerDraft();
7651
+ function areAgentComposerDraftsEqual(left, right) {
7652
+ const leftFiles = left.files ?? [];
7653
+ const rightFiles = right.files ?? [];
7654
+ return left.prompt === right.prompt && left.images.length === right.images.length && left.images.every((image, index) => {
7655
+ const other = right.images[index];
7656
+ if (!other) {
7657
+ return false;
7658
+ }
7659
+ return image.id === other.id && image.name === other.name && image.mimeType === other.mimeType && image.data === other.data && image.path === other.path && image.previewUrl === other.previewUrl && image.uploading === other.uploading && image.uploadError === other.uploadError;
7660
+ }) && leftFiles.length === rightFiles.length && leftFiles.every((file, index) => {
7661
+ const other = rightFiles[index];
7662
+ if (!other) {
7663
+ return false;
7664
+ }
7665
+ return file.id === other.id && file.name === other.name && file.mimeType === other.mimeType && file.path === other.path && file.hostPath === other.hostPath && file.assetId === other.assetId && file.sizeBytes === other.sizeBytes && file.uploading === other.uploading && file.uploadError === other.uploadError;
7666
+ });
7667
+ }
7389
7668
  function nodeDefaultDraftKey(agentProvider) {
7390
7669
  return `${NODE_DEFAULT_DRAFT_KEY}:${agentProvider}`;
7391
7670
  }
@@ -7449,6 +7728,39 @@ function conversationStatusFromStatePatch(patch) {
7449
7728
  return null;
7450
7729
  }
7451
7730
  }
7731
+ function completionKeyFromStatePatch(agentSessionId, patch) {
7732
+ const turnId = patch.turn?.turnId?.trim() ?? "";
7733
+ if (turnId && isCompletedOutcomeToken(patch.turn?.outcome)) {
7734
+ return `turn:${agentSessionId}:${turnId}:completed`;
7735
+ }
7736
+ return conversationStatusFromStatePatch(patch) === "completed" ? `session:${agentSessionId}:completed` : null;
7737
+ }
7738
+ function completionKeyFromSessionState(agentSessionId, state) {
7739
+ return conversationStatusFromSessionState(state) === "completed" ? `session:${agentSessionId}:completed` : null;
7740
+ }
7741
+ function completionKeyFromMessage(message) {
7742
+ const agentSessionId = message.agentSessionId.trim();
7743
+ if (!agentSessionId) {
7744
+ return null;
7745
+ }
7746
+ if ((message.role ?? "").trim().toLowerCase() !== "assistant") {
7747
+ return null;
7748
+ }
7749
+ const kind = (message.kind ?? "").trim().toLowerCase();
7750
+ if (kind !== "message" && kind !== "text") {
7751
+ return null;
7752
+ }
7753
+ const payload = message.payload && typeof message.payload === "object" ? message.payload : {};
7754
+ const status = message.status?.trim().toLowerCase() || (stringPayloadValue2(payload, "status") ?? "").toLowerCase();
7755
+ if (!isCompletedOutcomeToken(status)) {
7756
+ return null;
7757
+ }
7758
+ const subject = message.turnId?.trim() || message.messageId.trim();
7759
+ return subject ? `turn:${agentSessionId}:${subject}:completed` : null;
7760
+ }
7761
+ function isCompletedOutcomeToken(value) {
7762
+ return value?.trim().toLowerCase() === "completed";
7763
+ }
7452
7764
  function hasSessionControlStatePatch(patch) {
7453
7765
  return normalizeOptionalText2(patch.permissionModeId) !== null || patch.settings !== void 0 || patch.runtimeContext !== void 0 || patch.submitAvailability !== void 0 || patch.turn?.submitAvailability !== void 0 || patch.turn?.phase !== void 0;
7454
7766
  }
@@ -7565,7 +7877,7 @@ function conversationStatusFromTimelineItems(timelineItems) {
7565
7877
  }
7566
7878
  for (const item of latestByCallId.values()) {
7567
7879
  const status = normalizeTimelineStatus(
7568
- item.status ?? stringPayloadValue(item.payload, "status")
7880
+ item.status ?? stringPayloadValue2(item.payload, "status")
7569
7881
  );
7570
7882
  if (status === "working") {
7571
7883
  hasWorking = true;
@@ -7622,7 +7934,8 @@ function messageFromMessageUpdate(update) {
7622
7934
  ...isToolCall && update.title?.trim() ? { title: update.title.trim() } : {}
7623
7935
  },
7624
7936
  ...update.occurredAtUnixMs !== void 0 ? { occurredAtUnixMs: update.occurredAtUnixMs } : {},
7625
- ...update.startedAtUnixMs !== void 0 ? { startedAtUnixMs: update.startedAtUnixMs } : {}
7937
+ ...update.startedAtUnixMs !== void 0 ? { startedAtUnixMs: update.startedAtUnixMs } : {},
7938
+ ...update.completedAtUnixMs !== void 0 ? { completedAtUnixMs: update.completedAtUnixMs } : {}
7626
7939
  };
7627
7940
  }
7628
7941
  function normalizedPositiveNumber2(value) {
@@ -7700,6 +8013,7 @@ function useAgentGUINodeController({
7700
8013
  data.lastActiveAgentSessionId === null
7701
8014
  );
7702
8015
  const [draftBySessionId, setDraftBySessionId] = useState4({});
8016
+ const draftBySessionIdRef = useRef4(draftBySessionId);
7703
8017
  const [draftSettingsBySessionId, setDraftSettingsBySessionId] = useState4({});
7704
8018
  const [queuedPromptsBySessionId, setQueuedPromptsBySessionId] = useState4({});
7705
8019
  const hasLoadedConversations = conversationListState?.initialized ?? false;
@@ -7729,6 +8043,7 @@ function useAgentGUINodeController({
7729
8043
  setQueuedPromptRetryBlockBySessionId
7730
8044
  ] = useState4({});
7731
8045
  const [interruptingSessionIds, setInterruptingSessionIds] = useState4({});
8046
+ const [pendingInterruptSessionIds, setPendingInterruptSessionIds] = useState4({});
7732
8047
  const [
7733
8048
  suppressedPromptRequestIdsBySessionId,
7734
8049
  setSuppressedPromptRequestIdsBySessionId
@@ -7795,53 +8110,8 @@ function useAgentGUINodeController({
7795
8110
  }),
7796
8111
  [activeSessionRuntimeContext]
7797
8112
  );
7798
- const usageAlertStateBySessionIdRef = useRef4(
7799
- {}
7800
- );
7801
- const [usageAlertBySessionId, setUsageAlertBySessionId] = useState4({});
7802
8113
  const [dismissedPlanTurnIdBySessionId, setDismissedPlanTurnIdBySessionId] = useState4({});
7803
8114
  const planImplementationTurnIdRef = useRef4(null);
7804
- const usagePercentUsed = usage?.percentUsed ?? null;
7805
- useEffect4(() => {
7806
- const agentSessionId = activeConversationId;
7807
- if (!agentSessionId) {
7808
- return;
7809
- }
7810
- const previousState = usageAlertStateBySessionIdRef.current[agentSessionId] ?? INITIAL_USAGE_ALERT_STATE;
7811
- const { fire, state } = nextUsageAlert(usagePercentUsed, previousState);
7812
- usageAlertStateBySessionIdRef.current[agentSessionId] = state;
7813
- if (fire) {
7814
- setUsageAlertBySessionId(
7815
- (current) => current[agentSessionId] === fire ? current : { ...current, [agentSessionId]: fire }
7816
- );
7817
- return;
7818
- }
7819
- if (!state.warned && !state.criticaled) {
7820
- setUsageAlertBySessionId((current) => {
7821
- if (!(agentSessionId in current)) {
7822
- return current;
7823
- }
7824
- const next = { ...current };
7825
- delete next[agentSessionId];
7826
- return next;
7827
- });
7828
- }
7829
- }, [activeConversationId, usagePercentUsed]);
7830
- const usageAlert = activeConversationId ? usageAlertBySessionId[activeConversationId] ?? null : null;
7831
- const dismissUsageAlert = useCallback4(() => {
7832
- const agentSessionId = activeConversationId;
7833
- if (!agentSessionId) {
7834
- return;
7835
- }
7836
- setUsageAlertBySessionId((current) => {
7837
- if (!(agentSessionId in current)) {
7838
- return current;
7839
- }
7840
- const next = { ...current };
7841
- delete next[agentSessionId];
7842
- return next;
7843
- });
7844
- }, [activeConversationId]);
7845
8115
  const dismissPlanImplementation = useCallback4(() => {
7846
8116
  const agentSessionId = activeConversationIdRef.current;
7847
8117
  const planTurnId = planImplementationTurnIdRef.current;
@@ -7860,17 +8130,21 @@ function useAgentGUINodeController({
7860
8130
  if (!normalizedAgentSessionId) {
7861
8131
  return EMPTY_AGENT_GUI_MESSAGES;
7862
8132
  }
7863
- return mergeWorkspaceAgentActivityDurableAndOverlayMessages({
7864
- durableMessages: agentActivitySnapshot.sessionMessagesById[normalizedAgentSessionId],
7865
- localMessages: getAgentSessionView(sessionViewRef(normalizedAgentSessionId))?.overlayMessages ?? EMPTY_AGENT_GUI_MESSAGES
7866
- });
8133
+ const sessionView = getAgentSessionView(
8134
+ sessionViewRef(normalizedAgentSessionId)
8135
+ );
8136
+ return mergeWorkspaceAgentMessages(
8137
+ sessionView?.detailMessages ?? EMPTY_AGENT_GUI_MESSAGES,
8138
+ sessionView?.overlayMessages ?? EMPTY_AGENT_GUI_MESSAGES
8139
+ );
7867
8140
  },
7868
- [agentActivitySnapshot.sessionMessagesById, sessionViewRef]
8141
+ [sessionViewRef]
7869
8142
  );
7870
8143
  const activeMessages = useMemo4(() => {
7871
8144
  return activeConversationId ? resolveSessionMessages(activeConversationId) : activeSessionView?.overlayMessages ?? EMPTY_AGENT_GUI_MESSAGES;
7872
8145
  }, [
7873
8146
  activeConversationId,
8147
+ activeSessionView?.detailMessages,
7874
8148
  activeSessionView?.overlayMessages,
7875
8149
  resolveSessionMessages
7876
8150
  ]);
@@ -8054,6 +8328,7 @@ function useAgentGUINodeController({
8054
8328
  conversations: []
8055
8329
  });
8056
8330
  const selectedConversationMessageLoadSeqRef = useRef4(0);
8331
+ const selectedConversationOlderMessageLoadSeqRef = useRef4(0);
8057
8332
  const selectedConversationPendingMessageLoadIdsRef = useRef4(
8058
8333
  /* @__PURE__ */ new Set()
8059
8334
  );
@@ -8277,6 +8552,9 @@ function useAgentGUINodeController({
8277
8552
  useEffect4(() => {
8278
8553
  onShowMessageRef.current = onShowMessage;
8279
8554
  }, [onShowMessage]);
8555
+ useEffect4(() => {
8556
+ draftBySessionIdRef.current = draftBySessionId;
8557
+ }, [draftBySessionId]);
8280
8558
  useEffect4(() => {
8281
8559
  draftSettingsBySessionIdRef.current = draftSettingsBySessionId;
8282
8560
  }, [draftSettingsBySessionId]);
@@ -8396,6 +8674,9 @@ function useAgentGUINodeController({
8396
8674
  selectedConversationPendingMessageLoadIdsRef.current.add(
8397
8675
  normalizedAgentSessionId
8398
8676
  );
8677
+ resetAgentSessionViewDetailMessages(
8678
+ sessionViewRef(normalizedAgentSessionId)
8679
+ );
8399
8680
  setIsLoadingMessages(true);
8400
8681
  setAgentSessionViewMessagesLoading(
8401
8682
  sessionViewRef(normalizedAgentSessionId),
@@ -8718,12 +8999,30 @@ function useAgentGUINodeController({
8718
8999
  pendingTurnIdBySessionIdRef.current = nextPending;
8719
9000
  }
8720
9001
  const localMessages = getAgentSessionView(sessionViewRef(normalizedAgentSessionId))?.overlayMessages ?? [];
8721
- const overlayMessages = selectWorkspaceAgentActivityOverlayMessages({
9002
+ const detailMessages = getAgentSessionView(sessionViewRef(normalizedAgentSessionId))?.detailMessages ?? [];
9003
+ const durableTailMessages = detailMessages.length > 0 ? filterMessagesForDetailWindowOverlay({
9004
+ detailMessages,
9005
+ durableMessages,
9006
+ localMessages: durableMessages
9007
+ }) : [];
9008
+ const baseMessages = detailMessages.length > 0 ? mergeWorkspaceAgentMessages(detailMessages, durableTailMessages) : durableMessages;
9009
+ if (durableTailMessages.length > 0) {
9010
+ mergeAgentSessionViewDetailMessages(
9011
+ sessionViewRef(normalizedAgentSessionId),
9012
+ durableTailMessages
9013
+ );
9014
+ }
9015
+ const windowLocalMessages = filterMessagesForDetailWindowOverlay({
9016
+ detailMessages: baseMessages,
8722
9017
  durableMessages,
8723
9018
  localMessages
8724
9019
  });
9020
+ const overlayMessages = selectWorkspaceAgentActivityOverlayMessages({
9021
+ durableMessages: baseMessages,
9022
+ localMessages: windowLocalMessages
9023
+ });
8725
9024
  const mergedMessages = mergeWorkspaceAgentMessages(
8726
- durableMessages,
9025
+ baseMessages,
8727
9026
  overlayMessages
8728
9027
  );
8729
9028
  const mergedItems = projectAgentGUIMessagesToTimelineItems(mergedMessages);
@@ -8780,6 +9079,10 @@ function useAgentGUINodeController({
8780
9079
  if (!nextStatus && !title) {
8781
9080
  return;
8782
9081
  }
9082
+ const completionKey = completionKeyFromSessionState(
9083
+ agentSessionId,
9084
+ snapshot3
9085
+ );
8783
9086
  patchConversation(agentSessionId, (conversation2) => {
8784
9087
  const timelineItems = projectAgentGUIMessagesToTimelineItems(
8785
9088
  resolveSessionMessages(agentSessionId)
@@ -8807,13 +9110,15 @@ function useAgentGUINodeController({
8807
9110
  snapshotUpdatedAtUnixMs: shouldAdvanceConversationUpdatedAt ? snapshot3.updatedAtUnixMs : void 0,
8808
9111
  source: cause?.source
8809
9112
  }),
8810
- hasUnreadCompletion: status === "completed" ? conversation2.hasUnreadCompletion ?? false : false
9113
+ hasUnreadCompletion: status === "completed" ? conversation2.hasUnreadCompletion ?? false : false,
9114
+ unreadCompletionKey: status === "completed" ? conversation2.unreadCompletionKey ?? completionKey : null
8811
9115
  };
8812
9116
  });
8813
- if (nextStatus === "completed" && conversationListQuery) {
9117
+ if (completionKey && conversationListQuery) {
8814
9118
  markAgentGUIConversationCompletionObserved({
8815
9119
  query: conversationListQuery,
8816
- conversationId: agentSessionId
9120
+ conversationId: agentSessionId,
9121
+ completionKey
8817
9122
  });
8818
9123
  }
8819
9124
  const transient = transientConversationRef.current;
@@ -8844,7 +9149,8 @@ function useAgentGUINodeController({
8844
9149
  snapshotUpdatedAtUnixMs: shouldAdvanceConversationUpdatedAt ? snapshot3.updatedAtUnixMs : void 0,
8845
9150
  source: cause?.source
8846
9151
  }),
8847
- hasUnreadCompletion: transientStatus === "completed" && activeConversationIdRef.current !== agentSessionId
9152
+ hasUnreadCompletion: transientStatus === "completed" && activeConversationIdRef.current !== agentSessionId,
9153
+ unreadCompletionKey: transientStatus === "completed" ? transient.unreadCompletionKey ?? completionKey : null
8848
9154
  });
8849
9155
  }
8850
9156
  },
@@ -9035,9 +9341,15 @@ function useAgentGUINodeController({
9035
9341
  setIsLoadingMessages(true);
9036
9342
  }
9037
9343
  try {
9344
+ resetAgentSessionViewDetailMessages(
9345
+ sessionViewRef(normalizedAgentSessionId)
9346
+ );
9038
9347
  const page = await agentActivityRuntime.listSessionMessages({
9039
9348
  workspaceId,
9040
- agentSessionId: normalizedAgentSessionId
9349
+ agentSessionId: normalizedAgentSessionId,
9350
+ cache: false,
9351
+ limit: AGENT_GUI_DETAIL_MESSAGES_PAGE_SIZE,
9352
+ order: "desc"
9041
9353
  });
9042
9354
  if (!isMountedRef.current || activeConversationIdRef.current !== normalizedAgentSessionId || selectedConversationMessageLoadSeqRef.current !== requestId) {
9043
9355
  return;
@@ -9048,24 +9360,38 @@ function useAgentGUINodeController({
9048
9360
  clearSelectedConversationNotFoundRetryWhenInitialLoadsSettled(
9049
9361
  normalizedAgentSessionId
9050
9362
  );
9051
- const durableMessages = mergeWorkspaceAgentMessages(
9052
- [],
9053
- agentActivitySnapshotRef.current.sessionMessagesById[normalizedAgentSessionId] ?? []
9054
- );
9055
- const currentOverlayMessages = getAgentSessionView(sessionViewRef(normalizedAgentSessionId))?.overlayMessages ?? [];
9056
- const localMessages = mergeWorkspaceAgentMessages(
9057
- currentOverlayMessages,
9363
+ const currentDetailMessages = getAgentSessionView(sessionViewRef(normalizedAgentSessionId))?.detailMessages ?? [];
9364
+ const detailMessages = mergeWorkspaceAgentMessages(
9365
+ currentDetailMessages,
9058
9366
  page.messages
9059
9367
  );
9368
+ const currentOverlayMessages = getAgentSessionView(sessionViewRef(normalizedAgentSessionId))?.overlayMessages ?? [];
9369
+ const windowOverlayMessages = filterMessagesForDetailWindowOverlay({
9370
+ detailMessages,
9371
+ durableMessages: page.messages,
9372
+ localMessages: currentOverlayMessages
9373
+ });
9060
9374
  const overlayMessages = selectWorkspaceAgentActivityOverlayMessages({
9061
- durableMessages,
9062
- localMessages
9375
+ durableMessages: detailMessages,
9376
+ localMessages: windowOverlayMessages
9063
9377
  });
9378
+ setAgentSessionViewDetailMessages(
9379
+ sessionViewRef(normalizedAgentSessionId),
9380
+ detailMessages,
9381
+ {
9382
+ hasOlderMessages: page.hasMore && page.messages.length > 0,
9383
+ isLoadingOlderMessages: false
9384
+ }
9385
+ );
9064
9386
  setAgentSessionViewOverlayMessages(
9065
9387
  sessionViewRef(normalizedAgentSessionId),
9066
9388
  overlayMessages
9067
9389
  );
9068
- refreshMessagesFromSnapshot(normalizedAgentSessionId);
9390
+ setAgentSessionViewMessagesLoading(
9391
+ sessionViewRef(normalizedAgentSessionId),
9392
+ false
9393
+ );
9394
+ setIsLoadingMessages(false);
9069
9395
  } catch (error) {
9070
9396
  if (!isMountedRef.current || activeConversationIdRef.current !== normalizedAgentSessionId || selectedConversationMessageLoadSeqRef.current !== requestId) {
9071
9397
  return;
@@ -9095,12 +9421,94 @@ function useAgentGUINodeController({
9095
9421
  [
9096
9422
  agentActivityRuntime,
9097
9423
  clearSelectedConversationNotFoundRetryWhenInitialLoadsSettled,
9098
- refreshMessagesFromSnapshot,
9099
9424
  scheduleSelectedConversationNotFoundRetry,
9100
9425
  sessionViewRef,
9101
9426
  workspaceId
9102
9427
  ]
9103
9428
  );
9429
+ const loadOlderConversationMessages = useCallback4(
9430
+ async (agentSessionId) => {
9431
+ const normalizedAgentSessionId = (agentSessionId ?? activeConversationIdRef.current ?? "").trim();
9432
+ if (!normalizedAgentSessionId) {
9433
+ return;
9434
+ }
9435
+ const currentView = getAgentSessionView(
9436
+ sessionViewRef(normalizedAgentSessionId)
9437
+ );
9438
+ if (!currentView?.hasOlderMessages || currentView.isLoadingOlderMessages || currentView.oldestLoadedVersion === null || activeConversationIdRef.current !== normalizedAgentSessionId) {
9439
+ return;
9440
+ }
9441
+ const requestId = ++selectedConversationOlderMessageLoadSeqRef.current;
9442
+ setAgentSessionViewOlderMessagesLoading(
9443
+ sessionViewRef(normalizedAgentSessionId),
9444
+ true
9445
+ );
9446
+ try {
9447
+ const page = await agentActivityRuntime.listSessionMessages({
9448
+ workspaceId,
9449
+ agentSessionId: normalizedAgentSessionId,
9450
+ beforeVersion: currentView.oldestLoadedVersion,
9451
+ cache: false,
9452
+ limit: AGENT_GUI_DETAIL_MESSAGES_PAGE_SIZE,
9453
+ order: "desc"
9454
+ });
9455
+ if (!isMountedRef.current || activeConversationIdRef.current !== normalizedAgentSessionId || selectedConversationOlderMessageLoadSeqRef.current !== requestId) {
9456
+ setAgentSessionViewOlderMessagesLoading(
9457
+ sessionViewRef(normalizedAgentSessionId),
9458
+ false
9459
+ );
9460
+ return;
9461
+ }
9462
+ const nextDetailMessages = mergeWorkspaceAgentMessages(
9463
+ currentView.detailMessages,
9464
+ page.messages
9465
+ );
9466
+ const currentOverlayMessages = getAgentSessionView(sessionViewRef(normalizedAgentSessionId))?.overlayMessages ?? [];
9467
+ const windowOverlayMessages = filterMessagesForDetailWindowOverlay({
9468
+ detailMessages: nextDetailMessages,
9469
+ durableMessages: page.messages,
9470
+ localMessages: currentOverlayMessages
9471
+ });
9472
+ const overlayMessages = selectWorkspaceAgentActivityOverlayMessages({
9473
+ durableMessages: nextDetailMessages,
9474
+ localMessages: windowOverlayMessages
9475
+ });
9476
+ mergeAgentSessionViewDetailMessages(
9477
+ sessionViewRef(normalizedAgentSessionId),
9478
+ page.messages,
9479
+ {
9480
+ hasOlderMessages: page.hasMore && page.messages.length > 0,
9481
+ isLoadingOlderMessages: false
9482
+ }
9483
+ );
9484
+ setAgentSessionViewOverlayMessages(
9485
+ sessionViewRef(normalizedAgentSessionId),
9486
+ overlayMessages
9487
+ );
9488
+ } catch (error) {
9489
+ if (!isMountedRef.current || activeConversationIdRef.current !== normalizedAgentSessionId || selectedConversationOlderMessageLoadSeqRef.current !== requestId) {
9490
+ setAgentSessionViewOlderMessagesLoading(
9491
+ sessionViewRef(normalizedAgentSessionId),
9492
+ false
9493
+ );
9494
+ return;
9495
+ }
9496
+ reportAgentGUIRuntimeError({
9497
+ agentSessionId: normalizedAgentSessionId,
9498
+ error,
9499
+ phase: "load_session_messages",
9500
+ provider: dataRef.current.provider,
9501
+ runtime: agentActivityRuntime,
9502
+ workspaceId
9503
+ });
9504
+ setAgentSessionViewOlderMessagesLoading(
9505
+ sessionViewRef(normalizedAgentSessionId),
9506
+ false
9507
+ );
9508
+ }
9509
+ },
9510
+ [agentActivityRuntime, sessionViewRef, workspaceId]
9511
+ );
9104
9512
  const reloadSelectedConversation = useCallback4(
9105
9513
  (agentSessionId, options) => {
9106
9514
  if (!agentSessionId) {
@@ -9120,7 +9528,12 @@ function useAgentGUINodeController({
9120
9528
  const hadPendingMessageLoad = selectedConversationPendingMessageLoadIdsRef.current.delete(
9121
9529
  normalizedAgentSessionId
9122
9530
  );
9123
- if (hadPendingMessageLoad) {
9531
+ const hasRenderableMessages = sessionHasRenderableMessages({
9532
+ agentSessionId: normalizedAgentSessionId,
9533
+ sessionViewRef,
9534
+ snapshotMessagesById: agentActivitySnapshotRef.current.sessionMessagesById
9535
+ });
9536
+ if (hadPendingMessageLoad || !hasRenderableMessages) {
9124
9537
  void loadSelectedConversationMessages(normalizedAgentSessionId);
9125
9538
  } else {
9126
9539
  void refreshMessagesFromSnapshot(normalizedAgentSessionId);
@@ -9356,15 +9769,38 @@ function useAgentGUINodeController({
9356
9769
  return;
9357
9770
  }
9358
9771
  const currentMessages = getAgentSessionView(sessionViewRef(agentSessionId))?.overlayMessages ?? [];
9359
- const overlayMessages = selectWorkspaceAgentActivityOverlayMessages({
9360
- durableMessages: agentActivitySnapshot.sessionMessagesById[agentSessionId],
9772
+ const currentDetailMessages = getAgentSessionView(sessionViewRef(agentSessionId))?.detailMessages ?? [];
9773
+ const durableSnapshotMessages = agentActivitySnapshot.sessionMessagesById[agentSessionId] ?? [];
9774
+ const detailWindowMessages = filterMessagesForDetailWindowOverlay({
9775
+ detailMessages: currentDetailMessages,
9776
+ durableMessages: durableSnapshotMessages,
9777
+ localMessages: nextMessages
9778
+ });
9779
+ const nextDetailMessages = detailWindowMessages.length > 0 ? mergeWorkspaceAgentMessages(
9780
+ currentDetailMessages,
9781
+ detailWindowMessages
9782
+ ) : currentDetailMessages;
9783
+ if (detailWindowMessages.length > 0) {
9784
+ mergeAgentSessionViewDetailMessages(
9785
+ sessionViewRef(agentSessionId),
9786
+ detailWindowMessages
9787
+ );
9788
+ }
9789
+ const durableMessages = nextDetailMessages.length > 0 ? nextDetailMessages : durableSnapshotMessages;
9790
+ const nextLocalMessages = filterMessagesForDetailWindowOverlay({
9791
+ detailMessages: nextDetailMessages,
9792
+ durableMessages: durableSnapshotMessages,
9361
9793
  localMessages: mergeWorkspaceAgentMessages(
9362
9794
  currentMessages,
9363
9795
  nextMessages
9364
9796
  )
9365
9797
  });
9798
+ const overlayMessages = selectWorkspaceAgentActivityOverlayMessages({
9799
+ durableMessages,
9800
+ localMessages: nextLocalMessages
9801
+ });
9366
9802
  const mergedMessages = mergeWorkspaceAgentMessages(
9367
- agentActivitySnapshot.sessionMessagesById[agentSessionId] ?? [],
9803
+ durableMessages,
9368
9804
  overlayMessages
9369
9805
  );
9370
9806
  const nextItems = projectAgentGUIMessagesToTimelineItems(nextMessages);
@@ -9397,6 +9833,7 @@ function useAgentGUINodeController({
9397
9833
  }
9398
9834
  const normalizedLastError = patch.lastError?.trim() ?? "";
9399
9835
  const nextStatus = conversationStatusFromStatePatch(patch);
9836
+ const completionKey = completionKeyFromStatePatch(agentSessionId, patch);
9400
9837
  const hasStructuredTurnLifecycle = Boolean(patch.turn?.phase?.trim());
9401
9838
  const hasControlStatePatch = hasSessionControlStatePatch(patch);
9402
9839
  const pendingTurnId = pendingTurnIdBySessionIdRef.current[agentSessionId]?.trim() ?? "";
@@ -9435,7 +9872,7 @@ function useAgentGUINodeController({
9435
9872
  delete nextPending[agentSessionId];
9436
9873
  pendingTurnIdBySessionIdRef.current = nextPending;
9437
9874
  }
9438
- if (!nextStatus && !patch.title?.trim() && normalizedLastError === "" && !hasControlStatePatch && !clearedPendingSubmittedTurn) {
9875
+ if (!nextStatus && !completionKey && !patch.title?.trim() && normalizedLastError === "" && !hasControlStatePatch && !clearedPendingSubmittedTurn) {
9439
9876
  return;
9440
9877
  }
9441
9878
  if (hasControlStatePatch) {
@@ -9479,19 +9916,22 @@ function useAgentGUINodeController({
9479
9916
  timelineItems
9480
9917
  });
9481
9918
  const hasUnreadCompletion = status === "completed" ? conversation2.hasUnreadCompletion ?? false : false;
9482
- if (titleFields.title === conversation2.title && titleFields.titleFallback === conversation2.titleFallback && status === conversation2.status && hasUnreadCompletion === conversation2.hasUnreadCompletion && !clearedPendingSubmittedTurn) {
9919
+ if (titleFields.title === conversation2.title && titleFields.titleFallback === conversation2.titleFallback && status === conversation2.status && hasUnreadCompletion === conversation2.hasUnreadCompletion && (!completionKey || conversation2.unreadCompletionKey === completionKey) && !clearedPendingSubmittedTurn) {
9483
9920
  return null;
9484
9921
  }
9485
9922
  return {
9486
9923
  ...titleFields,
9487
9924
  status,
9488
- hasUnreadCompletion
9925
+ hasUnreadCompletion,
9926
+ unreadCompletionKey: status === "completed" || completionKey ? conversation2.unreadCompletionKey ?? completionKey : null
9489
9927
  };
9490
9928
  });
9491
- if (nextStatus === "completed" && conversationListQuery) {
9929
+ if (completionKey && conversationListQuery) {
9492
9930
  markAgentGUIConversationCompletionObserved({
9493
9931
  query: conversationListQuery,
9494
- conversationId: agentSessionId
9932
+ conversationId: agentSessionId,
9933
+ completionKey,
9934
+ allowReadyStatus: nextStatus !== "completed"
9495
9935
  });
9496
9936
  }
9497
9937
  const transient = transientConversationRef.current;
@@ -9511,7 +9951,8 @@ function useAgentGUINodeController({
9511
9951
  ...transient,
9512
9952
  ...transientTitleFields,
9513
9953
  status: transientStatus,
9514
- hasUnreadCompletion: transientStatus === "completed" && activeConversationIdRef.current !== agentSessionId
9954
+ hasUnreadCompletion: Boolean(completionKey) && activeConversationIdRef.current !== agentSessionId,
9955
+ unreadCompletionKey: transientStatus === "completed" || completionKey ? transient.unreadCompletionKey ?? completionKey : null
9515
9956
  });
9516
9957
  }
9517
9958
  },
@@ -9529,14 +9970,26 @@ function useAgentGUINodeController({
9529
9970
  const handleActivityStreamEvents = useCallback4(
9530
9971
  (events) => {
9531
9972
  const pendingMessagesBySessionId = /* @__PURE__ */ new Map();
9973
+ const pendingCompletionKeysBySessionId = /* @__PURE__ */ new Map();
9532
9974
  const flushPendingMessages = () => {
9533
9975
  for (const [agentSessionId, messages] of pendingMessagesBySessionId) {
9976
+ recordLocalMessages(agentSessionId, messages);
9534
9977
  applyBackgroundTimelineStatusUpdate(
9535
9978
  agentSessionId,
9536
9979
  projectAgentGUIMessagesToTimelineItems(messages)
9537
9980
  );
9981
+ const completionKey = pendingCompletionKeysBySessionId.get(agentSessionId);
9982
+ if (completionKey && conversationListQuery) {
9983
+ markAgentGUIConversationCompletionObserved({
9984
+ query: conversationListQuery,
9985
+ conversationId: agentSessionId,
9986
+ completionKey,
9987
+ allowReadyStatus: true
9988
+ });
9989
+ }
9538
9990
  }
9539
9991
  pendingMessagesBySessionId.clear();
9992
+ pendingCompletionKeysBySessionId.clear();
9540
9993
  };
9541
9994
  for (const event of events) {
9542
9995
  if (event.eventType === "available_commands_update") {
@@ -9548,6 +10001,10 @@ function useAgentGUINodeController({
9548
10001
  if (!agentSessionId) {
9549
10002
  continue;
9550
10003
  }
10004
+ const completionKey = completionKeyFromMessage(message);
10005
+ if (completionKey) {
10006
+ pendingCompletionKeysBySessionId.set(agentSessionId, completionKey);
10007
+ }
9551
10008
  const messages = pendingMessagesBySessionId.get(agentSessionId);
9552
10009
  if (messages) {
9553
10010
  messages.push(message);
@@ -9563,7 +10020,12 @@ function useAgentGUINodeController({
9563
10020
  }
9564
10021
  flushPendingMessages();
9565
10022
  },
9566
- [applyStatePatch, applyBackgroundTimelineStatusUpdate]
10023
+ [
10024
+ applyStatePatch,
10025
+ applyBackgroundTimelineStatusUpdate,
10026
+ conversationListQuery,
10027
+ recordLocalMessages
10028
+ ]
9567
10029
  );
9568
10030
  const handleBackgroundActivityStreamEvents = useCallback4(
9569
10031
  (events) => {
@@ -9678,10 +10140,13 @@ function useAgentGUINodeController({
9678
10140
  const initialDisplayPrompt = displayPrompt && displayPrompt.trim() ? displayPrompt : void 0;
9679
10141
  const normalizedInitialPrompt = initialDisplayPrompt ?? agentPromptContentDisplayText(normalizedInitialContent);
9680
10142
  const initialConversationTitle = normalizedInitialPrompt || AGENT_PROVIDER_LABEL[data.provider];
10143
+ const submittedHomeDraftKey = nodeDefaultDraftContentKey(data.provider);
10144
+ const submittedHomeDraft = draftBySessionIdRef.current[submittedHomeDraftKey] ?? EMPTY_AGENT_COMPOSER_DRAFT;
9681
10145
  isCreatingConversationRef.current = true;
9682
10146
  setLocalIsCreatingConversation(true);
9683
10147
  setDetailError(null);
9684
10148
  let pendingCreateAgentSessionId = null;
10149
+ let pendingOptimisticConversation = null;
9685
10150
  void (async () => {
9686
10151
  const provider = data.provider;
9687
10152
  const currentData = dataRef.current.provider === provider ? dataRef.current : data;
@@ -9752,48 +10217,7 @@ function useAgentGUINodeController({
9752
10217
  sortTimeUnixMs: createdAtUnixMs,
9753
10218
  updatedAtUnixMs: createdAtUnixMs
9754
10219
  };
9755
- setTransientConversation(optimisticConversation);
9756
- isComposerHomeRef.current = false;
9757
- setIsComposerHome(false);
9758
- activeConversationIdRef.current = agentSessionId;
9759
- setActiveConversationId(agentSessionId);
9760
- setIntent({ tag: "active", id: agentSessionId });
9761
- persistActiveConversation(agentSessionId);
9762
- setAgentSessionViewMessagesLoading(
9763
- sessionViewRef(agentSessionId),
9764
- true
9765
- );
9766
- reportAgentSubmitTraceDiagnostic({
9767
- event: "optimistic_state_applied",
9768
- runtime: agentActivityRuntime,
9769
- trace: submitTrace,
9770
- workspaceId,
9771
- fields: { mode: "new" }
9772
- });
9773
- recordLocalMessages(agentSessionId, [
9774
- createOptimisticPromptMessage({
9775
- workspaceId,
9776
- agentSessionId,
9777
- turnId: createPendingOptimisticTurnId(submitTrace.clientSubmitId),
9778
- clientSubmitId: submitTrace.clientSubmitId,
9779
- userId: currentUserId?.trim() || "user",
9780
- prompt: normalizedInitialPrompt,
9781
- content: [...normalizedInitialContent],
9782
- occurredAtUnixMs: createdAtUnixMs
9783
- })
9784
- ]);
9785
- reportAgentSubmitTraceDiagnostic({
9786
- event: "optimistic_user_message_recorded",
9787
- runtime: agentActivityRuntime,
9788
- trace: submitTrace,
9789
- workspaceId,
9790
- fields: { mode: "new" }
9791
- });
9792
- scheduleAgentSubmitTracePaint({
9793
- runtime: agentActivityRuntime,
9794
- trace: submitTrace,
9795
- workspaceId
9796
- });
10220
+ pendingOptimisticConversation = optimisticConversation;
9797
10221
  if (conversationListQuery) {
9798
10222
  markAgentGUIConversationCreatePending({
9799
10223
  query: conversationListQuery,
@@ -9808,14 +10232,8 @@ function useAgentGUINodeController({
9808
10232
  };
9809
10233
  setDraftSettingsBySessionId((current) => ({
9810
10234
  ...current,
9811
- [agentSessionId]: effectiveInitialSettings
9812
- }));
9813
- setDraftBySessionId((current) => ({
9814
- ...current,
9815
- [nodeDefaultDraftContentKey(dataRef.current.provider)]: emptyAgentComposerDraft(),
9816
- [agentSessionId]: emptyAgentComposerDraft()
10235
+ [agentSessionId]: effectiveInitialSettings
9817
10236
  }));
9818
- setIsLoadingMessages(true);
9819
10237
  reportAgentSubmitTraceDiagnostic({
9820
10238
  event: "activation.requested",
9821
10239
  runtime: agentActivityRuntime,
@@ -9860,6 +10278,7 @@ function useAgentGUINodeController({
9860
10278
  }
9861
10279
  });
9862
10280
  }
10281
+ const activationFailed = result.activation.status === "failed" || result.session.status === "failed";
9863
10282
  if (conversationListQuery) {
9864
10283
  clearAgentGUIConversationCreatePending({
9865
10284
  query: conversationListQuery,
@@ -9867,6 +10286,23 @@ function useAgentGUINodeController({
9867
10286
  conversationId: agentSessionId
9868
10287
  });
9869
10288
  }
10289
+ if (activationFailed) {
10290
+ failedNewConversationIdsRef.current.add(agentSessionId);
10291
+ if (startingConversationIdRef.current === agentSessionId) {
10292
+ startingConversationIdRef.current = null;
10293
+ }
10294
+ if (isMountedRef.current && activeConversationIdRef.current === null && isComposerHomeRef.current) {
10295
+ setIsLoadingMessages(false);
10296
+ setAgentSessionViewMessagesLoading(
10297
+ sessionViewRef(agentSessionId),
10298
+ false
10299
+ );
10300
+ setDetailError(
10301
+ result.error?.message?.trim() || "Session activation failed."
10302
+ );
10303
+ }
10304
+ return;
10305
+ }
9870
10306
  const projectedConversation = conversationSummaryFromAgentSession(
9871
10307
  result.session,
9872
10308
  {
@@ -9874,66 +10310,102 @@ function useAgentGUINodeController({
9874
10310
  userProjects: userProjectsRef.current
9875
10311
  }
9876
10312
  );
9877
- const optimisticSortTimeUnixMs = transientConversationRef.current?.id === agentSessionId ? transientConversationRef.current.sortTimeUnixMs : void 0;
9878
10313
  const conversation2 = {
9879
10314
  ...projectedConversation,
9880
10315
  sortTimeUnixMs: Math.max(
9881
10316
  projectedConversation.sortTimeUnixMs ?? 0,
9882
- optimisticSortTimeUnixMs ?? 0
10317
+ pendingOptimisticConversation?.sortTimeUnixMs ?? 0
9883
10318
  )
9884
10319
  };
9885
10320
  failedNewConversationIdsRef.current.delete(conversation2.id);
9886
- if (startingConversationIdRef.current === agentSessionId) {
9887
- startingConversationIdRef.current = null;
9888
- }
10321
+ const isPendingCreatedConversation = startingConversationIdRef.current === agentSessionId;
9889
10322
  if (!isMountedRef.current) {
9890
10323
  void activation.unactivate(conversation2.id);
9891
- if (transientConversationRef.current?.id === agentSessionId) {
9892
- setTransientConversation(null);
10324
+ if (isPendingCreatedConversation) {
10325
+ startingConversationIdRef.current = null;
9893
10326
  }
9894
10327
  return;
9895
10328
  }
9896
- const shouldAttachCreatedConversation = activeConversationIdRef.current === null && isComposerHomeRef.current;
10329
+ const shouldAttachCreatedConversation = activeConversationIdRef.current === null && isComposerHomeRef.current && isPendingCreatedConversation;
9897
10330
  if (!shouldAttachCreatedConversation && unactivateIfStale(conversation2.id)) {
9898
- if (transientConversationRef.current?.id === agentSessionId) {
9899
- setTransientConversation(null);
10331
+ if (isPendingCreatedConversation) {
10332
+ startingConversationIdRef.current = null;
9900
10333
  }
9901
10334
  return;
9902
10335
  }
9903
- const activationFailed = result.activation.status === "failed" || result.session.status === "failed";
9904
- if (!activationFailed) {
9905
- activatedConversationIdsRef.current.add(conversation2.id);
10336
+ if (isPendingCreatedConversation) {
10337
+ startingConversationIdRef.current = null;
9906
10338
  }
10339
+ activatedConversationIdsRef.current.add(conversation2.id);
9907
10340
  setTransientConversation(conversation2);
9908
10341
  if (conversationListQuery) {
9909
- if (hasLoadedConversations) {
9910
- upsertLocalCreatedAgentGUIConversation({
9911
- query: conversationListQuery,
9912
- conversation: conversation2
9913
- });
9914
- }
10342
+ upsertLocalCreatedAgentGUIConversation({
10343
+ query: conversationListQuery,
10344
+ conversation: conversation2
10345
+ });
9915
10346
  scheduleAgentGUIConversationListProjection(
9916
10347
  conversationListQuery,
9917
10348
  "local-create"
9918
10349
  );
9919
10350
  }
10351
+ setAgentSessionViewMessagesLoading(
10352
+ sessionViewRef(conversation2.id),
10353
+ true
10354
+ );
10355
+ if (submitTrace) {
10356
+ reportAgentSubmitTraceDiagnostic({
10357
+ event: "optimistic_state_applied",
10358
+ runtime: agentActivityRuntime,
10359
+ trace: submitTrace,
10360
+ workspaceId,
10361
+ fields: { mode: "new" }
10362
+ });
10363
+ recordLocalMessages(conversation2.id, [
10364
+ createOptimisticPromptMessage({
10365
+ workspaceId,
10366
+ agentSessionId: conversation2.id,
10367
+ turnId: createPendingOptimisticTurnId(
10368
+ submitTrace.clientSubmitId
10369
+ ),
10370
+ clientSubmitId: submitTrace.clientSubmitId,
10371
+ userId: currentUserId?.trim() || "user",
10372
+ prompt: normalizedInitialPrompt,
10373
+ content: [...normalizedInitialContent],
10374
+ occurredAtUnixMs: pendingOptimisticConversation?.sortTimeUnixMs ?? Date.now()
10375
+ })
10376
+ ]);
10377
+ reportAgentSubmitTraceDiagnostic({
10378
+ event: "optimistic_user_message_recorded",
10379
+ runtime: agentActivityRuntime,
10380
+ trace: submitTrace,
10381
+ workspaceId,
10382
+ fields: { mode: "new" }
10383
+ });
10384
+ scheduleAgentSubmitTracePaint({
10385
+ runtime: agentActivityRuntime,
10386
+ trace: submitTrace,
10387
+ workspaceId
10388
+ });
10389
+ }
9920
10390
  isComposerHomeRef.current = false;
9921
10391
  setIsComposerHome(false);
9922
10392
  activeConversationIdRef.current = conversation2.id;
9923
10393
  setActiveConversationId(conversation2.id);
9924
- setDraftBySessionId((current) => ({
9925
- ...current,
9926
- [nodeDefaultDraftContentKey(dataRef.current.provider)]: emptyAgentComposerDraft(),
9927
- [conversation2.id]: emptyAgentComposerDraft()
9928
- }));
10394
+ setIntent({ tag: "active", id: conversation2.id });
10395
+ setDraftBySessionId((current) => {
10396
+ const currentHomeDraft = current[submittedHomeDraftKey] ?? EMPTY_AGENT_COMPOSER_DRAFT;
10397
+ const shouldClearHomeDraft = areAgentComposerDraftsEqual(
10398
+ currentHomeDraft,
10399
+ submittedHomeDraft
10400
+ );
10401
+ return {
10402
+ ...current,
10403
+ ...shouldClearHomeDraft ? { [submittedHomeDraftKey]: emptyAgentComposerDraft() } : {},
10404
+ [conversation2.id]: emptyAgentComposerDraft()
10405
+ };
10406
+ });
9929
10407
  persistActiveConversation(conversation2.id);
9930
- if (activationFailed) {
9931
- failedNewConversationIdsRef.current.add(conversation2.id);
9932
- setIsLoadingMessages(false);
9933
- void refreshMessagesFromSnapshot(conversation2.id);
9934
- void syncConversationListProjection(conversation2.id);
9935
- return;
9936
- }
10408
+ setIsLoadingMessages(true);
9937
10409
  void refreshMessagesFromSnapshot(conversation2.id);
9938
10410
  void loadSessionState(conversation2.id);
9939
10411
  void syncConversationListProjection(conversation2.id);
@@ -9946,7 +10418,7 @@ function useAgentGUINodeController({
9946
10418
  conversationId: pendingCreateAgentSessionId ?? agentSessionId
9947
10419
  });
9948
10420
  }
9949
- const shouldShowFailedConversation = startingConversationIdRef.current === agentSessionId || activeConversationIdRef.current === null && isComposerHomeRef.current;
10421
+ const shouldShowErrorOnHome = startingConversationIdRef.current === agentSessionId || activeConversationIdRef.current === null && isComposerHomeRef.current;
9950
10422
  const submitTrace = submitTraceBySessionIdRef.current[agentSessionId];
9951
10423
  if (submitTrace) {
9952
10424
  const nextTraces = { ...submitTraceBySessionIdRef.current };
@@ -9963,7 +10435,7 @@ function useAgentGUINodeController({
9963
10435
  }
9964
10436
  });
9965
10437
  }
9966
- if (!shouldShowFailedConversation && !isCurrentConversation(agentSessionId)) {
10438
+ if (!shouldShowErrorOnHome && !isCurrentConversation(agentSessionId)) {
9967
10439
  setAgentSessionViewMessagesLoading(
9968
10440
  sessionViewRef(agentSessionId),
9969
10441
  false
@@ -9985,36 +10457,13 @@ function useAgentGUINodeController({
9985
10457
  runtime: agentActivityRuntime,
9986
10458
  workspaceId
9987
10459
  });
9988
- const failedAtUnixMs = Date.now();
9989
- const failedConversation = {
9990
- id: agentSessionId,
9991
- provider: dataRef.current.provider,
9992
- title: initialConversationTitle,
9993
- titleFallback: null,
9994
- cwd: selectedProjectPathRef.current ?? "",
9995
- project: resolveAgentGUIConversationProject(
9996
- selectedProjectPathRef.current,
9997
- userProjectsRef.current,
9998
- { isNoProjectPath: isNoProjectPathRef.current }
9999
- ),
10000
- status: "failed",
10001
- sortTimeUnixMs: failedAtUnixMs,
10002
- updatedAtUnixMs: failedAtUnixMs
10003
- };
10004
10460
  failedNewConversationIdsRef.current.add(agentSessionId);
10005
- setTransientConversation(failedConversation);
10006
10461
  if (startingConversationIdRef.current === agentSessionId) {
10007
10462
  startingConversationIdRef.current = null;
10008
10463
  }
10009
- isComposerHomeRef.current = false;
10010
- setIsComposerHome(false);
10011
- activeConversationIdRef.current = agentSessionId;
10012
- setActiveConversationId(agentSessionId);
10013
- setDraftBySessionId((current) => ({
10014
- ...current,
10015
- [nodeDefaultDraftContentKey(dataRef.current.provider)]: emptyAgentComposerDraft(),
10016
- [agentSessionId]: emptyAgentComposerDraft()
10017
- }));
10464
+ if (transientConversationRef.current?.id === agentSessionId) {
10465
+ setTransientConversation(null);
10466
+ }
10018
10467
  setIsLoadingMessages(false);
10019
10468
  setAgentSessionViewMessagesLoading(
10020
10469
  sessionViewRef(agentSessionId),
@@ -10655,9 +11104,6 @@ function useAgentGUINodeController({
10655
11104
  startConversation
10656
11105
  ]
10657
11106
  );
10658
- const submitCompact = useCallback4(() => {
10659
- submitPrompt(textPromptContent("/compact"));
10660
- }, [submitPrompt]);
10661
11107
  useEffect4(() => {
10662
11108
  if (previewMode) {
10663
11109
  return;
@@ -10811,25 +11257,33 @@ function useAgentGUINodeController({
10811
11257
  void loadSessionState(agentSessionId);
10812
11258
  void syncConversationListProjection(agentSessionId);
10813
11259
  }).catch((error) => {
10814
- if (isCurrentConversation(agentSessionId)) {
10815
- reportAgentGUIRuntimeError({
10816
- agentSessionId,
10817
- error,
10818
- phase: "interrupt_current_turn",
10819
- provider: dataRef.current.provider,
10820
- runtime: agentActivityRuntime,
10821
- workspaceId
10822
- });
10823
- setSuppressedPromptRequestIdsBySessionId((current) => {
10824
- if (current[agentSessionId] !== activePendingPrompt?.requestId) {
10825
- return current;
10826
- }
10827
- const next = { ...current };
10828
- delete next[agentSessionId];
10829
- return next;
10830
- });
10831
- setDetailError(getAgentGUIErrorMessage(error));
11260
+ if (!isCurrentConversation(agentSessionId)) {
11261
+ return;
11262
+ }
11263
+ if (isAgentSessionNotReadyError(error)) {
11264
+ setPendingInterruptSessionIds((current) => ({
11265
+ ...current,
11266
+ [agentSessionId]: true
11267
+ }));
11268
+ return;
10832
11269
  }
11270
+ reportAgentGUIRuntimeError({
11271
+ agentSessionId,
11272
+ error,
11273
+ phase: "interrupt_current_turn",
11274
+ provider: dataRef.current.provider,
11275
+ runtime: agentActivityRuntime,
11276
+ workspaceId
11277
+ });
11278
+ setSuppressedPromptRequestIdsBySessionId((current) => {
11279
+ if (current[agentSessionId] !== activePendingPrompt?.requestId) {
11280
+ return current;
11281
+ }
11282
+ const next = { ...current };
11283
+ delete next[agentSessionId];
11284
+ return next;
11285
+ });
11286
+ setDetailError(getAgentGUIErrorMessage(error));
10833
11287
  }).finally(() => {
10834
11288
  setInterruptingSessionIds((current) => {
10835
11289
  if (!current[agentSessionId]) {
@@ -10854,9 +11308,50 @@ function useAgentGUINodeController({
10854
11308
  agentActivityRuntime
10855
11309
  ]
10856
11310
  );
11311
+ useEffect4(() => {
11312
+ const agentSessionId = activeConversationId;
11313
+ if (!agentSessionId || !pendingInterruptSessionIds[agentSessionId]) {
11314
+ return;
11315
+ }
11316
+ const status = agentActivityDisplayStatuses.get(agentSessionId) ?? null;
11317
+ const action = pendingInterruptActionForDisplayStatus(status);
11318
+ if (action === "wait") {
11319
+ return;
11320
+ }
11321
+ setPendingInterruptSessionIds((current) => {
11322
+ if (!current[agentSessionId]) {
11323
+ return current;
11324
+ }
11325
+ const next = { ...current };
11326
+ delete next[agentSessionId];
11327
+ return next;
11328
+ });
11329
+ if (action === "fire") {
11330
+ interruptCurrentTurn("");
11331
+ }
11332
+ }, [
11333
+ activeConversationId,
11334
+ agentActivityDisplayStatuses,
11335
+ pendingInterruptSessionIds,
11336
+ interruptCurrentTurn
11337
+ ]);
11338
+ useEffect4(() => {
11339
+ const activeId = activeConversationId;
11340
+ setPendingInterruptSessionIds((current) => {
11341
+ const ids = Object.keys(current);
11342
+ if (ids.length === 0 || ids.length === 1 && ids[0] === activeId) {
11343
+ return current;
11344
+ }
11345
+ return activeId && current[activeId] ? { [activeId]: true } : {};
11346
+ });
11347
+ }, [activeConversationId]);
10857
11348
  const updateDraftContent = useCallback4((draftContent2) => {
10858
11349
  const agentSessionId = activeConversationIdRef.current;
10859
11350
  const draftKey = agentSessionId ?? nodeDefaultDraftContentKey(dataRef.current.provider);
11351
+ draftBySessionIdRef.current = {
11352
+ ...draftBySessionIdRef.current,
11353
+ [draftKey]: draftContent2
11354
+ };
10860
11355
  setDraftBySessionId((current) => ({
10861
11356
  ...current,
10862
11357
  [draftKey]: draftContent2
@@ -12132,6 +12627,7 @@ function useAgentGUINodeController({
12132
12627
  const pendingApproval = hasProviderSessionNotFoundError || isActivePromptSuppressed ? null : rawPendingApproval;
12133
12628
  const serverInteractivePrompt = hasProviderSessionNotFoundError || isActivePromptSuppressed ? null : rawPendingInteractivePrompt;
12134
12629
  const isInterrupting = activeConversationId !== null && Boolean(interruptingSessionIds[activeConversationId]);
12630
+ const isCancelPending = activeConversationId !== null && Boolean(pendingInterruptSessionIds[activeConversationId]);
12135
12631
  const queuedPrompts = useMemo4(
12136
12632
  () => activeConversationId !== null ? queuedPromptsBySessionId[activeConversationId] ?? [] : [],
12137
12633
  [activeConversationId, queuedPromptsBySessionId]
@@ -12392,15 +12888,15 @@ function useAgentGUINodeController({
12392
12888
  const stableRetryOpenclawGateway = useStableControllerEventCallback(
12393
12889
  ensureOpenclawGateway
12394
12890
  );
12395
- const stableSubmitCompact = useStableControllerEventCallback(submitCompact);
12396
- const stableDismissUsageAlert = useStableControllerEventCallback(dismissUsageAlert);
12891
+ const stableLoadOlderConversationMessages = useStableControllerEventCallback(
12892
+ loadOlderConversationMessages
12893
+ );
12397
12894
  const controllerActions = useMemo4(
12398
12895
  () => ({
12399
12896
  createConversation: stableCreateConversation,
12400
12897
  selectConversation: stableSelectConversation,
12401
12898
  submitPrompt: stableSubmitPrompt,
12402
- submitCompact: stableSubmitCompact,
12403
- dismissUsageAlert: stableDismissUsageAlert,
12899
+ loadOlderConversationMessages: stableLoadOlderConversationMessages,
12404
12900
  showPromptImagesUnsupported: stableShowPromptImagesUnsupported,
12405
12901
  submitApprovalOption: stableSubmitApprovalOption,
12406
12902
  submitInteractivePrompt: stableSubmitInteractivePrompt,
@@ -12430,9 +12926,9 @@ function useAgentGUINodeController({
12430
12926
  stableConfirmDeleteProjectConversations,
12431
12927
  stableContinueInNewConversation,
12432
12928
  stableCreateConversation,
12433
- stableDismissUsageAlert,
12434
12929
  stableEditQueuedPrompt,
12435
12930
  stableInterruptCurrentTurn,
12931
+ stableLoadOlderConversationMessages,
12436
12932
  stableRemoveProject,
12437
12933
  stableRemoveQueuedPrompt,
12438
12934
  stableRequestDeleteConversation,
@@ -12443,7 +12939,6 @@ function useAgentGUINodeController({
12443
12939
  stableSendQueuedPromptNext,
12444
12940
  stableShowPromptImagesUnsupported,
12445
12941
  stableSubmitApprovalOption,
12446
- stableSubmitCompact,
12447
12942
  stableSubmitInteractivePrompt,
12448
12943
  stableSubmitPrompt,
12449
12944
  stableToggleConversationPinned,
@@ -12469,14 +12964,16 @@ function useAgentGUINodeController({
12469
12964
  draftContent,
12470
12965
  isLoadingConversations,
12471
12966
  isLoadingMessages,
12967
+ isLoadingOlderMessages: activeSessionView?.isLoadingOlderMessages ?? false,
12968
+ hasOlderMessages: activeSessionView?.hasOlderMessages ?? false,
12472
12969
  isCreatingConversation,
12473
12970
  isSubmitting,
12474
12971
  isInterrupting,
12972
+ isCancelPending,
12475
12973
  isRespondingApproval,
12476
12974
  promptImagesSupported,
12477
12975
  compactSupported,
12478
12976
  usage,
12479
- usageAlert,
12480
12977
  listError,
12481
12978
  isDeletingConversation,
12482
12979
  isDeletingProjectConversations,
@@ -12529,11 +13026,12 @@ function useAgentGUINodeController({
12529
13026
  promptImagesSupported,
12530
13027
  compactSupported,
12531
13028
  usage,
12532
- usageAlert,
12533
- dismissUsageAlert,
12534
13029
  isInterrupting,
13030
+ isCancelPending,
12535
13031
  isLoadingConversations,
12536
13032
  isLoadingMessages,
13033
+ activeSessionView?.hasOlderMessages,
13034
+ activeSessionView?.isLoadingOlderMessages,
12537
13035
  isRespondingApproval,
12538
13036
  listError,
12539
13037
  isDeletingConversation,
@@ -12726,7 +13224,7 @@ import {
12726
13224
  } from "react";
12727
13225
  import { useSnapshot } from "valtio";
12728
13226
  import { proxy as proxy2 } from "valtio/vanilla";
12729
- import { ChevronRight as ChevronRight2, ExternalLink, Info, X as X2 } from "lucide-react";
13227
+ import { ChevronRight as ChevronRight2, ExternalLink, Info, Wrench } from "lucide-react";
12730
13228
  import {
12731
13229
  ReferenceSourcePicker,
12732
13230
  WorkspaceFileReferencePicker
@@ -12738,12 +13236,13 @@ import {
12738
13236
  TooltipTrigger as TooltipTrigger3,
12739
13237
  NewWorkspaceLinedIcon as NewWorkspaceLinedIcon2,
12740
13238
  ConfirmationDialog,
13239
+ toastVariants,
12741
13240
  cn as cn3
12742
13241
  } from "@tutti-os/ui-system";
12743
13242
  import { WorkspaceUserProjectSelect as WorkspaceUserProjectSelect2 } from "@tutti-os/workspace-user-project/ui";
12744
13243
  import { BareIconButton, ScrollArea } from "@tutti-os/ui-system/components";
12745
13244
  import {
12746
- EditIcon,
13245
+ CreateChatIcon,
12747
13246
  FolderIcon as FolderIcon2,
12748
13247
  MoreHorizontalIcon
12749
13248
  } from "@tutti-os/ui-system/icons";
@@ -14644,13 +15143,10 @@ var styles3 = {
14644
15143
  conversationUnreadLamp: "agent-gui-node__conversation-unread-lamp",
14645
15144
  detailHeader: "agent-gui-node__detail-header",
14646
15145
  detailHeaderPathInfo: "agent-gui-node__detail-header-path-info",
14647
- usageAlertBanner: "agent-gui-node__usage-alert-banner",
14648
- usageAlertMessage: "agent-gui-node__usage-alert-message",
14649
- usageAlertActions: "agent-gui-node__usage-alert-actions",
14650
- usageAlertDismiss: "agent-gui-node__usage-alert-dismiss",
14651
15146
  providerSetupNotice: "agent-gui-node__provider-setup-notice",
14652
15147
  providerSetupNoticeIcon: "agent-gui-node__provider-setup-notice-icon",
14653
15148
  providerSetupNoticeText: "agent-gui-node__provider-setup-notice-text",
15149
+ providerSetupNoticeAction: "agent-gui-node__provider-setup-notice-action",
14654
15150
  detailHeaderTitle: "agent-gui-node__detail-header-title",
14655
15151
  detailHeaderTitleGroup: "agent-gui-node__detail-header-title-group",
14656
15152
  detail: "agent-gui-node__detail",
@@ -17350,6 +17846,10 @@ var ComposerFloatingMenuSurface = forwardRef4(function ComposerFloatingMenuSurfa
17350
17846
  );
17351
17847
  });
17352
17848
 
17849
+ // agent-gui/agentGuiNode/model/agentUsageThresholds.ts
17850
+ var USAGE_WARN_PERCENT = 80;
17851
+ var USAGE_CRITICAL_PERCENT = 95;
17852
+
17353
17853
  // agent-gui/agentGuiNode/AgentComposer.tsx
17354
17854
  import { jsx as jsx26, jsxs as jsxs13 } from "react/jsx-runtime";
17355
17855
  function agentUsageChipLevel(percentUsed) {
@@ -19788,7 +20288,7 @@ function conversationMetaKind(conversation) {
19788
20288
  if (conversation.status === "failed") {
19789
20289
  return "failed";
19790
20290
  }
19791
- if (conversation.status === "completed" && conversation.hasUnreadCompletion) {
20291
+ if (conversation.hasUnreadCompletion) {
19792
20292
  return "unread-complete";
19793
20293
  }
19794
20294
  return "time";
@@ -19854,6 +20354,7 @@ function AttentionGlyph() {
19854
20354
  import { createRichTextMentionHref as createRichTextMentionHref2 } from "@tutti-os/ui-rich-text/core";
19855
20355
  import { Fragment as Fragment8, jsx as jsx29, jsxs as jsxs15 } from "react/jsx-runtime";
19856
20356
  var AGENT_GUI_STICK_TO_BOTTOM_THRESHOLD_PX = 24;
20357
+ var AGENT_GUI_TOP_HISTORY_PREFETCH_THRESHOLD_PX = 240;
19857
20358
  var AGENT_GUI_CONVERSATION_RAIL_SECTION_PAGE_SIZE = 5;
19858
20359
  var AGENT_GUI_TIMELINE_SCROLL_AREA_CONTENT_STYLE = {
19859
20360
  width: "100%",
@@ -20123,7 +20624,7 @@ function AgentGUINodeView({
20123
20624
  if (previewMode) {
20124
20625
  return emptyReferencePickResult;
20125
20626
  }
20126
- if (!workspaceFileReferenceAdapter && !referenceSourceAggregator || !workspaceFileReferenceCopy) {
20627
+ if (!workspaceFileReferenceAdapter && !referenceSourceAggregator) {
20127
20628
  return emptyReferencePickResult;
20128
20629
  }
20129
20630
  const target = entity && referenceSourceAggregator ? resolveMentionReferenceTarget?.(entity) ?? null : referenceSourceAggregator ? resolveWorkspaceReferenceInitialTarget?.({
@@ -20402,6 +20903,9 @@ function AgentGUINodeView({
20402
20903
  "--agent-gui-detail-min-width": `${detailMinWidthPx}px`,
20403
20904
  gridTemplateColumns: conversationRailCollapsed ? "0 minmax(var(--agent-gui-detail-min-width), 1fr)" : "var(--agent-gui-conversation-rail-width) minmax(var(--agent-gui-detail-min-width), 1fr)"
20404
20905
  };
20906
+ const openAgentEnvSetup = useCallback10(() => {
20907
+ openAgentEnvPanel({ provider: viewModel.data.provider, focus: null });
20908
+ }, [viewModel.data.provider]);
20405
20909
  const conversationRailStoreState = useMemo10(
20406
20910
  () => ({
20407
20911
  activeConversationId: viewModel.activeConversationId,
@@ -20418,6 +20922,7 @@ function AgentGUINodeView({
20418
20922
  openclawGateway,
20419
20923
  isCollapsed: conversationRailCollapsed,
20420
20924
  onCreateConversation: requestCreateConversation,
20925
+ onOpenAgentEnvSetup: openAgentEnvSetup,
20421
20926
  onRetryOpenclawGateway: retryOpenclawGateway,
20422
20927
  onSelectConversation: selectConversation,
20423
20928
  onToggleConversationPinned: toggleConversationPinned,
@@ -20436,6 +20941,7 @@ function AgentGUINodeView({
20436
20941
  conversationRailCollapsed,
20437
20942
  createConversationDisabled,
20438
20943
  labels,
20944
+ openAgentEnvSetup,
20439
20945
  openConversationWindow,
20440
20946
  openProjectFiles,
20441
20947
  openclawGateway,
@@ -20665,6 +21171,7 @@ var AgentGUIDetailPane = memo(function AgentGUIDetailPane2({
20665
21171
  const timelineRef = useRef13(null);
20666
21172
  const bottomDockRef = useRef13(null);
20667
21173
  const timelineScrollAnchorRef = useRef13(null);
21174
+ const pendingPrependScrollAnchorRef = useRef13(null);
20668
21175
  const [
20669
21176
  bottomDockDismissedPromptRequestId,
20670
21177
  setBottomDockDismissedPromptRequestId
@@ -20801,16 +21308,20 @@ var AgentGUIDetailPane = memo(function AgentGUIDetailPane2({
20801
21308
  approvalRequired: labels.approvalRequired,
20802
21309
  authRequired: labels.authRequired,
20803
21310
  authLogin: labels.authLogin,
20804
- activatingSession: labels.activatingSession,
21311
+ // While connecting, if the user already requested a cancel that is waiting
21312
+ // for the session to come up, show "cancelling" instead of "connecting".
21313
+ activatingSession: viewModel.isCancelPending ? labels.cancellingSession : labels.activatingSession,
20805
21314
  retryActivation: labels.retryActivation,
20806
21315
  continueInNewConversation: labels.continueInNewConversation
20807
21316
  }),
20808
21317
  [
20809
21318
  labels.activatingSession,
21319
+ labels.cancellingSession,
20810
21320
  labels.approvalRequired,
20811
21321
  labels.authRequired,
20812
21322
  labels.continueInNewConversation,
20813
- labels.retryActivation
21323
+ labels.retryActivation,
21324
+ viewModel.isCancelPending
20814
21325
  ]
20815
21326
  );
20816
21327
  const goalBannerLabels = useMemo10(
@@ -21060,10 +21571,6 @@ var AgentGUIDetailPane = memo(function AgentGUIDetailPane2({
21060
21571
  const handleInterruptCurrentTurn = useCallback10(() => {
21061
21572
  actions.interruptCurrentTurn(labels.noRunningResponse);
21062
21573
  }, [actions.interruptCurrentTurn, labels.noRunningResponse]);
21063
- const handleUsageAlertCompact = useCallback10(() => {
21064
- actions.submitCompact();
21065
- actions.dismissUsageAlert();
21066
- }, [actions]);
21067
21574
  const submitApprovalOption = useStableEventCallback2(
21068
21575
  actions.submitApprovalOption
21069
21576
  );
@@ -21269,10 +21776,25 @@ var AgentGUIDetailPane = memo(function AgentGUIDetailPane2({
21269
21776
  timeline.scrollHeight - timeline.clientHeight
21270
21777
  );
21271
21778
  const anchor = timelineScrollAnchorRef.current;
21779
+ const prependAnchor = pendingPrependScrollAnchorRef.current;
21272
21780
  let nextScrollTop = timeline.scrollTop;
21273
21781
  if (!anchor || anchor.conversationId !== activeConversationId) {
21274
21782
  timeline.scrollTop = maxScrollTop;
21275
21783
  nextScrollTop = maxScrollTop;
21784
+ } else if (prependAnchor?.conversationId === activeConversationId) {
21785
+ const nextScrollHeight = timeline.scrollHeight;
21786
+ const delta = nextScrollHeight - prependAnchor.scrollHeight;
21787
+ nextScrollTop = Math.max(0, prependAnchor.scrollTop + delta);
21788
+ timeline.scrollTop = nextScrollTop;
21789
+ if (viewModel.isLoadingOlderMessages) {
21790
+ pendingPrependScrollAnchorRef.current = {
21791
+ conversationId: activeConversationId,
21792
+ scrollHeight: nextScrollHeight,
21793
+ scrollTop: nextScrollTop
21794
+ };
21795
+ } else {
21796
+ pendingPrependScrollAnchorRef.current = null;
21797
+ }
21276
21798
  } else {
21277
21799
  const distanceFromBottom = anchor.scrollHeight - anchor.scrollTop - anchor.clientHeight;
21278
21800
  if (distanceFromBottom <= AGENT_GUI_STICK_TO_BOTTOM_THRESHOLD_PX) {
@@ -21289,7 +21811,12 @@ var AgentGUIDetailPane = memo(function AgentGUIDetailPane2({
21289
21811
  scrollTop: nextScrollTop,
21290
21812
  clientHeight: timeline.clientHeight
21291
21813
  };
21292
- }, [conversation, showTimelineSkeleton, viewModel.activeConversationId]);
21814
+ }, [
21815
+ conversation,
21816
+ showTimelineSkeleton,
21817
+ viewModel.activeConversationId,
21818
+ viewModel.isLoadingOlderMessages
21819
+ ]);
21293
21820
  useLayoutEffect5(() => {
21294
21821
  const timeline = timelineRef.current;
21295
21822
  const bottomDock = bottomDockRef.current;
@@ -21355,13 +21882,26 @@ var AgentGUIDetailPane = memo(function AgentGUIDetailPane2({
21355
21882
  scrollTop: timeline.scrollTop,
21356
21883
  clientHeight: timeline.clientHeight
21357
21884
  };
21885
+ if (viewModel.hasOlderMessages && !viewModel.isLoadingOlderMessages && timeline.scrollTop <= AGENT_GUI_TOP_HISTORY_PREFETCH_THRESHOLD_PX) {
21886
+ pendingPrependScrollAnchorRef.current = {
21887
+ conversationId: activeConversationId,
21888
+ scrollHeight: timeline.scrollHeight,
21889
+ scrollTop: timeline.scrollTop
21890
+ };
21891
+ actions.loadOlderConversationMessages();
21892
+ }
21358
21893
  };
21359
21894
  captureScrollAnchor();
21360
21895
  timeline.addEventListener("scroll", captureScrollAnchor, { passive: true });
21361
21896
  return () => {
21362
21897
  timeline.removeEventListener("scroll", captureScrollAnchor);
21363
21898
  };
21364
- }, [viewModel.activeConversationId]);
21899
+ }, [
21900
+ actions,
21901
+ viewModel.activeConversationId,
21902
+ viewModel.hasOlderMessages,
21903
+ viewModel.isLoadingOlderMessages
21904
+ ]);
21365
21905
  return /* @__PURE__ */ jsxs15("main", { className: AgentGUINode_styles_default.detail, children: [
21366
21906
  /* @__PURE__ */ jsx29(
21367
21907
  AgentGUIDetailHeader,
@@ -21380,20 +21920,28 @@ var AgentGUIDetailPane = memo(function AgentGUIDetailPane2({
21380
21920
  showProviderSetupNotice ? /* @__PURE__ */ jsxs15(
21381
21921
  "div",
21382
21922
  {
21383
- className: AgentGUINode_styles_default.providerSetupNotice,
21923
+ className: cn3(
21924
+ toastVariants({ variant: "default" }),
21925
+ AgentGUINode_styles_default.providerSetupNotice
21926
+ ),
21927
+ "data-slot": "toast",
21384
21928
  "data-testid": "agent-gui-provider-setup-notice",
21385
21929
  role: "status",
21386
21930
  children: [
21931
+ /* @__PURE__ */ jsx29("span", { className: "inline-flex max-w-full items-center justify-center gap-[6px] text-center text-[13px] font-normal leading-normal", children: /* @__PURE__ */ jsx29("span", { className: "min-w-0 break-words", children: labels.installRequiredPlaceholder }) }),
21387
21932
  /* @__PURE__ */ jsx29(
21388
- Info,
21933
+ "button",
21389
21934
  {
21390
- "aria-hidden": "true",
21391
- className: AgentGUINode_styles_default.providerSetupNoticeIcon,
21392
- size: 15,
21393
- strokeWidth: 2
21935
+ type: "button",
21936
+ className: AgentGUINode_styles_default.providerSetupNoticeAction,
21937
+ "data-testid": "agent-gui-provider-setup-notice-action",
21938
+ onClick: () => openAgentEnvPanel({
21939
+ provider: viewModel.data.provider,
21940
+ focus: "detect"
21941
+ }),
21942
+ children: labels.installRequiredAction
21394
21943
  }
21395
- ),
21396
- /* @__PURE__ */ jsx29("span", { className: AgentGUINode_styles_default.providerSetupNoticeText, children: labels.installRequiredPlaceholder })
21944
+ )
21397
21945
  ]
21398
21946
  }
21399
21947
  ) : null,
@@ -21426,6 +21974,7 @@ var AgentGUIDetailPane = memo(function AgentGUIDetailPane2({
21426
21974
  {
21427
21975
  conversation,
21428
21976
  isLoading: showTimelineSkeleton,
21977
+ isLoadingOlderMessages: viewModel.isLoadingOlderMessages,
21429
21978
  loadingLabel: labels.loadingConversation,
21430
21979
  empty: conversationFlowEmpty,
21431
21980
  onLinkAction: stableLinkAction,
@@ -21444,12 +21993,6 @@ var AgentGUIDetailPane = memo(function AgentGUIDetailPane2({
21444
21993
  bottomDockRef,
21445
21994
  bottomDockLiftedPrompt,
21446
21995
  bottomDockReplacementPrompt,
21447
- usageAlert: viewModel.usageAlert,
21448
- usagePercent: viewModel.usage?.percentUsed ?? null,
21449
- usageAlertShowCompactAction: viewModel.compactSupported !== false,
21450
- usageAlertLabels: labels,
21451
- onUsageAlertCompact: handleUsageAlertCompact,
21452
- onUsageAlertDismiss: actions.dismissUsageAlert,
21453
21996
  store: bottomDockStore,
21454
21997
  storeRevision: bottomDockStoreRevision,
21455
21998
  keyboardShortcutsEnabled: isActive,
@@ -21615,57 +22158,6 @@ function EmptyHeroTitle({
21615
22158
  label.slice(providerEnd)
21616
22159
  ] });
21617
22160
  }
21618
- function AgentUsageAlertBanner({
21619
- tier,
21620
- percent,
21621
- showCompactAction,
21622
- labels,
21623
- onCompact,
21624
- onDismiss
21625
- }) {
21626
- "use memo";
21627
- const resolvedPercent = percent ?? (tier === "critical" ? USAGE_CRITICAL_PERCENT : USAGE_WARN_PERCENT);
21628
- const message = tier === "critical" ? labels.usageAlertCriticalMessage({ percent: resolvedPercent }) : labels.usageAlertWarnMessage({ percent: resolvedPercent });
21629
- return /* @__PURE__ */ jsxs15(
21630
- "div",
21631
- {
21632
- className: AgentGUINode_styles_default.usageAlertBanner,
21633
- "data-testid": "agent-gui-usage-alert",
21634
- "data-usage-alert-tier": tier,
21635
- role: tier === "critical" ? "alert" : "status",
21636
- children: [
21637
- /* @__PURE__ */ jsx29("span", { className: AgentGUINode_styles_default.usageAlertMessage, children: message }),
21638
- /* @__PURE__ */ jsxs15("span", { className: AgentGUINode_styles_default.usageAlertActions, children: [
21639
- tier === "critical" && showCompactAction ? /* @__PURE__ */ jsx29(
21640
- Button,
21641
- {
21642
- type: "button",
21643
- variant: "ghost",
21644
- size: "sm",
21645
- "data-testid": "agent-gui-usage-alert-compact",
21646
- onClick: onCompact,
21647
- children: labels.usageCompactAction
21648
- }
21649
- ) : null,
21650
- /* @__PURE__ */ jsx29(
21651
- Button,
21652
- {
21653
- type: "button",
21654
- variant: "ghost",
21655
- size: "icon-xs",
21656
- className: AgentGUINode_styles_default.usageAlertDismiss,
21657
- "data-testid": "agent-gui-usage-alert-dismiss",
21658
- "aria-label": labels.usageAlertDismiss,
21659
- title: labels.usageAlertDismiss,
21660
- onClick: onDismiss,
21661
- children: /* @__PURE__ */ jsx29(X2, { size: 14, strokeWidth: 2, "aria-hidden": "true" })
21662
- }
21663
- )
21664
- ] })
21665
- ]
21666
- }
21667
- );
21668
- }
21669
22161
  var AgentGUIBottomDockPane = memo(function AgentGUIBottomDockPane2({
21670
22162
  bottomDockRef,
21671
22163
  bottomDockLiftedPrompt,
@@ -21673,12 +22165,6 @@ var AgentGUIBottomDockPane = memo(function AgentGUIBottomDockPane2({
21673
22165
  store,
21674
22166
  storeRevision: _storeRevision,
21675
22167
  keyboardShortcutsEnabled,
21676
- usageAlert,
21677
- usagePercent,
21678
- usageAlertShowCompactAction,
21679
- usageAlertLabels,
21680
- onUsageAlertCompact,
21681
- onUsageAlertDismiss,
21682
22168
  chromeLabels,
21683
22169
  goalBannerLabels,
21684
22170
  promptLabels,
@@ -21730,17 +22216,6 @@ var AgentGUIBottomDockPane = memo(function AgentGUIBottomDockPane2({
21730
22216
  )
21731
22217
  }
21732
22218
  ) : null,
21733
- usageAlert ? /* @__PURE__ */ jsx29(
21734
- AgentUsageAlertBanner,
21735
- {
21736
- tier: usageAlert,
21737
- percent: usagePercent,
21738
- showCompactAction: usageAlertShowCompactAction,
21739
- labels: usageAlertLabels,
21740
- onCompact: onUsageAlertCompact,
21741
- onDismiss: onUsageAlertDismiss
21742
- }
21743
- ) : null,
21744
22219
  inlineNoticeChrome ? /* @__PURE__ */ jsx29(
21745
22220
  AgentSessionChrome,
21746
22221
  {
@@ -21809,7 +22284,7 @@ function syncAgentGUIConversationRailStore(store, next) {
21809
22284
  Object.assign(store, next);
21810
22285
  }
21811
22286
  function agentGUIConversationRailStoreSnapshotsEqual(current, next) {
21812
- 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;
22287
+ 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.onOpenAgentEnvSetup === next.onOpenAgentEnvSetup && 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;
21813
22288
  }
21814
22289
  var AgentGUIConversationRailStorePane = memo(
21815
22290
  function AgentGUIConversationRailStorePane2({
@@ -21898,7 +22373,7 @@ function stabilizeConversationSectionItems(previous, next) {
21898
22373
  return changed ? stable : previous;
21899
22374
  }
21900
22375
  function conversationSummariesRenderEqual2(left, right) {
21901
- 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);
22376
+ 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 && left.unreadCompletionKey === right.unreadCompletionKey && conversationProjectsRenderEqual2(left.project, right.project) && conversationSyncStatesRenderEqual(left.syncState, right.syncState);
21902
22377
  }
21903
22378
  function conversationSyncStatesRenderEqual(left, right) {
21904
22379
  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);
@@ -21924,6 +22399,7 @@ var AgentGUIConversationRailPane = memo(
21924
22399
  openclawGateway,
21925
22400
  isCollapsed,
21926
22401
  onCreateConversation,
22402
+ onOpenAgentEnvSetup,
21927
22403
  onRetryOpenclawGateway,
21928
22404
  onSelectConversation,
21929
22405
  onToggleConversationPinned,
@@ -22052,12 +22528,13 @@ var AgentGUIConversationRailPane = memo(
22052
22528
  {
22053
22529
  type: "button",
22054
22530
  variant: "secondary",
22531
+ size: "dialog",
22055
22532
  className: AgentGUINode_styles_default.newConversationIconButton,
22056
22533
  title: labels.newConversation,
22057
22534
  disabled: createConversationDisabled,
22058
22535
  onClick: () => onCreateConversation(),
22059
22536
  children: [
22060
- /* @__PURE__ */ jsx29(EditIcon, { "aria-hidden": "true" }),
22537
+ /* @__PURE__ */ jsx29(CreateChatIcon, { "aria-hidden": "true" }),
22061
22538
  /* @__PURE__ */ jsx29("span", { children: labels.newConversation })
22062
22539
  ]
22063
22540
  }
@@ -22147,6 +22624,21 @@ var AgentGUIConversationRailPane = memo(
22147
22624
  })
22148
22625
  }
22149
22626
  ),
22627
+ /* @__PURE__ */ jsx29("div", { className: "shrink-0 border-t border-[var(--border-1)] px-2 py-1.5", children: /* @__PURE__ */ jsxs15(
22628
+ Button,
22629
+ {
22630
+ type: "button",
22631
+ variant: "ghost",
22632
+ className: "flex w-full items-center justify-start gap-2 text-[13px] text-[var(--text-secondary)] hover:text-[var(--text-primary)]",
22633
+ title: labels.agentEnvSetup,
22634
+ disabled: previewMode,
22635
+ onClick: () => onOpenAgentEnvSetup(),
22636
+ children: [
22637
+ /* @__PURE__ */ jsx29(Wrench, { "aria-hidden": "true", size: 16, strokeWidth: 1.8 }),
22638
+ /* @__PURE__ */ jsx29("span", { children: labels.agentEnvSetup })
22639
+ ]
22640
+ }
22641
+ ) }),
22150
22642
  /* @__PURE__ */ jsx29(
22151
22643
  ConfirmationDialog,
22152
22644
  {
@@ -22282,34 +22774,65 @@ var AgentGUIConversationRailSection = memo(
22282
22774
  }
22283
22775
  ) : /* @__PURE__ */ jsx29("div", { className: AgentGUINode_styles_default.conversationSectionToggle, children: /* @__PURE__ */ jsx29("span", { className: AgentGUINode_styles_default.conversationSectionLabel, children: /* @__PURE__ */ jsx29("span", { children: section.label }) }) }),
22284
22776
  canCreateConversationFromSection ? /* @__PURE__ */ jsxs15("div", { className: AgentGUINode_styles_default.conversationSectionActions, children: [
22285
- /* @__PURE__ */ jsxs15("span", { className: AgentGUINode_styles_default.conversationSectionActionTooltipWrap, children: [
22286
- /* @__PURE__ */ jsx29(
22287
- BareIconButton,
22777
+ previewMode ? /* @__PURE__ */ jsx29("span", { className: AgentGUINode_styles_default.conversationSectionActionTooltipWrap, children: /* @__PURE__ */ jsx29(
22778
+ BareIconButton,
22779
+ {
22780
+ className: AgentGUINode_styles_default.conversationSectionMoreButton,
22781
+ "aria-label": createConversationLabel,
22782
+ size: "sm",
22783
+ disabled: createConversationDisabled,
22784
+ onClick: handleCreateConversation,
22785
+ children: /* @__PURE__ */ jsx29(CreateChatIcon, { "aria-hidden": "true" })
22786
+ }
22787
+ ) }) : /* @__PURE__ */ jsxs15(Tooltip3, { children: [
22788
+ /* @__PURE__ */ jsx29(TooltipTrigger3, { asChild: true, children: /* @__PURE__ */ jsx29(
22789
+ "span",
22288
22790
  {
22289
- className: AgentGUINode_styles_default.conversationSectionMoreButton,
22290
- "aria-label": createConversationLabel,
22291
- size: "sm",
22292
- disabled: createConversationDisabled,
22293
- onClick: handleCreateConversation,
22294
- children: /* @__PURE__ */ jsx29(EditIcon, { "aria-hidden": "true" })
22791
+ className: AgentGUINode_styles_default.conversationSectionActionTooltipWrap,
22792
+ children: /* @__PURE__ */ jsx29(
22793
+ BareIconButton,
22794
+ {
22795
+ className: AgentGUINode_styles_default.conversationSectionMoreButton,
22796
+ "aria-label": createConversationLabel,
22797
+ size: "sm",
22798
+ disabled: createConversationDisabled,
22799
+ onClick: handleCreateConversation,
22800
+ children: /* @__PURE__ */ jsx29(CreateChatIcon, { "aria-hidden": "true" })
22801
+ }
22802
+ )
22295
22803
  }
22296
- ),
22804
+ ) }),
22297
22805
  /* @__PURE__ */ jsx29(
22298
- "span",
22806
+ TooltipContent3,
22299
22807
  {
22300
- "aria-hidden": "true",
22808
+ side: "right",
22809
+ sideOffset: 6,
22301
22810
  className: AgentGUINode_styles_default.conversationSectionActionTooltip,
22302
22811
  children: createConversationLabel
22303
22812
  }
22304
22813
  )
22305
22814
  ] }),
22306
22815
  projectPath ? /* @__PURE__ */ jsxs15(DropdownMenu, { children: [
22307
- /* @__PURE__ */ jsx29(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs15(
22816
+ previewMode ? /* @__PURE__ */ jsx29(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx29(
22308
22817
  "span",
22309
22818
  {
22310
22819
  className: AgentGUINode_styles_default.conversationSectionActionTooltipWrap,
22311
- children: [
22312
- /* @__PURE__ */ jsx29(
22820
+ children: /* @__PURE__ */ jsx29(
22821
+ BareIconButton,
22822
+ {
22823
+ className: AgentGUINode_styles_default.conversationSectionMoreButton,
22824
+ "aria-label": labels.projectSectionMoreActions,
22825
+ size: "sm",
22826
+ children: /* @__PURE__ */ jsx29(MoreHorizontalIcon, { "aria-hidden": "true" })
22827
+ }
22828
+ )
22829
+ }
22830
+ ) }) : /* @__PURE__ */ jsxs15(Tooltip3, { children: [
22831
+ /* @__PURE__ */ jsx29(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx29(TooltipTrigger3, { asChild: true, children: /* @__PURE__ */ jsx29(
22832
+ "span",
22833
+ {
22834
+ className: AgentGUINode_styles_default.conversationSectionActionTooltipWrap,
22835
+ children: /* @__PURE__ */ jsx29(
22313
22836
  BareIconButton,
22314
22837
  {
22315
22838
  className: AgentGUINode_styles_default.conversationSectionMoreButton,
@@ -22317,18 +22840,19 @@ var AgentGUIConversationRailSection = memo(
22317
22840
  size: "sm",
22318
22841
  children: /* @__PURE__ */ jsx29(MoreHorizontalIcon, { "aria-hidden": "true" })
22319
22842
  }
22320
- ),
22321
- /* @__PURE__ */ jsx29(
22322
- "span",
22323
- {
22324
- "aria-hidden": "true",
22325
- className: AgentGUINode_styles_default.conversationSectionActionTooltip,
22326
- children: labels.projectSectionMoreActions
22327
- }
22328
22843
  )
22329
- ]
22330
- }
22331
- ) }),
22844
+ }
22845
+ ) }) }),
22846
+ /* @__PURE__ */ jsx29(
22847
+ TooltipContent3,
22848
+ {
22849
+ side: "right",
22850
+ sideOffset: 6,
22851
+ className: AgentGUINode_styles_default.conversationSectionActionTooltip,
22852
+ children: labels.projectSectionMoreActions
22853
+ }
22854
+ )
22855
+ ] }),
22332
22856
  /* @__PURE__ */ jsxs15(
22333
22857
  DropdownMenuContent,
22334
22858
  {
@@ -22672,6 +23196,7 @@ var AgentGUIConversationTimelinePane = memo(
22672
23196
  function AgentGUIConversationTimelinePane2({
22673
23197
  conversation,
22674
23198
  isLoading,
23199
+ isLoadingOlderMessages,
22675
23200
  loadingLabel,
22676
23201
  empty,
22677
23202
  onLinkAction,
@@ -22682,21 +23207,32 @@ var AgentGUIConversationTimelinePane = memo(
22682
23207
  labels
22683
23208
  }) {
22684
23209
  "use memo";
22685
- return /* @__PURE__ */ jsx29(
22686
- AgentConversationFlow,
22687
- {
22688
- conversation,
22689
- isLoading,
22690
- loadingLabel,
22691
- empty,
22692
- onLinkAction,
22693
- onAuthLogin,
22694
- availableSkills,
22695
- workspaceAppIcons,
22696
- previewMode,
22697
- labels
22698
- }
22699
- );
23210
+ return /* @__PURE__ */ jsxs15(Fragment8, { children: [
23211
+ isLoadingOlderMessages && !isLoading ? /* @__PURE__ */ jsx29(
23212
+ "div",
23213
+ {
23214
+ className: "mx-auto flex h-8 items-center justify-center text-[12px] text-[var(--text-secondary)]",
23215
+ "data-testid": "agent-gui-older-messages-loading",
23216
+ role: "status",
23217
+ children: /* @__PURE__ */ jsx29("span", { className: "tsh-inline-loading-ellipsis", children: loadingLabel })
23218
+ }
23219
+ ) : null,
23220
+ /* @__PURE__ */ jsx29(
23221
+ AgentConversationFlow,
23222
+ {
23223
+ conversation,
23224
+ isLoading,
23225
+ loadingLabel,
23226
+ empty,
23227
+ onLinkAction,
23228
+ onAuthLogin,
23229
+ availableSkills,
23230
+ workspaceAppIcons,
23231
+ previewMode,
23232
+ labels
23233
+ }
23234
+ )
23235
+ ] });
22700
23236
  }
22701
23237
  );
22702
23238
  function setTimelineScrollTopInstantly(element, top) {
@@ -23602,6 +24138,7 @@ var AgentGUINode = memo2(function AgentGUINode2({
23602
24138
  provider: displayProviderLabel
23603
24139
  }
23604
24140
  ),
24141
+ installRequiredAction: t("agentHost.agentGui.installRequiredAction"),
23605
24142
  collaboratorSessionReadOnlyPlaceholder: t(
23606
24143
  "agentHost.agentGui.collaboratorSessionReadOnlyPlaceholder"
23607
24144
  ),
@@ -23696,13 +24233,6 @@ var AgentGUINode = memo2(function AgentGUINode2({
23696
24233
  usageTokensLabel: t("agentHost.agentGui.usageTokensLabel"),
23697
24234
  usageLimitsLabel: t("agentHost.agentGui.usageLimitsLabel"),
23698
24235
  usageCompactAction: t("agentHost.agentGui.usageCompactAction"),
23699
- usageAlertWarnMessage: (input) => t("agentHost.agentGui.usageAlertWarnMessage", {
23700
- percent: input.percent
23701
- }),
23702
- usageAlertCriticalMessage: (input) => t("agentHost.agentGui.usageAlertCriticalMessage", {
23703
- percent: input.percent
23704
- }),
23705
- usageAlertDismiss: t("agentHost.agentGui.usageAlertDismiss"),
23706
24236
  planImplementationLead: t("agentHost.agentGui.planImplementationLead"),
23707
24237
  planImplementationConfirm: t(
23708
24238
  "agentHost.agentGui.planImplementationConfirm"
@@ -23717,6 +24247,7 @@ var AgentGUINode = memo2(function AgentGUINode2({
23717
24247
  emptyProvider: displayProviderLabel,
23718
24248
  conversations: t("agentHost.agentGui.conversations"),
23719
24249
  newConversation: t("agentHost.agentGui.newConversation"),
24250
+ agentEnvSetup: t("agentHost.agentGui.agentEnvSetup"),
23720
24251
  noConversations: t("agentHost.agentGui.noConversations"),
23721
24252
  emptyProjectConversations: t(
23722
24253
  "agentHost.agentGui.emptyProjectConversations"
@@ -23772,6 +24303,7 @@ var AgentGUINode = memo2(function AgentGUINode2({
23772
24303
  authRequired: t("agentHost.agentGui.authRequired"),
23773
24304
  authLogin: t("agentHost.agentGui.authLogin"),
23774
24305
  activatingSession: t("agentHost.agentGui.activatingSession"),
24306
+ cancellingSession: t("agentHost.agentGui.cancellingSession"),
23775
24307
  retryActivation: t("agentHost.agentGui.retryActivation"),
23776
24308
  continueInNewConversation: t(
23777
24309
  "agentHost.agentGui.continueInNewConversation"
@@ -24030,9 +24562,12 @@ var AgentGUINode = memo2(function AgentGUINode2({
24030
24562
  if (!managedAgent) {
24031
24563
  return true;
24032
24564
  }
24565
+ if (!managedAgentsState) {
24566
+ return true;
24567
+ }
24033
24568
  return resolveAgentHostManagedToolchainAgentAction(
24034
24569
  managedAgent,
24035
- managedAgentsState ?? null
24570
+ managedAgentsState
24036
24571
  ) === "installed";
24037
24572
  }, [activeProbeProvider, managedAgentsState]);
24038
24573
  const runtimeSlashStatusQuotas = useMemo11(