@truefoundry/trueforge-ui 0.3.0-rc.1 → 0.3.0-rc.11

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 (112) hide show
  1. package/CHANGELOG.md +156 -0
  2. package/README.md +23 -8
  3. package/dist/{AgentCodeSnippets-NZOFJE57.js → AgentCodeSnippets-CHDVZHHK.js} +38 -23
  4. package/dist/AgentCodeSnippets-CHDVZHHK.js.map +1 -0
  5. package/dist/AgentMetricChart-6RGWFMJE.js +15 -0
  6. package/dist/AgentOverview-3LJZ6MO7.js +311 -0
  7. package/dist/AgentOverview-3LJZ6MO7.js.map +1 -0
  8. package/dist/{AgentSessionEventTimeline-26Z6IB3B.js → AgentSessionEventTimeline-5GOJKT7D.js} +19 -10
  9. package/dist/AgentSessionEventTimeline-5GOJKT7D.js.map +1 -0
  10. package/dist/{AgentSessionEventTimelineChart-CGCU7WBK.js → AgentSessionEventTimelineChart-UYG3JQMP.js} +144 -57
  11. package/dist/AgentSessionEventTimelineChart-UYG3JQMP.js.map +1 -0
  12. package/dist/{AgentSessions-3MPDZASK.js → AgentSessions-KUABCQ4W.js} +140 -27
  13. package/dist/AgentSessions-KUABCQ4W.js.map +1 -0
  14. package/dist/{ConnectorSettings-MXFRWWR6.js → ConnectorSettings-PED5C2P7.js} +162 -126
  15. package/dist/ConnectorSettings-PED5C2P7.js.map +1 -0
  16. package/dist/{DockLayout-LM4AFMFY.js → DockLayout-ACM7BVIH.js} +10 -10
  17. package/dist/{DrawerLayout-JR3O6RRB.js → DrawerLayout-SGBYRKHI.js} +14 -12
  18. package/dist/DrawerLayout-SGBYRKHI.js.map +1 -0
  19. package/dist/{ModelSettings-G7IFNC2Y.js → ModelSettings-GJTVFXFY.js} +22 -28
  20. package/dist/ModelSettings-GJTVFXFY.js.map +1 -0
  21. package/dist/OpenUiFenceBlock-MJTC2VJ5.js +9 -0
  22. package/dist/{SandboxSettings-NXAANMAX.js → SandboxSettings-VETQ7UPE.js} +17 -21
  23. package/dist/SandboxSettings-VETQ7UPE.js.map +1 -0
  24. package/dist/SchedulesPage-LHJ3U2VM.js +15 -0
  25. package/dist/{SettingsBuilder-L34IQ56S.js → SettingsBuilder-5ZCU66FF.js} +9 -9
  26. package/dist/{SettingsBuilder-L34IQ56S.js.map → SettingsBuilder-5ZCU66FF.js.map} +1 -1
  27. package/dist/{ShellRouteSync-CEBETGEJ.js → ShellRouteSync-SJXFKUAJ.js} +171 -26
  28. package/dist/ShellRouteSync-SJXFKUAJ.js.map +1 -0
  29. package/dist/{SidebarLayout-A2HJPPHS.js → SidebarLayout-TR5CAB6Q.js} +80 -58
  30. package/dist/SidebarLayout-TR5CAB6Q.js.map +1 -0
  31. package/dist/{SkillSettings-FGGMSUNO.js → SkillSettings-TRTY2YH3.js} +24 -22
  32. package/dist/SkillSettings-TRTY2YH3.js.map +1 -0
  33. package/dist/{TrueForgeUIWithRouter-MZVJWJMB.js → TrueForgeUIWithRouter-GZUSRZIR.js} +9 -9
  34. package/dist/{WidgetLayout-6QM45OQQ.js → WidgetLayout-2TM6I4LJ.js} +10 -10
  35. package/dist/{chunk-U7KURWW3.js → chunk-2GIZY3JJ.js} +276 -54
  36. package/dist/chunk-2GIZY3JJ.js.map +1 -0
  37. package/dist/{chunk-ZKNUMH2M.js → chunk-2MUK3SMR.js} +13 -11
  38. package/dist/chunk-2MUK3SMR.js.map +1 -0
  39. package/dist/{chunk-WVE3F724.js → chunk-2QTH6JHX.js} +21 -4
  40. package/dist/chunk-2QTH6JHX.js.map +1 -0
  41. package/dist/{chunk-XAU7QFG3.js → chunk-5N5V7YLO.js} +72 -256
  42. package/dist/chunk-5N5V7YLO.js.map +1 -0
  43. package/dist/{chunk-CYHJDKFB.js → chunk-75YEQUUD.js} +189 -52
  44. package/dist/chunk-75YEQUUD.js.map +1 -0
  45. package/dist/{chunk-QC42MLAK.js → chunk-7AAO4UDQ.js} +4 -3
  46. package/dist/chunk-7AAO4UDQ.js.map +1 -0
  47. package/dist/{chunk-M62XY4NC.js → chunk-BXG6XN2S.js} +235 -108
  48. package/dist/chunk-BXG6XN2S.js.map +1 -0
  49. package/dist/chunk-GKXDRULM.js +587 -0
  50. package/dist/chunk-GKXDRULM.js.map +1 -0
  51. package/dist/{chunk-ZTMKFZQ3.js → chunk-OH357IHR.js} +5572 -3367
  52. package/dist/chunk-OH357IHR.js.map +1 -0
  53. package/dist/{chunk-DMMZSZTN.js → chunk-P6MZDITS.js} +60 -16
  54. package/dist/chunk-P6MZDITS.js.map +1 -0
  55. package/dist/{chunk-MPNSO7E6.js → chunk-SLMFEG7X.js} +2 -2
  56. package/dist/chunk-T6QQV5LK.js +238 -0
  57. package/dist/chunk-T6QQV5LK.js.map +1 -0
  58. package/dist/{chunk-6O6FWUDW.js → chunk-UWLL327M.js} +641 -388
  59. package/dist/chunk-UWLL327M.js.map +1 -0
  60. package/dist/{chunk-FJW36PD7.js → chunk-VAOCM3QQ.js} +28 -24
  61. package/dist/chunk-VAOCM3QQ.js.map +1 -0
  62. package/dist/chunk-Y7O6CMC7.js +517 -0
  63. package/dist/chunk-Y7O6CMC7.js.map +1 -0
  64. package/dist/{createTrueFoundryServer-CmZnBh_l.d.ts → createTrueFoundryServer-B_FYhSdn.d.ts} +8 -5
  65. package/dist/index.d.ts +377 -108
  66. package/dist/index.js +69 -488
  67. package/dist/index.js.map +1 -1
  68. package/dist/plugins/trueforge-agent-server-adapter/index.d.ts +49 -14
  69. package/dist/plugins/trueforge-agent-server-adapter/index.js +161 -60
  70. package/dist/plugins/trueforge-agent-server-adapter/index.js.map +1 -1
  71. package/dist/styles.css +1 -1
  72. package/package.json +5 -4
  73. package/dist/AgentCodeSnippets-NZOFJE57.js.map +0 -1
  74. package/dist/AgentMetricChart-JPU3MFQ2.js +0 -15
  75. package/dist/AgentOverview-EEBHJC32.js +0 -167
  76. package/dist/AgentOverview-EEBHJC32.js.map +0 -1
  77. package/dist/AgentSessionEventTimeline-26Z6IB3B.js.map +0 -1
  78. package/dist/AgentSessionEventTimelineChart-CGCU7WBK.js.map +0 -1
  79. package/dist/AgentSessions-3MPDZASK.js.map +0 -1
  80. package/dist/ConnectorSettings-MXFRWWR6.js.map +0 -1
  81. package/dist/DrawerLayout-JR3O6RRB.js.map +0 -1
  82. package/dist/ModelSettings-G7IFNC2Y.js.map +0 -1
  83. package/dist/OpenUiFenceBlock-2TV5TQ6P.js +0 -9
  84. package/dist/SandboxSettings-NXAANMAX.js.map +0 -1
  85. package/dist/SchedulesPage-KKLUDO6B.js +0 -15
  86. package/dist/ShellRouteSync-CEBETGEJ.js.map +0 -1
  87. package/dist/SidebarLayout-A2HJPPHS.js.map +0 -1
  88. package/dist/SkillSettings-FGGMSUNO.js.map +0 -1
  89. package/dist/chunk-2XPPBEYF.js +0 -74
  90. package/dist/chunk-2XPPBEYF.js.map +0 -1
  91. package/dist/chunk-6O6FWUDW.js.map +0 -1
  92. package/dist/chunk-7Z2C2K7F.js +0 -578
  93. package/dist/chunk-7Z2C2K7F.js.map +0 -1
  94. package/dist/chunk-CYHJDKFB.js.map +0 -1
  95. package/dist/chunk-DMMZSZTN.js.map +0 -1
  96. package/dist/chunk-FJW36PD7.js.map +0 -1
  97. package/dist/chunk-M62XY4NC.js.map +0 -1
  98. package/dist/chunk-QC42MLAK.js.map +0 -1
  99. package/dist/chunk-SFZNIO6U.js +0 -18
  100. package/dist/chunk-SFZNIO6U.js.map +0 -1
  101. package/dist/chunk-U7KURWW3.js.map +0 -1
  102. package/dist/chunk-WVE3F724.js.map +0 -1
  103. package/dist/chunk-XAU7QFG3.js.map +0 -1
  104. package/dist/chunk-ZKNUMH2M.js.map +0 -1
  105. package/dist/chunk-ZTMKFZQ3.js.map +0 -1
  106. /package/dist/{AgentMetricChart-JPU3MFQ2.js.map → AgentMetricChart-6RGWFMJE.js.map} +0 -0
  107. /package/dist/{DockLayout-LM4AFMFY.js.map → DockLayout-ACM7BVIH.js.map} +0 -0
  108. /package/dist/{OpenUiFenceBlock-2TV5TQ6P.js.map → OpenUiFenceBlock-MJTC2VJ5.js.map} +0 -0
  109. /package/dist/{SchedulesPage-KKLUDO6B.js.map → SchedulesPage-LHJ3U2VM.js.map} +0 -0
  110. /package/dist/{TrueForgeUIWithRouter-MZVJWJMB.js.map → TrueForgeUIWithRouter-GZUSRZIR.js.map} +0 -0
  111. /package/dist/{WidgetLayout-6QM45OQQ.js.map → WidgetLayout-2TM6I4LJ.js.map} +0 -0
  112. /package/dist/{chunk-MPNSO7E6.js.map → chunk-SLMFEG7X.js.map} +0 -0
@@ -1,42 +1,64 @@
1
1
  "use client";
2
2
  import {
3
3
  buildSessionResumeHref
4
- } from "./chunk-WVE3F724.js";
4
+ } from "./chunk-2QTH6JHX.js";
5
5
  import {
6
6
  useOptionalResolvedRoutes
7
7
  } from "./chunk-Y6S4OVVQ.js";
8
- import {
9
- drainListPages
10
- } from "./chunk-SFZNIO6U.js";
11
8
  import {
12
9
  sessionIsCreateAgent
13
10
  } from "./chunk-RDXBMTP5.js";
14
11
  import {
12
+ Button,
13
+ Dialog,
14
+ DialogContent,
15
+ DialogFooter,
16
+ DialogHeader,
17
+ DialogTitle,
15
18
  EmptyScreen,
16
19
  Skeleton,
20
+ useResourcePermissions,
17
21
  useSessionShareSearch,
18
- useSlot
19
- } from "./chunk-ZTMKFZQ3.js";
20
- import "./chunk-2XPPBEYF.js";
22
+ useSlot,
23
+ useToasterOptional
24
+ } from "./chunk-OH357IHR.js";
25
+ import "./chunk-T6QQV5LK.js";
21
26
  import {
22
27
  Icon
23
- } from "./chunk-XAU7QFG3.js";
28
+ } from "./chunk-5N5V7YLO.js";
24
29
  import {
25
30
  sessionTimeRangeFromCreatedAt,
26
31
  useAgentSessionsServer,
27
32
  useOptionalShellMode,
28
33
  useServer
29
- } from "./chunk-U7KURWW3.js";
34
+ } from "./chunk-2GIZY3JJ.js";
30
35
  import "./chunk-M6MHLVSO.js";
31
- import "./chunk-MPNSO7E6.js";
36
+ import "./chunk-SLMFEG7X.js";
32
37
  import {
33
38
  cn
34
- } from "./chunk-7Z2C2K7F.js";
39
+ } from "./chunk-GKXDRULM.js";
35
40
 
36
41
  // src/atoms/agent-details/AgentSessions.tsx
37
42
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
38
43
  import { Group, Panel, Separator } from "react-resizable-panels";
44
+
45
+ // src/utils/drainListPages.ts
46
+ async function drainListPages({
47
+ fetchPage
48
+ }) {
49
+ const items = [];
50
+ let pageToken;
51
+ do {
52
+ const page = await fetchPage(pageToken);
53
+ items.push(...page.data);
54
+ pageToken = page.nextPageToken;
55
+ } while (pageToken != null && pageToken.length > 0);
56
+ return items;
57
+ }
58
+
59
+ // src/atoms/agent-details/AgentSessions.tsx
39
60
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
61
+ var LOAD_MORE_ROOT_MARGIN = "96px";
40
62
  function sessionTitle(entry) {
41
63
  const title = entry.title?.trim();
42
64
  return title != null && title.length > 0 ? title : "Untitled session";
@@ -47,9 +69,13 @@ function entryIsMutable(entry) {
47
69
  }
48
70
  return entry.agentName == null;
49
71
  }
72
+ function entrySourceType(entry) {
73
+ return "sourceType" in entry && Reflect.get(entry, "sourceType") === "schedule" ? "schedule" : void 0;
74
+ }
50
75
  function AgentSessions({ agentId, startTimestamp, endTimestamp, shareView }) {
51
76
  const sessionsServer = useAgentSessionsServer();
52
77
  const chatServer = useServer();
78
+ const toaster = useToasterOptional();
53
79
  const shell = useOptionalShellMode();
54
80
  const routes = useOptionalResolvedRoutes();
55
81
  const { sessionId: selectedSessionId, updateShareSearch } = useSessionShareSearch();
@@ -60,12 +86,28 @@ function AgentSessions({ agentId, startTimestamp, endTimestamp, shareView }) {
60
86
  const [nextPageToken, setNextPageToken] = useState();
61
87
  const [listLoading, setListLoading] = useState(true);
62
88
  const [listLoadingMore, setListLoadingMore] = useState(false);
89
+ const [listLoadMoreFailed, setListLoadMoreFailed] = useState(false);
63
90
  const [listFailed, setListFailed] = useState(false);
64
91
  const listRequestIdRef = useRef(0);
92
+ const loadMoreInflightRef = useRef(false);
93
+ const [listEl, setListEl] = useState(null);
94
+ const [sentinelEl, setSentinelEl] = useState(null);
65
95
  const [detailEvents, setDetailEvents] = useState();
66
96
  const [detailSession, setDetailSession] = useState();
67
97
  const [detailLoading, setDetailLoading] = useState(false);
68
98
  const [detailFailed, setDetailFailed] = useState(false);
99
+ const [pendingDelete, setPendingDelete] = useState(null);
100
+ const canDeleteSession = typeof chatServer.deleteSession === "function";
101
+ const permissionResourceIds = useMemo(() => {
102
+ const ids = new Set(entries.map((entry) => entry.id));
103
+ if (selectedSessionId != null && selectedSessionId.length > 0) ids.add(selectedSessionId);
104
+ return [...ids];
105
+ }, [entries, selectedSessionId]);
106
+ const { allows } = useResourcePermissions({
107
+ resourceType: "session",
108
+ resourceIds: permissionResourceIds
109
+ });
110
+ const canResume = allows(selectedSessionId, "MANAGE");
69
111
  const listRequest = useMemo(
70
112
  () => ({
71
113
  order: "desc",
@@ -79,8 +121,11 @@ function AgentSessions({ agentId, startTimestamp, endTimestamp, shareView }) {
79
121
  useEffect(() => {
80
122
  const requestId = ++listRequestIdRef.current;
81
123
  let cancelled = false;
124
+ loadMoreInflightRef.current = false;
125
+ setNextPageToken(void 0);
82
126
  setListLoading(true);
83
127
  setListLoadingMore(false);
128
+ setListLoadMoreFailed(false);
84
129
  setListFailed(false);
85
130
  void sessionsServer.listSessions(listRequest).then((page) => {
86
131
  if (cancelled || listRequestIdRef.current !== requestId) return;
@@ -99,19 +144,36 @@ function AgentSessions({ agentId, startTimestamp, endTimestamp, shareView }) {
99
144
  };
100
145
  }, [listRequest, sessionsServer]);
101
146
  const loadMore = useCallback(async () => {
102
- if (nextPageToken == null || listLoadingMore) return;
147
+ if (listLoading || nextPageToken == null || loadMoreInflightRef.current) return;
103
148
  const requestId = listRequestIdRef.current;
149
+ loadMoreInflightRef.current = true;
104
150
  setListLoadingMore(true);
151
+ setListLoadMoreFailed(false);
105
152
  try {
106
153
  const page = await sessionsServer.listSessions({ ...listRequest, pageToken: nextPageToken });
107
154
  if (listRequestIdRef.current !== requestId) return;
108
155
  setEntries((current) => [...current, ...page.data]);
109
156
  setNextPageToken(page.nextPageToken);
110
157
  } catch {
158
+ if (listRequestIdRef.current === requestId) setListLoadMoreFailed(true);
111
159
  } finally {
112
- if (listRequestIdRef.current === requestId) setListLoadingMore(false);
160
+ if (listRequestIdRef.current === requestId) {
161
+ loadMoreInflightRef.current = false;
162
+ setListLoadingMore(false);
163
+ }
113
164
  }
114
- }, [listLoadingMore, listRequest, nextPageToken, sessionsServer]);
165
+ }, [listLoading, listRequest, nextPageToken, sessionsServer]);
166
+ useEffect(() => {
167
+ if (listLoading || nextPageToken == null || listEl == null || sentinelEl == null) return;
168
+ const observer = new IntersectionObserver(
169
+ (observed) => {
170
+ if (observed.some((entry) => entry.isIntersecting)) void loadMore();
171
+ },
172
+ { root: listEl, rootMargin: LOAD_MORE_ROOT_MARGIN }
173
+ );
174
+ observer.observe(sentinelEl);
175
+ return () => observer.disconnect();
176
+ }, [entries.length, listEl, listLoading, loadMore, nextPageToken, sentinelEl]);
115
177
  useEffect(() => {
116
178
  if (selectedSessionId == null || selectedSessionId.length === 0) {
117
179
  setDetailEvents(void 0);
@@ -158,6 +220,19 @@ function AgentSessions({ agentId, startTimestamp, endTimestamp, shareView }) {
158
220
  if (selectedSessionId == null) return;
159
221
  updateShareSearch({ sessionId: null });
160
222
  };
223
+ const handleDelete = async (entry) => {
224
+ if (!allows(entry.id, "DELETE") || typeof chatServer.deleteSession !== "function") return;
225
+ setPendingDelete(null);
226
+ try {
227
+ await chatServer.deleteSession({ sessionId: entry.id });
228
+ setEntries((current) => current.filter((item) => item.id !== entry.id));
229
+ if (selectedSessionId === entry.id) {
230
+ updateShareSearch({ sessionId: null });
231
+ }
232
+ } catch (caught) {
233
+ toaster?.showError(caught);
234
+ }
235
+ };
161
236
  const selectedEntry = entries.find((entry) => entry.id === selectedSessionId);
162
237
  const selectedTitle = detailSession != null ? sessionTitle(detailSession) : selectedEntry != null ? sessionTitle(selectedEntry) : selectedSessionId ?? "Untitled session";
163
238
  const resumeIsCreateAgent = detailSession != null ? sessionIsCreateAgent(detailSession) : selectedEntry != null ? sessionIsCreateAgent(selectedEntry) : false;
@@ -165,7 +240,7 @@ function AgentSessions({ agentId, startTimestamp, endTimestamp, shareView }) {
165
240
  const resumeLabel = resumeIsCreateAgent ? "Resume Agent building" : "Resume Chat";
166
241
  const resumeHref = selectedSessionId != null && routes != null ? buildSessionResumeHref({ sessionId: selectedSessionId, routes }) : null;
167
242
  const handleResume = () => {
168
- if (selectedSessionId == null || shell == null) return;
243
+ if (!canResume || selectedSessionId == null || shell == null) return;
169
244
  const agentName = detailSession?.agentName ?? selectedEntry?.agentName;
170
245
  shell.openHistorySession({
171
246
  sessionId: selectedSessionId,
@@ -193,30 +268,34 @@ function AgentSessions({ agentId, startTimestamp, endTimestamp, shareView }) {
193
268
  className: "h-full min-h-0 w-full",
194
269
  resizeTargetMinimumSize: { coarse: 24, fine: 11 },
195
270
  children: [
196
- /* @__PURE__ */ jsx(Panel, { id: "agent-sessions-list", defaultSize: "35%", minSize: "20%", maxSize: "50%", children: /* @__PURE__ */ jsxs("aside", { className: "flex h-full min-h-0 w-full flex-col bg-sidebar-bg", children: [
197
- /* @__PURE__ */ jsx("div", { className: "scrollbar-none min-h-0 flex-1 overflow-y-auto", children: listLoading ? /* @__PURE__ */ jsx("div", { className: "space-y-2 p-3", role: "status", "aria-label": "Loading sessions", children: ["a", "b", "c"].map((key) => /* @__PURE__ */ jsx(Skeleton, { className: "h-16 rounded-md" }, key)) }) : listFailed ? /* @__PURE__ */ jsx("p", { className: "px-3 py-6 text-center text-xs text-text-secondary", children: "Sessions could not be loaded." }) : entries.map((entry) => /* @__PURE__ */ jsx(
271
+ /* @__PURE__ */ jsx(Panel, { id: "agent-sessions-list", defaultSize: "35%", minSize: "20%", maxSize: "50%", children: /* @__PURE__ */ jsx("aside", { className: "flex h-full min-h-0 w-full flex-col bg-sidebar-bg", children: /* @__PURE__ */ jsxs("div", { ref: setListEl, className: "scrollbar-none min-h-0 flex-1 overflow-y-auto", children: [
272
+ listLoading ? /* @__PURE__ */ jsx("div", { className: "space-y-2 p-3", role: "status", "aria-label": "Loading sessions", children: ["a", "b", "c"].map((key) => /* @__PURE__ */ jsx(Skeleton, { className: "h-16 rounded-md" }, key)) }) : listFailed ? /* @__PURE__ */ jsx("p", { className: "px-3 py-6 text-center text-xs text-text-secondary", children: "Sessions could not be loaded." }) : entries.map((entry) => /* @__PURE__ */ jsx(
198
273
  AgentSessionListRow,
199
274
  {
200
275
  title: sessionTitle(entry),
201
276
  agentName: entry.agentName ?? void 0,
277
+ sourceType: entrySourceType(entry),
202
278
  lastActivityAt: entry.lastActivityAt,
203
279
  metrics: entry.metrics,
204
280
  active: entry.id === selectedSessionId,
205
- onSelect: () => selectSession(entry)
281
+ onSelect: () => selectSession(entry),
282
+ ...canDeleteSession ? {
283
+ onRequestDelete: () => setPendingDelete(entry),
284
+ canDelete: allows(entry.id, "DELETE")
285
+ } : {}
206
286
  },
207
287
  entry.id
208
- )) }),
209
- nextPageToken != null && !listLoading ? /* @__PURE__ */ jsx("div", { className: "shrink-0 border-t border-border p-3", children: /* @__PURE__ */ jsx(
288
+ )),
289
+ nextPageToken != null && !listLoading && !listFailed ? /* @__PURE__ */ jsx("div", { ref: setSentinelEl, className: "px-3 py-2", children: listLoadingMore ? /* @__PURE__ */ jsx(Skeleton, { className: "h-16 rounded-md", role: "status", "aria-label": "Loading more sessions" }) : listLoadMoreFailed ? /* @__PURE__ */ jsx(
210
290
  "button",
211
291
  {
212
292
  type: "button",
213
- disabled: listLoadingMore,
293
+ className: "h-8 w-full rounded-md border border-border text-xs font-medium text-text-primary hover:bg-ghost-button-hover",
214
294
  onClick: () => void loadMore(),
215
- className: "h-8 w-full rounded-md border border-border text-xs font-medium text-text-primary hover:bg-ghost-button-hover disabled:opacity-60",
216
- children: listLoadingMore ? "Loading\u2026" : "Load more"
295
+ children: "Retry loading sessions"
217
296
  }
218
- ) }) : null
219
- ] }) }),
297
+ ) : null }) : null
298
+ ] }) }) }),
220
299
  /* @__PURE__ */ jsxs(
221
300
  Separator,
222
301
  {
@@ -253,6 +332,7 @@ function AgentSessions({ agentId, startTimestamp, endTimestamp, shareView }) {
253
332
  createdAt: detailSession?.createdAt ?? selectedEntry?.createdAt,
254
333
  view: shareView,
255
334
  onClose: clearSelectedSession,
335
+ canResume,
256
336
  ...resumeProps
257
337
  }
258
338
  ),
@@ -264,7 +344,40 @@ function AgentSessions({ agentId, startTimestamp, endTimestamp, shareView }) {
264
344
  listMetrics: selectedEntry?.metrics
265
345
  }
266
346
  )
267
- ] }) }) })
347
+ ] }) }) }),
348
+ pendingDelete != null ? /* @__PURE__ */ jsxs(
349
+ Dialog,
350
+ {
351
+ open: true,
352
+ onOpenChange: (open) => {
353
+ if (!open) setPendingDelete(null);
354
+ },
355
+ "aria-label": "Delete session",
356
+ className: "max-w-md",
357
+ children: [
358
+ /* @__PURE__ */ jsx(DialogContent, { children: /* @__PURE__ */ jsxs(DialogHeader, { children: [
359
+ /* @__PURE__ */ jsx(DialogTitle, { children: "Delete session" }),
360
+ /* @__PURE__ */ jsxs("p", { className: "text-text-secondary text-sm", children: [
361
+ "\u201C",
362
+ sessionTitle(pendingDelete),
363
+ "\u201D will be permanently deleted. This cannot be undone."
364
+ ] })
365
+ ] }) }),
366
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
367
+ /* @__PURE__ */ jsx(Button.Secondary, { type: "button", onClick: () => setPendingDelete(null), children: "Cancel" }),
368
+ /* @__PURE__ */ jsx(
369
+ Button.Destructive,
370
+ {
371
+ type: "button",
372
+ disabled: !allows(pendingDelete.id, "DELETE"),
373
+ onClick: () => void handleDelete(pendingDelete),
374
+ children: "Delete"
375
+ }
376
+ )
377
+ ] })
378
+ ]
379
+ }
380
+ ) : null
268
381
  ]
269
382
  }
270
383
  );
@@ -272,4 +385,4 @@ function AgentSessions({ agentId, startTimestamp, endTimestamp, shareView }) {
272
385
  export {
273
386
  AgentSessions
274
387
  };
275
- //# sourceMappingURL=AgentSessions-3MPDZASK.js.map
388
+ //# sourceMappingURL=AgentSessions-KUABCQ4W.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/atoms/agent-details/AgentSessions.tsx","../src/utils/drainListPages.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useMemo, useRef, useState, type ComponentType } from 'react';\nimport { Group, Panel, Separator } from 'react-resizable-panels';\n\nimport { useToasterOptional } from '../../containers/ToasterContainer.js';\nimport { useResourcePermissions } from '../../hooks/useResourcePermissions.js';\nimport { useSessionShareSearch } from '../../hooks/useSessionShareSearch.js';\nimport { Icon } from '../../icons/Icon.js';\nimport { buildSessionResumeHref } from '../../routing/paths.js';\nimport { useOptionalResolvedRoutes } from '../../routing/ResolvedRoutesContext.js';\nimport { useAgentSessionsServer, useServer } from '../../server/ServerContext.js';\nimport { useOptionalShellMode } from '../../server/ShellModeContext.js';\nimport type { Session, SessionEventItem, SessionListEntry } from '../../server/types.js';\nimport { useSlot } from '../../theme/SlotsProvider.js';\nimport { drainListPages } from '../../utils/drainListPages.js';\nimport { sessionTimeRangeFromCreatedAt } from '../../utils/sessionShareUrl.js';\nimport { EmptyScreen } from '../EmptyScreen.js';\nimport { cn } from '../lib/cn.js';\nimport { sessionIsCreateAgent } from '../lib/sessionCreateAgent.js';\nimport { Button } from '../primitives/Button.js';\nimport { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from '../primitives/Dialog.js';\nimport { Skeleton } from '../primitives/Skeleton.js';\nimport type { AgentSessionsProps } from './types.js';\n\nconst LOAD_MORE_ROOT_MARGIN = '96px';\n\nfunction sessionTitle(entry: Pick<SessionListEntry, 'title'>): string {\n const title = entry.title?.trim();\n return title != null && title.length > 0 ? title : 'Untitled session';\n}\n\nfunction entryIsMutable(entry: SessionListEntry): boolean {\n if ('isMutable' in entry && typeof Reflect.get(entry, 'isMutable') === 'boolean') {\n return Reflect.get(entry, 'isMutable') === true;\n }\n return entry.agentName == null;\n}\n\nfunction entrySourceType(entry: SessionListEntry): 'schedule' | undefined {\n return 'sourceType' in entry && Reflect.get(entry, 'sourceType') === 'schedule' ? 'schedule' : undefined;\n}\n\nexport function AgentSessions({ agentId, startTimestamp, endTimestamp, shareView }: AgentSessionsProps) {\n const sessionsServer = useAgentSessionsServer();\n const chatServer = useServer();\n const toaster = useToasterOptional();\n const shell = useOptionalShellMode();\n const routes = useOptionalResolvedRoutes();\n const { sessionId: selectedSessionId, updateShareSearch } = useSessionShareSearch();\n\n const AgentSessionListRow = useSlot('AgentSessionListRow');\n const AgentSessionDetailHeader = useSlot('AgentSessionDetailHeader');\n const AgentSessionTimelineContainer = useSlot('AgentSessionTimelineContainer');\n\n const [entries, setEntries] = useState<SessionListEntry[]>([]);\n const [nextPageToken, setNextPageToken] = useState<string | undefined>();\n const [listLoading, setListLoading] = useState(true);\n const [listLoadingMore, setListLoadingMore] = useState(false);\n const [listLoadMoreFailed, setListLoadMoreFailed] = useState(false);\n const [listFailed, setListFailed] = useState(false);\n const listRequestIdRef = useRef(0);\n const loadMoreInflightRef = useRef(false);\n const [listEl, setListEl] = useState<HTMLDivElement | null>(null);\n const [sentinelEl, setSentinelEl] = useState<HTMLDivElement | null>(null);\n const [detailEvents, setDetailEvents] = useState<SessionEventItem[]>();\n const [detailSession, setDetailSession] = useState<Session>();\n const [detailLoading, setDetailLoading] = useState(false);\n const [detailFailed, setDetailFailed] = useState(false);\n const [pendingDelete, setPendingDelete] = useState<SessionListEntry | null>(null);\n\n const canDeleteSession = typeof chatServer.deleteSession === 'function';\n const permissionResourceIds = useMemo(() => {\n const ids = new Set(entries.map(entry => entry.id));\n if (selectedSessionId != null && selectedSessionId.length > 0) ids.add(selectedSessionId);\n return [...ids];\n }, [entries, selectedSessionId]);\n const { allows } = useResourcePermissions({\n resourceType: 'session',\n resourceIds: permissionResourceIds,\n });\n const canResume = allows(selectedSessionId, 'MANAGE');\n\n const listRequest = useMemo(\n () => ({\n order: 'desc' as const,\n limit: 20,\n ...(agentId == null || agentId.length === 0 ? {} : { agentId }),\n ...(startTimestamp == null ? {} : { startTimestamp }),\n ...(endTimestamp == null ? {} : { endTimestamp }),\n }),\n [agentId, endTimestamp, startTimestamp],\n );\n\n useEffect(() => {\n const requestId = ++listRequestIdRef.current;\n let cancelled = false;\n loadMoreInflightRef.current = false;\n setNextPageToken(undefined);\n setListLoading(true);\n setListLoadingMore(false);\n setListLoadMoreFailed(false);\n setListFailed(false);\n void sessionsServer\n .listSessions(listRequest)\n .then(page => {\n if (cancelled || listRequestIdRef.current !== requestId) return;\n setEntries(page.data);\n setNextPageToken(page.nextPageToken);\n })\n .catch(() => {\n if (cancelled || listRequestIdRef.current !== requestId) return;\n setEntries([]);\n setNextPageToken(undefined);\n setListFailed(true);\n })\n .finally(() => {\n if (!cancelled && listRequestIdRef.current === requestId) setListLoading(false);\n });\n\n return () => {\n cancelled = true;\n };\n }, [listRequest, sessionsServer]);\n\n const loadMore = useCallback(async () => {\n // A ref, not `listLoadingMore`: the observer can fire twice before a re-render.\n if (listLoading || nextPageToken == null || loadMoreInflightRef.current) return;\n const requestId = listRequestIdRef.current;\n loadMoreInflightRef.current = true;\n setListLoadingMore(true);\n setListLoadMoreFailed(false);\n try {\n const page = await sessionsServer.listSessions({ ...listRequest, pageToken: nextPageToken });\n if (listRequestIdRef.current !== requestId) return;\n setEntries(current => [...current, ...page.data]);\n setNextPageToken(page.nextPageToken);\n } catch {\n if (listRequestIdRef.current === requestId) setListLoadMoreFailed(true);\n } finally {\n if (listRequestIdRef.current === requestId) {\n loadMoreInflightRef.current = false;\n setListLoadingMore(false);\n }\n }\n }, [listLoading, listRequest, nextPageToken, sessionsServer]);\n\n // `entries.length` re-arms the observer: an already-intersecting sentinel emits no new entry.\n useEffect(() => {\n if (listLoading || nextPageToken == null || listEl == null || sentinelEl == null) return;\n\n const observer = new IntersectionObserver(\n observed => {\n if (observed.some(entry => entry.isIntersecting)) void loadMore();\n },\n { root: listEl, rootMargin: LOAD_MORE_ROOT_MARGIN },\n );\n observer.observe(sentinelEl);\n return () => observer.disconnect();\n }, [entries.length, listEl, listLoading, loadMore, nextPageToken, sentinelEl]);\n\n useEffect(() => {\n if (selectedSessionId == null || selectedSessionId.length === 0) {\n setDetailEvents(undefined);\n setDetailSession(undefined);\n setDetailFailed(false);\n setDetailLoading(false);\n return;\n }\n\n let cancelled = false;\n setDetailLoading(true);\n setDetailFailed(false);\n setDetailEvents(undefined);\n setDetailSession(undefined);\n\n void Promise.all([\n drainListPages({\n fetchPage: pageToken =>\n sessionsServer.listSessionEvents({\n sessionId: selectedSessionId,\n limit: 100,\n ...(pageToken == null ? {} : { pageToken }),\n }),\n }),\n chatServer.getSession({ sessionId: selectedSessionId }).catch(() => undefined),\n ])\n .then(([itemsNewestFirst, session]) => {\n if (cancelled) return;\n setDetailEvents([...itemsNewestFirst].reverse());\n setDetailSession(session);\n })\n .catch(() => {\n if (!cancelled) setDetailFailed(true);\n })\n .finally(() => {\n if (!cancelled) setDetailLoading(false);\n });\n\n return () => {\n cancelled = true;\n };\n }, [chatServer, selectedSessionId, sessionsServer]);\n\n const selectSession = (entry: SessionListEntry) => {\n const pinned = shareView === 'sessions' ? sessionTimeRangeFromCreatedAt(entry.createdAt) : null;\n updateShareSearch({\n sessionId: entry.id,\n ...(shareView === 'sessions'\n ? { view: 'sessions', ...(pinned == null ? {} : { timeRange: pinned }) }\n : { agentId: agentId ?? null, tab: 'sessions', view: null, timeRange: null }),\n });\n };\n\n const clearSelectedSession = () => {\n if (selectedSessionId == null) return;\n updateShareSearch({ sessionId: null });\n };\n\n const handleDelete = async (entry: SessionListEntry) => {\n if (!allows(entry.id, 'DELETE') || typeof chatServer.deleteSession !== 'function') return;\n setPendingDelete(null);\n try {\n await chatServer.deleteSession({ sessionId: entry.id });\n setEntries(current => current.filter(item => item.id !== entry.id));\n if (selectedSessionId === entry.id) {\n updateShareSearch({ sessionId: null });\n }\n } catch (caught) {\n toaster?.showError(caught);\n }\n };\n\n const selectedEntry = entries.find(entry => entry.id === selectedSessionId);\n const selectedTitle =\n detailSession != null\n ? sessionTitle(detailSession)\n : selectedEntry != null\n ? sessionTitle(selectedEntry)\n : (selectedSessionId ?? 'Untitled session');\n\n const resumeIsCreateAgent =\n detailSession != null\n ? sessionIsCreateAgent(detailSession)\n : selectedEntry != null\n ? sessionIsCreateAgent(selectedEntry)\n : false;\n const resumeIsMutable =\n detailSession != null ? detailSession.isMutable : selectedEntry != null ? entryIsMutable(selectedEntry) : true;\n const resumeLabel = resumeIsCreateAgent ? 'Resume Agent building' : 'Resume Chat';\n const resumeHref =\n selectedSessionId != null && routes != null\n ? buildSessionResumeHref({ sessionId: selectedSessionId, routes })\n : null;\n\n const handleResume = () => {\n if (!canResume || selectedSessionId == null || shell == null) return;\n const agentName = detailSession?.agentName ?? selectedEntry?.agentName;\n shell.openHistorySession({\n sessionId: selectedSessionId,\n isMutable: resumeIsMutable,\n isCreateAgent: resumeIsCreateAgent,\n ...(agentName != null && agentName.length > 0 ? { agentName } : {}),\n });\n };\n\n const resumeProps =\n resumeHref != null ? { resumeHref, resumeLabel } : shell != null ? { onResume: handleResume, resumeLabel } : {};\n\n // Full empty only when nothing is selected — keep the detail pane for deep-linked sessionIds\n // (filters/time range can empty the list while share state still points at a session).\n if (\n !listLoading &&\n !listFailed &&\n entries.length === 0 &&\n (selectedSessionId == null || selectedSessionId.length === 0)\n ) {\n return (\n <EmptyScreen\n title=\"No Sessions Found\"\n description=\"There are no sessions available at the moment.\"\n className=\"bg-primary-bg\"\n />\n );\n }\n\n return (\n <Group\n id=\"agent-sessions-split\"\n orientation=\"horizontal\"\n className=\"h-full min-h-0 w-full\"\n resizeTargetMinimumSize={{ coarse: 24, fine: 11 }}\n >\n <Panel id=\"agent-sessions-list\" defaultSize=\"35%\" minSize=\"20%\" maxSize=\"50%\">\n <aside className=\"flex h-full min-h-0 w-full flex-col bg-sidebar-bg\">\n <div ref={setListEl} className=\"scrollbar-none min-h-0 flex-1 overflow-y-auto\">\n {listLoading ? (\n <div className=\"space-y-2 p-3\" role=\"status\" aria-label=\"Loading sessions\">\n {['a', 'b', 'c'].map(key => (\n <Skeleton key={key} className=\"h-16 rounded-md\" />\n ))}\n </div>\n ) : listFailed ? (\n <p className=\"px-3 py-6 text-center text-xs text-text-secondary\">Sessions could not be loaded.</p>\n ) : (\n entries.map(entry => (\n <AgentSessionListRow\n key={entry.id}\n title={sessionTitle(entry)}\n agentName={entry.agentName ?? undefined}\n sourceType={entrySourceType(entry)}\n lastActivityAt={entry.lastActivityAt}\n metrics={entry.metrics}\n active={entry.id === selectedSessionId}\n onSelect={() => selectSession(entry)}\n {...(canDeleteSession\n ? {\n onRequestDelete: () => setPendingDelete(entry),\n canDelete: allows(entry.id, 'DELETE'),\n }\n : {})}\n />\n ))\n )}\n\n {nextPageToken != null && !listLoading && !listFailed ? (\n <div ref={setSentinelEl} className=\"px-3 py-2\">\n {listLoadingMore ? (\n <Skeleton className=\"h-16 rounded-md\" role=\"status\" aria-label=\"Loading more sessions\" />\n ) : listLoadMoreFailed ? (\n <button\n type=\"button\"\n className=\"h-8 w-full rounded-md border border-border text-xs font-medium text-text-primary hover:bg-ghost-button-hover\"\n onClick={() => void loadMore()}\n >\n Retry loading sessions\n </button>\n ) : null}\n </div>\n ) : null}\n </div>\n </aside>\n </Panel>\n\n <Separator\n id=\"agent-sessions-resizer\"\n aria-label=\"Resize session list\"\n className=\"group/resizer relative z-10 w-0 cursor-col-resize focus-visible:outline-none\"\n >\n <div aria-hidden className=\"absolute inset-y-0 -left-1.25 w-2.75\" />\n <div aria-hidden className=\"absolute inset-y-0 left-0 w-px bg-border transition-colors\" />\n <div\n aria-hidden\n className={cn(\n 'absolute top-1/2 left-0 z-10 flex h-4 w-2 -translate-x-1/2 -translate-y-1/2 items-center justify-center rounded-xs shadow-sm transition-colors',\n 'bg-secondary-button-bg text-text-secondary',\n 'group-hover/resizer:bg-primary-button-bg group-hover/resizer:text-primary-button-text',\n 'group-active/resizer:bg-primary-button-bg group-active/resizer:text-primary-button-text',\n 'group-focus-visible/resizer:bg-primary-button-bg group-focus-visible/resizer:text-primary-button-text',\n )}\n >\n <Icon name=\"grip-vertical\" size={10} />\n </div>\n </Separator>\n\n <Panel id=\"agent-session-detail\" defaultSize=\"65%\" minSize=\"30%\">\n <section className=\"flex h-full min-w-0 flex-col bg-primary-bg\">\n {selectedSessionId == null ? (\n <div className=\"flex flex-1 items-center justify-center px-6 text-sm text-text-secondary\">\n Select a session to view details\n </div>\n ) : detailFailed ? (\n <div className=\"flex flex-1 items-center justify-center px-6 text-sm text-text-secondary\">\n Session details could not be loaded.\n </div>\n ) : (\n <>\n <AgentSessionDetailHeader\n title={selectedTitle}\n sessionId={selectedSessionId}\n agentId={agentId}\n createdAt={detailSession?.createdAt ?? selectedEntry?.createdAt}\n view={shareView}\n onClose={clearSelectedSession}\n canResume={canResume}\n {...resumeProps}\n />\n {detailLoading || detailEvents === undefined ? (\n <div className=\"flex flex-1 flex-col p-4\" role=\"status\" aria-label=\"Loading session details\">\n <Skeleton className=\"min-h-64 flex-1 rounded-lg\" />\n </div>\n ) : (\n <AgentSessionTimelineContainer\n sessionId={selectedSessionId}\n events={detailEvents}\n listMetrics={selectedEntry?.metrics}\n />\n )}\n </>\n )}\n </section>\n </Panel>\n\n {pendingDelete != null ? (\n <Dialog\n open\n onOpenChange={open => {\n if (!open) setPendingDelete(null);\n }}\n aria-label=\"Delete session\"\n className=\"max-w-md\"\n >\n <DialogContent>\n <DialogHeader>\n <DialogTitle>Delete session</DialogTitle>\n <p className=\"text-text-secondary text-sm\">\n “{sessionTitle(pendingDelete)}” will be permanently deleted. This cannot be undone.\n </p>\n </DialogHeader>\n </DialogContent>\n <DialogFooter>\n <Button.Secondary type=\"button\" onClick={() => setPendingDelete(null)}>\n Cancel\n </Button.Secondary>\n <Button.Destructive\n type=\"button\"\n disabled={!allows(pendingDelete.id, 'DELETE')}\n onClick={() => void handleDelete(pendingDelete)}\n >\n Delete\n </Button.Destructive>\n </DialogFooter>\n </Dialog>\n ) : null}\n </Group>\n );\n}\n\ndeclare module '../../theme/SlotsProvider.js' {\n interface AtomSlots {\n AgentSessions: ComponentType<AgentSessionsProps>;\n }\n}\n","import type { ListResult } from '../server/types.js';\n\n/** Drain token-paginated list endpoints until `nextPageToken` is exhausted. */\nexport async function drainListPages<T>({\n fetchPage,\n}: {\n fetchPage: (pageToken?: string) => Promise<ListResult<T>>;\n}): Promise<T[]> {\n const items: T[] = [];\n let pageToken: string | undefined;\n do {\n const page = await fetchPage(pageToken);\n items.push(...page.data);\n pageToken = page.nextPageToken;\n } while (pageToken != null && pageToken.length > 0);\n return items;\n}\n"],"mappings":"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","names":[]}