@lumerahq/ui 0.9.1 → 0.10.0-dev.0

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 (63) hide show
  1. package/dist/{RecordSheet-yAcqIdOK.js → RecordSheet-CZ-x6AeG.js} +3560 -2042
  2. package/dist/{automations-DNWw-HT7.js → automations-Bb9nlU05.js} +281 -22
  3. package/dist/components/agent-chat/AgentChat.d.ts +1 -1
  4. package/dist/components/agent-chat/AgentChat.d.ts.map +1 -1
  5. package/dist/components/agent-chat/ArtifactCard.d.ts +8 -0
  6. package/dist/components/agent-chat/ArtifactCard.d.ts.map +1 -0
  7. package/dist/components/agent-chat/index.d.ts +4 -0
  8. package/dist/components/agent-chat/index.d.ts.map +1 -1
  9. package/dist/components/agent-chat/tool-renderers/ArtifactRenderer.d.ts +3 -0
  10. package/dist/components/agent-chat/tool-renderers/ArtifactRenderer.d.ts.map +1 -0
  11. package/dist/components/agent-chat/tool-renderers/BashRenderer.d.ts +3 -0
  12. package/dist/components/agent-chat/tool-renderers/BashRenderer.d.ts.map +1 -0
  13. package/dist/components/agent-chat/tool-renderers/DefaultRenderer.d.ts +3 -0
  14. package/dist/components/agent-chat/tool-renderers/DefaultRenderer.d.ts.map +1 -0
  15. package/dist/components/agent-chat/tool-renderers/EditRenderer.d.ts +3 -0
  16. package/dist/components/agent-chat/tool-renderers/EditRenderer.d.ts.map +1 -0
  17. package/dist/components/agent-chat/tool-renderers/FindRenderer.d.ts +3 -0
  18. package/dist/components/agent-chat/tool-renderers/FindRenderer.d.ts.map +1 -0
  19. package/dist/components/agent-chat/tool-renderers/GrepRenderer.d.ts +3 -0
  20. package/dist/components/agent-chat/tool-renderers/GrepRenderer.d.ts.map +1 -0
  21. package/dist/components/agent-chat/tool-renderers/IntegrationProxyRenderer.d.ts +3 -0
  22. package/dist/components/agent-chat/tool-renderers/IntegrationProxyRenderer.d.ts.map +1 -0
  23. package/dist/components/agent-chat/tool-renderers/LsRenderer.d.ts +3 -0
  24. package/dist/components/agent-chat/tool-renderers/LsRenderer.d.ts.map +1 -0
  25. package/dist/components/agent-chat/tool-renderers/LumeraApiRenderer.d.ts +3 -0
  26. package/dist/components/agent-chat/tool-renderers/LumeraApiRenderer.d.ts.map +1 -0
  27. package/dist/components/agent-chat/tool-renderers/ReadRenderer.d.ts +3 -0
  28. package/dist/components/agent-chat/tool-renderers/ReadRenderer.d.ts.map +1 -0
  29. package/dist/components/agent-chat/tool-renderers/SqlQueryRenderer.d.ts +3 -0
  30. package/dist/components/agent-chat/tool-renderers/SqlQueryRenderer.d.ts.map +1 -0
  31. package/dist/components/agent-chat/tool-renderers/ToolCallCard.d.ts +15 -0
  32. package/dist/components/agent-chat/tool-renderers/ToolCallCard.d.ts.map +1 -0
  33. package/dist/components/agent-chat/tool-renderers/WebSearchRenderer.d.ts +3 -0
  34. package/dist/components/agent-chat/tool-renderers/WebSearchRenderer.d.ts.map +1 -0
  35. package/dist/components/agent-chat/tool-renderers/WriteRenderer.d.ts +3 -0
  36. package/dist/components/agent-chat/tool-renderers/WriteRenderer.d.ts.map +1 -0
  37. package/dist/components/agent-chat/tool-renderers/index.d.ts +13 -0
  38. package/dist/components/agent-chat/tool-renderers/index.d.ts.map +1 -0
  39. package/dist/components/agent-chat/tool-renderers/types.d.ts +27 -0
  40. package/dist/components/agent-chat/tool-renderers/types.d.ts.map +1 -0
  41. package/dist/components/agent-chat/tool-renderers/utils.d.ts +33 -0
  42. package/dist/components/agent-chat/tool-renderers/utils.d.ts.map +1 -0
  43. package/dist/components/agent-chat/types.d.ts +37 -0
  44. package/dist/components/agent-chat/types.d.ts.map +1 -1
  45. package/dist/components/index.d.ts +2 -2
  46. package/dist/components/index.d.ts.map +1 -1
  47. package/dist/components/index.js +15 -6
  48. package/dist/components/ui/button.d.ts +1 -1
  49. package/dist/{highlighted-body-OFNGDK62-CeR4x7F6.js → highlighted-body-OFNGDK62-DMJuoRQR.js} +1 -1
  50. package/dist/hooks/index.js +2 -2
  51. package/dist/hooks/use-agent-chat.d.ts +19 -1
  52. package/dist/hooks/use-agent-chat.d.ts.map +1 -1
  53. package/dist/hooks/use-discovery-poll.d.ts +26 -0
  54. package/dist/hooks/use-discovery-poll.d.ts.map +1 -0
  55. package/dist/index.js +64 -56
  56. package/dist/lib/index.js +52 -53
  57. package/dist/mermaid-GHXKKRXX-oYSoQafD.js +4 -0
  58. package/dist/ui.css +135 -10
  59. package/dist/{use-automation-run-D_1647k0.js → use-automation-run-BnhDQAJ6.js} +255 -11
  60. package/dist/{use-sql-table-8APCNryn.js → use-sql-table-D5tupvS2.js} +1 -2
  61. package/package.json +1 -1
  62. package/dist/api-Bm4dzr1n.js +0 -262
  63. package/dist/mermaid-GHXKKRXX-BCicmuVL.js +0 -4
package/dist/ui.css CHANGED
@@ -423,6 +423,14 @@
423
423
  pointer-events: none;
424
424
  }
425
425
 
426
+ .collapse {
427
+ visibility: collapse;
428
+ }
429
+
430
+ .visible {
431
+ visibility: visible;
432
+ }
433
+
426
434
  .sr-only {
427
435
  clip-path: inset(50%);
428
436
  white-space: nowrap;
@@ -611,6 +619,10 @@
611
619
  margin-right: calc(var(--spacing) * 2);
612
620
  }
613
621
 
622
+ .mb-1\.5 {
623
+ margin-bottom: calc(var(--spacing) * 1.5);
624
+ }
625
+
614
626
  .mb-2 {
615
627
  margin-bottom: calc(var(--spacing) * 2);
616
628
  }
@@ -668,6 +680,10 @@
668
680
  display: none;
669
681
  }
670
682
 
683
+ .inline {
684
+ display: inline;
685
+ }
686
+
671
687
  .inline-flex {
672
688
  display: inline-flex;
673
689
  }
@@ -720,6 +736,11 @@
720
736
  height: calc(var(--spacing) * 4);
721
737
  }
722
738
 
739
+ .size-5 {
740
+ width: calc(var(--spacing) * 5);
741
+ height: calc(var(--spacing) * 5);
742
+ }
743
+
723
744
  .size-6 {
724
745
  width: calc(var(--spacing) * 6);
725
746
  height: calc(var(--spacing) * 6);
@@ -799,6 +820,14 @@
799
820
  height: calc(var(--spacing) * 24);
800
821
  }
801
822
 
823
+ .h-56 {
824
+ height: calc(var(--spacing) * 56);
825
+ }
826
+
827
+ .h-\[500px\] {
828
+ height: 500px;
829
+ }
830
+
802
831
  .h-\[720px\] {
803
832
  height: 720px;
804
833
  }
@@ -835,14 +864,18 @@
835
864
  max-height: calc(var(--spacing) * 48);
836
865
  }
837
866
 
838
- .max-h-56 {
839
- max-height: calc(var(--spacing) * 56);
840
- }
841
-
842
867
  .max-h-60 {
843
868
  max-height: calc(var(--spacing) * 60);
844
869
  }
845
870
 
871
+ .max-h-\[200px\] {
872
+ max-height: 200px;
873
+ }
874
+
875
+ .max-h-\[600px\] {
876
+ max-height: 600px;
877
+ }
878
+
846
879
  .max-h-\[calc\(100vh-2rem\)\] {
847
880
  max-height: calc(100vh - 2rem);
848
881
  }
@@ -963,6 +996,10 @@
963
996
  max-width: 88%;
964
997
  }
965
998
 
999
+ .max-w-full {
1000
+ max-width: 100%;
1001
+ }
1002
+
966
1003
  .max-w-lg {
967
1004
  max-width: var(--container-lg);
968
1005
  }
@@ -1072,6 +1109,10 @@
1072
1109
  cursor: default;
1073
1110
  }
1074
1111
 
1112
+ .cursor-not-allowed {
1113
+ cursor: not-allowed;
1114
+ }
1115
+
1075
1116
  .cursor-pointer {
1076
1117
  cursor: pointer;
1077
1118
  }
@@ -1208,12 +1249,22 @@
1208
1249
  gap: calc(var(--spacing) * 8);
1209
1250
  }
1210
1251
 
1252
+ .gap-px {
1253
+ gap: 1px;
1254
+ }
1255
+
1211
1256
  :where(.space-y-1 > :not(:last-child)) {
1212
1257
  --tw-space-y-reverse: 0;
1213
1258
  margin-block-start: calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));
1214
1259
  margin-block-end: calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)));
1215
1260
  }
1216
1261
 
1262
+ :where(.space-y-1\.5 > :not(:last-child)) {
1263
+ --tw-space-y-reverse: 0;
1264
+ margin-block-start: calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse));
1265
+ margin-block-end: calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse)));
1266
+ }
1267
+
1217
1268
  :where(.space-y-2 > :not(:last-child)) {
1218
1269
  --tw-space-y-reverse: 0;
1219
1270
  margin-block-start: calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));
@@ -1502,13 +1553,13 @@
1502
1553
  }
1503
1554
  }
1504
1555
 
1505
- .bg-muted, .bg-muted\/30 {
1556
+ .bg-muted, .bg-muted\/20 {
1506
1557
  background-color: var(--muted);
1507
1558
  }
1508
1559
 
1509
1560
  @supports (color: color-mix(in lab, red, red)) {
1510
- .bg-muted\/30 {
1511
- background-color: color-mix(in oklab, var(--muted) 30%, transparent);
1561
+ .bg-muted\/20 {
1562
+ background-color: color-mix(in oklab, var(--muted) 20%, transparent);
1512
1563
  }
1513
1564
  }
1514
1565
 
@@ -1532,14 +1583,30 @@
1532
1583
  }
1533
1584
  }
1534
1585
 
1586
+ .bg-muted\/60 {
1587
+ background-color: var(--muted);
1588
+ }
1589
+
1590
+ @supports (color: color-mix(in lab, red, red)) {
1591
+ .bg-muted\/60 {
1592
+ background-color: color-mix(in oklab, var(--muted) 60%, transparent);
1593
+ }
1594
+ }
1595
+
1535
1596
  .bg-popover {
1536
1597
  background-color: var(--popover);
1537
1598
  }
1538
1599
 
1539
- .bg-primary {
1600
+ .bg-primary, .bg-primary\/10 {
1540
1601
  background-color: var(--primary);
1541
1602
  }
1542
1603
 
1604
+ @supports (color: color-mix(in lab, red, red)) {
1605
+ .bg-primary\/10 {
1606
+ background-color: color-mix(in oklab, var(--primary) 10%, transparent);
1607
+ }
1608
+ }
1609
+
1543
1610
  .bg-red-50 {
1544
1611
  background-color: var(--color-red-50);
1545
1612
  }
@@ -1580,6 +1647,10 @@
1580
1647
  background-color: #0000;
1581
1648
  }
1582
1649
 
1650
+ .bg-white {
1651
+ background-color: var(--color-white);
1652
+ }
1653
+
1583
1654
  .bg-clip-padding {
1584
1655
  background-clip: padding-box;
1585
1656
  }
@@ -1588,6 +1659,10 @@
1588
1659
  fill: var(--foreground);
1589
1660
  }
1590
1661
 
1662
+ .object-contain {
1663
+ object-fit: contain;
1664
+ }
1665
+
1591
1666
  .object-cover {
1592
1667
  object-fit: cover;
1593
1668
  }
@@ -1604,6 +1679,10 @@
1604
1679
  padding: calc(var(--spacing) * 2);
1605
1680
  }
1606
1681
 
1682
+ .p-2\.5 {
1683
+ padding: calc(var(--spacing) * 2.5);
1684
+ }
1685
+
1607
1686
  .p-3 {
1608
1687
  padding: calc(var(--spacing) * 3);
1609
1688
  }
@@ -1786,6 +1865,10 @@
1786
1865
  font-size: 11px;
1787
1866
  }
1788
1867
 
1868
+ .text-\[12px\] {
1869
+ font-size: 12px;
1870
+ }
1871
+
1789
1872
  .leading-none {
1790
1873
  --tw-leading: 1;
1791
1874
  line-height: 1;
@@ -1835,6 +1918,10 @@
1835
1918
  text-wrap: balance;
1836
1919
  }
1837
1920
 
1921
+ .break-words {
1922
+ overflow-wrap: break-word;
1923
+ }
1924
+
1838
1925
  .whitespace-nowrap {
1839
1926
  white-space: nowrap;
1840
1927
  }
@@ -1871,18 +1958,40 @@
1871
1958
  color: var(--color-emerald-800);
1872
1959
  }
1873
1960
 
1874
- .text-foreground {
1961
+ .text-foreground, .text-foreground\/80 {
1875
1962
  color: var(--foreground);
1876
1963
  }
1877
1964
 
1965
+ @supports (color: color-mix(in lab, red, red)) {
1966
+ .text-foreground\/80 {
1967
+ color: color-mix(in oklab, var(--foreground) 80%, transparent);
1968
+ }
1969
+ }
1970
+
1878
1971
  .text-green-600 {
1879
1972
  color: var(--color-green-600);
1880
1973
  }
1881
1974
 
1882
- .text-muted-foreground {
1975
+ .text-muted-foreground, .text-muted-foreground\/50 {
1976
+ color: var(--muted-foreground);
1977
+ }
1978
+
1979
+ @supports (color: color-mix(in lab, red, red)) {
1980
+ .text-muted-foreground\/50 {
1981
+ color: color-mix(in oklab, var(--muted-foreground) 50%, transparent);
1982
+ }
1983
+ }
1984
+
1985
+ .text-muted-foreground\/60 {
1883
1986
  color: var(--muted-foreground);
1884
1987
  }
1885
1988
 
1989
+ @supports (color: color-mix(in lab, red, red)) {
1990
+ .text-muted-foreground\/60 {
1991
+ color: color-mix(in oklab, var(--muted-foreground) 60%, transparent);
1992
+ }
1993
+ }
1994
+
1886
1995
  .text-popover-foreground {
1887
1996
  color: var(--popover-foreground);
1888
1997
  }
@@ -1942,14 +2051,26 @@
1942
2051
  font-variant-numeric: var(--tw-ordinal, ) var(--tw-slashed-zero, ) var(--tw-numeric-figure, ) var(--tw-numeric-spacing, ) var(--tw-numeric-fraction, );
1943
2052
  }
1944
2053
 
2054
+ .underline {
2055
+ text-decoration-line: underline;
2056
+ }
2057
+
1945
2058
  .underline-offset-4 {
1946
2059
  text-underline-offset: 4px;
1947
2060
  }
1948
2061
 
2062
+ .opacity-0 {
2063
+ opacity: 0;
2064
+ }
2065
+
1949
2066
  .opacity-30 {
1950
2067
  opacity: .3;
1951
2068
  }
1952
2069
 
2070
+ .opacity-40 {
2071
+ opacity: .4;
2072
+ }
2073
+
1953
2074
  .opacity-50 {
1954
2075
  opacity: .5;
1955
2076
  }
@@ -1958,6 +2079,10 @@
1958
2079
  opacity: .8;
1959
2080
  }
1960
2081
 
2082
+ .opacity-100 {
2083
+ opacity: 1;
2084
+ }
2085
+
1961
2086
  .mix-blend-color {
1962
2087
  mix-blend-mode: color;
1963
2088
  }
@@ -1,6 +1,90 @@
1
- import { useState, useRef, useCallback, useEffect, useMemo } from "react";
1
+ import { useEffect, useState, useRef, useCallback, useMemo } from "react";
2
2
  import { useQueryClient, useMutation, useQuery } from "@tanstack/react-query";
3
- import { n as ensureAutomationRun, l as createAutomationRun, h as cancelAutomationRun, f as automationStatuses, t as getAutomationRun } from "./automations-DNWw-HT7.js";
3
+ import { H as ensureAutomationRun, F as createAutomationRun, A as cancelAutomationRun, z as automationStatuses, K as getAutomationRun } from "./automations-Bb9nlU05.js";
4
+ const QUEUED_DISCOVERY_POLL_MS = 2e3;
5
+ const IDLE_DISCOVERY_POLL_MS = 1e4;
6
+ const HIDDEN_DISCOVERY_POLL_MS = 3e4;
7
+ function isDocumentHidden() {
8
+ return typeof document !== "undefined" && document.visibilityState === "hidden";
9
+ }
10
+ function discoveryPollDelayMs({
11
+ hasQueuedPending,
12
+ hidden
13
+ }) {
14
+ if (hidden) return HIDDEN_DISCOVERY_POLL_MS;
15
+ if (hasQueuedPending) return QUEUED_DISCOVERY_POLL_MS;
16
+ return IDLE_DISCOVERY_POLL_MS;
17
+ }
18
+ function useDiscoveryPoll({
19
+ enabled,
20
+ isTransportActive,
21
+ isRefreshInFlight,
22
+ hasQueuedPending,
23
+ refresh
24
+ }) {
25
+ useEffect(() => {
26
+ if (!enabled || typeof window === "undefined") {
27
+ return;
28
+ }
29
+ let timer = null;
30
+ let disposed = false;
31
+ const clearTimer = () => {
32
+ if (timer != null) {
33
+ window.clearTimeout(timer);
34
+ timer = null;
35
+ }
36
+ };
37
+ const schedule = () => {
38
+ if (disposed) return;
39
+ clearTimer();
40
+ timer = window.setTimeout(
41
+ () => {
42
+ timer = null;
43
+ if (isTransportActive() || isRefreshInFlight()) {
44
+ schedule();
45
+ return;
46
+ }
47
+ void refresh().finally(() => {
48
+ schedule();
49
+ });
50
+ },
51
+ discoveryPollDelayMs({
52
+ hidden: isDocumentHidden(),
53
+ hasQueuedPending: hasQueuedPending()
54
+ })
55
+ );
56
+ };
57
+ const refreshNow = () => {
58
+ if (disposed || isTransportActive() || isRefreshInFlight() || isDocumentHidden()) {
59
+ schedule();
60
+ return;
61
+ }
62
+ clearTimer();
63
+ void refresh().finally(() => {
64
+ schedule();
65
+ });
66
+ };
67
+ const onVisibilityChange = () => {
68
+ if (isDocumentHidden()) {
69
+ schedule();
70
+ return;
71
+ }
72
+ refreshNow();
73
+ };
74
+ const onFocus = () => {
75
+ refreshNow();
76
+ };
77
+ schedule();
78
+ document.addEventListener("visibilitychange", onVisibilityChange);
79
+ window.addEventListener("focus", onFocus);
80
+ return () => {
81
+ disposed = true;
82
+ clearTimer();
83
+ document.removeEventListener("visibilitychange", onVisibilityChange);
84
+ window.removeEventListener("focus", onFocus);
85
+ };
86
+ }, [enabled, hasQueuedPending, isRefreshInFlight, isTransportActive, refresh]);
87
+ }
4
88
  const DEFAULT_SESSION_ID = "default";
5
89
  const DEFAULT_HISTORY_LIMIT = 10;
6
90
  const DEFAULT_MAX_BUFFERED_MESSAGES = 100;
@@ -17,6 +101,10 @@ function useAgentChat({
17
101
  disabled = false,
18
102
  autoLoadHistory = true,
19
103
  autoSubscribe = true,
104
+ watchdog = false,
105
+ watchdogTimeoutMs = 3e4,
106
+ watchdogPollMs = 1e3,
107
+ discoveryPoll = false,
20
108
  idFactory = defaultIDFactory,
21
109
  onMessagesChange,
22
110
  onTurnComplete,
@@ -35,10 +123,18 @@ function useAgentChat({
35
123
  const [isSubmitting, setIsSubmitting] = useState(false);
36
124
  const [isStreaming, setIsStreaming] = useState(false);
37
125
  const [isAborting, setIsAborting] = useState(false);
126
+ const [historyLoaded, setHistoryLoaded] = useState(false);
38
127
  const messagesRef = useRef(messages);
39
128
  const subscriptionRef = useRef(null);
40
129
  const activeRequestIdRef = useRef(void 0);
41
130
  const mountedRef = useRef(true);
131
+ const refreshInFlightRef = useRef(false);
132
+ const isStreamingRef = useRef(false);
133
+ const isSubmittingRef = useRef(false);
134
+ const isAbortingRef = useRef(false);
135
+ const isLoadingHistoryRef = useRef(false);
136
+ const watchdogTimerRef = useRef(null);
137
+ const identityKeyRef = useRef(`${agentId ?? ""}::${sessionId}`);
42
138
  const setMessages = useCallback(
43
139
  (updater) => {
44
140
  setMessagesState((current) => {
@@ -57,8 +153,19 @@ function useAgentChat({
57
153
  mountedRef.current = false;
58
154
  subscriptionRef.current?.close();
59
155
  subscriptionRef.current = null;
156
+ if (watchdogTimerRef.current != null) {
157
+ clearTimeout(watchdogTimerRef.current);
158
+ watchdogTimerRef.current = null;
159
+ }
60
160
  };
61
161
  }, []);
162
+ useEffect(() => {
163
+ isStreamingRef.current = isStreaming;
164
+ isSubmittingRef.current = isSubmitting;
165
+ isAbortingRef.current = isAborting;
166
+ isLoadingHistoryRef.current = isLoadingHistory;
167
+ identityKeyRef.current = `${agentId ?? ""}::${sessionId}`;
168
+ });
62
169
  const handleError = useCallback(
63
170
  (err) => {
64
171
  if (!mountedRef.current) return;
@@ -77,18 +184,92 @@ function useAgentChat({
77
184
  );
78
185
  const loadHistory = useCallback(async () => {
79
186
  if (disabled) return;
187
+ const requestedIdentity = `${agentId ?? ""}::${sessionId}`;
80
188
  setIsLoadingHistory(true);
81
189
  setError(null);
82
190
  try {
83
191
  const result = await transport.loadHistory({ agentId, sessionId, limit: historyLimit });
84
- if (!mountedRef.current) return;
192
+ if (!mountedRef.current || identityKeyRef.current !== requestedIdentity) return;
85
193
  applyHistoryResult(result);
194
+ setHistoryLoaded(true);
86
195
  } catch (err) {
196
+ if (identityKeyRef.current !== requestedIdentity) return;
87
197
  handleError(err);
88
198
  } finally {
89
- if (mountedRef.current) setIsLoadingHistory(false);
199
+ if (mountedRef.current && identityKeyRef.current === requestedIdentity) setIsLoadingHistory(false);
90
200
  }
91
201
  }, [agentId, applyHistoryResult, disabled, handleError, historyLimit, sessionId, transport]);
202
+ const clearWatchdog = useCallback(() => {
203
+ if (watchdogTimerRef.current != null) {
204
+ clearTimeout(watchdogTimerRef.current);
205
+ watchdogTimerRef.current = null;
206
+ }
207
+ }, []);
208
+ const requestStillActive = useCallback((requestId) => {
209
+ return messagesRef.current.some(
210
+ (message) => message.role === "user" && message.requestId === requestId && isActiveStatus(message.status)
211
+ );
212
+ }, []);
213
+ const reconcileFromHistory = useCallback(async () => {
214
+ if (disabled || refreshInFlightRef.current) return;
215
+ const requestedIdentity = `${agentId ?? ""}::${sessionId}`;
216
+ refreshInFlightRef.current = true;
217
+ try {
218
+ const result = await transport.loadHistory({ agentId, sessionId, limit: historyLimit });
219
+ if (!mountedRef.current || identityKeyRef.current !== requestedIdentity) return;
220
+ applyHistoryResult(result);
221
+ setHistoryLoaded(true);
222
+ } catch {
223
+ } finally {
224
+ refreshInFlightRef.current = false;
225
+ }
226
+ }, [agentId, applyHistoryResult, disabled, historyLimit, sessionId, transport]);
227
+ const settleStaleRequest = useCallback(
228
+ (requestId) => {
229
+ setMessages(
230
+ (current) => current.map(
231
+ (message) => message.role === "user" && message.requestId === requestId && isActiveStatus(message.status) ? { ...message, status: "interrupted" } : message
232
+ )
233
+ );
234
+ handleError(new Error("Lost connection to the agent. The response may still be processing."));
235
+ },
236
+ [handleError, setMessages]
237
+ );
238
+ const startWatchdog = useCallback(
239
+ (requestId) => {
240
+ if (!watchdog) return;
241
+ clearWatchdog();
242
+ const startedAt = Date.now();
243
+ const tick = () => {
244
+ watchdogTimerRef.current = window.setTimeout(async () => {
245
+ watchdogTimerRef.current = null;
246
+ if (!mountedRef.current) return;
247
+ if (!requestStillActive(requestId)) return;
248
+ if (Date.now() - startedAt >= watchdogTimeoutMs) {
249
+ settleStaleRequest(requestId);
250
+ return;
251
+ }
252
+ await reconcileFromHistory();
253
+ if (!mountedRef.current || !requestStillActive(requestId)) return;
254
+ if (Date.now() - startedAt >= watchdogTimeoutMs) {
255
+ settleStaleRequest(requestId);
256
+ return;
257
+ }
258
+ tick();
259
+ }, watchdogPollMs);
260
+ };
261
+ tick();
262
+ },
263
+ [
264
+ clearWatchdog,
265
+ reconcileFromHistory,
266
+ requestStillActive,
267
+ settleStaleRequest,
268
+ watchdog,
269
+ watchdogPollMs,
270
+ watchdogTimeoutMs
271
+ ]
272
+ );
92
273
  const loadEarlier = useCallback(async () => {
93
274
  if (disabled || isLoadingEarlier || !page?.hasMoreBefore || !page.beforeCursor) return;
94
275
  const loadEarlierImpl = transport.loadEarlier ?? transport.loadHistory;
@@ -114,6 +295,7 @@ function useAgentChat({
114
295
  const settleRequest = useCallback(
115
296
  (requestId, status, errorMessage) => {
116
297
  if (!requestId) return;
298
+ clearWatchdog();
117
299
  setMessages(
118
300
  (current) => current.map(
119
301
  (messageItem) => messageItem.role === "user" && messageItem.requestId === requestId ? { ...messageItem, status, error: errorMessage ?? messageItem.error } : messageItem
@@ -125,7 +307,7 @@ function useAgentChat({
125
307
  if (!transport.subscribe) return;
126
308
  void loadHistory();
127
309
  },
128
- [loadHistory, onTurnComplete, setMessages, transport.subscribe]
310
+ [clearWatchdog, loadHistory, onTurnComplete, setMessages, transport.subscribe]
129
311
  );
130
312
  const applyStreamEvent = useCallback(
131
313
  (event) => {
@@ -189,6 +371,7 @@ function useAgentChat({
189
371
  (requestId) => {
190
372
  if (!autoSubscribe || !transport.subscribe || !requestId) return;
191
373
  subscriptionRef.current?.close();
374
+ clearWatchdog();
192
375
  activeRequestIdRef.current = requestId;
193
376
  setIsStreaming(true);
194
377
  subscriptionRef.current = transport.subscribe(
@@ -196,18 +379,36 @@ function useAgentChat({
196
379
  {
197
380
  onEvent: applyStreamEvent,
198
381
  onError: (err) => {
199
- handleError(err);
200
- if (mountedRef.current) setIsStreaming(false);
382
+ if (!mountedRef.current) return;
383
+ setIsStreaming(false);
384
+ if (watchdog && activeRequestIdRef.current === requestId && requestStillActive(requestId)) {
385
+ startWatchdog(requestId);
386
+ } else {
387
+ handleError(err);
388
+ }
201
389
  },
202
390
  onClose: () => {
203
- if (mountedRef.current && activeRequestIdRef.current === requestId) {
204
- setIsStreaming(false);
391
+ if (!mountedRef.current || activeRequestIdRef.current !== requestId) return;
392
+ setIsStreaming(false);
393
+ if (watchdog && requestStillActive(requestId)) {
394
+ startWatchdog(requestId);
205
395
  }
206
396
  }
207
397
  }
208
398
  );
209
399
  },
210
- [agentId, applyStreamEvent, autoSubscribe, handleError, sessionId, transport]
400
+ [
401
+ agentId,
402
+ applyStreamEvent,
403
+ autoSubscribe,
404
+ clearWatchdog,
405
+ handleError,
406
+ requestStillActive,
407
+ sessionId,
408
+ startWatchdog,
409
+ transport,
410
+ watchdog
411
+ ]
211
412
  );
212
413
  const sendMessage = useCallback(
213
414
  async (message) => {
@@ -272,6 +473,7 @@ function useAgentChat({
272
473
  await transport.abort?.({ agentId, sessionId, requestId });
273
474
  subscriptionRef.current?.close();
274
475
  subscriptionRef.current = null;
476
+ clearWatchdog();
275
477
  setIsStreaming(false);
276
478
  setMessages(
277
479
  (current) => current.map(
@@ -283,7 +485,7 @@ function useAgentChat({
283
485
  } finally {
284
486
  if (mountedRef.current) setIsAborting(false);
285
487
  }
286
- }, [agentId, disabled, handleError, isAborting, sessionId, setMessages, transport]);
488
+ }, [agentId, clearWatchdog, disabled, handleError, isAborting, sessionId, setMessages, transport]);
287
489
  const attachFiles = useCallback(
288
490
  async (files) => {
289
491
  if (files.length === 0) return;
@@ -327,6 +529,24 @@ function useAgentChat({
327
529
  const clearPendingFiles = useCallback(() => {
328
530
  setPendingFiles([]);
329
531
  }, []);
532
+ const identityKey = `${agentId ?? ""}::${sessionId}`;
533
+ const previousIdentityRef = useRef(identityKey);
534
+ useEffect(() => {
535
+ if (previousIdentityRef.current === identityKey) return;
536
+ previousIdentityRef.current = identityKey;
537
+ subscriptionRef.current?.close();
538
+ subscriptionRef.current = null;
539
+ clearWatchdog();
540
+ activeRequestIdRef.current = void 0;
541
+ setMessages([]);
542
+ setPage(initialPage ?? null);
543
+ setTokenUsage(null);
544
+ setError(null);
545
+ setIsStreaming(false);
546
+ setIsSubmitting(false);
547
+ setIsAborting(false);
548
+ setHistoryLoaded(false);
549
+ }, [identityKey, clearWatchdog, initialPage, setMessages]);
330
550
  useEffect(() => {
331
551
  if (!autoLoadHistory) return;
332
552
  void loadHistory();
@@ -342,6 +562,22 @@ function useAgentChat({
342
562
  const canSubmit = !disabled && !isSubmitting && !hasUploadingFiles && !hasFailedUploads && (input.trim().length > 0 || pendingFiles.length > 0) && (!hasActive || queueLength < maxQueuedMessages);
343
563
  const canAbort = !disabled && !isAborting && hasActive;
344
564
  const phase = derivePhase({ isLoadingHistory, isLoadingEarlier, isSubmitting, isStreaming, isAborting, error });
565
+ const isTransportActiveCb = useCallback(
566
+ () => isStreamingRef.current || isSubmittingRef.current || isAbortingRef.current,
567
+ []
568
+ );
569
+ const isRefreshInFlightCb = useCallback(() => isLoadingHistoryRef.current || refreshInFlightRef.current, []);
570
+ const hasQueuedPendingCb = useCallback(
571
+ () => messagesRef.current.some((message) => message.role === "user" && message.status === "queued"),
572
+ []
573
+ );
574
+ useDiscoveryPoll({
575
+ enabled: discoveryPoll && autoLoadHistory && historyLoaded && !disabled,
576
+ isTransportActive: isTransportActiveCb,
577
+ isRefreshInFlight: isRefreshInFlightCb,
578
+ hasQueuedPending: hasQueuedPendingCb,
579
+ refresh: reconcileFromHistory
580
+ });
345
581
  return {
346
582
  messages,
347
583
  turns,
@@ -593,8 +829,16 @@ function buildAgentChatTurns(messages) {
593
829
  turns.push({ id: message.requestId ?? message.id, requestId: message.requestId, assistants: [message] });
594
830
  }
595
831
  }
832
+ turns.sort((a, b) => turnRank(a) - turnRank(b));
833
+ const first = turns[0];
834
+ if (turns.length > 1 && first && !first.user && (first.systems?.length ?? 0) === 0 && first.assistants.length > 0) {
835
+ return turns.slice(1);
836
+ }
596
837
  return turns;
597
838
  }
839
+ function turnRank(turn) {
840
+ return turn.user?.status === "queued" ? 1 : 0;
841
+ }
598
842
  function accumulateTokenUsage(current, incoming) {
599
843
  if (!current) return incoming;
600
844
  return {
@@ -1,7 +1,6 @@
1
1
  import { useState, useCallback, useEffect, useMemo } from "react";
2
2
  import { useQueryClient, useQuery, useMutation } from "@tanstack/react-query";
3
- import { D as listRunsByAgent, f as automationStatuses, l as createAutomationRun, o as onInitMessage } from "./automations-DNWw-HT7.js";
4
- import { u as uploadFile, i as pbSql, k as pbUpdateRecord } from "./api-Bm4dzr1n.js";
3
+ import { T as listRunsByAgent, z as automationStatuses, F as createAutomationRun, y as uploadFile, o as onInitMessage, t as pbSql, v as pbUpdateRecord } from "./automations-Bb9nlU05.js";
5
4
  function useAgentChatSessions({
6
5
  agentId,
7
6
  transport,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumerahq/ui",
3
- "version": "0.9.1",
3
+ "version": "0.10.0-dev.0",
4
4
  "type": "module",
5
5
  "sideEffects": [
6
6
  "*.css"