@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
@@ -29,7 +29,7 @@ export interface UseAgentCountReturn {
29
29
  /** `true` while the count fetch is in flight. */
30
30
  readonly isLoading: boolean;
31
31
  /** Error message from the last failed fetch, or `null` when healthy. */
32
- readonly error: string | null;
32
+ readonly error: Error | null;
33
33
  /** Re-fetch the count with the same parameters. */
34
34
  readonly refetch: () => void;
35
35
  }
@@ -36,8 +36,8 @@ export interface UseAgentListReturn {
36
36
  readonly currentPage: number;
37
37
  /** `true` while the initial fetch or a refetch is in flight. */
38
38
  readonly isLoading: boolean;
39
- /** Error message from the last failed request, or `null` when healthy. */
40
- readonly error: string | null;
39
+ /** Error from the last failed request, or `null` when healthy. */
40
+ readonly error: Error | null;
41
41
  /** Discard cached data and re-fetch the current page from the server. */
42
42
  readonly refetch: () => void;
43
43
  }
@@ -1,11 +1,21 @@
1
1
  "use client";
2
2
 
3
- import { useCallback, useEffect, useState } from "react";
3
+ import { useEffect, useRef } from "react";
4
4
  import { create } from "@bufbuild/protobuf";
5
5
  import type { Agent } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/api_pb";
6
6
  import { GetDefaultAgentRequestSchema } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/io_pb";
7
7
  import { useStigmer } from "../hooks";
8
- import { toError } from "../internal/toError";
8
+ import { useFetch } from "../internal/useFetch";
9
+
10
+ /**
11
+ * Milliseconds the cached result can age before a visibility change
12
+ * triggers a background refetch. Keeps the hook quiet during fast
13
+ * window switches while ensuring fresh data after real idle periods.
14
+ */
15
+ const STALE_THRESHOLD_MS = 30_000;
16
+
17
+ const RETRY_DELAY_MS = 1_000;
18
+ const MAX_RETRIES = 1;
9
19
 
10
20
  /** Return value of {@link useDefaultAgent}. */
11
21
  export interface UseDefaultAgentReturn {
@@ -13,6 +23,8 @@ export interface UseDefaultAgentReturn {
13
23
  readonly agent: Agent | null;
14
24
  /** `true` while the initial fetch or a refetch is in flight. */
15
25
  readonly isLoading: boolean;
26
+ /** `true` while a background refetch is in flight and stale data is shown. */
27
+ readonly isRefetching: boolean;
16
28
  /** Error from the last failed request, or `null` when healthy. */
17
29
  readonly error: Error | null;
18
30
  /** Discard cached data and re-fetch the default agent from the server. */
@@ -27,8 +39,10 @@ export interface UseDefaultAgentReturn {
27
39
  * start a conversation without choosing an agent.
28
40
  *
29
41
  * Pass `null` for `org` to skip fetching (stable no-op). The hook
30
- * fetches once on mount and caches the result the default agent
31
- * rarely changes within a session.
42
+ * fetches once on mount and caches the result, then automatically
43
+ * refetches when the document becomes visible after being hidden for
44
+ * longer than {@link STALE_THRESHOLD_MS}. Transient failures are
45
+ * retried once before surfacing an error.
32
46
  *
33
47
  * @example
34
48
  * ```tsx
@@ -40,42 +54,60 @@ export interface UseDefaultAgentReturn {
40
54
  */
41
55
  export function useDefaultAgent(org: string | null): UseDefaultAgentReturn {
42
56
  const stigmer = useStigmer();
43
- const [agent, setAgent] = useState<Agent | null>(null);
44
- const [isLoading, setIsLoading] = useState(false);
45
- const [error, setError] = useState<Error | null>(null);
46
- const [fetchKey, setFetchKey] = useState(0);
57
+ const lastFetchedAt = useRef(0);
58
+
59
+ const fetchFn = org
60
+ ? async () => {
61
+ const doFetch = () =>
62
+ stigmer.agent.getDefault(
63
+ create(GetDefaultAgentRequestSchema, { org }),
64
+ );
65
+ const result = await fetchWithRetry(doFetch, MAX_RETRIES, RETRY_DELAY_MS);
66
+ lastFetchedAt.current = Date.now();
67
+ return result;
68
+ }
69
+ : null;
47
70
 
48
- const refetch = useCallback(() => setFetchKey((k) => k + 1), []);
71
+ const { data: agent, isLoading, isRefetching, error, refetch } = useFetch(
72
+ fetchFn,
73
+ [org, stigmer],
74
+ null,
75
+ );
49
76
 
50
77
  useEffect(() => {
51
- if (!org) {
52
- setAgent(null);
53
- setIsLoading(false);
54
- setError(null);
55
- return;
56
- }
78
+ if (typeof document === "undefined") return;
57
79
 
58
- const cancelled = { current: false };
59
- setIsLoading(true);
60
- setError(null);
80
+ const onVisible = () => {
81
+ if (document.visibilityState !== "visible") return;
82
+ if (Date.now() - lastFetchedAt.current >= STALE_THRESHOLD_MS) {
83
+ refetch();
84
+ }
85
+ };
61
86
 
62
- stigmer.agent.getDefault(create(GetDefaultAgentRequestSchema, { org })).then(
63
- (result) => {
64
- if (cancelled.current) return;
65
- setAgent(result);
66
- setIsLoading(false);
67
- },
68
- (err) => {
69
- if (cancelled.current) return;
70
- setError(toError(err));
71
- setIsLoading(false);
72
- },
73
- );
87
+ document.addEventListener("visibilitychange", onVisible);
88
+ return () => document.removeEventListener("visibilitychange", onVisible);
89
+ }, [refetch]);
74
90
 
75
- return () => {
76
- cancelled.current = true;
77
- };
78
- }, [org, stigmer, fetchKey]);
91
+ return { agent, isLoading, isRefetching, error, refetch };
92
+ }
93
+
94
+ // ---------------------------------------------------------------------------
95
+ // Internal helpers
96
+ // ---------------------------------------------------------------------------
79
97
 
80
- return { agent, isLoading, error, refetch };
98
+ /**
99
+ * Retries `fn` up to `retries` times with a fixed delay between attempts.
100
+ */
101
+ async function fetchWithRetry<T>(
102
+ fn: () => Promise<T>,
103
+ retries: number,
104
+ delayMs: number,
105
+ ): Promise<T> {
106
+ try {
107
+ return await fn();
108
+ } catch (err) {
109
+ if (retries <= 0) throw err;
110
+ await new Promise((r) => setTimeout(r, delayMs));
111
+ return fetchWithRetry(fn, retries - 1, delayMs);
112
+ }
81
113
  }
@@ -1,10 +1,9 @@
1
1
  "use client";
2
2
 
3
- import { useCallback, useEffect, useState } from "react";
4
3
  import type { AgentInstance } from "@stigmer/protos/ai/stigmer/agentic/agentinstance/v1/api_pb";
5
4
  import type { ResourceRef } from "@stigmer/sdk";
6
5
  import { useStigmer } from "../hooks";
7
- import { toError } from "../internal/toError";
6
+ import { useFetch } from "../internal/useFetch";
8
7
 
9
8
  /** Return value of {@link useAgentInstance}. */
10
9
  export interface UseAgentInstanceReturn {
@@ -12,6 +11,8 @@ export interface UseAgentInstanceReturn {
12
11
  readonly agentInstance: AgentInstance | null;
13
12
  /** `true` while the initial fetch or a refetch is in flight. */
14
13
  readonly isLoading: boolean;
14
+ /** `true` while a background refetch is in flight and stale data is shown. */
15
+ readonly isRefetching: boolean;
15
16
  /** Error from the last failed request, or `null` when healthy. */
16
17
  readonly error: Error | null;
17
18
  /** Discard cached data and re-fetch the agent instance from the server. */
@@ -47,46 +48,21 @@ export function useAgentInstance(
47
48
  ref: ResourceRef | null,
48
49
  ): UseAgentInstanceReturn {
49
50
  const stigmer = useStigmer();
50
- const [agentInstance, setAgentInstance] = useState<AgentInstance | null>(null);
51
- const [isLoading, setIsLoading] = useState(false);
52
- const [error, setError] = useState<Error | null>(null);
53
- const [fetchKey, setFetchKey] = useState(0);
54
-
55
- const refetch = useCallback(() => setFetchKey((k) => k + 1), []);
56
51
 
57
52
  const org = ref?.org;
58
53
  const slug = ref?.slug;
59
54
  const version = ref?.version;
60
55
 
61
- useEffect(() => {
62
- if (!org || !slug) {
63
- setAgentInstance(null);
64
- setIsLoading(false);
65
- setError(null);
66
- return;
67
- }
68
-
69
- const cancelled = { current: false };
70
- setIsLoading(true);
71
- setError(null);
72
-
73
- stigmer.agentInstance.getByReference({ org, slug, version }).then(
74
- (result) => {
75
- if (cancelled.current) return;
76
- setAgentInstance(result);
77
- setIsLoading(false);
78
- },
79
- (err) => {
80
- if (cancelled.current) return;
81
- setError(toError(err));
82
- setIsLoading(false);
83
- },
84
- );
56
+ const fetchFn =
57
+ org && slug
58
+ ? () => stigmer.agentInstance.getByReference({ org, slug, version })
59
+ : null;
85
60
 
86
- return () => {
87
- cancelled.current = true;
88
- };
89
- }, [org, slug, version, stigmer, fetchKey]);
61
+ const { data: agentInstance, isLoading, isRefetching, error, refetch } = useFetch(
62
+ fetchFn,
63
+ [org, slug, version, stigmer],
64
+ null,
65
+ );
90
66
 
91
- return { agentInstance, isLoading, error, refetch };
67
+ return { agentInstance, isLoading, isRefetching, error, refetch };
92
68
  }
@@ -1,11 +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 { AgentInstance } from "@stigmer/protos/ai/stigmer/agentic/agentinstance/v1/api_pb";
6
6
  import { ListAgentInstancesRequestSchema } from "@stigmer/protos/ai/stigmer/agentic/agentinstance/v1/io_pb";
7
7
  import { useStigmer } from "../hooks";
8
- import { toError } from "../internal/toError";
8
+ import { useFetch } from "../internal/useFetch";
9
9
 
10
10
  /** Return value of {@link useAgentInstanceList}. */
11
11
  export interface UseAgentInstanceListReturn {
@@ -15,6 +15,8 @@ export interface UseAgentInstanceListReturn {
15
15
  readonly totalCount: number;
16
16
  /** `true` while the initial fetch or a refetch is in flight. */
17
17
  readonly isLoading: boolean;
18
+ /** `true` while a background refetch is in flight and stale data is shown. */
19
+ readonly isRefetching: boolean;
18
20
  /** Error from the last failed request, or `null` when healthy. */
19
21
  readonly error: Error | null;
20
22
  /** Discard cached data and re-fetch the list from the server. */
@@ -45,11 +47,6 @@ export function useAgentInstanceList(
45
47
  labels?: Record<string, string>,
46
48
  ): UseAgentInstanceListReturn {
47
49
  const stigmer = useStigmer();
48
- const [agentInstances, setAgentInstances] = useState<AgentInstance[]>([]);
49
- const [totalCount, setTotalCount] = useState(0);
50
- const [isLoading, setIsLoading] = useState(false);
51
- const [error, setError] = useState<Error | null>(null);
52
- const [fetchKey, setFetchKey] = useState(0);
53
50
 
54
51
  const labelsRef = useRef(labels);
55
52
  if (
@@ -60,46 +57,33 @@ export function useAgentInstanceList(
60
57
  }
61
58
  const stableLabels = labelsRef.current;
62
59
 
63
- const refetch = useCallback(() => setFetchKey((k) => k + 1), []);
60
+ const fetchFn = org
61
+ ? async () => {
62
+ const result = await stigmer.agentInstance.list(
63
+ create(ListAgentInstancesRequestSchema, {
64
+ org,
65
+ labels: stableLabels ?? {},
66
+ }),
67
+ );
68
+ return {
69
+ agentInstances: result.items as AgentInstance[],
70
+ totalCount: result.totalCount,
71
+ };
72
+ }
73
+ : null;
64
74
 
65
- useEffect(() => {
66
- if (!org) {
67
- setAgentInstances([]);
68
- setTotalCount(0);
69
- setIsLoading(false);
70
- setError(null);
71
- return;
72
- }
75
+ const { data, isLoading, isRefetching, error, refetch } = useFetch(
76
+ fetchFn,
77
+ [org, stableLabels, stigmer],
78
+ { agentInstances: [] as AgentInstance[], totalCount: 0 },
79
+ );
73
80
 
74
- const cancelled = { current: false };
75
- setIsLoading(true);
76
- setError(null);
77
-
78
- stigmer.agentInstance
79
- .list(
80
- create(ListAgentInstancesRequestSchema, {
81
- org,
82
- labels: stableLabels ?? {},
83
- }),
84
- )
85
- .then(
86
- (result) => {
87
- if (cancelled.current) return;
88
- setAgentInstances(result.items);
89
- setTotalCount(result.totalCount);
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
- }, [org, stableLabels, stigmer, fetchKey]);
103
-
104
- return { agentInstances, totalCount, isLoading, error, refetch };
81
+ return {
82
+ agentInstances: data.agentInstances,
83
+ totalCount: data.totalCount,
84
+ isLoading,
85
+ isRefetching,
86
+ error,
87
+ refetch,
88
+ };
105
89
  }
@@ -1,9 +1,8 @@
1
1
  "use client";
2
2
 
3
- import { useCallback, useEffect, useState } from "react";
4
3
  import type { ApiKey } from "@stigmer/protos/ai/stigmer/iam/apikey/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 useApiKeyList}. */
9
8
  export interface UseApiKeyListReturn {
@@ -11,6 +10,8 @@ export interface UseApiKeyListReturn {
11
10
  readonly apiKeys: readonly ApiKey[];
12
11
  /** `true` while the initial fetch or a refetch is in flight. */
13
12
  readonly isLoading: boolean;
13
+ /** `true` while a background refetch is in flight and stale data is shown. */
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 key list from the server. */
@@ -38,36 +39,12 @@ export interface UseApiKeyListReturn {
38
39
  */
39
40
  export function useApiKeyList(): UseApiKeyListReturn {
40
41
  const stigmer = useStigmer();
41
- const [apiKeys, setApiKeys] = useState<ApiKey[]>([]);
42
- const [isLoading, setIsLoading] = useState(true);
43
- const [error, setError] = useState<Error | null>(null);
44
- const [fetchKey, setFetchKey] = useState(0);
45
42
 
46
- const refetch = useCallback(() => setFetchKey((k) => k + 1), []);
43
+ const { data: apiKeys, isLoading, isRefetching, error, refetch } = useFetch(
44
+ () => stigmer.apiKey.findAll().then((r) => [...r.entries]),
45
+ [stigmer],
46
+ [] as ApiKey[],
47
+ );
47
48
 
48
- useEffect(() => {
49
- const cancelled = { current: false };
50
-
51
- setIsLoading(true);
52
- setError(null);
53
-
54
- stigmer.apiKey.findAll().then(
55
- (result) => {
56
- if (cancelled.current) return;
57
- setApiKeys([...result.entries]);
58
- setIsLoading(false);
59
- },
60
- (err) => {
61
- if (cancelled.current) return;
62
- setError(toError(err));
63
- setIsLoading(false);
64
- },
65
- );
66
-
67
- return () => {
68
- cancelled.current = true;
69
- };
70
- }, [stigmer, fetchKey]);
71
-
72
- return { apiKeys, isLoading, error, refetch };
49
+ return { apiKeys, isLoading, isRefetching, error, refetch };
73
50
  }
@@ -4,7 +4,6 @@ import { cn } from "@stigmer/theme";
4
4
  import { ContextPopover } from "./ContextPopover";
5
5
  import { ConfigureMenu, type ConfigureMenuItem } from "./ConfigureMenu";
6
6
  import { ModelSelector } from "../models/ModelSelector";
7
- import { RunnerPicker } from "../runner/RunnerPicker";
8
7
  import {
9
8
  PaperclipIcon,
10
9
  WorkspaceIcon,
@@ -41,13 +40,6 @@ export interface ComposerToolbarProps {
41
40
  /** Render the picker content for a given configure panel id. */
42
41
  readonly renderConfigPanel: (itemId: string) => React.ReactNode;
43
42
 
44
- // -- Runner picker --------------------------------------------------------
45
-
46
- readonly showRunner: boolean;
47
- readonly runnerOrg: string;
48
- readonly runnerId: string | null;
49
- readonly onRunnerIdChange: (runnerId: string | null) => void;
50
-
51
43
  // -- Model selector -------------------------------------------------------
52
44
 
53
45
  readonly showModelSelector: boolean;
@@ -86,17 +78,13 @@ export function ComposerToolbar({
86
78
  configActivePanel,
87
79
  onConfigActivePanelChange,
88
80
  renderConfigPanel,
89
- showRunner,
90
- runnerOrg,
91
- runnerId,
92
- onRunnerIdChange,
93
81
  showModelSelector,
94
82
  modelId,
95
83
  onModelChange,
96
84
  }: ComposerToolbarProps) {
97
85
  const hasTier1 = showAttach || showWorkspace;
98
86
  const hasTier2 = configureItems.length > 0;
99
- const hasExecParams = showRunner || showModelSelector;
87
+ const hasExecParams = showModelSelector;
100
88
 
101
89
  return (
102
90
  <div className="flex items-center justify-between gap-2 border-t border-border-muted px-3 py-2">
@@ -161,15 +149,6 @@ export function ComposerToolbar({
161
149
  <div className="mx-0.5 h-4 w-px bg-border/50" aria-hidden="true" />
162
150
  )}
163
151
 
164
- {showRunner && (
165
- <RunnerPicker
166
- org={runnerOrg}
167
- value={runnerId}
168
- onChange={onRunnerIdChange}
169
- disabled={disabled}
170
- />
171
- )}
172
-
173
152
  {showModelSelector && (
174
153
  <ModelSelector
175
154
  value={modelId}
@@ -4,7 +4,7 @@ import { ChipSpinner, XIcon } from "./icons";
4
4
  export interface ChipItem {
5
5
  key: string;
6
6
  label: string;
7
- type: "agent" | "workspace" | "mcp" | "skill" | "secret";
7
+ type: "agent" | "workspace" | "mcp" | "skill" | "secret" | "runner";
8
8
  onRemove: () => void;
9
9
  /** Drives visual variant: amber for `needsSetup`, muted+spinner for `loading`/`submitting`. */
10
10
  status?: "loading" | "needsSetup" | "submitting" | "ready";
@@ -20,6 +20,7 @@ const CHIP_TYPE_LABELS: Record<ChipItem["type"], string> = {
20
20
  mcp: "MCP",
21
21
  skill: "Skill",
22
22
  secret: "1-time",
23
+ runner: "Runner",
23
24
  };
24
25
 
25
26
  export function ContextChip({