@steerable/agent-ui 0.6.62 → 0.6.63

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.
@@ -32,6 +32,7 @@ export function useChatList(options) {
32
32
  const pageRef = useRef(1);
33
33
  const hasMoreRef = useRef(false);
34
34
  const loadingMoreRef = useRef(false);
35
+ const failedPageRef = useRef(null);
35
36
  const mountedRef = useRef(true);
36
37
  useEffect(() => {
37
38
  mountedRef.current = true;
@@ -49,6 +50,7 @@ export function useChatList(options) {
49
50
  setHasMore(res.hasMore);
50
51
  hasMoreRef.current = res.hasMore;
51
52
  pageRef.current = 1;
53
+ failedPageRef.current = null;
52
54
  setError(null);
53
55
  }
54
56
  catch (err) {
@@ -72,12 +74,15 @@ export function useChatList(options) {
72
74
  }
73
75
  }, []);
74
76
  const loadMoreChats = useCallback(async () => {
75
- if (!hasMoreRef.current || loadingMoreRef.current)
77
+ const next = pageRef.current + 1;
78
+ if (!hasMoreRef.current ||
79
+ loadingMoreRef.current ||
80
+ failedPageRef.current === next) {
76
81
  return;
82
+ }
77
83
  loadingMoreRef.current = true;
78
84
  setLoadingMore(true);
79
85
  try {
80
- const next = pageRef.current + 1;
81
86
  const res = await transportRef.current.listChats({ page: next, pageSize });
82
87
  if (!mountedRef.current)
83
88
  return;
@@ -94,8 +99,10 @@ export function useChatList(options) {
94
99
  setHasMore(res.hasMore);
95
100
  hasMoreRef.current = res.hasMore;
96
101
  pageRef.current = next;
102
+ failedPageRef.current = null;
97
103
  }
98
104
  catch (err) {
105
+ failedPageRef.current = next;
99
106
  if (mountedRef.current)
100
107
  setError(err instanceof Error ? err : new Error(String(err)));
101
108
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@steerable/agent-ui",
3
- "version": "0.6.62",
3
+ "version": "0.6.63",
4
4
  "description": "Steerable framework Tier 4 — headless React hooks (useAgentSession, useChatStream, useToolCallStatus) and Tailwind-themed components (ChatPanel, MessageList, OrchestrationPlanCard, ToolCallRenderer, SSEStreamView) for embedding Steerable agents in any React app.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://steerableframework.com/storybook/",
@@ -57,7 +57,7 @@
57
57
  "README.md"
58
58
  ],
59
59
  "dependencies": {
60
- "@steerable/agent-protocol": "0.6.62"
60
+ "@steerable/agent-protocol": "0.6.63"
61
61
  },
62
62
  "peerDependencies": {
63
63
  "react": ">=18.0.0",
@@ -99,6 +99,36 @@ describe('useChatList', () => {
99
99
  expect(result.current.chats.map((c) => c.id)).toEqual(['c1', 'c2', 'c3']);
100
100
  });
101
101
 
102
+ it('does not automatically retry a failed page until the list is refreshed', async () => {
103
+ const { transport, listChats } = makeTransport();
104
+ listChats.mockImplementation(async ({ page }: { page: number }) => {
105
+ if (page === 2) throw new Error('second page unavailable');
106
+ return {
107
+ chats: [
108
+ { id: 'c1', title: 'First' },
109
+ { id: 'c2', title: 'Second' },
110
+ ],
111
+ hasMore: true,
112
+ };
113
+ });
114
+ const { result } = renderHook(() => useChatList({ transport, pageSize: 50 }));
115
+ await waitFor(() => expect(result.current.chats.length).toBe(2));
116
+
117
+ await act(async () => {
118
+ await result.current.loadMoreChats();
119
+ await result.current.loadMoreChats();
120
+ });
121
+ expect(listChats).toHaveBeenCalledTimes(2);
122
+ expect(result.current.error?.message).toBe('second page unavailable');
123
+ expect(result.current.chats.map((chat) => chat.id)).toEqual(['c1', 'c2']);
124
+
125
+ await act(async () => {
126
+ await result.current.refreshChats();
127
+ await result.current.loadMoreChats();
128
+ });
129
+ expect(listChats).toHaveBeenCalledTimes(4);
130
+ });
131
+
102
132
  it('patchChatTitle updates without re-fetching', async () => {
103
133
  const { transport, listChats } = makeTransport();
104
134
  const { result } = renderHook(() => useChatList({ transport, pageSize: 50 }));
@@ -90,6 +90,7 @@ export function useChatList<TChat, TAgent>(
90
90
  const pageRef = useRef(1);
91
91
  const hasMoreRef = useRef(false);
92
92
  const loadingMoreRef = useRef(false);
93
+ const failedPageRef = useRef<number | null>(null);
93
94
  const mountedRef = useRef(true);
94
95
 
95
96
  useEffect(() => {
@@ -108,6 +109,7 @@ export function useChatList<TChat, TAgent>(
108
109
  setHasMore(res.hasMore);
109
110
  hasMoreRef.current = res.hasMore;
110
111
  pageRef.current = 1;
112
+ failedPageRef.current = null;
111
113
  setError(null);
112
114
  } catch (err) {
113
115
  if (mountedRef.current) setError(err instanceof Error ? err : new Error(String(err)));
@@ -126,11 +128,17 @@ export function useChatList<TChat, TAgent>(
126
128
  }, []);
127
129
 
128
130
  const loadMoreChats = useCallback(async () => {
129
- if (!hasMoreRef.current || loadingMoreRef.current) return;
131
+ const next = pageRef.current + 1;
132
+ if (
133
+ !hasMoreRef.current ||
134
+ loadingMoreRef.current ||
135
+ failedPageRef.current === next
136
+ ) {
137
+ return;
138
+ }
130
139
  loadingMoreRef.current = true;
131
140
  setLoadingMore(true);
132
141
  try {
133
- const next = pageRef.current + 1;
134
142
  const res = await transportRef.current.listChats({ page: next, pageSize });
135
143
  if (!mountedRef.current) return;
136
144
  setChats((prev) => {
@@ -147,7 +155,9 @@ export function useChatList<TChat, TAgent>(
147
155
  setHasMore(res.hasMore);
148
156
  hasMoreRef.current = res.hasMore;
149
157
  pageRef.current = next;
158
+ failedPageRef.current = null;
150
159
  } catch (err) {
160
+ failedPageRef.current = next;
151
161
  if (mountedRef.current) setError(err instanceof Error ? err : new Error(String(err)));
152
162
  } finally {
153
163
  loadingMoreRef.current = false;