@tutti-os/agent-gui 0.0.32 → 0.0.34

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 (47) hide show
  1. package/README.md +5 -0
  2. package/dist/{AgentMessageMarkdown-Cp70W-Z2.d.ts → AgentMessageMarkdown-CU6gRvTm.d.ts} +53 -2
  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 +53 -76
  10. package/dist/{chunk-SGQ4C7JE.js → chunk-2ADVHCST.js} +2 -1
  11. package/dist/chunk-2ADVHCST.js.map +1 -0
  12. package/dist/{chunk-MFWZKYZ4.js → chunk-I5GHWK6X.js} +7 -11
  13. package/dist/chunk-I5GHWK6X.js.map +1 -0
  14. package/dist/{chunk-ZWYHKGQ7.js → chunk-NCS7X3XY.js} +11 -9
  15. package/dist/{chunk-ZWYHKGQ7.js.map → chunk-NCS7X3XY.js.map} +1 -1
  16. package/dist/{chunk-FG66NW5M.js → chunk-PVEFWBH6.js} +15 -2
  17. package/dist/chunk-PVEFWBH6.js.map +1 -0
  18. package/dist/{chunk-WPWZKKKV.js → chunk-RUO6SDNC.js} +2 -2
  19. package/dist/{chunk-OOQIJXCA.js → chunk-SV4UKLJO.js} +3 -2
  20. package/dist/{chunk-RDCBACWR.js → chunk-TCKH7GZ6.js} +5 -5
  21. package/dist/{chunk-RQ3HEF42.js → chunk-TQFYX7KY.js} +36 -6
  22. package/dist/chunk-TQFYX7KY.js.map +1 -0
  23. package/dist/{chunk-AFTAFV2C.js → chunk-XPVYQUFK.js} +78 -12
  24. package/dist/chunk-XPVYQUFK.js.map +1 -0
  25. package/dist/{chunk-J2DMJREM.js → chunk-ZR2LZVN6.js} +229 -21
  26. package/dist/chunk-ZR2LZVN6.js.map +1 -0
  27. package/dist/context-mention-palette/index.js +6 -6
  28. package/dist/i18n/index.d.ts +34 -4
  29. package/dist/i18n/index.js +1 -1
  30. package/dist/index.d.ts +5 -4
  31. package/dist/index.js +892 -199
  32. package/dist/index.js.map +1 -1
  33. package/dist/plan-decision-ops.d.ts +1 -1
  34. package/dist/workbench/contribution.js +1 -1
  35. package/dist/workbench/index.js +1 -1
  36. package/dist/workspace-agent-generated-files.js +2 -2
  37. package/dist/{workspaceLinkActions-CqWZaCY4.d.ts → workspaceLinkActions-C6POH38D.d.ts} +7 -1
  38. package/package.json +12 -12
  39. package/dist/chunk-AFTAFV2C.js.map +0 -1
  40. package/dist/chunk-FG66NW5M.js.map +0 -1
  41. package/dist/chunk-J2DMJREM.js.map +0 -1
  42. package/dist/chunk-MFWZKYZ4.js.map +0 -1
  43. package/dist/chunk-RQ3HEF42.js.map +0 -1
  44. package/dist/chunk-SGQ4C7JE.js.map +0 -1
  45. /package/dist/{chunk-WPWZKKKV.js.map → chunk-RUO6SDNC.js.map} +0 -0
  46. /package/dist/{chunk-OOQIJXCA.js.map → chunk-SV4UKLJO.js.map} +0 -0
  47. /package/dist/{chunk-RDCBACWR.js.map → chunk-TCKH7GZ6.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-TCKH7GZ6.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-J2DMJREM.js";
40
+ } from "./chunk-ZR2LZVN6.js";
40
41
  import {
41
42
  AgentMessageMarkdown,
42
43
  ZoomableImage,
43
44
  cn,
44
45
  resolveWorkspaceLinkAction
45
- } from "./chunk-AFTAFV2C.js";
46
+ } from "./chunk-XPVYQUFK.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-MFWZKYZ4.js";
54
+ } from "./chunk-I5GHWK6X.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-SV4UKLJO.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-RUO6SDNC.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-FG66NW5M.js";
82
+ } from "./chunk-PVEFWBH6.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-TQFYX7KY.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
 
@@ -6255,6 +6489,7 @@ function nextUsageAlert(percentUsed, state) {
6255
6489
  var EMPTY_AGENT_GUI_MESSAGES = [];
6256
6490
  var EMPTY_AGENT_GUI_AVAILABLE_COMMANDS = [];
6257
6491
  var ACTIVITY_STREAM_STATE_RELOAD_DEBOUNCE_MS = 150;
6492
+ var AGENT_GUI_DETAIL_MESSAGES_PAGE_SIZE = 100;
6258
6493
  function mergeAgentModelCatalogInvalidationEvents(events) {
6259
6494
  const providers = /* @__PURE__ */ new Set();
6260
6495
  let occurredAtUnixMs = 0;
@@ -6576,6 +6811,16 @@ function isNonRetryableResumeErrorCode(code) {
6576
6811
  function isSessionNotFoundErrorCode(code) {
6577
6812
  return code === AGENT_SESSION_NOT_FOUND_ERROR;
6578
6813
  }
6814
+ var WORKSPACE_AGENT_SESSION_NOT_READY_REASON = "workspace_agent_session_not_found";
6815
+ function isAgentSessionNotReadyError(error) {
6816
+ if (error && typeof error === "object") {
6817
+ const reason = error.reason;
6818
+ if (reason === WORKSPACE_AGENT_SESSION_NOT_READY_REASON) {
6819
+ return true;
6820
+ }
6821
+ }
6822
+ return getAgentGUIRawErrorMessage(error)?.trim() === "workspace agent session not found";
6823
+ }
6579
6824
  function isSettingsRequireNewSessionErrorCode(code) {
6580
6825
  return code === AGENT_SETTINGS_REQUIRE_NEW_SESSION_ERROR;
6581
6826
  }
@@ -6767,7 +7012,7 @@ function conversationSummariesRenderEqual(left, right) {
6767
7012
  return left.id === right.id && left.userId === right.userId && left.provider === right.provider && left.title === right.title && conversationTitleFallbacksRenderEqual(
6768
7013
  left.titleFallback,
6769
7014
  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);
7015
+ ) && 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
7016
  }
6772
7017
  function conversationTitleFallbacksRenderEqual(left, right) {
6773
7018
  return left === right || JSON.stringify(left ?? null) === JSON.stringify(right ?? null);
@@ -6820,10 +7065,58 @@ function sessionHasRenderableMessages(input) {
6820
7065
  const sessionView = getAgentSessionView(
6821
7066
  input.sessionViewRef(normalizedAgentSessionId)
6822
7067
  );
6823
- if ((sessionView?.overlayMessages?.length ?? 0) > 0) {
6824
- return true;
7068
+ return (sessionView?.detailMessages?.length ?? 0) > 0 || (sessionView?.overlayMessages?.length ?? 0) > 0;
7069
+ }
7070
+ function filterMessagesForDetailWindowOverlay(input) {
7071
+ if (input.localMessages.length === 0) {
7072
+ return [];
7073
+ }
7074
+ if (input.detailMessages.length === 0) {
7075
+ if (input.durableMessages.length <= AGENT_GUI_DETAIL_MESSAGES_PAGE_SIZE) {
7076
+ return [...input.localMessages];
7077
+ }
7078
+ const newestDurableVersion = maxFiniteMessageVersion(input.durableMessages);
7079
+ return input.localMessages.filter((message) => {
7080
+ if (isWorkspaceAgentActivityOptimisticMessage(message)) {
7081
+ return true;
7082
+ }
7083
+ return newestDurableVersion !== null && Number.isFinite(message.version) && message.version >= newestDurableVersion;
7084
+ });
7085
+ }
7086
+ const oldestDetailVersion = minFiniteMessageVersion(input.detailMessages);
7087
+ const newestDetailVersion = maxFiniteMessageVersion(input.detailMessages);
7088
+ return input.localMessages.filter((message) => {
7089
+ if (isWorkspaceAgentActivityOptimisticMessage(message)) {
7090
+ return true;
7091
+ }
7092
+ if (!Number.isFinite(message.version)) {
7093
+ return true;
7094
+ }
7095
+ if (newestDetailVersion !== null && message.version > newestDetailVersion) {
7096
+ return true;
7097
+ }
7098
+ return oldestDetailVersion !== null && message.version >= oldestDetailVersion;
7099
+ });
7100
+ }
7101
+ function minFiniteMessageVersion(messages) {
7102
+ let result = null;
7103
+ for (const message of messages) {
7104
+ if (!Number.isFinite(message.version)) {
7105
+ continue;
7106
+ }
7107
+ result = result === null ? message.version : Math.min(result, message.version);
7108
+ }
7109
+ return result;
7110
+ }
7111
+ function maxFiniteMessageVersion(messages) {
7112
+ let result = null;
7113
+ for (const message of messages) {
7114
+ if (!Number.isFinite(message.version)) {
7115
+ continue;
7116
+ }
7117
+ result = result === null ? message.version : Math.max(result, message.version);
6825
7118
  }
6826
- return (input.snapshotMessagesById[normalizedAgentSessionId]?.length ?? 0) > 0;
7119
+ return result;
6827
7120
  }
6828
7121
  function hasPromptConversationTitle2(conversation) {
6829
7122
  return resolveAgentGUIExplicitConversationTitle(conversation) !== null;
@@ -6864,7 +7157,7 @@ function hasSettledTimelineEvidence(timelineItems) {
6864
7157
  }
6865
7158
  return relevantItems.some((item) => {
6866
7159
  const normalizedStatus = normalizeOptionalWorkspaceAgentStatus({
6867
- status: item.status ?? stringPayloadValue(item.payload, "status")
7160
+ status: item.status ?? stringPayloadValue2(item.payload, "status")
6868
7161
  })?.kind;
6869
7162
  return item.role === "assistant" || normalizedStatus === "completed" || normalizedStatus === "ready" || normalizedStatus === "failed" || normalizedStatus === "canceled";
6870
7163
  });
@@ -6894,13 +7187,13 @@ function resolveConversationStatusFromTimelineEvidence(input) {
6894
7187
  function hasRejectedApprovalDecision(timelineItems) {
6895
7188
  return timelineItems.some((item) => {
6896
7189
  const payload = item.payload ?? {};
6897
- const callType = (item.callType ?? stringPayloadValue(payload, "callType") ?? "").trim().toLowerCase();
6898
- const toolName = (stringPayloadValue(payload, "toolName") ?? "").trim().toLowerCase();
7190
+ const callType = (item.callType ?? stringPayloadValue2(payload, "callType") ?? "").trim().toLowerCase();
7191
+ const toolName = (stringPayloadValue2(payload, "toolName") ?? "").trim().toLowerCase();
6899
7192
  if (callType !== "approval" && toolName !== "approval") {
6900
7193
  return false;
6901
7194
  }
6902
7195
  const status = normalizeOptionalWorkspaceAgentStatus({
6903
- status: item.status ?? stringPayloadValue(payload, "status")
7196
+ status: item.status ?? stringPayloadValue2(payload, "status")
6904
7197
  })?.kind;
6905
7198
  if (status !== "completed") {
6906
7199
  return false;
@@ -6910,7 +7203,7 @@ function hasRejectedApprovalDecision(timelineItems) {
6910
7203
  });
6911
7204
  }
6912
7205
  function isRejectedApprovalDecision(output) {
6913
- const selectedId = stringPayloadValue(output ?? void 0, "selectedId");
7206
+ const selectedId = stringPayloadValue2(output ?? void 0, "selectedId");
6914
7207
  if (!selectedId) {
6915
7208
  return false;
6916
7209
  }
@@ -6984,7 +7277,7 @@ function runtimeSessionSyncState(session) {
6984
7277
  }
6985
7278
  return syncState;
6986
7279
  }
6987
- function stringPayloadValue(value, key) {
7280
+ function stringPayloadValue2(value, key) {
6988
7281
  const nested = value?.[key];
6989
7282
  return typeof nested === "string" ? nested : void 0;
6990
7283
  }
@@ -7449,6 +7742,39 @@ function conversationStatusFromStatePatch(patch) {
7449
7742
  return null;
7450
7743
  }
7451
7744
  }
7745
+ function completionKeyFromStatePatch(agentSessionId, patch) {
7746
+ const turnId = patch.turn?.turnId?.trim() ?? "";
7747
+ if (turnId && isCompletedOutcomeToken(patch.turn?.outcome)) {
7748
+ return `turn:${agentSessionId}:${turnId}:completed`;
7749
+ }
7750
+ return conversationStatusFromStatePatch(patch) === "completed" ? `session:${agentSessionId}:completed` : null;
7751
+ }
7752
+ function completionKeyFromSessionState(agentSessionId, state) {
7753
+ return conversationStatusFromSessionState(state) === "completed" ? `session:${agentSessionId}:completed` : null;
7754
+ }
7755
+ function completionKeyFromMessage(message) {
7756
+ const agentSessionId = message.agentSessionId.trim();
7757
+ if (!agentSessionId) {
7758
+ return null;
7759
+ }
7760
+ if ((message.role ?? "").trim().toLowerCase() !== "assistant") {
7761
+ return null;
7762
+ }
7763
+ const kind = (message.kind ?? "").trim().toLowerCase();
7764
+ if (kind !== "message" && kind !== "text") {
7765
+ return null;
7766
+ }
7767
+ const payload = message.payload && typeof message.payload === "object" ? message.payload : {};
7768
+ const status = message.status?.trim().toLowerCase() || (stringPayloadValue2(payload, "status") ?? "").toLowerCase();
7769
+ if (!isCompletedOutcomeToken(status)) {
7770
+ return null;
7771
+ }
7772
+ const subject = message.turnId?.trim() || message.messageId.trim();
7773
+ return subject ? `turn:${agentSessionId}:${subject}:completed` : null;
7774
+ }
7775
+ function isCompletedOutcomeToken(value) {
7776
+ return value?.trim().toLowerCase() === "completed";
7777
+ }
7452
7778
  function hasSessionControlStatePatch(patch) {
7453
7779
  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
7780
  }
@@ -7565,7 +7891,7 @@ function conversationStatusFromTimelineItems(timelineItems) {
7565
7891
  }
7566
7892
  for (const item of latestByCallId.values()) {
7567
7893
  const status = normalizeTimelineStatus(
7568
- item.status ?? stringPayloadValue(item.payload, "status")
7894
+ item.status ?? stringPayloadValue2(item.payload, "status")
7569
7895
  );
7570
7896
  if (status === "working") {
7571
7897
  hasWorking = true;
@@ -7622,7 +7948,8 @@ function messageFromMessageUpdate(update) {
7622
7948
  ...isToolCall && update.title?.trim() ? { title: update.title.trim() } : {}
7623
7949
  },
7624
7950
  ...update.occurredAtUnixMs !== void 0 ? { occurredAtUnixMs: update.occurredAtUnixMs } : {},
7625
- ...update.startedAtUnixMs !== void 0 ? { startedAtUnixMs: update.startedAtUnixMs } : {}
7951
+ ...update.startedAtUnixMs !== void 0 ? { startedAtUnixMs: update.startedAtUnixMs } : {},
7952
+ ...update.completedAtUnixMs !== void 0 ? { completedAtUnixMs: update.completedAtUnixMs } : {}
7626
7953
  };
7627
7954
  }
7628
7955
  function normalizedPositiveNumber2(value) {
@@ -7729,6 +8056,7 @@ function useAgentGUINodeController({
7729
8056
  setQueuedPromptRetryBlockBySessionId
7730
8057
  ] = useState4({});
7731
8058
  const [interruptingSessionIds, setInterruptingSessionIds] = useState4({});
8059
+ const [pendingInterruptSessionIds, setPendingInterruptSessionIds] = useState4({});
7732
8060
  const [
7733
8061
  suppressedPromptRequestIdsBySessionId,
7734
8062
  setSuppressedPromptRequestIdsBySessionId
@@ -7860,17 +8188,21 @@ function useAgentGUINodeController({
7860
8188
  if (!normalizedAgentSessionId) {
7861
8189
  return EMPTY_AGENT_GUI_MESSAGES;
7862
8190
  }
7863
- return mergeWorkspaceAgentActivityDurableAndOverlayMessages({
7864
- durableMessages: agentActivitySnapshot.sessionMessagesById[normalizedAgentSessionId],
7865
- localMessages: getAgentSessionView(sessionViewRef(normalizedAgentSessionId))?.overlayMessages ?? EMPTY_AGENT_GUI_MESSAGES
7866
- });
8191
+ const sessionView = getAgentSessionView(
8192
+ sessionViewRef(normalizedAgentSessionId)
8193
+ );
8194
+ return mergeWorkspaceAgentMessages(
8195
+ sessionView?.detailMessages ?? EMPTY_AGENT_GUI_MESSAGES,
8196
+ sessionView?.overlayMessages ?? EMPTY_AGENT_GUI_MESSAGES
8197
+ );
7867
8198
  },
7868
- [agentActivitySnapshot.sessionMessagesById, sessionViewRef]
8199
+ [sessionViewRef]
7869
8200
  );
7870
8201
  const activeMessages = useMemo4(() => {
7871
8202
  return activeConversationId ? resolveSessionMessages(activeConversationId) : activeSessionView?.overlayMessages ?? EMPTY_AGENT_GUI_MESSAGES;
7872
8203
  }, [
7873
8204
  activeConversationId,
8205
+ activeSessionView?.detailMessages,
7874
8206
  activeSessionView?.overlayMessages,
7875
8207
  resolveSessionMessages
7876
8208
  ]);
@@ -8054,6 +8386,7 @@ function useAgentGUINodeController({
8054
8386
  conversations: []
8055
8387
  });
8056
8388
  const selectedConversationMessageLoadSeqRef = useRef4(0);
8389
+ const selectedConversationOlderMessageLoadSeqRef = useRef4(0);
8057
8390
  const selectedConversationPendingMessageLoadIdsRef = useRef4(
8058
8391
  /* @__PURE__ */ new Set()
8059
8392
  );
@@ -8396,6 +8729,9 @@ function useAgentGUINodeController({
8396
8729
  selectedConversationPendingMessageLoadIdsRef.current.add(
8397
8730
  normalizedAgentSessionId
8398
8731
  );
8732
+ resetAgentSessionViewDetailMessages(
8733
+ sessionViewRef(normalizedAgentSessionId)
8734
+ );
8399
8735
  setIsLoadingMessages(true);
8400
8736
  setAgentSessionViewMessagesLoading(
8401
8737
  sessionViewRef(normalizedAgentSessionId),
@@ -8718,12 +9054,30 @@ function useAgentGUINodeController({
8718
9054
  pendingTurnIdBySessionIdRef.current = nextPending;
8719
9055
  }
8720
9056
  const localMessages = getAgentSessionView(sessionViewRef(normalizedAgentSessionId))?.overlayMessages ?? [];
8721
- const overlayMessages = selectWorkspaceAgentActivityOverlayMessages({
9057
+ const detailMessages = getAgentSessionView(sessionViewRef(normalizedAgentSessionId))?.detailMessages ?? [];
9058
+ const durableTailMessages = detailMessages.length > 0 ? filterMessagesForDetailWindowOverlay({
9059
+ detailMessages,
9060
+ durableMessages,
9061
+ localMessages: durableMessages
9062
+ }) : [];
9063
+ const baseMessages = detailMessages.length > 0 ? mergeWorkspaceAgentMessages(detailMessages, durableTailMessages) : durableMessages;
9064
+ if (durableTailMessages.length > 0) {
9065
+ mergeAgentSessionViewDetailMessages(
9066
+ sessionViewRef(normalizedAgentSessionId),
9067
+ durableTailMessages
9068
+ );
9069
+ }
9070
+ const windowLocalMessages = filterMessagesForDetailWindowOverlay({
9071
+ detailMessages: baseMessages,
8722
9072
  durableMessages,
8723
9073
  localMessages
8724
9074
  });
9075
+ const overlayMessages = selectWorkspaceAgentActivityOverlayMessages({
9076
+ durableMessages: baseMessages,
9077
+ localMessages: windowLocalMessages
9078
+ });
8725
9079
  const mergedMessages = mergeWorkspaceAgentMessages(
8726
- durableMessages,
9080
+ baseMessages,
8727
9081
  overlayMessages
8728
9082
  );
8729
9083
  const mergedItems = projectAgentGUIMessagesToTimelineItems(mergedMessages);
@@ -8780,6 +9134,10 @@ function useAgentGUINodeController({
8780
9134
  if (!nextStatus && !title) {
8781
9135
  return;
8782
9136
  }
9137
+ const completionKey = completionKeyFromSessionState(
9138
+ agentSessionId,
9139
+ snapshot3
9140
+ );
8783
9141
  patchConversation(agentSessionId, (conversation2) => {
8784
9142
  const timelineItems = projectAgentGUIMessagesToTimelineItems(
8785
9143
  resolveSessionMessages(agentSessionId)
@@ -8807,13 +9165,15 @@ function useAgentGUINodeController({
8807
9165
  snapshotUpdatedAtUnixMs: shouldAdvanceConversationUpdatedAt ? snapshot3.updatedAtUnixMs : void 0,
8808
9166
  source: cause?.source
8809
9167
  }),
8810
- hasUnreadCompletion: status === "completed" ? conversation2.hasUnreadCompletion ?? false : false
9168
+ hasUnreadCompletion: status === "completed" ? conversation2.hasUnreadCompletion ?? false : false,
9169
+ unreadCompletionKey: status === "completed" ? conversation2.unreadCompletionKey ?? completionKey : null
8811
9170
  };
8812
9171
  });
8813
- if (nextStatus === "completed" && conversationListQuery) {
9172
+ if (completionKey && conversationListQuery) {
8814
9173
  markAgentGUIConversationCompletionObserved({
8815
9174
  query: conversationListQuery,
8816
- conversationId: agentSessionId
9175
+ conversationId: agentSessionId,
9176
+ completionKey
8817
9177
  });
8818
9178
  }
8819
9179
  const transient = transientConversationRef.current;
@@ -8844,7 +9204,8 @@ function useAgentGUINodeController({
8844
9204
  snapshotUpdatedAtUnixMs: shouldAdvanceConversationUpdatedAt ? snapshot3.updatedAtUnixMs : void 0,
8845
9205
  source: cause?.source
8846
9206
  }),
8847
- hasUnreadCompletion: transientStatus === "completed" && activeConversationIdRef.current !== agentSessionId
9207
+ hasUnreadCompletion: transientStatus === "completed" && activeConversationIdRef.current !== agentSessionId,
9208
+ unreadCompletionKey: transientStatus === "completed" ? transient.unreadCompletionKey ?? completionKey : null
8848
9209
  });
8849
9210
  }
8850
9211
  },
@@ -9035,9 +9396,15 @@ function useAgentGUINodeController({
9035
9396
  setIsLoadingMessages(true);
9036
9397
  }
9037
9398
  try {
9399
+ resetAgentSessionViewDetailMessages(
9400
+ sessionViewRef(normalizedAgentSessionId)
9401
+ );
9038
9402
  const page = await agentActivityRuntime.listSessionMessages({
9039
9403
  workspaceId,
9040
- agentSessionId: normalizedAgentSessionId
9404
+ agentSessionId: normalizedAgentSessionId,
9405
+ cache: false,
9406
+ limit: AGENT_GUI_DETAIL_MESSAGES_PAGE_SIZE,
9407
+ order: "desc"
9041
9408
  });
9042
9409
  if (!isMountedRef.current || activeConversationIdRef.current !== normalizedAgentSessionId || selectedConversationMessageLoadSeqRef.current !== requestId) {
9043
9410
  return;
@@ -9048,24 +9415,38 @@ function useAgentGUINodeController({
9048
9415
  clearSelectedConversationNotFoundRetryWhenInitialLoadsSettled(
9049
9416
  normalizedAgentSessionId
9050
9417
  );
9051
- const durableMessages = mergeWorkspaceAgentMessages(
9052
- [],
9053
- agentActivitySnapshotRef.current.sessionMessagesById[normalizedAgentSessionId] ?? []
9054
- );
9055
- const currentOverlayMessages = getAgentSessionView(sessionViewRef(normalizedAgentSessionId))?.overlayMessages ?? [];
9056
- const localMessages = mergeWorkspaceAgentMessages(
9057
- currentOverlayMessages,
9418
+ const currentDetailMessages = getAgentSessionView(sessionViewRef(normalizedAgentSessionId))?.detailMessages ?? [];
9419
+ const detailMessages = mergeWorkspaceAgentMessages(
9420
+ currentDetailMessages,
9058
9421
  page.messages
9059
9422
  );
9423
+ const currentOverlayMessages = getAgentSessionView(sessionViewRef(normalizedAgentSessionId))?.overlayMessages ?? [];
9424
+ const windowOverlayMessages = filterMessagesForDetailWindowOverlay({
9425
+ detailMessages,
9426
+ durableMessages: page.messages,
9427
+ localMessages: currentOverlayMessages
9428
+ });
9060
9429
  const overlayMessages = selectWorkspaceAgentActivityOverlayMessages({
9061
- durableMessages,
9062
- localMessages
9430
+ durableMessages: detailMessages,
9431
+ localMessages: windowOverlayMessages
9063
9432
  });
9433
+ setAgentSessionViewDetailMessages(
9434
+ sessionViewRef(normalizedAgentSessionId),
9435
+ detailMessages,
9436
+ {
9437
+ hasOlderMessages: page.hasMore && page.messages.length > 0,
9438
+ isLoadingOlderMessages: false
9439
+ }
9440
+ );
9064
9441
  setAgentSessionViewOverlayMessages(
9065
9442
  sessionViewRef(normalizedAgentSessionId),
9066
9443
  overlayMessages
9067
9444
  );
9068
- refreshMessagesFromSnapshot(normalizedAgentSessionId);
9445
+ setAgentSessionViewMessagesLoading(
9446
+ sessionViewRef(normalizedAgentSessionId),
9447
+ false
9448
+ );
9449
+ setIsLoadingMessages(false);
9069
9450
  } catch (error) {
9070
9451
  if (!isMountedRef.current || activeConversationIdRef.current !== normalizedAgentSessionId || selectedConversationMessageLoadSeqRef.current !== requestId) {
9071
9452
  return;
@@ -9095,12 +9476,94 @@ function useAgentGUINodeController({
9095
9476
  [
9096
9477
  agentActivityRuntime,
9097
9478
  clearSelectedConversationNotFoundRetryWhenInitialLoadsSettled,
9098
- refreshMessagesFromSnapshot,
9099
9479
  scheduleSelectedConversationNotFoundRetry,
9100
9480
  sessionViewRef,
9101
9481
  workspaceId
9102
9482
  ]
9103
9483
  );
9484
+ const loadOlderConversationMessages = useCallback4(
9485
+ async (agentSessionId) => {
9486
+ const normalizedAgentSessionId = (agentSessionId ?? activeConversationIdRef.current ?? "").trim();
9487
+ if (!normalizedAgentSessionId) {
9488
+ return;
9489
+ }
9490
+ const currentView = getAgentSessionView(
9491
+ sessionViewRef(normalizedAgentSessionId)
9492
+ );
9493
+ if (!currentView?.hasOlderMessages || currentView.isLoadingOlderMessages || currentView.oldestLoadedVersion === null || activeConversationIdRef.current !== normalizedAgentSessionId) {
9494
+ return;
9495
+ }
9496
+ const requestId = ++selectedConversationOlderMessageLoadSeqRef.current;
9497
+ setAgentSessionViewOlderMessagesLoading(
9498
+ sessionViewRef(normalizedAgentSessionId),
9499
+ true
9500
+ );
9501
+ try {
9502
+ const page = await agentActivityRuntime.listSessionMessages({
9503
+ workspaceId,
9504
+ agentSessionId: normalizedAgentSessionId,
9505
+ beforeVersion: currentView.oldestLoadedVersion,
9506
+ cache: false,
9507
+ limit: AGENT_GUI_DETAIL_MESSAGES_PAGE_SIZE,
9508
+ order: "desc"
9509
+ });
9510
+ if (!isMountedRef.current || activeConversationIdRef.current !== normalizedAgentSessionId || selectedConversationOlderMessageLoadSeqRef.current !== requestId) {
9511
+ setAgentSessionViewOlderMessagesLoading(
9512
+ sessionViewRef(normalizedAgentSessionId),
9513
+ false
9514
+ );
9515
+ return;
9516
+ }
9517
+ const nextDetailMessages = mergeWorkspaceAgentMessages(
9518
+ currentView.detailMessages,
9519
+ page.messages
9520
+ );
9521
+ const currentOverlayMessages = getAgentSessionView(sessionViewRef(normalizedAgentSessionId))?.overlayMessages ?? [];
9522
+ const windowOverlayMessages = filterMessagesForDetailWindowOverlay({
9523
+ detailMessages: nextDetailMessages,
9524
+ durableMessages: page.messages,
9525
+ localMessages: currentOverlayMessages
9526
+ });
9527
+ const overlayMessages = selectWorkspaceAgentActivityOverlayMessages({
9528
+ durableMessages: nextDetailMessages,
9529
+ localMessages: windowOverlayMessages
9530
+ });
9531
+ mergeAgentSessionViewDetailMessages(
9532
+ sessionViewRef(normalizedAgentSessionId),
9533
+ page.messages,
9534
+ {
9535
+ hasOlderMessages: page.hasMore && page.messages.length > 0,
9536
+ isLoadingOlderMessages: false
9537
+ }
9538
+ );
9539
+ setAgentSessionViewOverlayMessages(
9540
+ sessionViewRef(normalizedAgentSessionId),
9541
+ overlayMessages
9542
+ );
9543
+ } catch (error) {
9544
+ if (!isMountedRef.current || activeConversationIdRef.current !== normalizedAgentSessionId || selectedConversationOlderMessageLoadSeqRef.current !== requestId) {
9545
+ setAgentSessionViewOlderMessagesLoading(
9546
+ sessionViewRef(normalizedAgentSessionId),
9547
+ false
9548
+ );
9549
+ return;
9550
+ }
9551
+ reportAgentGUIRuntimeError({
9552
+ agentSessionId: normalizedAgentSessionId,
9553
+ error,
9554
+ phase: "load_session_messages",
9555
+ provider: dataRef.current.provider,
9556
+ runtime: agentActivityRuntime,
9557
+ workspaceId
9558
+ });
9559
+ setAgentSessionViewOlderMessagesLoading(
9560
+ sessionViewRef(normalizedAgentSessionId),
9561
+ false
9562
+ );
9563
+ }
9564
+ },
9565
+ [agentActivityRuntime, sessionViewRef, workspaceId]
9566
+ );
9104
9567
  const reloadSelectedConversation = useCallback4(
9105
9568
  (agentSessionId, options) => {
9106
9569
  if (!agentSessionId) {
@@ -9120,7 +9583,12 @@ function useAgentGUINodeController({
9120
9583
  const hadPendingMessageLoad = selectedConversationPendingMessageLoadIdsRef.current.delete(
9121
9584
  normalizedAgentSessionId
9122
9585
  );
9123
- if (hadPendingMessageLoad) {
9586
+ const hasRenderableMessages = sessionHasRenderableMessages({
9587
+ agentSessionId: normalizedAgentSessionId,
9588
+ sessionViewRef,
9589
+ snapshotMessagesById: agentActivitySnapshotRef.current.sessionMessagesById
9590
+ });
9591
+ if (hadPendingMessageLoad || !hasRenderableMessages) {
9124
9592
  void loadSelectedConversationMessages(normalizedAgentSessionId);
9125
9593
  } else {
9126
9594
  void refreshMessagesFromSnapshot(normalizedAgentSessionId);
@@ -9356,15 +9824,38 @@ function useAgentGUINodeController({
9356
9824
  return;
9357
9825
  }
9358
9826
  const currentMessages = getAgentSessionView(sessionViewRef(agentSessionId))?.overlayMessages ?? [];
9359
- const overlayMessages = selectWorkspaceAgentActivityOverlayMessages({
9360
- durableMessages: agentActivitySnapshot.sessionMessagesById[agentSessionId],
9827
+ const currentDetailMessages = getAgentSessionView(sessionViewRef(agentSessionId))?.detailMessages ?? [];
9828
+ const durableSnapshotMessages = agentActivitySnapshot.sessionMessagesById[agentSessionId] ?? [];
9829
+ const detailWindowMessages = filterMessagesForDetailWindowOverlay({
9830
+ detailMessages: currentDetailMessages,
9831
+ durableMessages: durableSnapshotMessages,
9832
+ localMessages: nextMessages
9833
+ });
9834
+ const nextDetailMessages = detailWindowMessages.length > 0 ? mergeWorkspaceAgentMessages(
9835
+ currentDetailMessages,
9836
+ detailWindowMessages
9837
+ ) : currentDetailMessages;
9838
+ if (detailWindowMessages.length > 0) {
9839
+ mergeAgentSessionViewDetailMessages(
9840
+ sessionViewRef(agentSessionId),
9841
+ detailWindowMessages
9842
+ );
9843
+ }
9844
+ const durableMessages = nextDetailMessages.length > 0 ? nextDetailMessages : durableSnapshotMessages;
9845
+ const nextLocalMessages = filterMessagesForDetailWindowOverlay({
9846
+ detailMessages: nextDetailMessages,
9847
+ durableMessages: durableSnapshotMessages,
9361
9848
  localMessages: mergeWorkspaceAgentMessages(
9362
9849
  currentMessages,
9363
9850
  nextMessages
9364
9851
  )
9365
9852
  });
9853
+ const overlayMessages = selectWorkspaceAgentActivityOverlayMessages({
9854
+ durableMessages,
9855
+ localMessages: nextLocalMessages
9856
+ });
9366
9857
  const mergedMessages = mergeWorkspaceAgentMessages(
9367
- agentActivitySnapshot.sessionMessagesById[agentSessionId] ?? [],
9858
+ durableMessages,
9368
9859
  overlayMessages
9369
9860
  );
9370
9861
  const nextItems = projectAgentGUIMessagesToTimelineItems(nextMessages);
@@ -9397,6 +9888,7 @@ function useAgentGUINodeController({
9397
9888
  }
9398
9889
  const normalizedLastError = patch.lastError?.trim() ?? "";
9399
9890
  const nextStatus = conversationStatusFromStatePatch(patch);
9891
+ const completionKey = completionKeyFromStatePatch(agentSessionId, patch);
9400
9892
  const hasStructuredTurnLifecycle = Boolean(patch.turn?.phase?.trim());
9401
9893
  const hasControlStatePatch = hasSessionControlStatePatch(patch);
9402
9894
  const pendingTurnId = pendingTurnIdBySessionIdRef.current[agentSessionId]?.trim() ?? "";
@@ -9435,7 +9927,7 @@ function useAgentGUINodeController({
9435
9927
  delete nextPending[agentSessionId];
9436
9928
  pendingTurnIdBySessionIdRef.current = nextPending;
9437
9929
  }
9438
- if (!nextStatus && !patch.title?.trim() && normalizedLastError === "" && !hasControlStatePatch && !clearedPendingSubmittedTurn) {
9930
+ if (!nextStatus && !completionKey && !patch.title?.trim() && normalizedLastError === "" && !hasControlStatePatch && !clearedPendingSubmittedTurn) {
9439
9931
  return;
9440
9932
  }
9441
9933
  if (hasControlStatePatch) {
@@ -9479,19 +9971,22 @@ function useAgentGUINodeController({
9479
9971
  timelineItems
9480
9972
  });
9481
9973
  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) {
9974
+ if (titleFields.title === conversation2.title && titleFields.titleFallback === conversation2.titleFallback && status === conversation2.status && hasUnreadCompletion === conversation2.hasUnreadCompletion && (!completionKey || conversation2.unreadCompletionKey === completionKey) && !clearedPendingSubmittedTurn) {
9483
9975
  return null;
9484
9976
  }
9485
9977
  return {
9486
9978
  ...titleFields,
9487
9979
  status,
9488
- hasUnreadCompletion
9980
+ hasUnreadCompletion,
9981
+ unreadCompletionKey: status === "completed" || completionKey ? conversation2.unreadCompletionKey ?? completionKey : null
9489
9982
  };
9490
9983
  });
9491
- if (nextStatus === "completed" && conversationListQuery) {
9984
+ if (completionKey && conversationListQuery) {
9492
9985
  markAgentGUIConversationCompletionObserved({
9493
9986
  query: conversationListQuery,
9494
- conversationId: agentSessionId
9987
+ conversationId: agentSessionId,
9988
+ completionKey,
9989
+ allowReadyStatus: nextStatus !== "completed"
9495
9990
  });
9496
9991
  }
9497
9992
  const transient = transientConversationRef.current;
@@ -9511,7 +10006,8 @@ function useAgentGUINodeController({
9511
10006
  ...transient,
9512
10007
  ...transientTitleFields,
9513
10008
  status: transientStatus,
9514
- hasUnreadCompletion: transientStatus === "completed" && activeConversationIdRef.current !== agentSessionId
10009
+ hasUnreadCompletion: Boolean(completionKey) && activeConversationIdRef.current !== agentSessionId,
10010
+ unreadCompletionKey: transientStatus === "completed" || completionKey ? transient.unreadCompletionKey ?? completionKey : null
9515
10011
  });
9516
10012
  }
9517
10013
  },
@@ -9529,14 +10025,26 @@ function useAgentGUINodeController({
9529
10025
  const handleActivityStreamEvents = useCallback4(
9530
10026
  (events) => {
9531
10027
  const pendingMessagesBySessionId = /* @__PURE__ */ new Map();
10028
+ const pendingCompletionKeysBySessionId = /* @__PURE__ */ new Map();
9532
10029
  const flushPendingMessages = () => {
9533
10030
  for (const [agentSessionId, messages] of pendingMessagesBySessionId) {
10031
+ recordLocalMessages(agentSessionId, messages);
9534
10032
  applyBackgroundTimelineStatusUpdate(
9535
10033
  agentSessionId,
9536
10034
  projectAgentGUIMessagesToTimelineItems(messages)
9537
10035
  );
10036
+ const completionKey = pendingCompletionKeysBySessionId.get(agentSessionId);
10037
+ if (completionKey && conversationListQuery) {
10038
+ markAgentGUIConversationCompletionObserved({
10039
+ query: conversationListQuery,
10040
+ conversationId: agentSessionId,
10041
+ completionKey,
10042
+ allowReadyStatus: true
10043
+ });
10044
+ }
9538
10045
  }
9539
10046
  pendingMessagesBySessionId.clear();
10047
+ pendingCompletionKeysBySessionId.clear();
9540
10048
  };
9541
10049
  for (const event of events) {
9542
10050
  if (event.eventType === "available_commands_update") {
@@ -9548,6 +10056,10 @@ function useAgentGUINodeController({
9548
10056
  if (!agentSessionId) {
9549
10057
  continue;
9550
10058
  }
10059
+ const completionKey = completionKeyFromMessage(message);
10060
+ if (completionKey) {
10061
+ pendingCompletionKeysBySessionId.set(agentSessionId, completionKey);
10062
+ }
9551
10063
  const messages = pendingMessagesBySessionId.get(agentSessionId);
9552
10064
  if (messages) {
9553
10065
  messages.push(message);
@@ -9563,7 +10075,12 @@ function useAgentGUINodeController({
9563
10075
  }
9564
10076
  flushPendingMessages();
9565
10077
  },
9566
- [applyStatePatch, applyBackgroundTimelineStatusUpdate]
10078
+ [
10079
+ applyStatePatch,
10080
+ applyBackgroundTimelineStatusUpdate,
10081
+ conversationListQuery,
10082
+ recordLocalMessages
10083
+ ]
9567
10084
  );
9568
10085
  const handleBackgroundActivityStreamEvents = useCallback4(
9569
10086
  (events) => {
@@ -9906,12 +10423,10 @@ function useAgentGUINodeController({
9906
10423
  }
9907
10424
  setTransientConversation(conversation2);
9908
10425
  if (conversationListQuery) {
9909
- if (hasLoadedConversations) {
9910
- upsertLocalCreatedAgentGUIConversation({
9911
- query: conversationListQuery,
9912
- conversation: conversation2
9913
- });
9914
- }
10426
+ upsertLocalCreatedAgentGUIConversation({
10427
+ query: conversationListQuery,
10428
+ conversation: conversation2
10429
+ });
9915
10430
  scheduleAgentGUIConversationListProjection(
9916
10431
  conversationListQuery,
9917
10432
  "local-create"
@@ -10811,25 +11326,33 @@ function useAgentGUINodeController({
10811
11326
  void loadSessionState(agentSessionId);
10812
11327
  void syncConversationListProjection(agentSessionId);
10813
11328
  }).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));
11329
+ if (!isCurrentConversation(agentSessionId)) {
11330
+ return;
10832
11331
  }
11332
+ if (isAgentSessionNotReadyError(error)) {
11333
+ setPendingInterruptSessionIds((current) => ({
11334
+ ...current,
11335
+ [agentSessionId]: true
11336
+ }));
11337
+ return;
11338
+ }
11339
+ reportAgentGUIRuntimeError({
11340
+ agentSessionId,
11341
+ error,
11342
+ phase: "interrupt_current_turn",
11343
+ provider: dataRef.current.provider,
11344
+ runtime: agentActivityRuntime,
11345
+ workspaceId
11346
+ });
11347
+ setSuppressedPromptRequestIdsBySessionId((current) => {
11348
+ if (current[agentSessionId] !== activePendingPrompt?.requestId) {
11349
+ return current;
11350
+ }
11351
+ const next = { ...current };
11352
+ delete next[agentSessionId];
11353
+ return next;
11354
+ });
11355
+ setDetailError(getAgentGUIErrorMessage(error));
10833
11356
  }).finally(() => {
10834
11357
  setInterruptingSessionIds((current) => {
10835
11358
  if (!current[agentSessionId]) {
@@ -10854,6 +11377,43 @@ function useAgentGUINodeController({
10854
11377
  agentActivityRuntime
10855
11378
  ]
10856
11379
  );
11380
+ useEffect4(() => {
11381
+ const agentSessionId = activeConversationId;
11382
+ if (!agentSessionId || !pendingInterruptSessionIds[agentSessionId]) {
11383
+ return;
11384
+ }
11385
+ const status = agentActivityDisplayStatuses.get(agentSessionId) ?? null;
11386
+ const action = pendingInterruptActionForDisplayStatus(status);
11387
+ if (action === "wait") {
11388
+ return;
11389
+ }
11390
+ setPendingInterruptSessionIds((current) => {
11391
+ if (!current[agentSessionId]) {
11392
+ return current;
11393
+ }
11394
+ const next = { ...current };
11395
+ delete next[agentSessionId];
11396
+ return next;
11397
+ });
11398
+ if (action === "fire") {
11399
+ interruptCurrentTurn("");
11400
+ }
11401
+ }, [
11402
+ activeConversationId,
11403
+ agentActivityDisplayStatuses,
11404
+ pendingInterruptSessionIds,
11405
+ interruptCurrentTurn
11406
+ ]);
11407
+ useEffect4(() => {
11408
+ const activeId = activeConversationId;
11409
+ setPendingInterruptSessionIds((current) => {
11410
+ const ids = Object.keys(current);
11411
+ if (ids.length === 0 || ids.length === 1 && ids[0] === activeId) {
11412
+ return current;
11413
+ }
11414
+ return activeId && current[activeId] ? { [activeId]: true } : {};
11415
+ });
11416
+ }, [activeConversationId]);
10857
11417
  const updateDraftContent = useCallback4((draftContent2) => {
10858
11418
  const agentSessionId = activeConversationIdRef.current;
10859
11419
  const draftKey = agentSessionId ?? nodeDefaultDraftContentKey(dataRef.current.provider);
@@ -12132,6 +12692,7 @@ function useAgentGUINodeController({
12132
12692
  const pendingApproval = hasProviderSessionNotFoundError || isActivePromptSuppressed ? null : rawPendingApproval;
12133
12693
  const serverInteractivePrompt = hasProviderSessionNotFoundError || isActivePromptSuppressed ? null : rawPendingInteractivePrompt;
12134
12694
  const isInterrupting = activeConversationId !== null && Boolean(interruptingSessionIds[activeConversationId]);
12695
+ const isCancelPending = activeConversationId !== null && Boolean(pendingInterruptSessionIds[activeConversationId]);
12135
12696
  const queuedPrompts = useMemo4(
12136
12697
  () => activeConversationId !== null ? queuedPromptsBySessionId[activeConversationId] ?? [] : [],
12137
12698
  [activeConversationId, queuedPromptsBySessionId]
@@ -12394,6 +12955,9 @@ function useAgentGUINodeController({
12394
12955
  );
12395
12956
  const stableSubmitCompact = useStableControllerEventCallback(submitCompact);
12396
12957
  const stableDismissUsageAlert = useStableControllerEventCallback(dismissUsageAlert);
12958
+ const stableLoadOlderConversationMessages = useStableControllerEventCallback(
12959
+ loadOlderConversationMessages
12960
+ );
12397
12961
  const controllerActions = useMemo4(
12398
12962
  () => ({
12399
12963
  createConversation: stableCreateConversation,
@@ -12401,6 +12965,7 @@ function useAgentGUINodeController({
12401
12965
  submitPrompt: stableSubmitPrompt,
12402
12966
  submitCompact: stableSubmitCompact,
12403
12967
  dismissUsageAlert: stableDismissUsageAlert,
12968
+ loadOlderConversationMessages: stableLoadOlderConversationMessages,
12404
12969
  showPromptImagesUnsupported: stableShowPromptImagesUnsupported,
12405
12970
  submitApprovalOption: stableSubmitApprovalOption,
12406
12971
  submitInteractivePrompt: stableSubmitInteractivePrompt,
@@ -12433,6 +12998,7 @@ function useAgentGUINodeController({
12433
12998
  stableDismissUsageAlert,
12434
12999
  stableEditQueuedPrompt,
12435
13000
  stableInterruptCurrentTurn,
13001
+ stableLoadOlderConversationMessages,
12436
13002
  stableRemoveProject,
12437
13003
  stableRemoveQueuedPrompt,
12438
13004
  stableRequestDeleteConversation,
@@ -12469,9 +13035,12 @@ function useAgentGUINodeController({
12469
13035
  draftContent,
12470
13036
  isLoadingConversations,
12471
13037
  isLoadingMessages,
13038
+ isLoadingOlderMessages: activeSessionView?.isLoadingOlderMessages ?? false,
13039
+ hasOlderMessages: activeSessionView?.hasOlderMessages ?? false,
12472
13040
  isCreatingConversation,
12473
13041
  isSubmitting,
12474
13042
  isInterrupting,
13043
+ isCancelPending,
12475
13044
  isRespondingApproval,
12476
13045
  promptImagesSupported,
12477
13046
  compactSupported,
@@ -12532,8 +13101,11 @@ function useAgentGUINodeController({
12532
13101
  usageAlert,
12533
13102
  dismissUsageAlert,
12534
13103
  isInterrupting,
13104
+ isCancelPending,
12535
13105
  isLoadingConversations,
12536
13106
  isLoadingMessages,
13107
+ activeSessionView?.hasOlderMessages,
13108
+ activeSessionView?.isLoadingOlderMessages,
12537
13109
  isRespondingApproval,
12538
13110
  listError,
12539
13111
  isDeletingConversation,
@@ -12726,7 +13298,7 @@ import {
12726
13298
  } from "react";
12727
13299
  import { useSnapshot } from "valtio";
12728
13300
  import { proxy as proxy2 } from "valtio/vanilla";
12729
- import { ChevronRight as ChevronRight2, ExternalLink, Info, X as X2 } from "lucide-react";
13301
+ import { ChevronRight as ChevronRight2, ExternalLink, Info, Wrench, X as X2 } from "lucide-react";
12730
13302
  import {
12731
13303
  ReferenceSourcePicker,
12732
13304
  WorkspaceFileReferencePicker
@@ -12738,12 +13310,13 @@ import {
12738
13310
  TooltipTrigger as TooltipTrigger3,
12739
13311
  NewWorkspaceLinedIcon as NewWorkspaceLinedIcon2,
12740
13312
  ConfirmationDialog,
13313
+ toastVariants,
12741
13314
  cn as cn3
12742
13315
  } from "@tutti-os/ui-system";
12743
13316
  import { WorkspaceUserProjectSelect as WorkspaceUserProjectSelect2 } from "@tutti-os/workspace-user-project/ui";
12744
13317
  import { BareIconButton, ScrollArea } from "@tutti-os/ui-system/components";
12745
13318
  import {
12746
- EditIcon,
13319
+ CreateChatIcon,
12747
13320
  FolderIcon as FolderIcon2,
12748
13321
  MoreHorizontalIcon
12749
13322
  } from "@tutti-os/ui-system/icons";
@@ -14651,6 +15224,7 @@ var styles3 = {
14651
15224
  providerSetupNotice: "agent-gui-node__provider-setup-notice",
14652
15225
  providerSetupNoticeIcon: "agent-gui-node__provider-setup-notice-icon",
14653
15226
  providerSetupNoticeText: "agent-gui-node__provider-setup-notice-text",
15227
+ providerSetupNoticeAction: "agent-gui-node__provider-setup-notice-action",
14654
15228
  detailHeaderTitle: "agent-gui-node__detail-header-title",
14655
15229
  detailHeaderTitleGroup: "agent-gui-node__detail-header-title-group",
14656
15230
  detail: "agent-gui-node__detail",
@@ -19788,7 +20362,7 @@ function conversationMetaKind(conversation) {
19788
20362
  if (conversation.status === "failed") {
19789
20363
  return "failed";
19790
20364
  }
19791
- if (conversation.status === "completed" && conversation.hasUnreadCompletion) {
20365
+ if (conversation.hasUnreadCompletion) {
19792
20366
  return "unread-complete";
19793
20367
  }
19794
20368
  return "time";
@@ -19854,6 +20428,7 @@ function AttentionGlyph() {
19854
20428
  import { createRichTextMentionHref as createRichTextMentionHref2 } from "@tutti-os/ui-rich-text/core";
19855
20429
  import { Fragment as Fragment8, jsx as jsx29, jsxs as jsxs15 } from "react/jsx-runtime";
19856
20430
  var AGENT_GUI_STICK_TO_BOTTOM_THRESHOLD_PX = 24;
20431
+ var AGENT_GUI_TOP_HISTORY_PREFETCH_THRESHOLD_PX = 240;
19857
20432
  var AGENT_GUI_CONVERSATION_RAIL_SECTION_PAGE_SIZE = 5;
19858
20433
  var AGENT_GUI_TIMELINE_SCROLL_AREA_CONTENT_STYLE = {
19859
20434
  width: "100%",
@@ -20123,7 +20698,7 @@ function AgentGUINodeView({
20123
20698
  if (previewMode) {
20124
20699
  return emptyReferencePickResult;
20125
20700
  }
20126
- if (!workspaceFileReferenceAdapter && !referenceSourceAggregator || !workspaceFileReferenceCopy) {
20701
+ if (!workspaceFileReferenceAdapter && !referenceSourceAggregator) {
20127
20702
  return emptyReferencePickResult;
20128
20703
  }
20129
20704
  const target = entity && referenceSourceAggregator ? resolveMentionReferenceTarget?.(entity) ?? null : referenceSourceAggregator ? resolveWorkspaceReferenceInitialTarget?.({
@@ -20192,7 +20767,6 @@ function AgentGUINodeView({
20192
20767
  workspaceId: viewModel.workspaceId,
20193
20768
  source: handle.source,
20194
20769
  ...handle.groupId?.trim() ? { groupId: handle.groupId.trim() } : {},
20195
- ...bundleIconUrl ? { icon: bundleIconUrl } : {},
20196
20770
  ...bundle.fileCount > 0 ? { count: String(bundle.fileCount) } : {}
20197
20771
  }
20198
20772
  }),
@@ -20403,6 +20977,9 @@ function AgentGUINodeView({
20403
20977
  "--agent-gui-detail-min-width": `${detailMinWidthPx}px`,
20404
20978
  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)"
20405
20979
  };
20980
+ const openAgentEnvSetup = useCallback10(() => {
20981
+ openAgentEnvPanel({ provider: viewModel.data.provider, focus: null });
20982
+ }, [viewModel.data.provider]);
20406
20983
  const conversationRailStoreState = useMemo10(
20407
20984
  () => ({
20408
20985
  activeConversationId: viewModel.activeConversationId,
@@ -20419,6 +20996,7 @@ function AgentGUINodeView({
20419
20996
  openclawGateway,
20420
20997
  isCollapsed: conversationRailCollapsed,
20421
20998
  onCreateConversation: requestCreateConversation,
20999
+ onOpenAgentEnvSetup: openAgentEnvSetup,
20422
21000
  onRetryOpenclawGateway: retryOpenclawGateway,
20423
21001
  onSelectConversation: selectConversation,
20424
21002
  onToggleConversationPinned: toggleConversationPinned,
@@ -20437,6 +21015,7 @@ function AgentGUINodeView({
20437
21015
  conversationRailCollapsed,
20438
21016
  createConversationDisabled,
20439
21017
  labels,
21018
+ openAgentEnvSetup,
20440
21019
  openConversationWindow,
20441
21020
  openProjectFiles,
20442
21021
  openclawGateway,
@@ -20666,6 +21245,7 @@ var AgentGUIDetailPane = memo(function AgentGUIDetailPane2({
20666
21245
  const timelineRef = useRef13(null);
20667
21246
  const bottomDockRef = useRef13(null);
20668
21247
  const timelineScrollAnchorRef = useRef13(null);
21248
+ const pendingPrependScrollAnchorRef = useRef13(null);
20669
21249
  const [
20670
21250
  bottomDockDismissedPromptRequestId,
20671
21251
  setBottomDockDismissedPromptRequestId
@@ -20802,16 +21382,20 @@ var AgentGUIDetailPane = memo(function AgentGUIDetailPane2({
20802
21382
  approvalRequired: labels.approvalRequired,
20803
21383
  authRequired: labels.authRequired,
20804
21384
  authLogin: labels.authLogin,
20805
- activatingSession: labels.activatingSession,
21385
+ // While connecting, if the user already requested a cancel that is waiting
21386
+ // for the session to come up, show "cancelling" instead of "connecting".
21387
+ activatingSession: viewModel.isCancelPending ? labels.cancellingSession : labels.activatingSession,
20806
21388
  retryActivation: labels.retryActivation,
20807
21389
  continueInNewConversation: labels.continueInNewConversation
20808
21390
  }),
20809
21391
  [
20810
21392
  labels.activatingSession,
21393
+ labels.cancellingSession,
20811
21394
  labels.approvalRequired,
20812
21395
  labels.authRequired,
20813
21396
  labels.continueInNewConversation,
20814
- labels.retryActivation
21397
+ labels.retryActivation,
21398
+ viewModel.isCancelPending
20815
21399
  ]
20816
21400
  );
20817
21401
  const goalBannerLabels = useMemo10(
@@ -21270,10 +21854,25 @@ var AgentGUIDetailPane = memo(function AgentGUIDetailPane2({
21270
21854
  timeline.scrollHeight - timeline.clientHeight
21271
21855
  );
21272
21856
  const anchor = timelineScrollAnchorRef.current;
21857
+ const prependAnchor = pendingPrependScrollAnchorRef.current;
21273
21858
  let nextScrollTop = timeline.scrollTop;
21274
21859
  if (!anchor || anchor.conversationId !== activeConversationId) {
21275
21860
  timeline.scrollTop = maxScrollTop;
21276
21861
  nextScrollTop = maxScrollTop;
21862
+ } else if (prependAnchor?.conversationId === activeConversationId) {
21863
+ const nextScrollHeight = timeline.scrollHeight;
21864
+ const delta = nextScrollHeight - prependAnchor.scrollHeight;
21865
+ nextScrollTop = Math.max(0, prependAnchor.scrollTop + delta);
21866
+ timeline.scrollTop = nextScrollTop;
21867
+ if (viewModel.isLoadingOlderMessages) {
21868
+ pendingPrependScrollAnchorRef.current = {
21869
+ conversationId: activeConversationId,
21870
+ scrollHeight: nextScrollHeight,
21871
+ scrollTop: nextScrollTop
21872
+ };
21873
+ } else {
21874
+ pendingPrependScrollAnchorRef.current = null;
21875
+ }
21277
21876
  } else {
21278
21877
  const distanceFromBottom = anchor.scrollHeight - anchor.scrollTop - anchor.clientHeight;
21279
21878
  if (distanceFromBottom <= AGENT_GUI_STICK_TO_BOTTOM_THRESHOLD_PX) {
@@ -21290,7 +21889,12 @@ var AgentGUIDetailPane = memo(function AgentGUIDetailPane2({
21290
21889
  scrollTop: nextScrollTop,
21291
21890
  clientHeight: timeline.clientHeight
21292
21891
  };
21293
- }, [conversation, showTimelineSkeleton, viewModel.activeConversationId]);
21892
+ }, [
21893
+ conversation,
21894
+ showTimelineSkeleton,
21895
+ viewModel.activeConversationId,
21896
+ viewModel.isLoadingOlderMessages
21897
+ ]);
21294
21898
  useLayoutEffect5(() => {
21295
21899
  const timeline = timelineRef.current;
21296
21900
  const bottomDock = bottomDockRef.current;
@@ -21356,13 +21960,26 @@ var AgentGUIDetailPane = memo(function AgentGUIDetailPane2({
21356
21960
  scrollTop: timeline.scrollTop,
21357
21961
  clientHeight: timeline.clientHeight
21358
21962
  };
21963
+ if (viewModel.hasOlderMessages && !viewModel.isLoadingOlderMessages && timeline.scrollTop <= AGENT_GUI_TOP_HISTORY_PREFETCH_THRESHOLD_PX) {
21964
+ pendingPrependScrollAnchorRef.current = {
21965
+ conversationId: activeConversationId,
21966
+ scrollHeight: timeline.scrollHeight,
21967
+ scrollTop: timeline.scrollTop
21968
+ };
21969
+ actions.loadOlderConversationMessages();
21970
+ }
21359
21971
  };
21360
21972
  captureScrollAnchor();
21361
21973
  timeline.addEventListener("scroll", captureScrollAnchor, { passive: true });
21362
21974
  return () => {
21363
21975
  timeline.removeEventListener("scroll", captureScrollAnchor);
21364
21976
  };
21365
- }, [viewModel.activeConversationId]);
21977
+ }, [
21978
+ actions,
21979
+ viewModel.activeConversationId,
21980
+ viewModel.hasOlderMessages,
21981
+ viewModel.isLoadingOlderMessages
21982
+ ]);
21366
21983
  return /* @__PURE__ */ jsxs15("main", { className: AgentGUINode_styles_default.detail, children: [
21367
21984
  /* @__PURE__ */ jsx29(
21368
21985
  AgentGUIDetailHeader,
@@ -21381,20 +21998,28 @@ var AgentGUIDetailPane = memo(function AgentGUIDetailPane2({
21381
21998
  showProviderSetupNotice ? /* @__PURE__ */ jsxs15(
21382
21999
  "div",
21383
22000
  {
21384
- className: AgentGUINode_styles_default.providerSetupNotice,
22001
+ className: cn3(
22002
+ toastVariants({ variant: "default" }),
22003
+ AgentGUINode_styles_default.providerSetupNotice
22004
+ ),
22005
+ "data-slot": "toast",
21385
22006
  "data-testid": "agent-gui-provider-setup-notice",
21386
22007
  role: "status",
21387
22008
  children: [
22009
+ /* @__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 }) }),
21388
22010
  /* @__PURE__ */ jsx29(
21389
- Info,
22011
+ "button",
21390
22012
  {
21391
- "aria-hidden": "true",
21392
- className: AgentGUINode_styles_default.providerSetupNoticeIcon,
21393
- size: 15,
21394
- strokeWidth: 2
22013
+ type: "button",
22014
+ className: AgentGUINode_styles_default.providerSetupNoticeAction,
22015
+ "data-testid": "agent-gui-provider-setup-notice-action",
22016
+ onClick: () => openAgentEnvPanel({
22017
+ provider: viewModel.data.provider,
22018
+ focus: "detect"
22019
+ }),
22020
+ children: labels.installRequiredAction
21395
22021
  }
21396
- ),
21397
- /* @__PURE__ */ jsx29("span", { className: AgentGUINode_styles_default.providerSetupNoticeText, children: labels.installRequiredPlaceholder })
22022
+ )
21398
22023
  ]
21399
22024
  }
21400
22025
  ) : null,
@@ -21427,6 +22052,7 @@ var AgentGUIDetailPane = memo(function AgentGUIDetailPane2({
21427
22052
  {
21428
22053
  conversation,
21429
22054
  isLoading: showTimelineSkeleton,
22055
+ isLoadingOlderMessages: viewModel.isLoadingOlderMessages,
21430
22056
  loadingLabel: labels.loadingConversation,
21431
22057
  empty: conversationFlowEmpty,
21432
22058
  onLinkAction: stableLinkAction,
@@ -21810,7 +22436,7 @@ function syncAgentGUIConversationRailStore(store, next) {
21810
22436
  Object.assign(store, next);
21811
22437
  }
21812
22438
  function agentGUIConversationRailStoreSnapshotsEqual(current, next) {
21813
- 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;
22439
+ 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;
21814
22440
  }
21815
22441
  var AgentGUIConversationRailStorePane = memo(
21816
22442
  function AgentGUIConversationRailStorePane2({
@@ -21899,7 +22525,7 @@ function stabilizeConversationSectionItems(previous, next) {
21899
22525
  return changed ? stable : previous;
21900
22526
  }
21901
22527
  function conversationSummariesRenderEqual2(left, right) {
21902
- 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);
22528
+ 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);
21903
22529
  }
21904
22530
  function conversationSyncStatesRenderEqual(left, right) {
21905
22531
  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);
@@ -21925,6 +22551,7 @@ var AgentGUIConversationRailPane = memo(
21925
22551
  openclawGateway,
21926
22552
  isCollapsed,
21927
22553
  onCreateConversation,
22554
+ onOpenAgentEnvSetup,
21928
22555
  onRetryOpenclawGateway,
21929
22556
  onSelectConversation,
21930
22557
  onToggleConversationPinned,
@@ -22053,12 +22680,13 @@ var AgentGUIConversationRailPane = memo(
22053
22680
  {
22054
22681
  type: "button",
22055
22682
  variant: "secondary",
22683
+ size: "dialog",
22056
22684
  className: AgentGUINode_styles_default.newConversationIconButton,
22057
22685
  title: labels.newConversation,
22058
22686
  disabled: createConversationDisabled,
22059
22687
  onClick: () => onCreateConversation(),
22060
22688
  children: [
22061
- /* @__PURE__ */ jsx29(EditIcon, { "aria-hidden": "true" }),
22689
+ /* @__PURE__ */ jsx29(CreateChatIcon, { "aria-hidden": "true" }),
22062
22690
  /* @__PURE__ */ jsx29("span", { children: labels.newConversation })
22063
22691
  ]
22064
22692
  }
@@ -22148,6 +22776,21 @@ var AgentGUIConversationRailPane = memo(
22148
22776
  })
22149
22777
  }
22150
22778
  ),
22779
+ /* @__PURE__ */ jsx29("div", { className: "shrink-0 border-t border-[var(--border-1)] px-2 py-1.5", children: /* @__PURE__ */ jsxs15(
22780
+ Button,
22781
+ {
22782
+ type: "button",
22783
+ variant: "ghost",
22784
+ className: "flex w-full items-center justify-start gap-2 text-[13px] text-[var(--text-secondary)] hover:text-[var(--text-primary)]",
22785
+ title: labels.agentEnvSetup,
22786
+ disabled: previewMode,
22787
+ onClick: () => onOpenAgentEnvSetup(),
22788
+ children: [
22789
+ /* @__PURE__ */ jsx29(Wrench, { "aria-hidden": "true", size: 16, strokeWidth: 1.8 }),
22790
+ /* @__PURE__ */ jsx29("span", { children: labels.agentEnvSetup })
22791
+ ]
22792
+ }
22793
+ ) }),
22151
22794
  /* @__PURE__ */ jsx29(
22152
22795
  ConfirmationDialog,
22153
22796
  {
@@ -22283,34 +22926,65 @@ var AgentGUIConversationRailSection = memo(
22283
22926
  }
22284
22927
  ) : /* @__PURE__ */ jsx29("div", { className: AgentGUINode_styles_default.conversationSectionToggle, children: /* @__PURE__ */ jsx29("span", { className: AgentGUINode_styles_default.conversationSectionLabel, children: /* @__PURE__ */ jsx29("span", { children: section.label }) }) }),
22285
22928
  canCreateConversationFromSection ? /* @__PURE__ */ jsxs15("div", { className: AgentGUINode_styles_default.conversationSectionActions, children: [
22286
- /* @__PURE__ */ jsxs15("span", { className: AgentGUINode_styles_default.conversationSectionActionTooltipWrap, children: [
22287
- /* @__PURE__ */ jsx29(
22288
- BareIconButton,
22929
+ previewMode ? /* @__PURE__ */ jsx29("span", { className: AgentGUINode_styles_default.conversationSectionActionTooltipWrap, children: /* @__PURE__ */ jsx29(
22930
+ BareIconButton,
22931
+ {
22932
+ className: AgentGUINode_styles_default.conversationSectionMoreButton,
22933
+ "aria-label": createConversationLabel,
22934
+ size: "sm",
22935
+ disabled: createConversationDisabled,
22936
+ onClick: handleCreateConversation,
22937
+ children: /* @__PURE__ */ jsx29(CreateChatIcon, { "aria-hidden": "true" })
22938
+ }
22939
+ ) }) : /* @__PURE__ */ jsxs15(Tooltip3, { children: [
22940
+ /* @__PURE__ */ jsx29(TooltipTrigger3, { asChild: true, children: /* @__PURE__ */ jsx29(
22941
+ "span",
22289
22942
  {
22290
- className: AgentGUINode_styles_default.conversationSectionMoreButton,
22291
- "aria-label": createConversationLabel,
22292
- size: "sm",
22293
- disabled: createConversationDisabled,
22294
- onClick: handleCreateConversation,
22295
- children: /* @__PURE__ */ jsx29(EditIcon, { "aria-hidden": "true" })
22943
+ className: AgentGUINode_styles_default.conversationSectionActionTooltipWrap,
22944
+ children: /* @__PURE__ */ jsx29(
22945
+ BareIconButton,
22946
+ {
22947
+ className: AgentGUINode_styles_default.conversationSectionMoreButton,
22948
+ "aria-label": createConversationLabel,
22949
+ size: "sm",
22950
+ disabled: createConversationDisabled,
22951
+ onClick: handleCreateConversation,
22952
+ children: /* @__PURE__ */ jsx29(CreateChatIcon, { "aria-hidden": "true" })
22953
+ }
22954
+ )
22296
22955
  }
22297
- ),
22956
+ ) }),
22298
22957
  /* @__PURE__ */ jsx29(
22299
- "span",
22958
+ TooltipContent3,
22300
22959
  {
22301
- "aria-hidden": "true",
22960
+ side: "right",
22961
+ sideOffset: 6,
22302
22962
  className: AgentGUINode_styles_default.conversationSectionActionTooltip,
22303
22963
  children: createConversationLabel
22304
22964
  }
22305
22965
  )
22306
22966
  ] }),
22307
22967
  projectPath ? /* @__PURE__ */ jsxs15(DropdownMenu, { children: [
22308
- /* @__PURE__ */ jsx29(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs15(
22968
+ previewMode ? /* @__PURE__ */ jsx29(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx29(
22309
22969
  "span",
22310
22970
  {
22311
22971
  className: AgentGUINode_styles_default.conversationSectionActionTooltipWrap,
22312
- children: [
22313
- /* @__PURE__ */ jsx29(
22972
+ children: /* @__PURE__ */ jsx29(
22973
+ BareIconButton,
22974
+ {
22975
+ className: AgentGUINode_styles_default.conversationSectionMoreButton,
22976
+ "aria-label": labels.projectSectionMoreActions,
22977
+ size: "sm",
22978
+ children: /* @__PURE__ */ jsx29(MoreHorizontalIcon, { "aria-hidden": "true" })
22979
+ }
22980
+ )
22981
+ }
22982
+ ) }) : /* @__PURE__ */ jsxs15(Tooltip3, { children: [
22983
+ /* @__PURE__ */ jsx29(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx29(TooltipTrigger3, { asChild: true, children: /* @__PURE__ */ jsx29(
22984
+ "span",
22985
+ {
22986
+ className: AgentGUINode_styles_default.conversationSectionActionTooltipWrap,
22987
+ children: /* @__PURE__ */ jsx29(
22314
22988
  BareIconButton,
22315
22989
  {
22316
22990
  className: AgentGUINode_styles_default.conversationSectionMoreButton,
@@ -22318,18 +22992,19 @@ var AgentGUIConversationRailSection = memo(
22318
22992
  size: "sm",
22319
22993
  children: /* @__PURE__ */ jsx29(MoreHorizontalIcon, { "aria-hidden": "true" })
22320
22994
  }
22321
- ),
22322
- /* @__PURE__ */ jsx29(
22323
- "span",
22324
- {
22325
- "aria-hidden": "true",
22326
- className: AgentGUINode_styles_default.conversationSectionActionTooltip,
22327
- children: labels.projectSectionMoreActions
22328
- }
22329
22995
  )
22330
- ]
22331
- }
22332
- ) }),
22996
+ }
22997
+ ) }) }),
22998
+ /* @__PURE__ */ jsx29(
22999
+ TooltipContent3,
23000
+ {
23001
+ side: "right",
23002
+ sideOffset: 6,
23003
+ className: AgentGUINode_styles_default.conversationSectionActionTooltip,
23004
+ children: labels.projectSectionMoreActions
23005
+ }
23006
+ )
23007
+ ] }),
22333
23008
  /* @__PURE__ */ jsxs15(
22334
23009
  DropdownMenuContent,
22335
23010
  {
@@ -22673,6 +23348,7 @@ var AgentGUIConversationTimelinePane = memo(
22673
23348
  function AgentGUIConversationTimelinePane2({
22674
23349
  conversation,
22675
23350
  isLoading,
23351
+ isLoadingOlderMessages,
22676
23352
  loadingLabel,
22677
23353
  empty,
22678
23354
  onLinkAction,
@@ -22683,21 +23359,32 @@ var AgentGUIConversationTimelinePane = memo(
22683
23359
  labels
22684
23360
  }) {
22685
23361
  "use memo";
22686
- return /* @__PURE__ */ jsx29(
22687
- AgentConversationFlow,
22688
- {
22689
- conversation,
22690
- isLoading,
22691
- loadingLabel,
22692
- empty,
22693
- onLinkAction,
22694
- onAuthLogin,
22695
- availableSkills,
22696
- workspaceAppIcons,
22697
- previewMode,
22698
- labels
22699
- }
22700
- );
23362
+ return /* @__PURE__ */ jsxs15(Fragment8, { children: [
23363
+ isLoadingOlderMessages && !isLoading ? /* @__PURE__ */ jsx29(
23364
+ "div",
23365
+ {
23366
+ className: "mx-auto flex h-8 items-center justify-center text-[12px] text-[var(--text-secondary)]",
23367
+ "data-testid": "agent-gui-older-messages-loading",
23368
+ role: "status",
23369
+ children: /* @__PURE__ */ jsx29("span", { className: "tsh-inline-loading-ellipsis", children: loadingLabel })
23370
+ }
23371
+ ) : null,
23372
+ /* @__PURE__ */ jsx29(
23373
+ AgentConversationFlow,
23374
+ {
23375
+ conversation,
23376
+ isLoading,
23377
+ loadingLabel,
23378
+ empty,
23379
+ onLinkAction,
23380
+ onAuthLogin,
23381
+ availableSkills,
23382
+ workspaceAppIcons,
23383
+ previewMode,
23384
+ labels
23385
+ }
23386
+ )
23387
+ ] });
22701
23388
  }
22702
23389
  );
22703
23390
  function setTimelineScrollTopInstantly(element, top) {
@@ -23603,6 +24290,7 @@ var AgentGUINode = memo2(function AgentGUINode2({
23603
24290
  provider: displayProviderLabel
23604
24291
  }
23605
24292
  ),
24293
+ installRequiredAction: t("agentHost.agentGui.installRequiredAction"),
23606
24294
  collaboratorSessionReadOnlyPlaceholder: t(
23607
24295
  "agentHost.agentGui.collaboratorSessionReadOnlyPlaceholder"
23608
24296
  ),
@@ -23718,6 +24406,7 @@ var AgentGUINode = memo2(function AgentGUINode2({
23718
24406
  emptyProvider: displayProviderLabel,
23719
24407
  conversations: t("agentHost.agentGui.conversations"),
23720
24408
  newConversation: t("agentHost.agentGui.newConversation"),
24409
+ agentEnvSetup: t("agentHost.agentGui.agentEnvSetup"),
23721
24410
  noConversations: t("agentHost.agentGui.noConversations"),
23722
24411
  emptyProjectConversations: t(
23723
24412
  "agentHost.agentGui.emptyProjectConversations"
@@ -23773,6 +24462,7 @@ var AgentGUINode = memo2(function AgentGUINode2({
23773
24462
  authRequired: t("agentHost.agentGui.authRequired"),
23774
24463
  authLogin: t("agentHost.agentGui.authLogin"),
23775
24464
  activatingSession: t("agentHost.agentGui.activatingSession"),
24465
+ cancellingSession: t("agentHost.agentGui.cancellingSession"),
23776
24466
  retryActivation: t("agentHost.agentGui.retryActivation"),
23777
24467
  continueInNewConversation: t(
23778
24468
  "agentHost.agentGui.continueInNewConversation"
@@ -24031,9 +24721,12 @@ var AgentGUINode = memo2(function AgentGUINode2({
24031
24721
  if (!managedAgent) {
24032
24722
  return true;
24033
24723
  }
24724
+ if (!managedAgentsState) {
24725
+ return true;
24726
+ }
24034
24727
  return resolveAgentHostManagedToolchainAgentAction(
24035
24728
  managedAgent,
24036
- managedAgentsState ?? null
24729
+ managedAgentsState
24037
24730
  ) === "installed";
24038
24731
  }, [activeProbeProvider, managedAgentsState]);
24039
24732
  const runtimeSlashStatusQuotas = useMemo11(