@stigmer/react 0.0.91 → 0.0.93

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 (328) hide show
  1. package/README.md +57 -12
  2. package/agent/AgentDetailView.js +7 -7
  3. package/agent/AgentDetailView.js.map +1 -1
  4. package/agent/AgentPicker.js +3 -3
  5. package/agent/AgentPicker.js.map +1 -1
  6. package/api-key/ApiKeyCreatedAlert.js +1 -1
  7. package/api-key/ApiKeyCreatedAlert.js.map +1 -1
  8. package/api-key/ApiKeyListPanel.js +4 -4
  9. package/api-key/ApiKeyListPanel.js.map +1 -1
  10. package/api-key/CreateApiKeyForm.js +1 -1
  11. package/api-key/CreateApiKeyForm.js.map +1 -1
  12. package/attachment/AttachmentChipList.js +2 -2
  13. package/attachment/AttachmentChipList.js.map +1 -1
  14. package/color-mode.d.ts +48 -0
  15. package/color-mode.d.ts.map +1 -0
  16. package/color-mode.js +53 -0
  17. package/color-mode.js.map +1 -0
  18. package/composer/ComposerToolbar.d.ts +6 -2
  19. package/composer/ComposerToolbar.d.ts.map +1 -1
  20. package/composer/ComposerToolbar.js +5 -3
  21. package/composer/ComposerToolbar.js.map +1 -1
  22. package/composer/ConfigureMenu.js +3 -3
  23. package/composer/ConfigureMenu.js.map +1 -1
  24. package/composer/ContextChip.js +1 -1
  25. package/composer/ContextChip.js.map +1 -1
  26. package/composer/ContextPopover.js +1 -1
  27. package/composer/ContextPopover.js.map +1 -1
  28. package/composer/SessionComposer.d.ts +24 -3
  29. package/composer/SessionComposer.d.ts.map +1 -1
  30. package/composer/SessionComposer.js +5 -4
  31. package/composer/SessionComposer.js.map +1 -1
  32. package/environment/CreateEnvironmentForm.js +1 -1
  33. package/environment/CreateEnvironmentForm.js.map +1 -1
  34. package/environment/EnvVarForm.js +2 -2
  35. package/environment/EnvVarForm.js.map +1 -1
  36. package/environment/EnvironmentListPanel.js +2 -2
  37. package/environment/EnvironmentListPanel.js.map +1 -1
  38. package/environment/EnvironmentVariableEditor.js +6 -6
  39. package/environment/EnvironmentVariableEditor.js.map +1 -1
  40. package/error/ErrorMessage.js +1 -1
  41. package/error/ErrorMessage.js.map +1 -1
  42. package/execution/ApprovalCard.js +4 -4
  43. package/execution/ApprovalCard.js.map +1 -1
  44. package/execution/ArtifactCard.js +1 -1
  45. package/execution/ArtifactCard.js.map +1 -1
  46. package/execution/ArtifactPreviewModal.js +4 -4
  47. package/execution/ArtifactPreviewModal.js.map +1 -1
  48. package/execution/FollowUpInput.js +1 -1
  49. package/execution/FollowUpInput.js.map +1 -1
  50. package/execution/McpToolDetail.js +4 -4
  51. package/execution/McpToolDetail.js.map +1 -1
  52. package/execution/MessageEntry.js +1 -1
  53. package/execution/MessageEntry.js.map +1 -1
  54. package/execution/MessageThread.js +1 -1
  55. package/execution/MessageThread.js.map +1 -1
  56. package/execution/SessionVariablesInput.js +4 -4
  57. package/execution/SessionVariablesInput.js.map +1 -1
  58. package/execution/SubAgentSection.js +2 -2
  59. package/execution/SubAgentSection.js.map +1 -1
  60. package/execution/ToolCallDetail.js +2 -2
  61. package/execution/ToolCallDetail.js.map +1 -1
  62. package/execution/ToolCallGroup.js +1 -1
  63. package/execution/ToolCallGroup.js.map +1 -1
  64. package/execution/ToolCallItem.js +2 -2
  65. package/execution/ToolCallItem.js.map +1 -1
  66. package/execution/WriteBackCard.js +3 -3
  67. package/execution/WriteBackCard.js.map +1 -1
  68. package/execution/tool-rendering-primitives.js +3 -3
  69. package/execution/tool-rendering-primitives.js.map +1 -1
  70. package/github/GitHubRepoPicker.js +5 -5
  71. package/github/GitHubRepoPicker.js.map +1 -1
  72. package/iam-policy/GrantAccessForm.js +1 -1
  73. package/iam-policy/GrantAccessForm.js.map +1 -1
  74. package/iam-policy/OrgMembersPanel.js +5 -5
  75. package/iam-policy/OrgMembersPanel.js.map +1 -1
  76. package/identity-provider/CreateIdentityProviderForm.js +3 -3
  77. package/identity-provider/CreateIdentityProviderForm.js.map +1 -1
  78. package/identity-provider/IdentityProviderDetailPanel.js +5 -5
  79. package/identity-provider/IdentityProviderDetailPanel.js.map +1 -1
  80. package/identity-provider/IdentityProviderListPanel.js +3 -3
  81. package/identity-provider/IdentityProviderListPanel.js.map +1 -1
  82. package/identity-provider/IdentityProviderWizard.js +7 -7
  83. package/identity-provider/IdentityProviderWizard.js.map +1 -1
  84. package/identity-provider/ProviderPicker.js +2 -2
  85. package/identity-provider/ProviderPicker.js.map +1 -1
  86. package/index.d.ts +8 -4
  87. package/index.d.ts.map +1 -1
  88. package/index.js +7 -3
  89. package/index.js.map +1 -1
  90. package/internal/CloudFeatureNotice.js +1 -1
  91. package/internal/CloudFeatureNotice.js.map +1 -1
  92. package/internal/Tabs.js +1 -1
  93. package/internal/Tabs.js.map +1 -1
  94. package/internal/markdown-components.js +2 -2
  95. package/internal/markdown-components.js.map +1 -1
  96. package/invitation/InvitationCreatedAlert.js +1 -1
  97. package/invitation/InvitationCreatedAlert.js.map +1 -1
  98. package/invitation/InvitationManager.js +5 -5
  99. package/invitation/InvitationManager.js.map +1 -1
  100. package/invitation/InvitationRedemption.js +4 -4
  101. package/invitation/InvitationRedemption.js.map +1 -1
  102. package/library/ResourceCountCard.js +1 -1
  103. package/library/ResourceCountCard.js.map +1 -1
  104. package/library/ResourceListView.js +5 -5
  105. package/library/ResourceListView.js.map +1 -1
  106. package/mcp-server/McpServerConfigPanel.js +5 -5
  107. package/mcp-server/McpServerConfigPanel.js.map +1 -1
  108. package/mcp-server/McpServerConnectDialog.js +4 -4
  109. package/mcp-server/McpServerConnectDialog.js.map +1 -1
  110. package/mcp-server/McpServerDetailView.js +14 -14
  111. package/mcp-server/McpServerDetailView.js.map +1 -1
  112. package/mcp-server/McpServerPicker.js +4 -4
  113. package/mcp-server/McpServerPicker.js.map +1 -1
  114. package/mcp-server/McpToolSelector.js +3 -3
  115. package/mcp-server/McpToolSelector.js.map +1 -1
  116. package/mcp-server/OAuthAppForm.js +1 -1
  117. package/mcp-server/OAuthAppForm.js.map +1 -1
  118. package/models/ModelSelector.js +1 -1
  119. package/models/ModelSelector.js.map +1 -1
  120. package/oauth-app/CreateOAuthAppForm.js +1 -1
  121. package/oauth-app/CreateOAuthAppForm.js.map +1 -1
  122. package/oauth-app/OAuthAppDetailPanel.js +3 -3
  123. package/oauth-app/OAuthAppDetailPanel.js.map +1 -1
  124. package/oauth-app/OAuthAppListPanel.js +2 -2
  125. package/oauth-app/OAuthAppListPanel.js.map +1 -1
  126. package/organization/CreateOrganizationForm.js +1 -1
  127. package/organization/CreateOrganizationForm.js.map +1 -1
  128. package/organization/OrgProfilePanel.js +3 -3
  129. package/organization/OrgProfilePanel.js.map +1 -1
  130. package/package.json +4 -4
  131. package/platform-client/CreatePlatformClientForm.js +2 -2
  132. package/platform-client/CreatePlatformClientForm.js.map +1 -1
  133. package/platform-client/PlatformClientDetailPanel.js +4 -4
  134. package/platform-client/PlatformClientDetailPanel.js.map +1 -1
  135. package/platform-client/PlatformClientListPanel.js +3 -3
  136. package/platform-client/PlatformClientListPanel.js.map +1 -1
  137. package/platform-client/PlatformClientSecretAlert.js +2 -2
  138. package/platform-client/PlatformClientSecretAlert.js.map +1 -1
  139. package/provider.d.ts +39 -2
  140. package/provider.d.ts.map +1 -1
  141. package/provider.js +11 -3
  142. package/provider.js.map +1 -1
  143. package/runner/RunnerListPanel.d.ts +65 -0
  144. package/runner/RunnerListPanel.d.ts.map +1 -0
  145. package/runner/RunnerListPanel.js +237 -0
  146. package/runner/RunnerListPanel.js.map +1 -0
  147. package/runner/RunnerPicker.d.ts +54 -0
  148. package/runner/RunnerPicker.d.ts.map +1 -0
  149. package/runner/RunnerPicker.js +133 -0
  150. package/runner/RunnerPicker.js.map +1 -0
  151. package/runner/__tests__/useDeleteRunner.test.d.ts +2 -0
  152. package/runner/__tests__/useDeleteRunner.test.d.ts.map +1 -0
  153. package/runner/__tests__/useDeleteRunner.test.js +108 -0
  154. package/runner/__tests__/useDeleteRunner.test.js.map +1 -0
  155. package/runner/__tests__/useLaunchLocalRunner.test.d.ts +2 -0
  156. package/runner/__tests__/useLaunchLocalRunner.test.d.ts.map +1 -0
  157. package/runner/__tests__/useLaunchLocalRunner.test.js +143 -0
  158. package/runner/__tests__/useLaunchLocalRunner.test.js.map +1 -0
  159. package/runner/__tests__/useStopRunner.test.d.ts +2 -0
  160. package/runner/__tests__/useStopRunner.test.d.ts.map +1 -0
  161. package/runner/__tests__/useStopRunner.test.js +114 -0
  162. package/runner/__tests__/useStopRunner.test.js.map +1 -0
  163. package/runner/index.d.ts +14 -0
  164. package/runner/index.d.ts.map +1 -0
  165. package/runner/index.js +8 -0
  166. package/runner/index.js.map +1 -0
  167. package/runner/phase.d.ts +30 -0
  168. package/runner/phase.d.ts.map +1 -0
  169. package/runner/phase.js +58 -0
  170. package/runner/phase.js.map +1 -0
  171. package/runner/useDeleteRunner.d.ts +36 -0
  172. package/runner/useDeleteRunner.d.ts.map +1 -0
  173. package/runner/useDeleteRunner.js +42 -0
  174. package/runner/useDeleteRunner.js.map +1 -0
  175. package/runner/useLaunchLocalRunner.d.ts +84 -0
  176. package/runner/useLaunchLocalRunner.d.ts.map +1 -0
  177. package/runner/useLaunchLocalRunner.js +75 -0
  178. package/runner/useLaunchLocalRunner.js.map +1 -0
  179. package/runner/useRunnerList.d.ts +49 -0
  180. package/runner/useRunnerList.d.ts.map +1 -0
  181. package/runner/useRunnerList.js +70 -0
  182. package/runner/useRunnerList.js.map +1 -0
  183. package/runner/useStopRunner.d.ts +53 -0
  184. package/runner/useStopRunner.d.ts.map +1 -0
  185. package/runner/useStopRunner.js +50 -0
  186. package/runner/useStopRunner.js.map +1 -0
  187. package/session/draft.d.ts +53 -0
  188. package/session/draft.d.ts.map +1 -0
  189. package/session/draft.js +45 -0
  190. package/session/draft.js.map +1 -0
  191. package/session/index.d.ts +10 -0
  192. package/session/index.d.ts.map +1 -1
  193. package/session/index.js +5 -0
  194. package/session/index.js.map +1 -1
  195. package/session/useCreateSession.d.ts +8 -0
  196. package/session/useCreateSession.d.ts.map +1 -1
  197. package/session/useCreateSession.js +1 -0
  198. package/session/useCreateSession.js.map +1 -1
  199. package/session/useEditSessionPrep.d.ts +26 -0
  200. package/session/useEditSessionPrep.d.ts.map +1 -0
  201. package/session/useEditSessionPrep.js +83 -0
  202. package/session/useEditSessionPrep.js.map +1 -0
  203. package/session/useNewSessionFlow.d.ts +110 -0
  204. package/session/useNewSessionFlow.d.ts.map +1 -0
  205. package/session/useNewSessionFlow.js +184 -0
  206. package/session/useNewSessionFlow.js.map +1 -0
  207. package/session/usePersistedModel.d.ts +18 -0
  208. package/session/usePersistedModel.d.ts.map +1 -0
  209. package/session/usePersistedModel.js +31 -0
  210. package/session/usePersistedModel.js.map +1 -0
  211. package/session/useSessionPageFlow.d.ts +104 -0
  212. package/session/useSessionPageFlow.d.ts.map +1 -0
  213. package/session/useSessionPageFlow.js +172 -0
  214. package/session/useSessionPageFlow.js.map +1 -0
  215. package/skill/SkillDetailView.js +3 -3
  216. package/skill/SkillDetailView.js.map +1 -1
  217. package/skill/SkillPicker.js +3 -3
  218. package/skill/SkillPicker.js.map +1 -1
  219. package/src/agent/AgentDetailView.tsx +8 -8
  220. package/src/agent/AgentPicker.tsx +3 -3
  221. package/src/api-key/ApiKeyCreatedAlert.tsx +2 -2
  222. package/src/api-key/ApiKeyListPanel.tsx +6 -6
  223. package/src/api-key/CreateApiKeyForm.tsx +2 -2
  224. package/src/attachment/AttachmentChipList.tsx +3 -3
  225. package/src/color-mode.ts +75 -0
  226. package/src/composer/ComposerToolbar.tsx +29 -7
  227. package/src/composer/ConfigureMenu.tsx +6 -6
  228. package/src/composer/ContextChip.tsx +1 -1
  229. package/src/composer/ContextPopover.tsx +2 -2
  230. package/src/composer/SessionComposer.tsx +34 -5
  231. package/src/environment/CreateEnvironmentForm.tsx +3 -3
  232. package/src/environment/EnvVarForm.tsx +6 -6
  233. package/src/environment/EnvironmentListPanel.tsx +3 -3
  234. package/src/environment/EnvironmentVariableEditor.tsx +7 -7
  235. package/src/error/ErrorMessage.tsx +5 -5
  236. package/src/execution/ApprovalCard.tsx +5 -5
  237. package/src/execution/ArtifactCard.tsx +2 -2
  238. package/src/execution/ArtifactPreviewModal.tsx +4 -4
  239. package/src/execution/FollowUpInput.tsx +2 -2
  240. package/src/execution/McpToolDetail.tsx +4 -4
  241. package/src/execution/MessageEntry.tsx +1 -1
  242. package/src/execution/MessageThread.tsx +1 -1
  243. package/src/execution/SessionVariablesInput.tsx +7 -7
  244. package/src/execution/SubAgentSection.tsx +5 -5
  245. package/src/execution/ToolCallDetail.tsx +2 -2
  246. package/src/execution/ToolCallGroup.tsx +3 -3
  247. package/src/execution/ToolCallItem.tsx +4 -4
  248. package/src/execution/WriteBackCard.tsx +5 -5
  249. package/src/execution/tool-rendering-primitives.tsx +5 -5
  250. package/src/github/GitHubRepoPicker.tsx +5 -5
  251. package/src/iam-policy/GrantAccessForm.tsx +2 -2
  252. package/src/iam-policy/OrgMembersPanel.tsx +11 -11
  253. package/src/identity-provider/CreateIdentityProviderForm.tsx +4 -4
  254. package/src/identity-provider/IdentityProviderDetailPanel.tsx +7 -7
  255. package/src/identity-provider/IdentityProviderListPanel.tsx +7 -7
  256. package/src/identity-provider/IdentityProviderWizard.tsx +8 -8
  257. package/src/identity-provider/ProviderPicker.tsx +2 -2
  258. package/src/index.ts +46 -7
  259. package/src/internal/CloudFeatureNotice.tsx +1 -1
  260. package/src/internal/Tabs.tsx +1 -1
  261. package/src/internal/markdown-components.tsx +2 -2
  262. package/src/invitation/InvitationCreatedAlert.tsx +2 -2
  263. package/src/invitation/InvitationManager.tsx +9 -9
  264. package/src/invitation/InvitationRedemption.tsx +11 -11
  265. package/src/library/ResourceCountCard.tsx +1 -1
  266. package/src/library/ResourceListView.tsx +7 -7
  267. package/src/mcp-server/McpServerConfigPanel.tsx +7 -7
  268. package/src/mcp-server/McpServerConnectDialog.tsx +5 -5
  269. package/src/mcp-server/McpServerDetailView.tsx +19 -19
  270. package/src/mcp-server/McpServerPicker.tsx +6 -6
  271. package/src/mcp-server/McpToolSelector.tsx +4 -4
  272. package/src/mcp-server/OAuthAppForm.tsx +3 -3
  273. package/src/models/ModelSelector.tsx +1 -1
  274. package/src/oauth-app/CreateOAuthAppForm.tsx +3 -3
  275. package/src/oauth-app/OAuthAppDetailPanel.tsx +7 -7
  276. package/src/oauth-app/OAuthAppListPanel.tsx +3 -3
  277. package/src/organization/CreateOrganizationForm.tsx +3 -3
  278. package/src/organization/OrgProfilePanel.tsx +4 -5
  279. package/src/platform-client/CreatePlatformClientForm.tsx +6 -6
  280. package/src/platform-client/PlatformClientDetailPanel.tsx +19 -19
  281. package/src/platform-client/PlatformClientListPanel.tsx +7 -7
  282. package/src/platform-client/PlatformClientSecretAlert.tsx +2 -2
  283. package/src/provider.tsx +52 -2
  284. package/src/runner/RunnerListPanel.tsx +725 -0
  285. package/src/runner/RunnerPicker.tsx +319 -0
  286. package/src/runner/__tests__/useDeleteRunner.test.tsx +150 -0
  287. package/src/runner/__tests__/useLaunchLocalRunner.test.tsx +223 -0
  288. package/src/runner/__tests__/useStopRunner.test.tsx +154 -0
  289. package/src/runner/index.ts +34 -0
  290. package/src/runner/phase.ts +62 -0
  291. package/src/runner/useDeleteRunner.ts +67 -0
  292. package/src/runner/useLaunchLocalRunner.ts +139 -0
  293. package/src/runner/useRunnerList.ts +114 -0
  294. package/src/runner/useStopRunner.ts +92 -0
  295. package/src/session/draft.ts +82 -0
  296. package/src/session/index.ts +28 -0
  297. package/src/session/useCreateSession.ts +9 -0
  298. package/src/session/useEditSessionPrep.ts +111 -0
  299. package/src/session/useNewSessionFlow.ts +283 -0
  300. package/src/session/usePersistedModel.ts +41 -0
  301. package/src/session/useSessionPageFlow.ts +280 -0
  302. package/src/skill/SkillDetailView.tsx +5 -5
  303. package/src/skill/SkillPicker.tsx +3 -3
  304. package/src/styles.css +25 -1
  305. package/src/usage/OrgUsagePanel.tsx +4 -4
  306. package/src/workspace/WorkspaceEditor.tsx +78 -66
  307. package/src/workspace/index.ts +0 -8
  308. package/styles.css +1 -1
  309. package/usage/OrgUsagePanel.js +2 -2
  310. package/usage/OrgUsagePanel.js.map +1 -1
  311. package/workspace/WorkspaceEditor.d.ts +28 -3
  312. package/workspace/WorkspaceEditor.d.ts.map +1 -1
  313. package/workspace/WorkspaceEditor.js +24 -25
  314. package/workspace/WorkspaceEditor.js.map +1 -1
  315. package/workspace/index.d.ts +0 -4
  316. package/workspace/index.d.ts.map +1 -1
  317. package/workspace/index.js +0 -2
  318. package/workspace/index.js.map +1 -1
  319. package/src/workspace/FolderBrowser.tsx +0 -579
  320. package/src/workspace/useFolderListing.ts +0 -164
  321. package/workspace/FolderBrowser.d.ts +0 -37
  322. package/workspace/FolderBrowser.d.ts.map +0 -1
  323. package/workspace/FolderBrowser.js +0 -188
  324. package/workspace/FolderBrowser.js.map +0 -1
  325. package/workspace/useFolderListing.d.ts +0 -73
  326. package/workspace/useFolderListing.d.ts.map +0 -1
  327. package/workspace/useFolderListing.js +0 -110
  328. package/workspace/useFolderListing.js.map +0 -1
@@ -0,0 +1,154 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { renderHook, act } from "@testing-library/react";
3
+ import type { ReactNode } from "react";
4
+ import { create } from "@bufbuild/protobuf";
5
+ import {
6
+ RunnerSchema,
7
+ RunnerStatusSchema,
8
+ type Runner,
9
+ } from "@stigmer/protos/ai/stigmer/agentic/runner/v1/api_pb";
10
+ import { RunnerPhase } from "@stigmer/protos/ai/stigmer/agentic/runner/v1/enum_pb";
11
+ import { ApiResourceMetadataSchema } from "@stigmer/protos/ai/stigmer/commons/apiresource/metadata_pb";
12
+ import type { Stigmer } from "@stigmer/sdk";
13
+ import { StigmerContext } from "../../context";
14
+ import { useStopRunner } from "../useStopRunner";
15
+
16
+ function makeRunner(id: string, phase: RunnerPhase): Runner {
17
+ const runner = create(RunnerSchema);
18
+ runner.metadata = create(ApiResourceMetadataSchema);
19
+ runner.metadata.id = id;
20
+ runner.status = create(RunnerStatusSchema);
21
+ runner.status.phase = phase;
22
+ return runner;
23
+ }
24
+
25
+ function buildMockClient(overrides: {
26
+ stop?: ReturnType<typeof vi.fn>;
27
+ } = {}) {
28
+ return {
29
+ runner: {
30
+ stop: overrides.stop ?? vi.fn(),
31
+ },
32
+ } as unknown as Stigmer;
33
+ }
34
+
35
+ function makeWrapper(client: Stigmer) {
36
+ return ({ children }: { children: ReactNode }) => (
37
+ <StigmerContext.Provider value={client}>
38
+ {children}
39
+ </StigmerContext.Provider>
40
+ );
41
+ }
42
+
43
+ describe("useStopRunner", () => {
44
+ let stopMock: ReturnType<typeof vi.fn>;
45
+ let client: Stigmer;
46
+
47
+ beforeEach(() => {
48
+ stopMock = vi.fn();
49
+ client = buildMockClient({ stop: stopMock });
50
+ });
51
+
52
+ it("calls runner.stop with correct proto message and returns the runner", async () => {
53
+ const stoppedRunner = makeRunner("rnr_1", RunnerPhase.STOPPED);
54
+ stopMock.mockResolvedValueOnce(stoppedRunner);
55
+
56
+ const { result } = renderHook(() => useStopRunner(), {
57
+ wrapper: makeWrapper(client),
58
+ });
59
+
60
+ expect(result.current.isStopping).toBe(false);
61
+ expect(result.current.error).toBeNull();
62
+
63
+ let returned: Runner;
64
+ await act(async () => {
65
+ returned = await result.current.stop({
66
+ runnerId: "rnr_1",
67
+ reason: "user requested",
68
+ });
69
+ });
70
+
71
+ expect(stopMock).toHaveBeenCalledOnce();
72
+ const protoArg = stopMock.mock.calls[0][0];
73
+ expect(protoArg.runnerId).toBe("rnr_1");
74
+ expect(protoArg.reason).toBe("user requested");
75
+
76
+ expect(returned!).toBe(stoppedRunner);
77
+ expect(result.current.isStopping).toBe(false);
78
+ expect(result.current.error).toBeNull();
79
+ });
80
+
81
+ it("defaults reason to empty string when omitted", async () => {
82
+ const stoppedRunner = makeRunner("rnr_2", RunnerPhase.STOPPED);
83
+ stopMock.mockResolvedValueOnce(stoppedRunner);
84
+
85
+ const { result } = renderHook(() => useStopRunner(), {
86
+ wrapper: makeWrapper(client),
87
+ });
88
+
89
+ await act(async () => {
90
+ await result.current.stop({ runnerId: "rnr_2" });
91
+ });
92
+
93
+ const protoArg = stopMock.mock.calls[0][0];
94
+ expect(protoArg.reason).toBe("");
95
+ });
96
+
97
+ it("sets error and rethrows on failure", async () => {
98
+ const rpcError = new Error("runner not found");
99
+ stopMock.mockRejectedValueOnce(rpcError);
100
+
101
+ const { result } = renderHook(() => useStopRunner(), {
102
+ wrapper: makeWrapper(client),
103
+ });
104
+
105
+ await act(async () => {
106
+ await expect(
107
+ result.current.stop({ runnerId: "rnr_missing" }),
108
+ ).rejects.toThrow("runner not found");
109
+ });
110
+
111
+ expect(result.current.error).toBeInstanceOf(Error);
112
+ expect(result.current.error!.message).toBe("runner not found");
113
+ expect(result.current.isStopping).toBe(false);
114
+ });
115
+
116
+ it("clears error via clearError", async () => {
117
+ stopMock.mockRejectedValueOnce(new Error("fail"));
118
+
119
+ const { result } = renderHook(() => useStopRunner(), {
120
+ wrapper: makeWrapper(client),
121
+ });
122
+
123
+ await act(async () => {
124
+ await result.current.stop({ runnerId: "rnr_1" }).catch(() => {});
125
+ });
126
+ expect(result.current.error).not.toBeNull();
127
+
128
+ act(() => {
129
+ result.current.clearError();
130
+ });
131
+ expect(result.current.error).toBeNull();
132
+ });
133
+
134
+ it("resets previous error on a new successful stop", async () => {
135
+ stopMock.mockRejectedValueOnce(new Error("first fail"));
136
+
137
+ const { result } = renderHook(() => useStopRunner(), {
138
+ wrapper: makeWrapper(client),
139
+ });
140
+
141
+ await act(async () => {
142
+ await result.current.stop({ runnerId: "rnr_1" }).catch(() => {});
143
+ });
144
+ expect(result.current.error).not.toBeNull();
145
+
146
+ const stoppedRunner = makeRunner("rnr_1", RunnerPhase.STOPPED);
147
+ stopMock.mockResolvedValueOnce(stoppedRunner);
148
+
149
+ await act(async () => {
150
+ await result.current.stop({ runnerId: "rnr_1" });
151
+ });
152
+ expect(result.current.error).toBeNull();
153
+ });
154
+ });
@@ -0,0 +1,34 @@
1
+ export { useRunnerList } from "./useRunnerList";
2
+ export type {
3
+ UseRunnerListOptions,
4
+ UseRunnerListReturn,
5
+ } from "./useRunnerList";
6
+
7
+ export { useLaunchLocalRunner } from "./useLaunchLocalRunner";
8
+ export type {
9
+ UseLaunchLocalRunnerOptions,
10
+ UseLaunchLocalRunnerReturn,
11
+ LaunchLocalRunnerResult,
12
+ } from "./useLaunchLocalRunner";
13
+
14
+ export { useStopRunner } from "./useStopRunner";
15
+ export type {
16
+ StopRunnerInput,
17
+ UseStopRunnerReturn,
18
+ } from "./useStopRunner";
19
+
20
+ export { useDeleteRunner } from "./useDeleteRunner";
21
+ export type { UseDeleteRunnerReturn } from "./useDeleteRunner";
22
+
23
+ export { RunnerPicker } from "./RunnerPicker";
24
+ export type { RunnerPickerProps } from "./RunnerPicker";
25
+
26
+ export { RunnerListPanel } from "./RunnerListPanel";
27
+ export type { RunnerListPanelProps } from "./RunnerListPanel";
28
+
29
+ export {
30
+ phaseLabel,
31
+ phaseDotColor,
32
+ isActivePhase,
33
+ PHASE_SORT_ORDER,
34
+ } from "./phase";
@@ -0,0 +1,62 @@
1
+ import { RunnerPhase } from "@stigmer/protos/ai/stigmer/agentic/runner/v1/enum_pb";
2
+
3
+ /**
4
+ * Sort order for runner phases — active phases first, then by severity.
5
+ *
6
+ * Used by both {@link RunnerPicker} and {@link RunnerListPanel} to present
7
+ * runners in a consistent, predictable order across all UI surfaces.
8
+ */
9
+ export const PHASE_SORT_ORDER: Record<RunnerPhase, number> = {
10
+ [RunnerPhase.READY]: 0,
11
+ [RunnerPhase.BUSY]: 1,
12
+ [RunnerPhase.PENDING]: 2,
13
+ [RunnerPhase.STOPPED]: 3,
14
+ [RunnerPhase.FAILED]: 4,
15
+ [RunnerPhase.UNSPECIFIED]: 5,
16
+ };
17
+
18
+ const LABELS: Record<RunnerPhase, string> = {
19
+ [RunnerPhase.READY]: "Ready",
20
+ [RunnerPhase.BUSY]: "Busy",
21
+ [RunnerPhase.PENDING]: "Pending",
22
+ [RunnerPhase.STOPPED]: "Stopped",
23
+ [RunnerPhase.FAILED]: "Failed",
24
+ [RunnerPhase.UNSPECIFIED]: "Unknown",
25
+ };
26
+
27
+ /**
28
+ * Human-readable label for a runner phase.
29
+ *
30
+ * Returns title-case labels suitable for both compact indicators
31
+ * ("Ready") and full-row display ("Stopped").
32
+ */
33
+ export function phaseLabel(phase: RunnerPhase): string {
34
+ return LABELS[phase] ?? "Unknown";
35
+ }
36
+
37
+ /**
38
+ * Tailwind `bg-*` class for the small colored dot indicator.
39
+ *
40
+ * - Ready → `bg-success` (green)
41
+ * - Busy → `bg-warning` (amber)
42
+ * - Others → `bg-muted-foreground` (neutral)
43
+ */
44
+ export function phaseDotColor(phase: RunnerPhase): string {
45
+ switch (phase) {
46
+ case RunnerPhase.READY:
47
+ return "bg-success";
48
+ case RunnerPhase.BUSY:
49
+ return "bg-warning";
50
+ default:
51
+ return "bg-muted-foreground";
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Whether the runner is in an active (workload-accepting) phase.
57
+ *
58
+ * Active = `READY` or `BUSY`. Inactive = everything else.
59
+ */
60
+ export function isActivePhase(phase: RunnerPhase): boolean {
61
+ return phase === RunnerPhase.READY || phase === RunnerPhase.BUSY;
62
+ }
@@ -0,0 +1,67 @@
1
+ "use client";
2
+
3
+ import { useCallback, useState } from "react";
4
+ import type { Runner } from "@stigmer/protos/ai/stigmer/agentic/runner/v1/api_pb";
5
+ import { useStigmer } from "../hooks";
6
+ import { toError } from "../internal/toError";
7
+
8
+ /** Return value of {@link useDeleteRunner}. */
9
+ export interface UseDeleteRunnerReturn {
10
+ /**
11
+ * Delete a runner by its resource ID. Resolves with the deleted
12
+ * {@link Runner} for confirmation display.
13
+ *
14
+ * The deletion is permanent — any sessions bound to this runner will
15
+ * fall back to auto-provisioning on their next execution.
16
+ */
17
+ readonly deleteRunner: (id: string) => Promise<Runner>;
18
+ /** `true` while the delete request is in flight. */
19
+ readonly isDeleting: boolean;
20
+ /** Error from the last failed delete, or `null` when healthy. */
21
+ readonly error: Error | null;
22
+ /** Reset `error` to `null`. */
23
+ readonly clearError: () => void;
24
+ }
25
+
26
+ /**
27
+ * Mutation hook that wraps `runner.delete()` with loading and error
28
+ * state.
29
+ *
30
+ * Deletes a runner by its resource ID. Returns the deleted
31
+ * {@link Runner} on success so callers can confirm which runner was
32
+ * removed (e.g., in a toast or undo prompt).
33
+ *
34
+ * @example
35
+ * ```tsx
36
+ * const { deleteRunner, isDeleting, error } = useDeleteRunner();
37
+ *
38
+ * await deleteRunner("rnr_abc123");
39
+ * refetch(); // refresh the runner list
40
+ * ```
41
+ */
42
+ export function useDeleteRunner(): UseDeleteRunnerReturn {
43
+ const stigmer = useStigmer();
44
+ const [isDeleting, setIsDeleting] = useState(false);
45
+ const [error, setError] = useState<Error | null>(null);
46
+
47
+ const clearError = useCallback(() => setError(null), []);
48
+
49
+ const deleteRunner = useCallback(
50
+ async (id: string): Promise<Runner> => {
51
+ setIsDeleting(true);
52
+ setError(null);
53
+
54
+ try {
55
+ return await stigmer.runner.delete(id);
56
+ } catch (err) {
57
+ setError(toError(err));
58
+ throw err;
59
+ } finally {
60
+ setIsDeleting(false);
61
+ }
62
+ },
63
+ [stigmer],
64
+ );
65
+
66
+ return { deleteRunner, isDeleting, error, clearError };
67
+ }
@@ -0,0 +1,139 @@
1
+ "use client";
2
+
3
+ import { useCallback, useState } from "react";
4
+ import { create } from "@bufbuild/protobuf";
5
+ import { timestampDate } from "@bufbuild/protobuf/wkt";
6
+ import { CreateLaunchTokenRequestSchema } from "@stigmer/protos/ai/stigmer/agentic/runner/v1/io_pb";
7
+ import { useStigmer } from "../hooks";
8
+ import { toError } from "../internal/toError";
9
+
10
+ const LAUNCH_RUNNER_SCHEME = "stigmer://launch-runner";
11
+
12
+ function defaultOpenUrl(url: string): void {
13
+ window.location.href = url;
14
+ }
15
+
16
+ /** Options for {@link useLaunchLocalRunner}. */
17
+ export interface UseLaunchLocalRunnerOptions {
18
+ /**
19
+ * Custom handler for opening the `stigmer://` URL.
20
+ *
21
+ * Defaults to `window.location.href` assignment, which is the standard
22
+ * browser pattern for dispatching custom URL schemes to the OS. The
23
+ * browser does not navigate away — it hands the scheme to the OS and
24
+ * the page stays on screen.
25
+ *
26
+ * Override this when embedding in non-standard environments (Electron,
27
+ * iframe, React Native WebView) where the default browser mechanism
28
+ * does not apply.
29
+ */
30
+ readonly openUrl?: (url: string) => void;
31
+ }
32
+
33
+ /** Result returned by a successful {@link UseLaunchLocalRunnerReturn.launch} call. */
34
+ export interface LaunchLocalRunnerResult {
35
+ /** The `stigmer://launch-runner?token=...` URL that was opened. */
36
+ readonly url: string;
37
+ /**
38
+ * Absolute expiry time of the launch token. The desktop app must
39
+ * exchange the token before this time (typically 60 seconds).
40
+ */
41
+ readonly expiresAt: Date | undefined;
42
+ }
43
+
44
+ /** Return value of {@link useLaunchLocalRunner}. */
45
+ export interface UseLaunchLocalRunnerReturn {
46
+ /**
47
+ * Create a one-time launch token and open the `stigmer://` URL to
48
+ * trigger the desktop app (or CLI fallback) to start a local runner.
49
+ *
50
+ * Resolves with the constructed URL and token expiry on success.
51
+ * The caller cannot detect whether the desktop app received the URL —
52
+ * use {@link useRunnerList} with `refetch()` to poll for the runner
53
+ * appearing in the list.
54
+ */
55
+ readonly launch: (input: { org: string }) => Promise<LaunchLocalRunnerResult>;
56
+ /** `true` while the token creation and URL dispatch are in flight. */
57
+ readonly isLaunching: boolean;
58
+ /** Error from the last failed launch attempt, or `null` when healthy. */
59
+ readonly error: Error | null;
60
+ /** Reset `error` to `null`. */
61
+ readonly clearError: () => void;
62
+ }
63
+
64
+ /**
65
+ * Behavior hook that initiates a browser-to-desktop runner launch.
66
+ *
67
+ * Orchestrates two steps:
68
+ * 1. Calls `stigmer.runner.createLaunchToken({ org })` to mint a
69
+ * one-time, 60-second token backed by the caller's credentials.
70
+ * 2. Opens `stigmer://launch-runner?token={token}` so the OS dispatches
71
+ * to the Stigmer Desktop app (which exchanges the token for a JWT
72
+ * and starts a local runner via its CLI sidecar).
73
+ *
74
+ * The hook reports success when the URL is opened. It does **not**
75
+ * detect whether the desktop app is installed or whether the runner
76
+ * actually started — those are observable via `useRunnerList`.
77
+ *
78
+ * @example
79
+ * ```tsx
80
+ * function LaunchButton({ org }: { org: string }) {
81
+ * const { launch, isLaunching, error } = useLaunchLocalRunner();
82
+ *
83
+ * return (
84
+ * <button onClick={() => launch({ org })} disabled={isLaunching}>
85
+ * {isLaunching ? "Launching…" : "Launch Local Runner"}
86
+ * </button>
87
+ * );
88
+ * }
89
+ * ```
90
+ *
91
+ * @example
92
+ * ```tsx
93
+ * // Custom URL handler for non-browser environments
94
+ * const { launch } = useLaunchLocalRunner({
95
+ * openUrl: (url) => nativeBridge.openExternal(url),
96
+ * });
97
+ * ```
98
+ */
99
+ export function useLaunchLocalRunner(
100
+ options?: UseLaunchLocalRunnerOptions,
101
+ ): UseLaunchLocalRunnerReturn {
102
+ const stigmer = useStigmer();
103
+ const [isLaunching, setIsLaunching] = useState(false);
104
+ const [error, setError] = useState<Error | null>(null);
105
+
106
+ const openUrl = options?.openUrl ?? defaultOpenUrl;
107
+
108
+ const clearError = useCallback(() => setError(null), []);
109
+
110
+ const launch = useCallback(
111
+ async (input: { org: string }): Promise<LaunchLocalRunnerResult> => {
112
+ setIsLaunching(true);
113
+ setError(null);
114
+
115
+ try {
116
+ const response = await stigmer.runner.createLaunchToken(
117
+ create(CreateLaunchTokenRequestSchema, { org: input.org }),
118
+ );
119
+
120
+ const url = `${LAUNCH_RUNNER_SCHEME}?token=${encodeURIComponent(response.token)}`;
121
+ const expiresAt = response.expiresAt
122
+ ? timestampDate(response.expiresAt)
123
+ : undefined;
124
+
125
+ openUrl(url);
126
+
127
+ return { url, expiresAt };
128
+ } catch (err) {
129
+ setError(toError(err));
130
+ throw err;
131
+ } finally {
132
+ setIsLaunching(false);
133
+ }
134
+ },
135
+ [stigmer, openUrl],
136
+ );
137
+
138
+ return { launch, isLaunching, error, clearError };
139
+ }
@@ -0,0 +1,114 @@
1
+ "use client";
2
+
3
+ import { useCallback, useEffect, useState } from "react";
4
+ import { create } from "@bufbuild/protobuf";
5
+ import type { Runner } from "@stigmer/protos/ai/stigmer/agentic/runner/v1/api_pb";
6
+ import { ListRunnersRequestSchema } from "@stigmer/protos/ai/stigmer/agentic/runner/v1/io_pb";
7
+ import { useStigmer } from "../hooks";
8
+ import { toError } from "../internal/toError";
9
+
10
+ const SYSTEM_MANAGED_LABEL = "stigmer.ai/system-managed";
11
+
12
+ /** Options for {@link useRunnerList}. */
13
+ export interface UseRunnerListOptions {
14
+ /**
15
+ * Include system-managed (ephemeral cloud) runners in the result.
16
+ *
17
+ * System-managed runners are labeled `stigmer.ai/system-managed: "true"`
18
+ * and are created by the platform for cloud executions. They are hidden
19
+ * from the session composer by default but useful in admin views.
20
+ *
21
+ * @default false
22
+ */
23
+ readonly includeSystemManaged?: boolean;
24
+ }
25
+
26
+ /** Return value of {@link useRunnerList}. */
27
+ export interface UseRunnerListReturn {
28
+ /** User-created runners for the organization, empty while loading or on error. */
29
+ readonly runners: readonly Runner[];
30
+ /** `true` while the fetch is in flight. */
31
+ readonly isLoading: boolean;
32
+ /** Error from the last failed fetch, or `null` when healthy. */
33
+ readonly error: Error | null;
34
+ /** Discard cached data and re-fetch the runner list from the server. */
35
+ readonly refetch: () => void;
36
+ }
37
+
38
+ /**
39
+ * Data hook that fetches runners for an organization.
40
+ *
41
+ * Calls `stigmer.runner.list({ org })` and returns the result with
42
+ * loading/error state. System-managed (ephemeral cloud) runners are
43
+ * filtered out by default — pass `includeSystemManaged: true` to
44
+ * include them (useful for admin panels like Settings > Runners).
45
+ *
46
+ * Returns an empty array when `org` is `null` (no organization selected).
47
+ *
48
+ * @example
49
+ * ```tsx
50
+ * // Session composer — user-created runners only
51
+ * const { runners, isLoading } = useRunnerList("acme");
52
+ * ```
53
+ *
54
+ * @example
55
+ * ```tsx
56
+ * // Admin page — all runners including system-managed
57
+ * const { runners } = useRunnerList("acme", { includeSystemManaged: true });
58
+ * ```
59
+ */
60
+ export function useRunnerList(
61
+ org: string | null,
62
+ options?: UseRunnerListOptions,
63
+ ): UseRunnerListReturn {
64
+ const stigmer = useStigmer();
65
+ const [runners, setRunners] = useState<Runner[]>([]);
66
+ const [isLoading, setIsLoading] = useState(false);
67
+ const [error, setError] = useState<Error | null>(null);
68
+ const [fetchKey, setFetchKey] = useState(0);
69
+
70
+ const includeSystemManaged = options?.includeSystemManaged ?? false;
71
+
72
+ const refetch = useCallback(() => setFetchKey((k) => k + 1), []);
73
+
74
+ useEffect(() => {
75
+ if (!org) {
76
+ setRunners([]);
77
+ setIsLoading(false);
78
+ setError(null);
79
+ return;
80
+ }
81
+
82
+ const cancelled = { current: false };
83
+ setIsLoading(true);
84
+ setError(null);
85
+
86
+ stigmer.runner
87
+ .list(create(ListRunnersRequestSchema, { org }))
88
+ .then(
89
+ (result) => {
90
+ if (cancelled.current) return;
91
+
92
+ const items = includeSystemManaged
93
+ ? result.items
94
+ : result.items.filter(
95
+ (r) => r.metadata?.labels[SYSTEM_MANAGED_LABEL] !== "true",
96
+ );
97
+
98
+ setRunners(items);
99
+ setIsLoading(false);
100
+ },
101
+ (err) => {
102
+ if (cancelled.current) return;
103
+ setError(toError(err));
104
+ setIsLoading(false);
105
+ },
106
+ );
107
+
108
+ return () => {
109
+ cancelled.current = true;
110
+ };
111
+ }, [stigmer, org, includeSystemManaged, fetchKey]);
112
+
113
+ return { runners, isLoading, error, refetch };
114
+ }
@@ -0,0 +1,92 @@
1
+ "use client";
2
+
3
+ import { useCallback, useState } from "react";
4
+ import { create } from "@bufbuild/protobuf";
5
+ import type { Runner } from "@stigmer/protos/ai/stigmer/agentic/runner/v1/api_pb";
6
+ import { RunnerStopInputSchema } from "@stigmer/protos/ai/stigmer/agentic/runner/v1/io_pb";
7
+ import { useStigmer } from "../hooks";
8
+ import { toError } from "../internal/toError";
9
+
10
+ /** Input for {@link UseStopRunnerReturn.stop}. */
11
+ export interface StopRunnerInput {
12
+ /** ID of the runner to stop. */
13
+ readonly runnerId: string;
14
+ /**
15
+ * Optional reason for the stop, logged on the runner and in audit.
16
+ *
17
+ * @example "user requested via web console"
18
+ */
19
+ readonly reason?: string;
20
+ }
21
+
22
+ /** Return value of {@link useStopRunner}. */
23
+ export interface UseStopRunnerReturn {
24
+ /**
25
+ * Stop a runner gracefully.
26
+ *
27
+ * If the runner is connected, sends a stop command via the bidi stream
28
+ * and waits for acknowledgment. If offline, directly transitions to
29
+ * STOPPED. Idempotent — stopping an already-stopped or failed runner
30
+ * returns the resource as-is.
31
+ *
32
+ * Resolves with the updated {@link Runner} resource.
33
+ */
34
+ readonly stop: (input: StopRunnerInput) => Promise<Runner>;
35
+ /** `true` while the stop RPC is in flight. */
36
+ readonly isStopping: boolean;
37
+ /** Error from the last failed stop attempt, or `null` when healthy. */
38
+ readonly error: Error | null;
39
+ /** Reset `error` to `null`. */
40
+ readonly clearError: () => void;
41
+ }
42
+
43
+ /**
44
+ * Mutation hook that wraps `runner.stop()` with loading and error state.
45
+ *
46
+ * Stops a runner gracefully. Connected runners receive a stop command
47
+ * via the bidi stream; offline runners transition directly to STOPPED.
48
+ * The operation is idempotent — stopping an already-stopped or failed
49
+ * runner succeeds without error.
50
+ *
51
+ * Returns the updated {@link Runner} resource on success so callers
52
+ * can reflect the new phase in the UI without a separate refetch.
53
+ *
54
+ * @example
55
+ * ```tsx
56
+ * const { stop, isStopping, error } = useStopRunner();
57
+ *
58
+ * await stop({ runnerId: "rnr_abc123", reason: "user requested" });
59
+ * refetch(); // refresh the runner list
60
+ * ```
61
+ */
62
+ export function useStopRunner(): UseStopRunnerReturn {
63
+ const stigmer = useStigmer();
64
+ const [isStopping, setIsStopping] = useState(false);
65
+ const [error, setError] = useState<Error | null>(null);
66
+
67
+ const clearError = useCallback(() => setError(null), []);
68
+
69
+ const stop = useCallback(
70
+ async (input: StopRunnerInput): Promise<Runner> => {
71
+ setIsStopping(true);
72
+ setError(null);
73
+
74
+ try {
75
+ return await stigmer.runner.stop(
76
+ create(RunnerStopInputSchema, {
77
+ runnerId: input.runnerId,
78
+ reason: input.reason ?? "",
79
+ }),
80
+ );
81
+ } catch (err) {
82
+ setError(toError(err));
83
+ throw err;
84
+ } finally {
85
+ setIsStopping(false);
86
+ }
87
+ },
88
+ [stigmer],
89
+ );
90
+
91
+ return { stop, isStopping, error, clearError };
92
+ }