@stigmer/react 0.0.100 → 0.1.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 (262) hide show
  1. package/agent/AgentPicker.js +1 -1
  2. package/agent/AgentPicker.js.map +1 -1
  3. package/agent/__tests__/useDefaultAgent.test.d.ts +2 -0
  4. package/agent/__tests__/useDefaultAgent.test.d.ts.map +1 -0
  5. package/agent/__tests__/useDefaultAgent.test.js +252 -0
  6. package/agent/__tests__/useDefaultAgent.test.js.map +1 -0
  7. package/agent/useAgent.d.ts +2 -0
  8. package/agent/useAgent.d.ts.map +1 -1
  9. package/agent/useAgent.js +14 -37
  10. package/agent/useAgent.js.map +1 -1
  11. package/agent/useAgentCount.d.ts +1 -1
  12. package/agent/useAgentCount.d.ts.map +1 -1
  13. package/agent/useAgentList.d.ts +2 -2
  14. package/agent/useAgentList.d.ts.map +1 -1
  15. package/agent/useDefaultAgent.d.ts +6 -2
  16. package/agent/useDefaultAgent.d.ts.map +1 -1
  17. package/agent/useDefaultAgent.js +51 -31
  18. package/agent/useDefaultAgent.js.map +1 -1
  19. package/agent-instance/useAgentInstance.d.ts +2 -0
  20. package/agent-instance/useAgentInstance.d.ts.map +1 -1
  21. package/agent-instance/useAgentInstance.js +6 -33
  22. package/agent-instance/useAgentInstance.js.map +1 -1
  23. package/agent-instance/useAgentInstanceList.d.ts +2 -0
  24. package/agent-instance/useAgentInstanceList.d.ts.map +1 -1
  25. package/agent-instance/useAgentInstanceList.js +22 -40
  26. package/agent-instance/useAgentInstanceList.js.map +1 -1
  27. package/api-key/useApiKeyList.d.ts +2 -0
  28. package/api-key/useApiKeyList.d.ts.map +1 -1
  29. package/api-key/useApiKeyList.js +3 -27
  30. package/api-key/useApiKeyList.js.map +1 -1
  31. package/composer/ComposerToolbar.d.ts +1 -5
  32. package/composer/ComposerToolbar.d.ts.map +1 -1
  33. package/composer/ComposerToolbar.js +3 -4
  34. package/composer/ComposerToolbar.js.map +1 -1
  35. package/composer/ContextChip.d.ts +1 -1
  36. package/composer/ContextChip.d.ts.map +1 -1
  37. package/composer/ContextChip.js +1 -0
  38. package/composer/ContextChip.js.map +1 -1
  39. package/composer/SessionComposer.d.ts.map +1 -1
  40. package/composer/SessionComposer.js +77 -4
  41. package/composer/SessionComposer.js.map +1 -1
  42. package/composer/icons.d.ts +1 -0
  43. package/composer/icons.d.ts.map +1 -1
  44. package/composer/icons.js +3 -0
  45. package/composer/icons.js.map +1 -1
  46. package/environment/useEnvironment.d.ts +2 -0
  47. package/environment/useEnvironment.d.ts.map +1 -1
  48. package/environment/useEnvironment.js +6 -33
  49. package/environment/useEnvironment.js.map +1 -1
  50. package/environment/useEnvironmentList.d.ts +2 -0
  51. package/environment/useEnvironmentList.d.ts.map +1 -1
  52. package/environment/useEnvironmentList.js +23 -53
  53. package/environment/useEnvironmentList.js.map +1 -1
  54. package/execution/ArtifactPreviewModal.js +1 -1
  55. package/execution/ArtifactPreviewModal.js.map +1 -1
  56. package/execution/useArtifactContent.d.ts +4 -2
  57. package/execution/useArtifactContent.d.ts.map +1 -1
  58. package/execution/useArtifactContent.js +23 -45
  59. package/execution/useArtifactContent.js.map +1 -1
  60. package/iam-policy/usePrincipalsCount.d.ts +2 -0
  61. package/iam-policy/usePrincipalsCount.d.ts.map +1 -1
  62. package/iam-policy/usePrincipalsCount.js +7 -46
  63. package/iam-policy/usePrincipalsCount.js.map +1 -1
  64. package/iam-policy/useResourceAccess.d.ts +2 -0
  65. package/iam-policy/useResourceAccess.d.ts.map +1 -1
  66. package/iam-policy/useResourceAccess.js +12 -47
  67. package/iam-policy/useResourceAccess.js.map +1 -1
  68. package/identity-provider/useIdentityProvider.d.ts +2 -0
  69. package/identity-provider/useIdentityProvider.d.ts.map +1 -1
  70. package/identity-provider/useIdentityProvider.js +3 -33
  71. package/identity-provider/useIdentityProvider.js.map +1 -1
  72. package/identity-provider/useIdentityProviderList.d.ts +2 -0
  73. package/identity-provider/useIdentityProviderList.d.ts.map +1 -1
  74. package/identity-provider/useIdentityProviderList.js +7 -33
  75. package/identity-provider/useIdentityProviderList.js.map +1 -1
  76. package/index.d.ts +2 -2
  77. package/index.d.ts.map +1 -1
  78. package/index.js +1 -1
  79. package/index.js.map +1 -1
  80. package/internal/__tests__/useFetch.test.d.ts +2 -0
  81. package/internal/__tests__/useFetch.test.d.ts.map +1 -0
  82. package/internal/__tests__/useFetch.test.js +95 -0
  83. package/internal/__tests__/useFetch.test.js.map +1 -0
  84. package/internal/useFetch.d.ts +51 -0
  85. package/internal/useFetch.d.ts.map +1 -0
  86. package/internal/useFetch.js +75 -0
  87. package/internal/useFetch.js.map +1 -0
  88. package/invitation/useInvitationPreview.d.ts +2 -0
  89. package/invitation/useInvitationPreview.d.ts.map +1 -1
  90. package/invitation/useInvitationPreview.js +5 -35
  91. package/invitation/useInvitationPreview.js.map +1 -1
  92. package/invitation/useOrgInvitations.d.ts +2 -0
  93. package/invitation/useOrgInvitations.d.ts.map +1 -1
  94. package/invitation/useOrgInvitations.js +6 -34
  95. package/invitation/useOrgInvitations.js.map +1 -1
  96. package/library/ResourceListView.d.ts +2 -2
  97. package/library/ResourceListView.d.ts.map +1 -1
  98. package/library/ResourceListView.js +1 -1
  99. package/library/ResourceListView.js.map +1 -1
  100. package/library/useDetectSkillPackage.d.ts +1 -1
  101. package/library/useDetectSkillPackage.d.ts.map +1 -1
  102. package/mcp-server/McpServerPicker.js +1 -1
  103. package/mcp-server/McpServerPicker.js.map +1 -1
  104. package/mcp-server/useMcpServer.d.ts +2 -0
  105. package/mcp-server/useMcpServer.d.ts.map +1 -1
  106. package/mcp-server/useMcpServer.js +13 -37
  107. package/mcp-server/useMcpServer.js.map +1 -1
  108. package/mcp-server/useMcpServerCount.d.ts +1 -1
  109. package/mcp-server/useMcpServerCount.d.ts.map +1 -1
  110. package/mcp-server/useMcpServerList.d.ts +2 -2
  111. package/mcp-server/useMcpServerList.d.ts.map +1 -1
  112. package/mcp-server/useOAuthGrantStatus.d.ts +2 -0
  113. package/mcp-server/useOAuthGrantStatus.d.ts.map +1 -1
  114. package/mcp-server/useOAuthGrantStatus.js +14 -61
  115. package/mcp-server/useOAuthGrantStatus.js.map +1 -1
  116. package/mcp-server/useOrgOAuthApp.d.ts +2 -0
  117. package/mcp-server/useOrgOAuthApp.d.ts.map +1 -1
  118. package/mcp-server/useOrgOAuthApp.js +19 -43
  119. package/mcp-server/useOrgOAuthApp.js.map +1 -1
  120. package/oauth-app/useOAuthAppList.d.ts +2 -0
  121. package/oauth-app/useOAuthAppList.d.ts.map +1 -1
  122. package/oauth-app/useOAuthAppList.js +6 -34
  123. package/oauth-app/useOAuthAppList.js.map +1 -1
  124. package/organization/useOrganization.d.ts +2 -0
  125. package/organization/useOrganization.d.ts.map +1 -1
  126. package/organization/useOrganization.js +3 -33
  127. package/organization/useOrganization.js.map +1 -1
  128. package/package.json +4 -4
  129. package/platform-client/usePlatformClient.d.ts +2 -0
  130. package/platform-client/usePlatformClient.d.ts.map +1 -1
  131. package/platform-client/usePlatformClient.js +3 -33
  132. package/platform-client/usePlatformClient.js.map +1 -1
  133. package/platform-client/usePlatformClientList.d.ts +2 -0
  134. package/platform-client/usePlatformClientList.d.ts.map +1 -1
  135. package/platform-client/usePlatformClientList.js +6 -34
  136. package/platform-client/usePlatformClientList.js.map +1 -1
  137. package/runner/RunnerListPanel.d.ts.map +1 -1
  138. package/runner/RunnerListPanel.js +21 -5
  139. package/runner/RunnerListPanel.js.map +1 -1
  140. package/runner/__tests__/phase.test.d.ts +2 -0
  141. package/runner/__tests__/phase.test.d.ts.map +1 -0
  142. package/runner/__tests__/phase.test.js +33 -0
  143. package/runner/__tests__/phase.test.js.map +1 -0
  144. package/runner/__tests__/useRunnerList.test.d.ts +2 -0
  145. package/runner/__tests__/useRunnerList.test.d.ts.map +1 -0
  146. package/runner/__tests__/useRunnerList.test.js +68 -0
  147. package/runner/__tests__/useRunnerList.test.js.map +1 -0
  148. package/runner/index.d.ts +3 -1
  149. package/runner/index.d.ts.map +1 -1
  150. package/runner/index.js +2 -1
  151. package/runner/index.js.map +1 -1
  152. package/runner/phase.d.ts +13 -0
  153. package/runner/phase.d.ts.map +1 -1
  154. package/runner/phase.js +15 -0
  155. package/runner/phase.js.map +1 -1
  156. package/runner/useRunnerCredential.d.ts +66 -0
  157. package/runner/useRunnerCredential.d.ts.map +1 -0
  158. package/runner/useRunnerCredential.js +50 -0
  159. package/runner/useRunnerCredential.js.map +1 -0
  160. package/runner/useRunnerList.d.ts +21 -0
  161. package/runner/useRunnerList.d.ts.map +1 -1
  162. package/runner/useRunnerList.js +9 -36
  163. package/runner/useRunnerList.js.map +1 -1
  164. package/search/useResourceCount.d.ts +2 -1
  165. package/search/useResourceCount.d.ts.map +1 -1
  166. package/search/useResourceCount.js +13 -37
  167. package/search/useResourceCount.js.map +1 -1
  168. package/search/useResourceList.d.ts +2 -1
  169. package/search/useResourceList.d.ts.map +1 -1
  170. package/search/useResourceList.js +26 -46
  171. package/search/useResourceList.js.map +1 -1
  172. package/search/useResourceSearch.d.ts +4 -2
  173. package/search/useResourceSearch.d.ts.map +1 -1
  174. package/search/useResourceSearch.js +7 -27
  175. package/search/useResourceSearch.js.map +1 -1
  176. package/session/useNewSessionFlow.d.ts.map +1 -1
  177. package/session/useNewSessionFlow.js +26 -1
  178. package/session/useNewSessionFlow.js.map +1 -1
  179. package/session/useSession.d.ts +3 -1
  180. package/session/useSession.d.ts.map +1 -1
  181. package/session/useSession.js +3 -33
  182. package/session/useSession.js.map +1 -1
  183. package/session/useSessionExecutions.d.ts +3 -1
  184. package/session/useSessionExecutions.d.ts.map +1 -1
  185. package/session/useSessionExecutions.js +6 -34
  186. package/session/useSessionExecutions.js.map +1 -1
  187. package/session/useSessionList.d.ts +5 -3
  188. package/session/useSessionList.d.ts.map +1 -1
  189. package/session/useSessionList.js +9 -31
  190. package/session/useSessionList.js.map +1 -1
  191. package/skill/SkillPicker.js +1 -1
  192. package/skill/SkillPicker.js.map +1 -1
  193. package/skill/useSkill.d.ts +2 -0
  194. package/skill/useSkill.d.ts.map +1 -1
  195. package/skill/useSkill.js +13 -37
  196. package/skill/useSkill.js.map +1 -1
  197. package/skill/useSkillCount.d.ts +1 -1
  198. package/skill/useSkillCount.d.ts.map +1 -1
  199. package/skill/useSkillList.d.ts +2 -2
  200. package/skill/useSkillList.d.ts.map +1 -1
  201. package/src/agent/AgentPicker.tsx +1 -1
  202. package/src/agent/__tests__/useDefaultAgent.test.tsx +308 -0
  203. package/src/agent/useAgent.ts +19 -41
  204. package/src/agent/useAgentCount.ts +1 -1
  205. package/src/agent/useAgentList.ts +2 -2
  206. package/src/agent/useDefaultAgent.ts +67 -35
  207. package/src/agent-instance/useAgentInstance.ts +13 -37
  208. package/src/agent-instance/useAgentInstanceList.ts +31 -47
  209. package/src/api-key/useApiKeyList.ts +9 -32
  210. package/src/composer/ComposerToolbar.tsx +1 -22
  211. package/src/composer/ContextChip.tsx +2 -1
  212. package/src/composer/SessionComposer.tsx +206 -5
  213. package/src/composer/icons.tsx +27 -0
  214. package/src/environment/useEnvironment.ts +13 -37
  215. package/src/environment/useEnvironmentList.ts +31 -58
  216. package/src/execution/ArtifactPreviewModal.tsx +2 -2
  217. package/src/execution/useArtifactContent.ts +48 -65
  218. package/src/iam-policy/usePrincipalsCount.ts +17 -53
  219. package/src/iam-policy/useResourceAccess.ts +18 -55
  220. package/src/identity-provider/useIdentityProvider.ts +9 -39
  221. package/src/identity-provider/useIdentityProviderList.ts +14 -40
  222. package/src/index.ts +4 -0
  223. package/src/internal/__tests__/useFetch.test.ts +133 -0
  224. package/src/internal/useFetch.ts +121 -0
  225. package/src/invitation/useInvitationPreview.ts +14 -40
  226. package/src/invitation/useOrgInvitations.ts +15 -41
  227. package/src/library/ResourceListView.tsx +3 -3
  228. package/src/library/useDetectSkillPackage.ts +1 -1
  229. package/src/mcp-server/McpServerPicker.tsx +1 -1
  230. package/src/mcp-server/useMcpServer.ts +17 -42
  231. package/src/mcp-server/useMcpServerCount.ts +1 -1
  232. package/src/mcp-server/useMcpServerList.ts +2 -2
  233. package/src/mcp-server/useOAuthGrantStatus.ts +31 -69
  234. package/src/mcp-server/useOrgOAuthApp.ts +34 -51
  235. package/src/oauth-app/useOAuthAppList.ts +15 -41
  236. package/src/organization/useOrganization.ts +9 -38
  237. package/src/platform-client/usePlatformClient.ts +9 -39
  238. package/src/platform-client/usePlatformClientList.ts +14 -42
  239. package/src/runner/RunnerListPanel.tsx +49 -41
  240. package/src/runner/__tests__/phase.test.ts +35 -0
  241. package/src/runner/__tests__/useRunnerList.test.tsx +96 -0
  242. package/src/runner/index.ts +7 -0
  243. package/src/runner/phase.ts +16 -0
  244. package/src/runner/useRunnerCredential.ts +89 -0
  245. package/src/runner/useRunnerList.ts +36 -44
  246. package/src/search/useResourceCount.ts +20 -48
  247. package/src/search/useResourceList.ts +40 -57
  248. package/src/search/useResourceSearch.ts +22 -43
  249. package/src/session/useNewSessionFlow.ts +35 -1
  250. package/src/session/useSession.ts +10 -39
  251. package/src/session/useSessionExecutions.ts +20 -46
  252. package/src/session/useSessionList.ts +21 -42
  253. package/src/skill/SkillPicker.tsx +1 -1
  254. package/src/skill/useSkill.ts +17 -42
  255. package/src/skill/useSkillCount.ts +1 -1
  256. package/src/skill/useSkillList.ts +2 -2
  257. package/src/usage/useOrgUsageReport.ts +18 -46
  258. package/styles.css +1 -1
  259. package/usage/useOrgUsageReport.d.ts +2 -0
  260. package/usage/useOrgUsageReport.d.ts.map +1 -1
  261. package/usage/useOrgUsageReport.js +5 -35
  262. package/usage/useOrgUsageReport.js.map +1 -1
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
 
3
- import { useCallback, useEffect, useState } from "react";
4
3
  import type { ListParams, ListResult } from "@stigmer/sdk";
5
4
  import type { ResourceListScope } from "./useResourceList";
5
+ import { useFetch } from "../internal/useFetch";
6
6
 
7
7
  export interface UseResourceCountOptions {
8
8
  /** Text query to filter results before counting. */
@@ -26,7 +26,8 @@ export interface UseResourceCountReturn {
26
26
  */
27
27
  readonly count: number | undefined;
28
28
  readonly isLoading: boolean;
29
- readonly error: string | null;
29
+ readonly isRefetching: boolean;
30
+ readonly error: Error | null;
30
31
  readonly refetch: () => void;
31
32
  }
32
33
 
@@ -49,54 +50,25 @@ export function useResourceCount(
49
50
  org: string | null,
50
51
  options?: UseResourceCountOptions,
51
52
  ): UseResourceCountReturn {
52
- const [count, setCount] = useState<number | undefined>(undefined);
53
- const [isLoading, setIsLoading] = useState(false);
54
- const [error, setError] = useState<string | null>(null);
55
- const [fetchKey, setFetchKey] = useState(0);
56
-
57
53
  const query = options?.query;
58
54
  const scope = options?.scope ?? "org";
59
55
 
60
- const refetch = useCallback(() => setFetchKey((k) => k + 1), []);
61
-
62
- useEffect(() => {
63
- if (!org) {
64
- setCount(undefined);
65
- setIsLoading(false);
66
- setError(null);
67
- return;
68
- }
69
-
70
- const cancelled = { current: false };
71
- setIsLoading(true);
72
- setError(null);
73
-
74
- const params: ListParams = {
75
- org: scope === "all" ? "" : org,
76
- query: query || undefined,
77
- excludePublic: false,
78
- page: { num: 1, size: 1 },
79
- };
80
-
81
- listFn(params).then(
82
- (result) => {
83
- if (cancelled.current) return;
84
- setCount(result.totalCount);
85
- setIsLoading(false);
86
- },
87
- (err) => {
88
- if (cancelled.current) return;
89
- setError(
90
- err instanceof Error ? err.message : "Failed to load resource count",
91
- );
92
- setIsLoading(false);
93
- },
94
- );
95
-
96
- return () => {
97
- cancelled.current = true;
98
- };
99
- }, [listFn, org, query, scope, fetchKey]);
56
+ const { data: count, isLoading, isRefetching, error, refetch } = useFetch<number | undefined>(
57
+ org
58
+ ? async () => {
59
+ const params: ListParams = {
60
+ org: scope === "all" ? "" : org,
61
+ query: query || undefined,
62
+ excludePublic: false,
63
+ page: { num: 1, size: 1 },
64
+ };
65
+ const result = await listFn(params);
66
+ return result.totalCount;
67
+ }
68
+ : null,
69
+ [listFn, org, query, scope],
70
+ undefined,
71
+ );
100
72
 
101
- return { count, isLoading, error, refetch };
73
+ return { count, isLoading, isRefetching, error, refetch };
102
74
  }
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
 
3
- import { useCallback, useEffect, useRef, useState } from "react";
4
3
  import type { ListParams, ListResult } from "@stigmer/sdk";
5
4
  import type { SearchResult } from "@stigmer/protos/ai/stigmer/search/v1/io_pb";
5
+ import { useFetch } from "../internal/useFetch";
6
6
 
7
7
  /**
8
8
  * Scope controls resource listing boundaries.
@@ -38,13 +38,26 @@ export interface UseResourceListReturn {
38
38
  readonly totalPages: number;
39
39
  readonly currentPage: number;
40
40
  readonly isLoading: boolean;
41
- readonly error: string | null;
41
+ readonly isRefetching: boolean;
42
+ readonly error: Error | null;
42
43
  readonly refetch: () => void;
43
44
  }
44
45
 
45
46
  const DEFAULT_PAGE_SIZE = 20;
46
47
  const DEFAULT_PAGE = 1;
47
48
 
49
+ interface ResourceListData {
50
+ entries: readonly SearchResult[];
51
+ totalCount: number;
52
+ totalPages: number;
53
+ }
54
+
55
+ const INITIAL_DATA: ResourceListData = {
56
+ entries: [],
57
+ totalCount: 0,
58
+ totalPages: 0,
59
+ };
60
+
48
61
  /**
49
62
  * Internal hook that provides paginated, scope-aware resource listing.
50
63
  *
@@ -61,70 +74,40 @@ export function useResourceList(
61
74
  org: string | null,
62
75
  options?: UseResourceListOptions,
63
76
  ): UseResourceListReturn {
64
- const [entries, setEntries] = useState<SearchResult[]>([]);
65
- const [totalCount, setTotalCount] = useState(0);
66
- const [totalPages, setTotalPages] = useState(0);
67
- const [isLoading, setIsLoading] = useState(false);
68
- const [error, setError] = useState<string | null>(null);
69
- const [fetchKey, setFetchKey] = useState(0);
70
-
71
77
  const pageSize = options?.pageSize ?? DEFAULT_PAGE_SIZE;
72
78
  const page = options?.page ?? DEFAULT_PAGE;
73
79
  const query = options?.query;
74
80
  const scope = options?.scope ?? "org";
75
81
 
76
- const refetch = useCallback(() => setFetchKey((k) => k + 1), []);
77
-
78
- useEffect(() => {
79
- if (!org) {
80
- setEntries([]);
81
- setTotalCount(0);
82
- setTotalPages(0);
83
- setIsLoading(false);
84
- setError(null);
85
- return;
86
- }
87
-
88
- const cancelled = { current: false };
89
- setIsLoading(true);
90
- setError(null);
91
-
92
- const params: ListParams = {
93
- org,
94
- query: query || undefined,
95
- excludePublic: false,
96
- crossOrgPublic: scope === "all",
97
- page: { num: page, size: pageSize },
98
- };
99
-
100
- listFn(params).then(
101
- (result) => {
102
- if (cancelled.current) return;
103
- setEntries([...result.entries]);
104
- setTotalCount(result.totalCount);
105
- setTotalPages(result.totalPages);
106
- setIsLoading(false);
107
- },
108
- (err) => {
109
- if (cancelled.current) return;
110
- setError(
111
- err instanceof Error ? err.message : "Failed to load resources",
112
- );
113
- setIsLoading(false);
114
- },
115
- );
116
-
117
- return () => {
118
- cancelled.current = true;
119
- };
120
- }, [listFn, org, query, scope, page, pageSize, fetchKey]);
82
+ const { data, isLoading, isRefetching, error, refetch } = useFetch<ResourceListData>(
83
+ org
84
+ ? async () => {
85
+ const params: ListParams = {
86
+ org,
87
+ query: query || undefined,
88
+ excludePublic: false,
89
+ crossOrgPublic: scope === "all",
90
+ page: { num: page, size: pageSize },
91
+ };
92
+ const result = await listFn(params);
93
+ return {
94
+ entries: [...result.entries],
95
+ totalCount: result.totalCount,
96
+ totalPages: result.totalPages,
97
+ };
98
+ }
99
+ : null,
100
+ [listFn, org, query, scope, page, pageSize],
101
+ INITIAL_DATA,
102
+ );
121
103
 
122
104
  return {
123
- entries,
124
- totalCount,
125
- totalPages,
105
+ entries: data.entries,
106
+ totalCount: data.totalCount,
107
+ totalPages: data.totalPages,
126
108
  currentPage: page,
127
109
  isLoading,
110
+ isRefetching,
128
111
  error,
129
112
  refetch,
130
113
  };
@@ -1,8 +1,9 @@
1
1
  "use client";
2
2
 
3
- import { useCallback, useEffect, useRef, useState } from "react";
3
+ import { useEffect, useState } from "react";
4
4
  import type { ListParams, ListResult } from "@stigmer/sdk";
5
5
  import type { SearchResult } from "@stigmer/protos/ai/stigmer/search/v1/io_pb";
6
+ import { useFetch } from "../internal/useFetch";
6
7
 
7
8
  /** Shared options for resource search hooks (`useAgentSearch`, `useMcpServerSearch`, `useSkillSearch`). */
8
9
  export interface UseResourceSearchOptions {
@@ -28,8 +29,10 @@ export interface UseResourceSearchReturn {
28
29
  readonly results: readonly SearchResult[];
29
30
  /** `true` while a search request is in flight. */
30
31
  readonly isLoading: boolean;
31
- /** Error message from the last failed search, or `null` when healthy. */
32
- readonly error: string | null;
32
+ /** `true` while a background refetch is in flight. */
33
+ readonly isRefetching: boolean;
34
+ /** Error from the last failed search, or `null` when healthy. */
35
+ readonly error: Error | null;
33
36
  /** Current search query text. */
34
37
  readonly query: string;
35
38
  /** Update the search query (triggers a debounced re-search). */
@@ -55,57 +58,33 @@ export function useResourceSearch(
55
58
  org: string,
56
59
  options?: UseResourceSearchOptions,
57
60
  ): UseResourceSearchReturn {
58
- const [results, setResults] = useState<SearchResult[]>([]);
59
- const [isLoading, setIsLoading] = useState(false);
60
- const [error, setError] = useState<string | null>(null);
61
61
  const [query, setQuery] = useState("");
62
62
  const [debouncedQuery, setDebouncedQuery] = useState("");
63
- const [fetchKey, setFetchKey] = useState(0);
64
63
 
65
64
  const pageSize = options?.pageSize ?? DEFAULT_PAGE_SIZE;
66
65
  const debounceMs = options?.debounceMs ?? DEFAULT_DEBOUNCE_MS;
67
66
  const scope = options?.scope ?? "org";
68
67
 
69
- // Debounce query changes
70
68
  useEffect(() => {
71
69
  const timer = setTimeout(() => setDebouncedQuery(query), debounceMs);
72
70
  return () => clearTimeout(timer);
73
71
  }, [query, debounceMs]);
74
72
 
75
- const refetch = useCallback(() => setFetchKey((k) => k + 1), []);
73
+ const { data: results, isLoading, isRefetching, error, refetch } = useFetch<SearchResult[]>(
74
+ async () => {
75
+ const params: ListParams = {
76
+ org,
77
+ query: debouncedQuery || undefined,
78
+ excludePublic: false,
79
+ crossOrgPublic: scope === "all",
80
+ page: { num: 1, size: pageSize },
81
+ };
82
+ const result = await listFn(params);
83
+ return [...result.entries];
84
+ },
85
+ [listFn, org, debouncedQuery, pageSize, scope],
86
+ [],
87
+ );
76
88
 
77
- useEffect(() => {
78
- const cancelled = { current: false };
79
- setIsLoading(true);
80
- setError(null);
81
-
82
- const params: ListParams = {
83
- org,
84
- query: debouncedQuery || undefined,
85
- excludePublic: false,
86
- crossOrgPublic: scope === "all",
87
- page: { num: 1, size: pageSize },
88
- };
89
-
90
- listFn(params).then(
91
- (result) => {
92
- if (cancelled.current) return;
93
- setResults([...result.entries]);
94
- setIsLoading(false);
95
- },
96
- (err) => {
97
- if (cancelled.current) return;
98
- setError(
99
- err instanceof Error ? err.message : "Failed to load resources",
100
- );
101
- setIsLoading(false);
102
- },
103
- );
104
-
105
- return () => {
106
- cancelled.current = true;
107
- };
108
- }, [listFn, org, debouncedQuery, pageSize, scope, fetchKey]);
109
-
110
- return { results, isLoading, error, query, setQuery, refetch };
89
+ return { results, isLoading, isRefetching, error, query, setQuery, refetch };
111
90
  }
@@ -12,6 +12,7 @@ import { useCreateSession } from "./useCreateSession";
12
12
  import { useCreateAgentExecution } from "../execution/useCreateAgentExecution";
13
13
 
14
14
  const STORAGE_KEY_MODEL = "stigmer:session:model";
15
+ const STORAGE_KEY_RUNNER = "stigmer:session:runner";
15
16
 
16
17
  /** Options for {@link useNewSessionFlow}. */
17
18
  export interface UseNewSessionFlowOptions {
@@ -138,7 +139,11 @@ export function useNewSessionFlow(
138
139
  const { getModel } = useModelRegistry();
139
140
  const { create: createSession } = useCreateSession();
140
141
  const { create: createExecution } = useCreateAgentExecution();
141
- const { agent: defaultAgent } = useDefaultAgent(org);
142
+ const {
143
+ agent: defaultAgent,
144
+ isLoading: isDefaultAgentLoading,
145
+ error: defaultAgentError,
146
+ } = useDefaultAgent(org);
142
147
  const workspace = useWorkspaceEntries();
143
148
  const sessionVariables = useSessionVariables();
144
149
 
@@ -168,6 +173,23 @@ export function useNewSessionFlow(
168
173
  }
169
174
  }, [modelId]);
170
175
 
176
+ // Restore persisted runner on mount
177
+ useEffect(() => {
178
+ const stored = localStorage.getItem(STORAGE_KEY_RUNNER);
179
+ if (stored) {
180
+ setRunnerId(stored);
181
+ }
182
+ }, []);
183
+
184
+ // Persist runner on change
185
+ useEffect(() => {
186
+ if (runnerId) {
187
+ localStorage.setItem(STORAGE_KEY_RUNNER, runnerId);
188
+ } else {
189
+ localStorage.removeItem(STORAGE_KEY_RUNNER);
190
+ }
191
+ }, [runnerId]);
192
+
171
193
  const submit = useCallback(
172
194
  async (
173
195
  message: string,
@@ -221,6 +243,16 @@ export function useNewSessionFlow(
221
243
  } else {
222
244
  const defaultInstanceId = defaultAgent?.status?.defaultInstanceId;
223
245
  if (!defaultInstanceId) {
246
+ if (isDefaultAgentLoading) {
247
+ throw new Error(
248
+ "Loading default agent. Please try again in a moment.",
249
+ );
250
+ }
251
+ if (defaultAgentError) {
252
+ throw new Error(
253
+ "Failed to load default agent. Please try again.",
254
+ );
255
+ }
224
256
  throw new Error(
225
257
  "No default agent available. Select an agent to start a session.",
226
258
  );
@@ -253,6 +285,8 @@ export function useNewSessionFlow(
253
285
  agentRef,
254
286
  resolution,
255
287
  defaultAgent,
288
+ isDefaultAgentLoading,
289
+ defaultAgentError,
256
290
  createSession,
257
291
  createExecution,
258
292
  sessionVariables,
@@ -1,16 +1,17 @@
1
1
  "use client";
2
2
 
3
- import { useCallback, useEffect, useState } from "react";
4
3
  import type { Session } from "@stigmer/protos/ai/stigmer/agentic/session/v1/api_pb";
5
4
  import { useStigmer } from "../hooks";
6
- import { toError } from "../internal/toError";
5
+ import { useFetch } from "../internal/useFetch";
7
6
 
8
7
  /** Return value of {@link useSession}. */
9
8
  export interface UseSessionReturn {
10
9
  /** The fetched Session, or `null` while loading or on error. */
11
10
  readonly session: Session | null;
12
- /** `true` while the initial fetch or a refetch is in flight. */
11
+ /** `true` while the initial fetch is in flight. */
13
12
  readonly isLoading: boolean;
13
+ /** `true` while a background refetch is in flight. */
14
+ readonly isRefetching: boolean;
14
15
  /** Error from the last failed request, or `null` when healthy. */
15
16
  readonly error: Error | null;
16
17
  /** Discard cached data and re-fetch the session from the server. */
@@ -56,42 +57,12 @@ export interface UseSessionReturn {
56
57
  */
57
58
  export function useSession(id: string | null): UseSessionReturn {
58
59
  const stigmer = useStigmer();
59
- const [session, setSession] = useState<Session | null>(null);
60
- const [isLoading, setIsLoading] = useState(false);
61
- const [error, setError] = useState<Error | null>(null);
62
- const [fetchKey, setFetchKey] = useState(0);
63
60
 
64
- const refetch = useCallback(() => setFetchKey((k) => k + 1), []);
61
+ const { data: session, isLoading, isRefetching, error, refetch } = useFetch(
62
+ id ? () => stigmer.session.get(id) : null,
63
+ [id, stigmer],
64
+ null as Session | null,
65
+ );
65
66
 
66
- useEffect(() => {
67
- if (!id) {
68
- setSession(null);
69
- setIsLoading(false);
70
- setError(null);
71
- return;
72
- }
73
-
74
- const cancelled = { current: false };
75
- setIsLoading(true);
76
- setError(null);
77
-
78
- stigmer.session.get(id).then(
79
- (result) => {
80
- if (cancelled.current) return;
81
- setSession(result);
82
- setIsLoading(false);
83
- },
84
- (err) => {
85
- if (cancelled.current) return;
86
- setError(toError(err));
87
- setIsLoading(false);
88
- },
89
- );
90
-
91
- return () => {
92
- cancelled.current = true;
93
- };
94
- }, [id, stigmer, fetchKey]);
95
-
96
- return { session, isLoading, error, refetch };
67
+ return { session, isLoading, isRefetching, error, refetch };
97
68
  }
@@ -1,18 +1,19 @@
1
1
  "use client";
2
2
 
3
- import { useCallback, useEffect, useState } from "react";
4
3
  import { create } from "@bufbuild/protobuf";
5
4
  import type { AgentExecution } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
6
5
  import { ListAgentExecutionsBySessionRequestSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/io_pb";
7
6
  import { useStigmer } from "../hooks";
8
- import { toError } from "../internal/toError";
7
+ import { useFetch } from "../internal/useFetch";
9
8
 
10
9
  /** Return value of {@link useSessionExecutions}. */
11
10
  export interface UseSessionExecutionsReturn {
12
11
  /** All executions for the session, empty while loading or on error. */
13
12
  readonly executions: readonly AgentExecution[];
14
- /** `true` while the initial fetch or a refetch is in flight. */
13
+ /** `true` while the initial fetch is in flight. */
15
14
  readonly isLoading: boolean;
15
+ /** `true` while a background refetch is in flight. */
16
+ readonly isRefetching: boolean;
16
17
  /** Error from the last failed request, or `null` when healthy. */
17
18
  readonly error: Error | null;
18
19
  /** Discard cached data and re-fetch the execution list from the server. */
@@ -57,49 +58,22 @@ export function useSessionExecutions(
57
58
  sessionId: string | null,
58
59
  ): UseSessionExecutionsReturn {
59
60
  const stigmer = useStigmer();
60
- const [executions, setExecutions] = useState<AgentExecution[]>([]);
61
- const [isLoading, setIsLoading] = useState(false);
62
- const [error, setError] = useState<Error | null>(null);
63
- const [fetchKey, setFetchKey] = useState(0);
64
61
 
65
- const refetch = useCallback(() => setFetchKey((k) => k + 1), []);
62
+ const { data: executions, isLoading, isRefetching, error, refetch } = useFetch(
63
+ sessionId
64
+ ? () =>
65
+ stigmer.agentExecution
66
+ .listBySession(
67
+ create(ListAgentExecutionsBySessionRequestSchema, {
68
+ sessionId,
69
+ pageSize: 100,
70
+ }),
71
+ )
72
+ .then((result) => result.entries)
73
+ : null,
74
+ [sessionId, stigmer],
75
+ [] as AgentExecution[],
76
+ );
66
77
 
67
- useEffect(() => {
68
- if (!sessionId) {
69
- setExecutions([]);
70
- setIsLoading(false);
71
- setError(null);
72
- return;
73
- }
74
-
75
- const cancelled = { current: false };
76
- setIsLoading(true);
77
- setError(null);
78
-
79
- stigmer.agentExecution
80
- .listBySession(
81
- create(ListAgentExecutionsBySessionRequestSchema, {
82
- sessionId,
83
- pageSize: 100,
84
- }),
85
- )
86
- .then(
87
- (result) => {
88
- if (cancelled.current) return;
89
- setExecutions(result.entries);
90
- setIsLoading(false);
91
- },
92
- (err) => {
93
- if (cancelled.current) return;
94
- setError(toError(err));
95
- setIsLoading(false);
96
- },
97
- );
98
-
99
- return () => {
100
- cancelled.current = true;
101
- };
102
- }, [sessionId, stigmer, fetchKey]);
103
-
104
- return { executions, isLoading, error, refetch };
78
+ return { executions, isLoading, isRefetching, error, refetch };
105
79
  }
@@ -1,10 +1,11 @@
1
1
  "use client";
2
2
 
3
- import { useCallback, useEffect, useRef, useState } from "react";
3
+ import { useRef } from "react";
4
4
  import { create } from "@bufbuild/protobuf";
5
5
  import type { Session } from "@stigmer/protos/ai/stigmer/agentic/session/v1/api_pb";
6
6
  import { ListSessionsRequestSchema } from "@stigmer/protos/ai/stigmer/agentic/session/v1/io_pb";
7
7
  import { useStigmer } from "../hooks";
8
+ import { useFetch } from "../internal/useFetch";
8
9
 
9
10
  /** Options for {@link useSessionList}. */
10
11
  export interface UseSessionListOptions {
@@ -18,10 +19,12 @@ export interface UseSessionListOptions {
18
19
  export interface UseSessionListReturn {
19
20
  /** The fetched list of Sessions, empty while loading or on error. */
20
21
  readonly sessions: readonly Session[];
21
- /** `true` while the initial fetch or a refetch is in flight. */
22
+ /** `true` while the initial fetch is in flight. */
22
23
  readonly isLoading: boolean;
23
- /** Error message from the last failed request, or `null` when healthy. */
24
- readonly error: string | null;
24
+ /** `true` while a background refetch is in flight. */
25
+ readonly isRefetching: boolean;
26
+ /** Error from the last failed request, or `null` when healthy. */
27
+ readonly error: Error | null;
25
28
  /** Discard cached data and re-fetch the session list from the server. */
26
29
  readonly refetch: () => void;
27
30
  }
@@ -61,10 +64,6 @@ export function useSessionList(
61
64
  options?: UseSessionListOptions,
62
65
  ): UseSessionListReturn {
63
66
  const stigmer = useStigmer();
64
- const [sessions, setSessions] = useState<Session[]>([]);
65
- const [isLoading, setIsLoading] = useState(false);
66
- const [error, setError] = useState<string | null>(null);
67
- const [fetchKey, setFetchKey] = useState(0);
68
67
 
69
68
  const pageSize = options?.pageSize ?? DEFAULT_PAGE_SIZE;
70
69
  const tagsRef = useRef(options?.tags);
@@ -77,39 +76,19 @@ export function useSessionList(
77
76
  }
78
77
  const tags = tagsRef.current;
79
78
 
80
- const refetch = useCallback(() => setFetchKey((k) => k + 1), []);
79
+ const { data: sessions, isLoading, isRefetching, error, refetch } = useFetch(
80
+ () =>
81
+ stigmer.session
82
+ .list(
83
+ create(ListSessionsRequestSchema, {
84
+ pageSize,
85
+ tags: tags ?? [],
86
+ }),
87
+ )
88
+ .then((result) => result.entries),
89
+ [stigmer, pageSize, tags],
90
+ [] as Session[],
91
+ );
81
92
 
82
- useEffect(() => {
83
- const cancelled = { current: false };
84
- setIsLoading(true);
85
- setError(null);
86
-
87
- stigmer.session
88
- .list(
89
- create(ListSessionsRequestSchema, {
90
- pageSize,
91
- tags: tags ?? [],
92
- }),
93
- )
94
- .then(
95
- (result) => {
96
- if (cancelled.current) return;
97
- setSessions(result.entries);
98
- setIsLoading(false);
99
- },
100
- (err) => {
101
- if (cancelled.current) return;
102
- setError(
103
- err instanceof Error ? err.message : "Failed to load sessions",
104
- );
105
- setIsLoading(false);
106
- },
107
- );
108
-
109
- return () => {
110
- cancelled.current = true;
111
- };
112
- }, [stigmer, fetchKey, pageSize, tags]);
113
-
114
- return { sessions, isLoading, error, refetch };
93
+ return { sessions, isLoading, isRefetching, error, refetch };
115
94
  }
@@ -217,7 +217,7 @@ export function SkillPicker({
217
217
  autoFocus
218
218
  />
219
219
 
220
- {error && <p className="text-xs text-destructive">{error}</p>}
220
+ {error && <p className="text-xs text-destructive">{error.message}</p>}
221
221
 
222
222
  {/* Scrollable results list */}
223
223
  <div className="relative">