@stigmer/react 3.5.3 → 3.7.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 (283) hide show
  1. package/agent/AgentPicker.d.ts +10 -1
  2. package/agent/AgentPicker.d.ts.map +1 -1
  3. package/agent/AgentPicker.js +2 -2
  4. package/agent/AgentPicker.js.map +1 -1
  5. package/billing/CreditLedgerTable.d.ts.map +1 -1
  6. package/billing/CreditLedgerTable.js +2 -7
  7. package/billing/CreditLedgerTable.js.map +1 -1
  8. package/channel/AgentChannelsPanel.d.ts.map +1 -1
  9. package/channel/AgentChannelsPanel.js +22 -5
  10. package/channel/AgentChannelsPanel.js.map +1 -1
  11. package/channel/ChannelTemplatesDialog.d.ts +53 -0
  12. package/channel/ChannelTemplatesDialog.d.ts.map +1 -0
  13. package/channel/ChannelTemplatesDialog.js +150 -0
  14. package/channel/ChannelTemplatesDialog.js.map +1 -0
  15. package/channel/index.d.ts +8 -0
  16. package/channel/index.d.ts.map +1 -1
  17. package/channel/index.js +4 -0
  18. package/channel/index.js.map +1 -1
  19. package/channel/providers.d.ts +12 -0
  20. package/channel/providers.d.ts.map +1 -1
  21. package/channel/providers.js +14 -2
  22. package/channel/providers.js.map +1 -1
  23. package/channel/templatePresentation.d.ts +37 -0
  24. package/channel/templatePresentation.d.ts.map +1 -0
  25. package/channel/templatePresentation.js +69 -0
  26. package/channel/templatePresentation.js.map +1 -0
  27. package/channel/useChannelTemplateList.d.ts +64 -0
  28. package/channel/useChannelTemplateList.d.ts.map +1 -0
  29. package/channel/useChannelTemplateList.js +69 -0
  30. package/channel/useChannelTemplateList.js.map +1 -0
  31. package/channel/useChannelTemplateReadiness.d.ts +53 -0
  32. package/channel/useChannelTemplateReadiness.d.ts.map +1 -0
  33. package/channel/useChannelTemplateReadiness.js +42 -0
  34. package/channel/useChannelTemplateReadiness.js.map +1 -0
  35. package/composer/ComposerToolbar.d.ts +6 -1
  36. package/composer/ComposerToolbar.d.ts.map +1 -1
  37. package/composer/ComposerToolbar.js +2 -2
  38. package/composer/ComposerToolbar.js.map +1 -1
  39. package/composer/SessionComposer.d.ts +9 -0
  40. package/composer/SessionComposer.d.ts.map +1 -1
  41. package/composer/SessionComposer.js +13 -2
  42. package/composer/SessionComposer.js.map +1 -1
  43. package/dashboard/useDashboardFailedRuns.d.ts.map +1 -1
  44. package/dashboard/useDashboardFailedRuns.js +2 -0
  45. package/dashboard/useDashboardFailedRuns.js.map +1 -1
  46. package/execution/useCreateAgentExecution.d.ts +14 -0
  47. package/execution/useCreateAgentExecution.d.ts.map +1 -1
  48. package/execution/useCreateAgentExecution.js +5 -0
  49. package/execution/useCreateAgentExecution.js.map +1 -1
  50. package/index.d.ts +4 -2
  51. package/index.d.ts.map +1 -1
  52. package/index.js +10 -1
  53. package/index.js.map +1 -1
  54. package/internal/Pagination.d.ts +15 -0
  55. package/internal/Pagination.d.ts.map +1 -0
  56. package/internal/Pagination.js +20 -0
  57. package/internal/Pagination.js.map +1 -0
  58. package/library/useExportResource.d.ts +4 -3
  59. package/library/useExportResource.d.ts.map +1 -1
  60. package/library/useExportResource.js +1 -1
  61. package/library/useExportResource.js.map +1 -1
  62. package/models/ModelSelector.d.ts +26 -1
  63. package/models/ModelSelector.d.ts.map +1 -1
  64. package/models/ModelSelector.js +27 -6
  65. package/models/ModelSelector.js.map +1 -1
  66. package/models/index.d.ts +2 -0
  67. package/models/index.d.ts.map +1 -1
  68. package/models/index.js +1 -0
  69. package/models/index.js.map +1 -1
  70. package/models/registry.d.ts +10 -0
  71. package/models/registry.d.ts.map +1 -1
  72. package/models/registry.js +3 -0
  73. package/models/registry.js.map +1 -1
  74. package/models/service-tier.d.ts +27 -0
  75. package/models/service-tier.d.ts.map +1 -0
  76. package/models/service-tier.js +34 -0
  77. package/models/service-tier.js.map +1 -0
  78. package/organization/OrgProvider.d.ts +11 -4
  79. package/organization/OrgProvider.d.ts.map +1 -1
  80. package/organization/OrgProvider.js +28 -4
  81. package/organization/OrgProvider.js.map +1 -1
  82. package/package.json +4 -4
  83. package/resource-detail/useDeleteResource.d.ts +1 -1
  84. package/resource-detail/useDeleteResource.d.ts.map +1 -1
  85. package/resource-detail/useDeleteResource.js +5 -0
  86. package/resource-detail/useDeleteResource.js.map +1 -1
  87. package/resource-workbench/components/ResourceWorkbench.d.ts +10 -1
  88. package/resource-workbench/components/ResourceWorkbench.d.ts.map +1 -1
  89. package/resource-workbench/components/ResourceWorkbench.js +6 -2
  90. package/resource-workbench/components/ResourceWorkbench.js.map +1 -1
  91. package/resource-workbench/hooks/useResourceCollection.d.ts +8 -0
  92. package/resource-workbench/hooks/useResourceCollection.d.ts.map +1 -1
  93. package/resource-workbench/hooks/useResourceCollection.js +2 -2
  94. package/resource-workbench/hooks/useResourceCollection.js.map +1 -1
  95. package/schedule/CadenceField.d.ts +31 -0
  96. package/schedule/CadenceField.d.ts.map +1 -0
  97. package/schedule/CadenceField.js +116 -0
  98. package/schedule/CadenceField.js.map +1 -0
  99. package/schedule/ScheduleDetailView.d.ts +81 -0
  100. package/schedule/ScheduleDetailView.d.ts.map +1 -0
  101. package/schedule/ScheduleDetailView.js +648 -0
  102. package/schedule/ScheduleDetailView.js.map +1 -0
  103. package/schedule/ScheduleForm.d.ts +46 -0
  104. package/schedule/ScheduleForm.d.ts.map +1 -0
  105. package/schedule/ScheduleForm.js +214 -0
  106. package/schedule/ScheduleForm.js.map +1 -0
  107. package/schedule/ScheduleRowActions.d.ts +20 -0
  108. package/schedule/ScheduleRowActions.d.ts.map +1 -0
  109. package/schedule/ScheduleRowActions.js +54 -0
  110. package/schedule/ScheduleRowActions.js.map +1 -0
  111. package/schedule/ScheduleRunsTable.d.ts +72 -0
  112. package/schedule/ScheduleRunsTable.d.ts.map +1 -0
  113. package/schedule/ScheduleRunsTable.js +161 -0
  114. package/schedule/ScheduleRunsTable.js.map +1 -0
  115. package/schedule/TimeZoneField.d.ts +32 -0
  116. package/schedule/TimeZoneField.d.ts.map +1 -0
  117. package/schedule/TimeZoneField.js +104 -0
  118. package/schedule/TimeZoneField.js.map +1 -0
  119. package/schedule/cadence.d.ts +95 -0
  120. package/schedule/cadence.d.ts.map +1 -0
  121. package/schedule/cadence.js +242 -0
  122. package/schedule/cadence.js.map +1 -0
  123. package/schedule/index.d.ts +37 -0
  124. package/schedule/index.d.ts.map +1 -0
  125. package/schedule/index.js +19 -0
  126. package/schedule/index.js.map +1 -0
  127. package/schedule/scheduleColumns.d.ts +26 -0
  128. package/schedule/scheduleColumns.d.ts.map +1 -0
  129. package/schedule/scheduleColumns.js +77 -0
  130. package/schedule/scheduleColumns.js.map +1 -0
  131. package/schedule/scheduleListFn.d.ts +50 -0
  132. package/schedule/scheduleListFn.d.ts.map +1 -0
  133. package/schedule/scheduleListFn.js +65 -0
  134. package/schedule/scheduleListFn.js.map +1 -0
  135. package/schedule/scheduleState.d.ts +43 -0
  136. package/schedule/scheduleState.d.ts.map +1 -0
  137. package/schedule/scheduleState.js +67 -0
  138. package/schedule/scheduleState.js.map +1 -0
  139. package/schedule/useCreateSchedule.d.ts +57 -0
  140. package/schedule/useCreateSchedule.d.ts.map +1 -0
  141. package/schedule/useCreateSchedule.js +61 -0
  142. package/schedule/useCreateSchedule.js.map +1 -0
  143. package/schedule/useResumeSchedule.d.ts +30 -0
  144. package/schedule/useResumeSchedule.d.ts.map +1 -0
  145. package/schedule/useResumeSchedule.js +45 -0
  146. package/schedule/useResumeSchedule.js.map +1 -0
  147. package/schedule/useSchedule.d.ts +39 -0
  148. package/schedule/useSchedule.d.ts.map +1 -0
  149. package/schedule/useSchedule.js +44 -0
  150. package/schedule/useSchedule.js.map +1 -0
  151. package/schedule/useScheduleCount.d.ts +38 -0
  152. package/schedule/useScheduleCount.d.ts.map +1 -0
  153. package/schedule/useScheduleCount.js +30 -0
  154. package/schedule/useScheduleCount.js.map +1 -0
  155. package/schedule/useScheduleList.d.ts +48 -0
  156. package/schedule/useScheduleList.d.ts.map +1 -0
  157. package/schedule/useScheduleList.js +43 -0
  158. package/schedule/useScheduleList.js.map +1 -0
  159. package/schedule/useScheduleRuns.d.ts +44 -0
  160. package/schedule/useScheduleRuns.d.ts.map +1 -0
  161. package/schedule/useScheduleRuns.js +48 -0
  162. package/schedule/useScheduleRuns.js.map +1 -0
  163. package/schedule/useSetScheduleEnabled.d.ts +31 -0
  164. package/schedule/useSetScheduleEnabled.d.ts.map +1 -0
  165. package/schedule/useSetScheduleEnabled.js +53 -0
  166. package/schedule/useSetScheduleEnabled.js.map +1 -0
  167. package/schedule/useTriggerSchedule.d.ts +41 -0
  168. package/schedule/useTriggerSchedule.d.ts.map +1 -0
  169. package/schedule/useTriggerSchedule.js +62 -0
  170. package/schedule/useTriggerSchedule.js.map +1 -0
  171. package/schedule/useUpdateScheduleSpec.d.ts +49 -0
  172. package/schedule/useUpdateScheduleSpec.d.ts.map +1 -0
  173. package/schedule/useUpdateScheduleSpec.js +70 -0
  174. package/schedule/useUpdateScheduleSpec.js.map +1 -0
  175. package/search/useResourceCount.d.ts +2 -1
  176. package/search/useResourceCount.d.ts.map +1 -1
  177. package/search/useResourceCount.js +6 -1
  178. package/search/useResourceCount.js.map +1 -1
  179. package/session/useNewSessionFlow.d.ts.map +1 -1
  180. package/session/useNewSessionFlow.js +1 -0
  181. package/session/useNewSessionFlow.js.map +1 -1
  182. package/session/useSessionConversation.d.ts +11 -0
  183. package/session/useSessionConversation.d.ts.map +1 -1
  184. package/session/useSessionConversation.js +1 -0
  185. package/session/useSessionConversation.js.map +1 -1
  186. package/session/useSessionPageFlow.d.ts.map +1 -1
  187. package/session/useSessionPageFlow.js +1 -0
  188. package/session/useSessionPageFlow.js.map +1 -1
  189. package/src/agent/AgentPicker.tsx +13 -1
  190. package/src/billing/CreditLedgerTable.tsx +3 -42
  191. package/src/channel/AgentChannelsPanel.tsx +43 -1
  192. package/src/channel/ChannelTemplatesDialog.tsx +433 -0
  193. package/src/channel/__tests__/AgentChannelsPanel.test.tsx +73 -0
  194. package/src/channel/__tests__/ChannelTemplatesDialog.test.tsx +292 -0
  195. package/src/channel/__tests__/providers.test.ts +9 -0
  196. package/src/channel/__tests__/templatePresentation.test.ts +96 -0
  197. package/src/channel/__tests__/useChannelTemplateList.test.tsx +216 -0
  198. package/src/channel/__tests__/useChannelTemplateReadiness.test.tsx +124 -0
  199. package/src/channel/index.ts +15 -0
  200. package/src/channel/providers.ts +26 -2
  201. package/src/channel/templatePresentation.ts +82 -0
  202. package/src/channel/useChannelTemplateList.ts +123 -0
  203. package/src/channel/useChannelTemplateReadiness.ts +69 -0
  204. package/src/composer/ComposerToolbar.tsx +9 -0
  205. package/src/composer/SessionComposer.tsx +24 -1
  206. package/src/composer/__tests__/SessionComposer-serviceTier.test.tsx +156 -0
  207. package/src/dashboard/__tests__/useDashboardFailedRuns.test.tsx +106 -0
  208. package/src/dashboard/useDashboardFailedRuns.ts +2 -0
  209. package/src/execution/useCreateAgentExecution.ts +18 -0
  210. package/src/index.ts +70 -0
  211. package/src/internal/Pagination.tsx +72 -0
  212. package/src/library/useExportResource.ts +4 -3
  213. package/src/models/ModelSelector.tsx +100 -3
  214. package/src/models/__tests__/ModelSelector-serviceTier.test.tsx +172 -0
  215. package/src/models/__tests__/useModelRegistry.test.tsx +15 -0
  216. package/src/models/index.ts +2 -0
  217. package/src/models/registry.ts +16 -0
  218. package/src/models/service-tier.ts +49 -0
  219. package/src/organization/OrgProvider.tsx +29 -4
  220. package/src/organization/__tests__/OrgProvider.orgSwitch.test.tsx +89 -0
  221. package/src/resource-detail/useDeleteResource.ts +11 -1
  222. package/src/resource-workbench/__tests__/ResourceWorkbench.test.tsx +112 -0
  223. package/src/resource-workbench/components/ResourceWorkbench.tsx +32 -15
  224. package/src/resource-workbench/hooks/useResourceCollection.ts +11 -1
  225. package/src/schedule/CadenceField.tsx +365 -0
  226. package/src/schedule/ScheduleDetailView.tsx +1651 -0
  227. package/src/schedule/ScheduleForm.tsx +615 -0
  228. package/src/schedule/ScheduleRowActions.tsx +124 -0
  229. package/src/schedule/ScheduleRunsTable.tsx +424 -0
  230. package/src/schedule/TimeZoneField.tsx +199 -0
  231. package/src/schedule/__tests__/ScheduleDetailView.test.tsx +683 -0
  232. package/src/schedule/__tests__/cadence.test.ts +278 -0
  233. package/src/schedule/__tests__/scheduleColumnsRowActions.test.tsx +176 -0
  234. package/src/schedule/__tests__/scheduleCreation.test.tsx +537 -0
  235. package/src/schedule/__tests__/scheduleHooks.test.tsx +399 -0
  236. package/src/schedule/__tests__/scheduleListFn.test.ts +128 -0
  237. package/src/schedule/__tests__/scheduleState.test.ts +133 -0
  238. package/src/schedule/cadence.ts +298 -0
  239. package/src/schedule/index.ts +69 -0
  240. package/src/schedule/scheduleColumns.tsx +118 -0
  241. package/src/schedule/scheduleListFn.ts +108 -0
  242. package/src/schedule/scheduleState.ts +96 -0
  243. package/src/schedule/useCreateSchedule.ts +88 -0
  244. package/src/schedule/useResumeSchedule.ts +67 -0
  245. package/src/schedule/useSchedule.ts +67 -0
  246. package/src/schedule/useScheduleCount.ts +57 -0
  247. package/src/schedule/useScheduleList.ts +83 -0
  248. package/src/schedule/useScheduleRuns.ts +93 -0
  249. package/src/schedule/useSetScheduleEnabled.ts +84 -0
  250. package/src/schedule/useTriggerSchedule.ts +90 -0
  251. package/src/schedule/useUpdateScheduleSpec.ts +108 -0
  252. package/src/search/__tests__/useResourceCount.test.tsx +89 -0
  253. package/src/search/useResourceCount.ts +8 -2
  254. package/src/session/useNewSessionFlow.ts +1 -0
  255. package/src/session/useSessionConversation.ts +12 -0
  256. package/src/session/useSessionPageFlow.ts +1 -0
  257. package/src/workflow/WorkflowDashboard.tsx +1 -0
  258. package/src/workflow/__tests__/inspector-forms.test.tsx +119 -2
  259. package/src/workflow/inspector/forms/AgentCallForm.tsx +260 -37
  260. package/src/workflow/inspector/tabs/RuntimeTab.tsx +72 -33
  261. package/src/workflow/starter-workflow-yaml.ts +2 -1
  262. package/src/workflow/useWorkflowExecutionList.ts +13 -2
  263. package/styles.css +1 -1
  264. package/workflow/CanvasTransitionEdge.d.ts +1 -1
  265. package/workflow/WorkflowDashboard.d.ts.map +1 -1
  266. package/workflow/WorkflowDashboard.js +1 -0
  267. package/workflow/WorkflowDashboard.js.map +1 -1
  268. package/workflow/inspector/forms/AgentCallForm.d.ts +18 -2
  269. package/workflow/inspector/forms/AgentCallForm.d.ts.map +1 -1
  270. package/workflow/inspector/forms/AgentCallForm.js +87 -9
  271. package/workflow/inspector/forms/AgentCallForm.js.map +1 -1
  272. package/workflow/inspector/tabs/RuntimeTab.d.ts +2 -1
  273. package/workflow/inspector/tabs/RuntimeTab.d.ts.map +1 -1
  274. package/workflow/inspector/tabs/RuntimeTab.js +31 -5
  275. package/workflow/inspector/tabs/RuntimeTab.js.map +1 -1
  276. package/workflow/starter-workflow-yaml.d.ts +1 -1
  277. package/workflow/starter-workflow-yaml.d.ts.map +1 -1
  278. package/workflow/starter-workflow-yaml.js +2 -1
  279. package/workflow/starter-workflow-yaml.js.map +1 -1
  280. package/workflow/useWorkflowExecutionList.d.ts +10 -0
  281. package/workflow/useWorkflowExecutionList.d.ts.map +1 -1
  282. package/workflow/useWorkflowExecutionList.js +3 -2
  283. package/workflow/useWorkflowExecutionList.js.map +1 -1
@@ -0,0 +1,108 @@
1
+ "use client";
2
+
3
+ import { useCallback, useMemo, useState } from "react";
4
+ import { clone } from "@bufbuild/protobuf";
5
+ import {
6
+ ScheduleSchema,
7
+ type Schedule,
8
+ } from "@stigmer/protos/ai/stigmer/agentic/schedule/v1/api_pb";
9
+ import type { ScheduleSpec } from "@stigmer/protos/ai/stigmer/agentic/schedule/v1/spec_pb";
10
+ import { getUserMessage, manifestDocumentForResource } from "@stigmer/sdk";
11
+ import { useStigmer } from "../hooks.js";
12
+ import { toast } from "../feedback/toast.js";
13
+
14
+ /** Return value of {@link useUpdateScheduleSpec}. */
15
+ export interface UseUpdateScheduleSpecReturn {
16
+ /**
17
+ * Apply one spec mutation to the schedule. Takes the full fetched
18
+ * schedule (not just an id) because the write re-applies the whole
19
+ * resource. Resolves with the post-apply schedule; the caller
20
+ * refetches or replaces its local copy from it.
21
+ */
22
+ readonly updateSpec: (
23
+ schedule: Schedule,
24
+ mutate: (spec: ScheduleSpec) => void,
25
+ ) => Promise<Schedule>;
26
+ /** `true` while the apply is in flight. */
27
+ readonly isUpdating: boolean;
28
+ /** Error from the last failed apply, or `null` when healthy. */
29
+ readonly error: Error | null;
30
+ }
31
+
32
+ /**
33
+ * Behavior hook behind the schedule detail view's inline editing: apply
34
+ * a single spec mutation losslessly.
35
+ *
36
+ * The write path is deliberate, and it is NOT the agent detail view's
37
+ * input-based one: clone the fetched proto, let the caller mutate the
38
+ * spec in place, and re-apply the WHOLE resource through the manifest
39
+ * engine (`stigmer.manifest.apply`) — never down-convert to the curated
40
+ * `ScheduleInput` shape. The server replaces spec and mutable metadata
41
+ * wholesale on update, and `ScheduleInput` cannot express every
42
+ * metadata field (tags, description), so the input route would silently
43
+ * wipe them. The full-proto route is lossless by construction; the
44
+ * server ignores client-provided status, and re-applying never clears a
45
+ * platform pause (Resume is the one clearing path — see
46
+ * `useResumeSchedule`). Same mechanics as {@link useSetScheduleEnabled},
47
+ * generalized from one hardcoded field to any spec edit.
48
+ *
49
+ * Errors are NOT toasted here: inline editors surface the server's
50
+ * message next to the field that was edited (DD-006), and a toast on
51
+ * top would say the same thing twice.
52
+ *
53
+ * @example
54
+ * ```tsx
55
+ * const { updateSpec, isUpdating } = useUpdateScheduleSpec();
56
+ *
57
+ * await updateSpec(schedule, (spec) => {
58
+ * spec.cron = "0 9 * * 1-5";
59
+ * spec.timeZone = "Asia/Kolkata";
60
+ * });
61
+ * ```
62
+ */
63
+ export function useUpdateScheduleSpec(): UseUpdateScheduleSpecReturn {
64
+ const stigmer = useStigmer();
65
+ const [isUpdating, setIsUpdating] = useState(false);
66
+ const [error, setError] = useState<Error | null>(null);
67
+
68
+ const updateSpec = useCallback(
69
+ async (
70
+ schedule: Schedule,
71
+ mutate: (spec: ScheduleSpec) => void,
72
+ ): Promise<Schedule> => {
73
+ if (!schedule.spec) {
74
+ throw new Error(
75
+ "Cannot edit this schedule: it has no spec. Pass a schedule " +
76
+ "as returned by the server (get/getByReference).",
77
+ );
78
+ }
79
+
80
+ setIsUpdating(true);
81
+ setError(null);
82
+ try {
83
+ const updated = clone(ScheduleSchema, schedule);
84
+ mutate(updated.spec!);
85
+
86
+ const applied = await stigmer.manifest.apply(
87
+ manifestDocumentForResource(updated),
88
+ );
89
+ toast.success("Schedule updated");
90
+ return applied.message as Schedule;
91
+ } catch (err) {
92
+ // Rethrow with the user-facing message: the inline editors render
93
+ // `message` verbatim next to the edited field.
94
+ const wrapped = new Error(getUserMessage(err));
95
+ setError(wrapped);
96
+ throw wrapped;
97
+ } finally {
98
+ setIsUpdating(false);
99
+ }
100
+ },
101
+ [stigmer],
102
+ );
103
+
104
+ return useMemo(
105
+ () => ({ updateSpec, isUpdating, error }),
106
+ [updateSpec, isUpdating, error],
107
+ );
108
+ }
@@ -0,0 +1,89 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { renderHook, waitFor } from "@testing-library/react";
3
+ import type { ListParams, ListResult } from "@stigmer/sdk";
4
+ import { useResourceCount } from "../useResourceCount";
5
+
6
+ function mockListFn(totalCount = 0) {
7
+ return vi.fn<(params: ListParams) => Promise<ListResult>>().mockResolvedValue({
8
+ entries: [],
9
+ totalCount,
10
+ totalPages: totalCount > 0 ? 1 : 0,
11
+ } as never);
12
+ }
13
+
14
+ describe("useResourceCount", () => {
15
+ it("counts in org scope by default: org sent, crossOrgPublic false", async () => {
16
+ const listFn = mockListFn(7);
17
+
18
+ const { result } = renderHook(() => useResourceCount(listFn, "acme"));
19
+
20
+ await waitFor(() => expect(result.current.isLoading).toBe(false));
21
+
22
+ expect(result.current.count).toBe(7);
23
+ const params = listFn.mock.calls[0][0];
24
+ expect(params.org).toBe("acme");
25
+ expect(params.crossOrgPublic).toBe(false);
26
+ expect(params.page).toEqual({ num: 1, size: 1 });
27
+ });
28
+
29
+ it('always sends org in "all" scope (empty org means a cross-org FGA dump to the backend)', async () => {
30
+ const listFn = mockListFn(3);
31
+
32
+ const { result } = renderHook(() =>
33
+ useResourceCount(listFn, "acme", { scope: "all" }),
34
+ );
35
+
36
+ await waitFor(() => expect(result.current.isLoading).toBe(false));
37
+
38
+ const params = listFn.mock.calls[0][0];
39
+ // Regression guard: the pre-fix hook blanked org for "all", which the
40
+ // search backend defines as "every org the caller can access" — the
41
+ // Library cards then counted platform-wide instead of org + public.
42
+ expect(params.org).toBe("acme");
43
+ expect(params.crossOrgPublic).toBe(true);
44
+ });
45
+
46
+ it("does not fetch when org is null", async () => {
47
+ const listFn = mockListFn();
48
+
49
+ const { result } = renderHook(() => useResourceCount(listFn, null));
50
+
51
+ await waitFor(() => expect(result.current.isLoading).toBe(false));
52
+
53
+ expect(listFn).not.toHaveBeenCalled();
54
+ expect(result.current.count).toBeUndefined();
55
+ });
56
+
57
+ it("recounts when refetchToken changes", async () => {
58
+ const listFn = mockListFn(1);
59
+
60
+ const { result, rerender } = renderHook(
61
+ ({ token }: { token: number }) =>
62
+ useResourceCount(listFn, "acme", { refetchToken: token }),
63
+ { initialProps: { token: 0 } },
64
+ );
65
+
66
+ await waitFor(() => expect(result.current.isLoading).toBe(false));
67
+ expect(listFn).toHaveBeenCalledTimes(1);
68
+
69
+ rerender({ token: 1 });
70
+
71
+ await waitFor(() => expect(listFn).toHaveBeenCalledTimes(2));
72
+ });
73
+
74
+ it("refetches with the new org when the org changes", async () => {
75
+ const listFn = mockListFn(2);
76
+
77
+ const { result, rerender } = renderHook(
78
+ ({ org }: { org: string }) => useResourceCount(listFn, org),
79
+ { initialProps: { org: "acme" } },
80
+ );
81
+
82
+ await waitFor(() => expect(result.current.isLoading).toBe(false));
83
+
84
+ rerender({ org: "globex" });
85
+
86
+ await waitFor(() => expect(listFn).toHaveBeenCalledTimes(2));
87
+ expect(listFn.mock.calls[1][0].org).toBe("globex");
88
+ });
89
+ });
@@ -11,7 +11,8 @@ export interface UseResourceCountOptions {
11
11
  * Controls resource visibility scope.
12
12
  *
13
13
  * - `"org"` — all resources owned by the given organization, regardless of visibility.
14
- * - `"all"` — all resources the caller is authorized to access, across all organizations.
14
+ * - `"all"` — all resources owned by the given organization plus public resources
15
+ * from other organizations.
15
16
  *
16
17
  * @default "org"
17
18
  */
@@ -64,10 +65,15 @@ export function useResourceCount(
64
65
  const { data: count, isLoading, isRefetching, error, refetch } = useFetch<number | undefined>(
65
66
  org
66
67
  ? async () => {
68
+ // Identical params to useResourceList so a card's count always matches
69
+ // the list page it links to. `org` is ALWAYS sent: an empty org means
70
+ // "every org the caller can access" to the search backend (a global FGA
71
+ // dump), and `crossOrgPublic` with an empty org matches zero rows.
67
72
  const params: ListParams = {
68
- org: scope === "all" ? "" : org,
73
+ org,
69
74
  query: query || undefined,
70
75
  excludePublic: false,
76
+ crossOrgPublic: scope === "all",
71
77
  page: { num: 1, size: 1 },
72
78
  };
73
79
  const result = await listFn(params);
@@ -389,6 +389,7 @@ export function useNewSessionFlow(
389
389
  runtimeEnv,
390
390
  attachments: context?.attachments,
391
391
  interactionMode: context?.interactionMode,
392
+ serviceTier: context?.serviceTier,
392
393
  workspaceFileRefs: context?.workspaceFileRefs,
393
394
  };
394
395
 
@@ -17,6 +17,7 @@ import type {
17
17
  WorkspaceEntryInput,
18
18
  } from "@stigmer/sdk";
19
19
  import { isTerminalPhase } from "../execution/execution-phases.js";
20
+ import type { ServiceTierOption } from "../models/service-tier.js";
20
21
  import { useStigmer } from "../hooks.js";
21
22
  import { toError } from "../internal/toError.js";
22
23
  import { useConversationStoreRef } from "../internal/store/index.js";
@@ -96,6 +97,16 @@ export interface SendFollowUpOptions {
96
97
  * - `"plan"`: read-only analysis, no file mutations.
97
98
  */
98
99
  readonly interactionMode?: "agent" | "plan";
100
+ /**
101
+ * Service tier for this execution's model calls (stigmer/stigmer#357).
102
+ *
103
+ * Only set when the user actively selected `"fast"`; `undefined` preserves
104
+ * the unspecified-vs-explicit distinction (unset resolves to standard on
105
+ * the platform side — never the provider account default).
106
+ *
107
+ * @see {@link CreateAgentExecutionInput.serviceTier}
108
+ */
109
+ readonly serviceTier?: ServiceTierOption;
99
110
  /**
100
111
  * Marks this execution as a Build-from-plan turn.
101
112
  *
@@ -642,6 +653,7 @@ export function useSessionConversation(
642
653
  runtimeEnv: options?.runtimeEnv,
643
654
  attachments: options?.attachments,
644
655
  interactionMode: options?.interactionMode,
656
+ serviceTier: options?.serviceTier,
645
657
  buildFromPlan: options?.buildFromPlan,
646
658
  autoApproveAll: options?.autoApproveAll,
647
659
  workspaceFileRefs: options?.workspaceFileRefs,
@@ -438,6 +438,7 @@ export function useSessionPageFlow(
438
438
  runtimeEnv,
439
439
  attachments: context?.attachments,
440
440
  interactionMode: context?.interactionMode,
441
+ serviceTier: context?.serviceTier,
441
442
  buildFromPlan: context?.buildFromPlan,
442
443
  // Sourced from the session-scoped preference set at the approval gate,
443
444
  // not from the composer (the pre-arm toggle was removed).
@@ -82,6 +82,7 @@ export const WorkflowDashboard = memo(function WorkflowDashboard({
82
82
  create(ListWorkflowExecutionsRequestSchema, {
83
83
  pageSize: 5,
84
84
  phase: ExecutionPhase.EXECUTION_FAILED,
85
+ org,
85
86
  }),
86
87
  );
87
88
  return [...resp.entries] as readonly WorkflowExecution[];
@@ -34,15 +34,132 @@ describe("AgentCallForm", () => {
34
34
  expect(textarea.value).toBe("Hello world");
35
35
  });
36
36
 
37
- it("renders model input from nested config", () => {
37
+ it("renders model input from the run_config block", () => {
38
38
  const node = makeNode(WorkflowTaskKind.agent_call, {
39
39
  agent: "x",
40
40
  message: "y",
41
- config: { model: "claude-sonnet-4", timeout: 120 },
41
+ run_config: { model_name: "claude-sonnet-4", max_cost_usd: 0.5 },
42
42
  });
43
43
  render(<AgentCallForm node={node} onFieldChange={vi.fn()} />);
44
44
  const input = screen.getByTestId("agent-call-model-input") as HTMLInputElement;
45
45
  expect(input.value).toBe("claude-sonnet-4");
46
+ const budget = screen.getByTestId("agent-call-budget-input") as HTMLInputElement;
47
+ expect(budget.value).toBe("0.5");
48
+ });
49
+
50
+ it("emits an undefined run_config when the last field is cleared", () => {
51
+ const onFieldChange = vi.fn();
52
+ const node = makeNode(WorkflowTaskKind.agent_call, {
53
+ agent: "x",
54
+ message: "y",
55
+ run_config: { max_cost_usd: 0.5 },
56
+ });
57
+ render(<AgentCallForm node={node} onFieldChange={onFieldChange} />);
58
+
59
+ const budget = screen.getByTestId("agent-call-budget-input") as HTMLInputElement;
60
+ fireEvent.change(budget, { target: { value: "" } });
61
+
62
+ // Empty means omit: a blank field must not persist a zero override.
63
+ expect(onFieldChange).toHaveBeenCalledWith("run_config", undefined);
64
+ });
65
+
66
+ it("writes budget changes into run_config.max_cost_usd", () => {
67
+ const onFieldChange = vi.fn();
68
+ const node = makeNode(WorkflowTaskKind.agent_call, {
69
+ agent: "x",
70
+ message: "y",
71
+ run_config: { model_name: "claude-sonnet-4" },
72
+ });
73
+ render(<AgentCallForm node={node} onFieldChange={onFieldChange} />);
74
+
75
+ const budget = screen.getByTestId("agent-call-budget-input") as HTMLInputElement;
76
+ fireEvent.change(budget, { target: { value: "1.25" } });
77
+
78
+ expect(onFieldChange).toHaveBeenCalledWith("run_config", {
79
+ model_name: "claude-sonnet-4",
80
+ max_cost_usd: 1.25,
81
+ });
82
+ });
83
+
84
+ it("renders workspace entries and edits the git url in place", () => {
85
+ const onFieldChange = vi.fn();
86
+ const node = makeNode(WorkflowTaskKind.agent_call, {
87
+ agent: "x",
88
+ message: "y",
89
+ workspace_entries: [
90
+ { name: "app", source: { git_repo: { url: "https://github.com/acme/app", branch: "main" } } },
91
+ ],
92
+ });
93
+ render(<AgentCallForm node={node} onFieldChange={onFieldChange} />);
94
+
95
+ const url = screen.getByTestId("agent-call-workspace-url-0") as HTMLInputElement;
96
+ expect(url.value).toBe("https://github.com/acme/app");
97
+
98
+ fireEvent.change(url, { target: { value: "https://github.com/acme/other" } });
99
+ expect(onFieldChange).toHaveBeenCalledWith("workspace_entries", [
100
+ { name: "app", source: { git_repo: { url: "https://github.com/acme/other", branch: "main" } } },
101
+ ]);
102
+ });
103
+
104
+ it("adds a workspace entry row", () => {
105
+ const onFieldChange = vi.fn();
106
+ const node = makeNode(WorkflowTaskKind.agent_call, { agent: "x", message: "y" });
107
+ render(<AgentCallForm node={node} onFieldChange={onFieldChange} />);
108
+
109
+ fireEvent.click(screen.getByTestId("agent-call-workspace-add"));
110
+ expect(onFieldChange).toHaveBeenCalledWith("workspace_entries", [
111
+ { source: { git_repo: { url: "" } } },
112
+ ]);
113
+ });
114
+
115
+ it("emits undefined when the last workspace entry is removed", () => {
116
+ const onFieldChange = vi.fn();
117
+ const node = makeNode(WorkflowTaskKind.agent_call, {
118
+ agent: "x",
119
+ message: "y",
120
+ workspace_entries: [
121
+ { source: { git_repo: { url: "https://github.com/acme/app" } } },
122
+ ],
123
+ });
124
+ render(<AgentCallForm node={node} onFieldChange={onFieldChange} />);
125
+
126
+ fireEvent.click(screen.getByLabelText("Remove workspace entry 1"));
127
+ expect(onFieldChange).toHaveBeenCalledWith("workspace_entries", undefined);
128
+ });
129
+
130
+ it("edits environment references as org/slug rows with empty-org omitted", () => {
131
+ const onFieldChange = vi.fn();
132
+ const node = makeNode(WorkflowTaskKind.agent_call, {
133
+ agent: "x",
134
+ message: "y",
135
+ environment_refs: [{ slug: "shared-secrets" }],
136
+ });
137
+ render(<AgentCallForm node={node} onFieldChange={onFieldChange} />);
138
+
139
+ const slug = screen.getByTestId("agent-call-envref-slug-0") as HTMLInputElement;
140
+ expect(slug.value).toBe("shared-secrets");
141
+
142
+ fireEvent.change(slug, { target: { value: "other-secrets" } });
143
+ // Empty org stays omitted — an empty string must not become a stored
144
+ // org override (relative refs resolve to the workflow's own org).
145
+ expect(onFieldChange).toHaveBeenCalledWith("environment_refs", [
146
+ { slug: "other-secrets" },
147
+ ]);
148
+ });
149
+
150
+ it("adds and removes environment reference rows", () => {
151
+ const onFieldChange = vi.fn();
152
+ const node = makeNode(WorkflowTaskKind.agent_call, {
153
+ agent: "x",
154
+ message: "y",
155
+ environment_refs: [{ slug: "a" }, { org: "acme", slug: "b" }],
156
+ });
157
+ render(<AgentCallForm node={node} onFieldChange={onFieldChange} />);
158
+
159
+ fireEvent.click(screen.getByLabelText("Remove environment reference 1"));
160
+ expect(onFieldChange).toHaveBeenCalledWith("environment_refs", [
161
+ { org: "acme", slug: "b" },
162
+ ]);
46
163
  });
47
164
 
48
165
  it("shows structured output section when output is present", () => {