@stigmer/react 3.12.3 → 3.12.5

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 (493) hide show
  1. package/agent/steps/IdentityStep.d.ts.map +1 -1
  2. package/agent/steps/IdentityStep.js +2 -1
  3. package/agent/steps/IdentityStep.js.map +1 -1
  4. package/agent-instance/AgentInstanceDetailPanel.d.ts.map +1 -1
  5. package/agent-instance/AgentInstanceDetailPanel.js +2 -1
  6. package/agent-instance/AgentInstanceDetailPanel.js.map +1 -1
  7. package/api-key/CreateApiKeyForm.d.ts.map +1 -1
  8. package/api-key/CreateApiKeyForm.js +2 -1
  9. package/api-key/CreateApiKeyForm.js.map +1 -1
  10. package/attachment/AttachmentChipList.js +16 -6
  11. package/attachment/AttachmentChipList.js.map +1 -1
  12. package/attachment/index.d.ts +1 -1
  13. package/attachment/index.d.ts.map +1 -1
  14. package/attachment/index.js.map +1 -1
  15. package/attachment/useAttachments.d.ts +51 -8
  16. package/attachment/useAttachments.d.ts.map +1 -1
  17. package/attachment/useAttachments.js +74 -3
  18. package/attachment/useAttachments.js.map +1 -1
  19. package/channel/ChannelConversationsDialog.d.ts.map +1 -1
  20. package/channel/ChannelConversationsDialog.js +2 -1
  21. package/channel/ChannelConversationsDialog.js.map +1 -1
  22. package/channel/ChannelTemplatesDialog.d.ts.map +1 -1
  23. package/channel/ChannelTemplatesDialog.js +2 -1
  24. package/channel/ChannelTemplatesDialog.js.map +1 -1
  25. package/channel/ConnectSlackDialog.d.ts.map +1 -1
  26. package/channel/ConnectSlackDialog.js +2 -1
  27. package/channel/ConnectSlackDialog.js.map +1 -1
  28. package/channel/connect/ServingAppSection.d.ts.map +1 -1
  29. package/channel/connect/ServingAppSection.js +2 -1
  30. package/channel/connect/ServingAppSection.js.map +1 -1
  31. package/channel-app/CreateChannelAppForm.d.ts.map +1 -1
  32. package/channel-app/CreateChannelAppForm.js +2 -1
  33. package/channel-app/CreateChannelAppForm.js.map +1 -1
  34. package/composer/SessionComposer.d.ts.map +1 -1
  35. package/composer/SessionComposer.js +17 -10
  36. package/composer/SessionComposer.js.map +1 -1
  37. package/conversation/ConversationComposer.d.ts +19 -3
  38. package/conversation/ConversationComposer.d.ts.map +1 -1
  39. package/conversation/ConversationComposer.js +4 -4
  40. package/conversation/ConversationComposer.js.map +1 -1
  41. package/conversation/ConversationListPane.d.ts.map +1 -1
  42. package/conversation/ConversationListPane.js +2 -1
  43. package/conversation/ConversationListPane.js.map +1 -1
  44. package/conversation/ConversationTemplatePickerDialog.d.ts +63 -0
  45. package/conversation/ConversationTemplatePickerDialog.d.ts.map +1 -0
  46. package/conversation/ConversationTemplatePickerDialog.js +151 -0
  47. package/conversation/ConversationTemplatePickerDialog.js.map +1 -0
  48. package/conversation/ConversationTimelineView.d.ts.map +1 -1
  49. package/conversation/ConversationTimelineView.js +2 -1
  50. package/conversation/ConversationTimelineView.js.map +1 -1
  51. package/conversation/ConversationsWorkbench.d.ts.map +1 -1
  52. package/conversation/ConversationsWorkbench.js +16 -5
  53. package/conversation/ConversationsWorkbench.js.map +1 -1
  54. package/conversation/index.d.ts +3 -1
  55. package/conversation/index.d.ts.map +1 -1
  56. package/conversation/index.js +1 -0
  57. package/conversation/index.js.map +1 -1
  58. package/conversation/useConversationParticipation.d.ts +41 -4
  59. package/conversation/useConversationParticipation.d.ts.map +1 -1
  60. package/conversation/useConversationParticipation.js +13 -2
  61. package/conversation/useConversationParticipation.js.map +1 -1
  62. package/cursor-accounts/CursorAccountsConsole.d.ts.map +1 -1
  63. package/cursor-accounts/CursorAccountsConsole.js +2 -1
  64. package/cursor-accounts/CursorAccountsConsole.js.map +1 -1
  65. package/cursor-accounts/MemberKeysPanel.d.ts.map +1 -1
  66. package/cursor-accounts/MemberKeysPanel.js +2 -1
  67. package/cursor-accounts/MemberKeysPanel.js.map +1 -1
  68. package/dashboard/DashboardFailedRuns.d.ts.map +1 -1
  69. package/dashboard/DashboardFailedRuns.js +2 -1
  70. package/dashboard/DashboardFailedRuns.js.map +1 -1
  71. package/dependency-graph/DependencyGraph.d.ts.map +1 -1
  72. package/dependency-graph/DependencyGraph.js +2 -1
  73. package/dependency-graph/DependencyGraph.js.map +1 -1
  74. package/dependency-graph/DependencyTreeNode.d.ts.map +1 -1
  75. package/dependency-graph/DependencyTreeNode.js +2 -1
  76. package/dependency-graph/DependencyTreeNode.js.map +1 -1
  77. package/environment/EnvironmentPicker.d.ts.map +1 -1
  78. package/environment/EnvironmentPicker.js +2 -1
  79. package/environment/EnvironmentPicker.js.map +1 -1
  80. package/error/SecretFlowErrorGuide.d.ts.map +1 -1
  81. package/error/SecretFlowErrorGuide.js +2 -1
  82. package/error/SecretFlowErrorGuide.js.map +1 -1
  83. package/execution/ArtifactContentBody.d.ts.map +1 -1
  84. package/execution/ArtifactContentBody.js +2 -1
  85. package/execution/ArtifactContentBody.js.map +1 -1
  86. package/execution/ArtifactsWidget.d.ts.map +1 -1
  87. package/execution/ArtifactsWidget.js +2 -1
  88. package/execution/ArtifactsWidget.js.map +1 -1
  89. package/execution/FileChangeProgressBar.d.ts.map +1 -1
  90. package/execution/FileChangeProgressBar.js +2 -1
  91. package/execution/FileChangeProgressBar.js.map +1 -1
  92. package/execution/TodoList.d.ts.map +1 -1
  93. package/execution/TodoList.js +2 -1
  94. package/execution/TodoList.js.map +1 -1
  95. package/execution/WriteBacksWidget.d.ts.map +1 -1
  96. package/execution/WriteBacksWidget.js +2 -1
  97. package/execution/WriteBacksWidget.js.map +1 -1
  98. package/execution/tool-categories.d.ts +5 -22
  99. package/execution/tool-categories.d.ts.map +1 -1
  100. package/execution/tool-categories.js +8 -38
  101. package/execution/tool-categories.js.map +1 -1
  102. package/iam-policy/PeopleWithAccess.d.ts.map +1 -1
  103. package/iam-policy/PeopleWithAccess.js +2 -1
  104. package/iam-policy/PeopleWithAccess.js.map +1 -1
  105. package/iam-policy/PrincipalPicker.d.ts.map +1 -1
  106. package/iam-policy/PrincipalPicker.js +2 -1
  107. package/iam-policy/PrincipalPicker.js.map +1 -1
  108. package/iam-policy/RoleSelector.d.ts.map +1 -1
  109. package/iam-policy/RoleSelector.js +2 -1
  110. package/iam-policy/RoleSelector.js.map +1 -1
  111. package/identity-account/AccountPreferencesPanel.d.ts +35 -0
  112. package/identity-account/AccountPreferencesPanel.d.ts.map +1 -0
  113. package/identity-account/AccountPreferencesPanel.js +187 -0
  114. package/identity-account/AccountPreferencesPanel.js.map +1 -0
  115. package/identity-account/index.d.ts +8 -0
  116. package/identity-account/index.d.ts.map +1 -1
  117. package/identity-account/index.js +4 -0
  118. package/identity-account/index.js.map +1 -1
  119. package/identity-account/useAccountExecutionDefaults.d.ts +36 -0
  120. package/identity-account/useAccountExecutionDefaults.d.ts.map +1 -0
  121. package/identity-account/useAccountExecutionDefaults.js +46 -0
  122. package/identity-account/useAccountExecutionDefaults.js.map +1 -0
  123. package/identity-account/useMyIdentityAccount.d.ts +55 -0
  124. package/identity-account/useMyIdentityAccount.d.ts.map +1 -0
  125. package/identity-account/useMyIdentityAccount.js +37 -0
  126. package/identity-account/useMyIdentityAccount.js.map +1 -0
  127. package/identity-account/useUpdateIdentityAccount.d.ts +42 -0
  128. package/identity-account/useUpdateIdentityAccount.d.ts.map +1 -0
  129. package/identity-account/useUpdateIdentityAccount.js +53 -0
  130. package/identity-account/useUpdateIdentityAccount.js.map +1 -0
  131. package/identity-provider/CreateIdentityProviderForm.d.ts.map +1 -1
  132. package/identity-provider/CreateIdentityProviderForm.js +2 -1
  133. package/identity-provider/CreateIdentityProviderForm.js.map +1 -1
  134. package/identity-provider/IdentityProviderDetailPanel.d.ts.map +1 -1
  135. package/identity-provider/IdentityProviderDetailPanel.js +2 -1
  136. package/identity-provider/IdentityProviderDetailPanel.js.map +1 -1
  137. package/identity-provider/IdentityProviderWizard.d.ts.map +1 -1
  138. package/identity-provider/IdentityProviderWizard.js +2 -1
  139. package/identity-provider/IdentityProviderWizard.js.map +1 -1
  140. package/index.d.ts +14 -10
  141. package/index.d.ts.map +1 -1
  142. package/index.js +11 -5
  143. package/index.js.map +1 -1
  144. package/internal/StandingContextField.d.ts +37 -0
  145. package/internal/StandingContextField.d.ts.map +1 -0
  146. package/internal/StandingContextField.js +24 -0
  147. package/internal/StandingContextField.js.map +1 -0
  148. package/internal/element-resets.d.ts +17 -0
  149. package/internal/element-resets.d.ts.map +1 -0
  150. package/internal/element-resets.js +32 -0
  151. package/internal/element-resets.js.map +1 -0
  152. package/internal/file-tree/FileTreeNode.d.ts.map +1 -1
  153. package/internal/file-tree/FileTreeNode.js +2 -1
  154. package/internal/file-tree/FileTreeNode.js.map +1 -1
  155. package/internal/markdown-components.d.ts +7 -0
  156. package/internal/markdown-components.d.ts.map +1 -1
  157. package/internal/markdown-components.js +15 -5
  158. package/internal/markdown-components.js.map +1 -1
  159. package/invitation/InvitationManager.d.ts.map +1 -1
  160. package/invitation/InvitationManager.js +2 -1
  161. package/invitation/InvitationManager.js.map +1 -1
  162. package/library/InstanceVisibilitySelector.d.ts.map +1 -1
  163. package/library/InstanceVisibilitySelector.js +9 -2
  164. package/library/InstanceVisibilitySelector.js.map +1 -1
  165. package/library/ResourceVisibilityControl.d.ts +13 -3
  166. package/library/ResourceVisibilityControl.d.ts.map +1 -1
  167. package/library/ResourceVisibilityControl.js +33 -5
  168. package/library/ResourceVisibilityControl.js.map +1 -1
  169. package/library/VisibilityOptionRow.d.ts +6 -0
  170. package/library/VisibilityOptionRow.d.ts.map +1 -1
  171. package/library/VisibilityOptionRow.js +11 -1
  172. package/library/VisibilityOptionRow.js.map +1 -1
  173. package/library/VisibilitySelector.d.ts.map +1 -1
  174. package/library/VisibilitySelector.js +14 -2
  175. package/library/VisibilitySelector.js.map +1 -1
  176. package/library/index.d.ts +4 -2
  177. package/library/index.d.ts.map +1 -1
  178. package/library/index.js +2 -1
  179. package/library/index.js.map +1 -1
  180. package/library/useCanSetPublicVisibility.d.ts +30 -0
  181. package/library/useCanSetPublicVisibility.d.ts.map +1 -0
  182. package/library/useCanSetPublicVisibility.js +41 -0
  183. package/library/useCanSetPublicVisibility.js.map +1 -0
  184. package/library/visibilityLevels.d.ts +32 -1
  185. package/library/visibilityLevels.d.ts.map +1 -1
  186. package/library/visibilityLevels.js +34 -14
  187. package/library/visibilityLevels.js.map +1 -1
  188. package/manifest/ApplyManifestDialog.d.ts.map +1 -1
  189. package/manifest/ApplyManifestDialog.js +2 -1
  190. package/manifest/ApplyManifestDialog.js.map +1 -1
  191. package/mcp-server/steps/IdentityTransportStep.d.ts.map +1 -1
  192. package/mcp-server/steps/IdentityTransportStep.js +2 -1
  193. package/mcp-server/steps/IdentityTransportStep.js.map +1 -1
  194. package/mcp-server/useMcpServerConnect.d.ts +8 -5
  195. package/mcp-server/useMcpServerConnect.d.ts.map +1 -1
  196. package/mcp-server/useMcpServerConnect.js +2 -1
  197. package/mcp-server/useMcpServerConnect.js.map +1 -1
  198. package/mcp-server/useMcpServerOAuthConnect.d.ts.map +1 -1
  199. package/mcp-server/useMcpServerOAuthConnect.js +7 -2
  200. package/mcp-server/useMcpServerOAuthConnect.js.map +1 -1
  201. package/oauth-app/CreateOAuthAppForm.d.ts +2 -2
  202. package/oauth-app/CreateOAuthAppForm.d.ts.map +1 -1
  203. package/oauth-app/CreateOAuthAppForm.js +13 -4
  204. package/oauth-app/CreateOAuthAppForm.js.map +1 -1
  205. package/oauth-app/OAuthAppDetailPanel.d.ts.map +1 -1
  206. package/oauth-app/OAuthAppDetailPanel.js +32 -5
  207. package/oauth-app/OAuthAppDetailPanel.js.map +1 -1
  208. package/organization/OrgPreferencesPanel.d.ts +32 -0
  209. package/organization/OrgPreferencesPanel.d.ts.map +1 -0
  210. package/organization/OrgPreferencesPanel.js +90 -0
  211. package/organization/OrgPreferencesPanel.js.map +1 -0
  212. package/organization/OrgProfilePanel.d.ts.map +1 -1
  213. package/organization/OrgProfilePanel.js +5 -4
  214. package/organization/OrgProfilePanel.js.map +1 -1
  215. package/organization/index.d.ts +2 -0
  216. package/organization/index.d.ts.map +1 -1
  217. package/organization/index.js +1 -0
  218. package/organization/index.js.map +1 -1
  219. package/package.json +4 -4
  220. package/platform-client/CreatePlatformClientForm.d.ts.map +1 -1
  221. package/platform-client/CreatePlatformClientForm.js +2 -1
  222. package/platform-client/CreatePlatformClientForm.js.map +1 -1
  223. package/platform-client/PlatformClientDetailPanel.d.ts.map +1 -1
  224. package/platform-client/PlatformClientDetailPanel.js +2 -1
  225. package/platform-client/PlatformClientDetailPanel.js.map +1 -1
  226. package/pricing-governance/BaselineEditor.d.ts.map +1 -1
  227. package/pricing-governance/BaselineEditor.js +2 -1
  228. package/pricing-governance/BaselineEditor.js.map +1 -1
  229. package/pricing-governance/PricingGovernanceConsole.d.ts.map +1 -1
  230. package/pricing-governance/PricingGovernanceConsole.js +2 -1
  231. package/pricing-governance/PricingGovernanceConsole.js.map +1 -1
  232. package/provider-standing/ProviderStandingAccessNotice.d.ts +11 -0
  233. package/provider-standing/ProviderStandingAccessNotice.d.ts.map +1 -0
  234. package/provider-standing/ProviderStandingAccessNotice.js +14 -0
  235. package/provider-standing/ProviderStandingAccessNotice.js.map +1 -0
  236. package/provider-standing/ProviderStandingConsole.d.ts +24 -0
  237. package/provider-standing/ProviderStandingConsole.d.ts.map +1 -0
  238. package/provider-standing/ProviderStandingConsole.js +98 -0
  239. package/provider-standing/ProviderStandingConsole.js.map +1 -0
  240. package/provider-standing/index.d.ts +6 -0
  241. package/provider-standing/index.d.ts.map +1 -0
  242. package/provider-standing/index.js +6 -0
  243. package/provider-standing/index.js.map +1 -0
  244. package/provider-standing/useProviderStanding.d.ts +34 -0
  245. package/provider-standing/useProviderStanding.d.ts.map +1 -0
  246. package/provider-standing/useProviderStanding.js +26 -0
  247. package/provider-standing/useProviderStanding.js.map +1 -0
  248. package/provider.d.ts +12 -0
  249. package/provider.d.ts.map +1 -1
  250. package/provider.js +12 -0
  251. package/provider.js.map +1 -1
  252. package/resource-creation/StepIndicator.d.ts.map +1 -1
  253. package/resource-creation/StepIndicator.js +2 -1
  254. package/resource-creation/StepIndicator.js.map +1 -1
  255. package/schedule/ScheduleDetailView.d.ts.map +1 -1
  256. package/schedule/ScheduleDetailView.js +3 -2
  257. package/schedule/ScheduleDetailView.js.map +1 -1
  258. package/schedule/ScheduleRunsTable.d.ts.map +1 -1
  259. package/schedule/ScheduleRunsTable.js +2 -1
  260. package/schedule/ScheduleRunsTable.js.map +1 -1
  261. package/session/NewSessionViewer.d.ts +10 -1
  262. package/session/NewSessionViewer.d.ts.map +1 -1
  263. package/session/NewSessionViewer.js +2 -1
  264. package/session/NewSessionViewer.js.map +1 -1
  265. package/session/facets/ArtifactsTab.d.ts.map +1 -1
  266. package/session/facets/ArtifactsTab.js +2 -1
  267. package/session/facets/ArtifactsTab.js.map +1 -1
  268. package/session/facets/ChangesTab.d.ts.map +1 -1
  269. package/session/facets/ChangesTab.js +2 -1
  270. package/session/facets/ChangesTab.js.map +1 -1
  271. package/session/facets/UsageTab.d.ts +10 -3
  272. package/session/facets/UsageTab.d.ts.map +1 -1
  273. package/session/facets/UsageTab.js +42 -6
  274. package/session/facets/UsageTab.js.map +1 -1
  275. package/session/index.d.ts +1 -1
  276. package/session/index.d.ts.map +1 -1
  277. package/session/index.js.map +1 -1
  278. package/session/useNewSessionFlow.d.ts +27 -1
  279. package/session/useNewSessionFlow.d.ts.map +1 -1
  280. package/session/useNewSessionFlow.js +45 -8
  281. package/session/useNewSessionFlow.js.map +1 -1
  282. package/session/useSessionUsage.d.ts +25 -0
  283. package/session/useSessionUsage.d.ts.map +1 -1
  284. package/session/useSessionUsage.js +11 -0
  285. package/session/useSessionUsage.js.map +1 -1
  286. package/settings/AccountPreferencesSection.d.ts +3 -0
  287. package/settings/AccountPreferencesSection.d.ts.map +1 -0
  288. package/settings/AccountPreferencesSection.js +10 -0
  289. package/settings/AccountPreferencesSection.js.map +1 -0
  290. package/settings/OrgPreferencesSection.d.ts +3 -0
  291. package/settings/OrgPreferencesSection.d.ts.map +1 -0
  292. package/settings/OrgPreferencesSection.js +13 -0
  293. package/settings/OrgPreferencesSection.js.map +1 -0
  294. package/settings/index.d.ts +2 -0
  295. package/settings/index.d.ts.map +1 -1
  296. package/settings/index.js +2 -0
  297. package/settings/index.js.map +1 -1
  298. package/settings/settings-nav.d.ts.map +1 -1
  299. package/settings/settings-nav.js +23 -1
  300. package/settings/settings-nav.js.map +1 -1
  301. package/settings/useSettingsNavGroups.d.ts.map +1 -1
  302. package/settings/useSettingsNavGroups.js +4 -1
  303. package/settings/useSettingsNavGroups.js.map +1 -1
  304. package/sharing/ShareAgentDialog.d.ts.map +1 -1
  305. package/sharing/ShareAgentDialog.js +2 -1
  306. package/sharing/ShareAgentDialog.js.map +1 -1
  307. package/sidebar/WorkspaceSidebar.d.ts.map +1 -1
  308. package/sidebar/WorkspaceSidebar.js +2 -1
  309. package/sidebar/WorkspaceSidebar.js.map +1 -1
  310. package/skill/SkillFileBrowser.d.ts.map +1 -1
  311. package/skill/SkillFileBrowser.js +2 -1
  312. package/skill/SkillFileBrowser.js.map +1 -1
  313. package/skill/internal/fetchAndUnpackArtifact.d.ts +1 -1
  314. package/skill/internal/fetchAndUnpackArtifact.d.ts.map +1 -1
  315. package/skill/internal/fetchAndUnpackArtifact.js +47 -3
  316. package/skill/internal/fetchAndUnpackArtifact.js.map +1 -1
  317. package/skill/useSkillDiff.d.ts.map +1 -1
  318. package/skill/useSkillDiff.js +7 -1
  319. package/skill/useSkillDiff.js.map +1 -1
  320. package/skill/useSkillUpload.d.ts.map +1 -1
  321. package/skill/useSkillUpload.js +22 -4
  322. package/skill/useSkillUpload.js.map +1 -1
  323. package/src/__tests__/bare-host-resets.layout.test.tsx +184 -0
  324. package/src/agent/steps/IdentityStep.tsx +2 -1
  325. package/src/agent-instance/AgentInstanceDetailPanel.tsx +2 -1
  326. package/src/api-key/CreateApiKeyForm.tsx +2 -1
  327. package/src/attachment/AttachmentChipList.tsx +19 -8
  328. package/src/attachment/__tests__/AttachmentChipList.test.tsx +12 -0
  329. package/src/attachment/__tests__/useAttachments.test.ts +293 -0
  330. package/src/attachment/index.ts +1 -0
  331. package/src/attachment/useAttachments.ts +143 -11
  332. package/src/channel/ChannelConversationsDialog.tsx +2 -1
  333. package/src/channel/ChannelTemplatesDialog.tsx +2 -1
  334. package/src/channel/ConnectSlackDialog.tsx +2 -1
  335. package/src/channel/connect/ServingAppSection.tsx +2 -1
  336. package/src/channel-app/CreateChannelAppForm.tsx +2 -1
  337. package/src/composer/SessionComposer.tsx +23 -14
  338. package/src/conversation/ConversationComposer.tsx +38 -4
  339. package/src/conversation/ConversationListPane.tsx +2 -5
  340. package/src/conversation/ConversationTemplatePickerDialog.tsx +441 -0
  341. package/src/conversation/ConversationTimelineView.tsx +2 -1
  342. package/src/conversation/ConversationsWorkbench.tsx +40 -4
  343. package/src/conversation/__tests__/ConversationComposer.test.tsx +56 -1
  344. package/src/conversation/__tests__/ConversationTemplatePickerDialog.test.tsx +224 -0
  345. package/src/conversation/__tests__/useConversationParticipation.test.tsx +45 -4
  346. package/src/conversation/index.ts +4 -0
  347. package/src/conversation/useConversationParticipation.ts +58 -6
  348. package/src/cursor-accounts/CursorAccountsConsole.tsx +2 -1
  349. package/src/cursor-accounts/MemberKeysPanel.tsx +2 -1
  350. package/src/dashboard/DashboardFailedRuns.tsx +2 -1
  351. package/src/dependency-graph/DependencyGraph.tsx +2 -1
  352. package/src/dependency-graph/DependencyTreeNode.tsx +2 -1
  353. package/src/environment/EnvironmentPicker.tsx +2 -1
  354. package/src/error/SecretFlowErrorGuide.tsx +3 -2
  355. package/src/execution/ArtifactContentBody.tsx +2 -1
  356. package/src/execution/ArtifactsWidget.tsx +2 -1
  357. package/src/execution/FileChangeProgressBar.tsx +2 -1
  358. package/src/execution/TodoList.tsx +2 -1
  359. package/src/execution/WriteBacksWidget.tsx +2 -1
  360. package/src/execution/tool-categories.ts +17 -41
  361. package/src/iam-policy/PeopleWithAccess.tsx +2 -1
  362. package/src/iam-policy/PrincipalPicker.tsx +2 -0
  363. package/src/iam-policy/RoleSelector.tsx +2 -1
  364. package/src/identity-account/AccountPreferencesPanel.tsx +446 -0
  365. package/src/identity-account/__tests__/AccountPreferencesPanel.test.tsx +233 -0
  366. package/src/identity-account/__tests__/useAccountExecutionDefaults.test.tsx +116 -0
  367. package/src/identity-account/__tests__/useMyIdentityAccount.test.tsx +121 -0
  368. package/src/identity-account/index.ts +11 -0
  369. package/src/identity-account/useAccountExecutionDefaults.ts +63 -0
  370. package/src/identity-account/useMyIdentityAccount.ts +75 -0
  371. package/src/identity-account/useUpdateIdentityAccount.ts +73 -0
  372. package/src/identity-provider/CreateIdentityProviderForm.tsx +2 -1
  373. package/src/identity-provider/IdentityProviderDetailPanel.tsx +2 -1
  374. package/src/identity-provider/IdentityProviderWizard.tsx +2 -1
  375. package/src/index.ts +37 -2
  376. package/src/internal/StandingContextField.tsx +92 -0
  377. package/src/internal/element-resets.ts +33 -0
  378. package/src/internal/file-tree/FileTreeNode.tsx +2 -1
  379. package/src/internal/markdown-components.tsx +15 -5
  380. package/src/invitation/InvitationManager.tsx +3 -2
  381. package/src/library/InstanceVisibilitySelector.tsx +9 -2
  382. package/src/library/ResourceVisibilityControl.tsx +46 -13
  383. package/src/library/VisibilityOptionRow.tsx +28 -6
  384. package/src/library/VisibilitySelector.tsx +14 -2
  385. package/src/library/__tests__/VisibilitySelector.test.tsx +48 -1
  386. package/src/library/__tests__/visibilityLevels.test.ts +67 -1
  387. package/src/library/index.ts +6 -1
  388. package/src/library/useCanSetPublicVisibility.ts +56 -0
  389. package/src/library/visibilityLevels.ts +65 -14
  390. package/src/manifest/ApplyManifestDialog.tsx +2 -1
  391. package/src/mcp-server/__tests__/useMcpServerConnect.asyncLane.test.tsx +117 -0
  392. package/src/mcp-server/steps/IdentityTransportStep.tsx +3 -2
  393. package/src/mcp-server/useMcpServerConnect.ts +9 -6
  394. package/src/mcp-server/useMcpServerOAuthConnect.ts +7 -2
  395. package/src/oauth-app/CreateOAuthAppForm.tsx +58 -3
  396. package/src/oauth-app/OAuthAppDetailPanel.tsx +84 -3
  397. package/src/organization/OrgPreferencesPanel.tsx +221 -0
  398. package/src/organization/OrgProfilePanel.tsx +5 -4
  399. package/src/organization/__tests__/OrgPreferencesPanel.test.tsx +145 -0
  400. package/src/organization/__tests__/OrgProfilePanel.test.tsx +108 -0
  401. package/src/organization/index.ts +2 -0
  402. package/src/platform-client/CreatePlatformClientForm.tsx +4 -3
  403. package/src/platform-client/PlatformClientDetailPanel.tsx +4 -3
  404. package/src/pricing-governance/BaselineEditor.tsx +3 -2
  405. package/src/pricing-governance/PricingGovernanceConsole.tsx +2 -1
  406. package/src/provider-standing/ProviderStandingAccessNotice.tsx +36 -0
  407. package/src/provider-standing/ProviderStandingConsole.tsx +216 -0
  408. package/src/provider-standing/__tests__/ProviderStandingConsole.test.tsx +143 -0
  409. package/src/provider-standing/index.ts +8 -0
  410. package/src/provider-standing/useProviderStanding.ts +50 -0
  411. package/src/provider.tsx +12 -0
  412. package/src/resource-creation/StepIndicator.tsx +2 -1
  413. package/src/schedule/ScheduleDetailView.tsx +3 -2
  414. package/src/schedule/ScheduleRunsTable.tsx +2 -1
  415. package/src/session/NewSessionViewer.tsx +12 -0
  416. package/src/session/__tests__/UsageTab.test.tsx +136 -0
  417. package/src/session/__tests__/useNewSessionFlow.test.tsx +151 -0
  418. package/src/session/__tests__/useSessionUsage.test.ts +19 -0
  419. package/src/session/facets/ArtifactsTab.tsx +2 -1
  420. package/src/session/facets/ChangesTab.tsx +2 -1
  421. package/src/session/facets/UsageTab.tsx +98 -6
  422. package/src/session/index.ts +1 -0
  423. package/src/session/useNewSessionFlow.ts +76 -9
  424. package/src/session/useSessionUsage.ts +37 -0
  425. package/src/settings/AccountPreferencesSection.tsx +25 -0
  426. package/src/settings/OrgPreferencesSection.tsx +35 -0
  427. package/src/settings/__tests__/settings-nav.test.ts +27 -0
  428. package/src/settings/__tests__/useSettingsNavGroups.test.tsx +11 -1
  429. package/src/settings/index.ts +2 -0
  430. package/src/settings/settings-nav.ts +25 -0
  431. package/src/settings/useSettingsNavGroups.ts +8 -1
  432. package/src/sharing/ShareAgentDialog.tsx +2 -1
  433. package/src/sidebar/WorkspaceSidebar.tsx +2 -1
  434. package/src/skill/SkillFileBrowser.tsx +2 -1
  435. package/src/skill/__tests__/SkillFileBrowser.test.tsx +10 -2
  436. package/src/skill/__tests__/fetchAndUnpackArtifact.test.ts +90 -0
  437. package/src/skill/__tests__/useSkillUpload.test.ts +69 -0
  438. package/src/skill/internal/fetchAndUnpackArtifact.ts +54 -4
  439. package/src/skill/useSkillDiff.ts +7 -1
  440. package/src/skill/useSkillUpload.ts +25 -6
  441. package/src/workflow/ApprovalFormBuilder.tsx +5 -27
  442. package/src/workflow/FailedRunsWidget.tsx +2 -1
  443. package/src/workflow/PendingApprovalsWidget.tsx +2 -1
  444. package/src/workflow/facets/WorkflowArtifactsTab.tsx +2 -1
  445. package/src/workflow/facets/WorkflowUsageTab.tsx +2 -1
  446. package/src/workflow/inspector/WorkflowSummaryPanel.tsx +2 -1
  447. package/src/workflow/inspector/tabs/BranchesTab.tsx +2 -1
  448. package/src/workflow/inspector/tabs/IterationTab.tsx +2 -1
  449. package/src/workflow/instance/WorkflowInstanceDetailPanel.tsx +2 -1
  450. package/src/workspace/ExplorerTree.tsx +2 -1
  451. package/src/workspace/WorkspaceContentSearch.tsx +2 -1
  452. package/src/workspace/WorkspaceFileSearch.tsx +2 -1
  453. package/src/workspace/WorkspaceSummary.tsx +2 -1
  454. package/styles.css +1 -1
  455. package/workflow/ApprovalFormBuilder.d.ts.map +1 -1
  456. package/workflow/ApprovalFormBuilder.js +8 -5
  457. package/workflow/ApprovalFormBuilder.js.map +1 -1
  458. package/workflow/FailedRunsWidget.d.ts.map +1 -1
  459. package/workflow/FailedRunsWidget.js +2 -1
  460. package/workflow/FailedRunsWidget.js.map +1 -1
  461. package/workflow/PendingApprovalsWidget.d.ts.map +1 -1
  462. package/workflow/PendingApprovalsWidget.js +2 -1
  463. package/workflow/PendingApprovalsWidget.js.map +1 -1
  464. package/workflow/facets/WorkflowArtifactsTab.d.ts.map +1 -1
  465. package/workflow/facets/WorkflowArtifactsTab.js +2 -1
  466. package/workflow/facets/WorkflowArtifactsTab.js.map +1 -1
  467. package/workflow/facets/WorkflowUsageTab.d.ts.map +1 -1
  468. package/workflow/facets/WorkflowUsageTab.js +2 -1
  469. package/workflow/facets/WorkflowUsageTab.js.map +1 -1
  470. package/workflow/inspector/WorkflowSummaryPanel.d.ts.map +1 -1
  471. package/workflow/inspector/WorkflowSummaryPanel.js +2 -1
  472. package/workflow/inspector/WorkflowSummaryPanel.js.map +1 -1
  473. package/workflow/inspector/tabs/BranchesTab.d.ts.map +1 -1
  474. package/workflow/inspector/tabs/BranchesTab.js +2 -1
  475. package/workflow/inspector/tabs/BranchesTab.js.map +1 -1
  476. package/workflow/inspector/tabs/IterationTab.d.ts.map +1 -1
  477. package/workflow/inspector/tabs/IterationTab.js +2 -1
  478. package/workflow/inspector/tabs/IterationTab.js.map +1 -1
  479. package/workflow/instance/WorkflowInstanceDetailPanel.d.ts.map +1 -1
  480. package/workflow/instance/WorkflowInstanceDetailPanel.js +2 -1
  481. package/workflow/instance/WorkflowInstanceDetailPanel.js.map +1 -1
  482. package/workspace/ExplorerTree.d.ts.map +1 -1
  483. package/workspace/ExplorerTree.js +2 -1
  484. package/workspace/ExplorerTree.js.map +1 -1
  485. package/workspace/WorkspaceContentSearch.d.ts.map +1 -1
  486. package/workspace/WorkspaceContentSearch.js +2 -1
  487. package/workspace/WorkspaceContentSearch.js.map +1 -1
  488. package/workspace/WorkspaceFileSearch.d.ts.map +1 -1
  489. package/workspace/WorkspaceFileSearch.js +2 -1
  490. package/workspace/WorkspaceFileSearch.js.map +1 -1
  491. package/workspace/WorkspaceSummary.d.ts.map +1 -1
  492. package/workspace/WorkspaceSummary.js +2 -1
  493. package/workspace/WorkspaceSummary.js.map +1 -1
@@ -0,0 +1,108 @@
1
+ import { describe, it, expect, vi, afterEach } from "vitest";
2
+ import { cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react";
3
+ import { create } from "@bufbuild/protobuf";
4
+ import {
5
+ OrganizationSchema,
6
+ type Organization,
7
+ } from "@stigmer/protos/ai/stigmer/tenancy/organization/v1/api_pb";
8
+ import type { OrganizationInput } from "@stigmer/sdk";
9
+ import { StigmerContext } from "../../context";
10
+ import { DeploymentModeContext } from "../../deployment-mode";
11
+ import { OrgProfilePanel } from "../OrgProfilePanel";
12
+
13
+ /**
14
+ * Regression suite for the full-spec-replace wipe bug (oss#293 Phase 1):
15
+ * `organization.update()` wholesale replaces the stored spec, so a profile
16
+ * save that sends only the edited fields silently wipes every other mutable
17
+ * spec field — most visibly `spec.preferences.standing_context` set via the
18
+ * CLI or the preferences page. The panel must spread the complete mapped
19
+ * input and override only what it edits.
20
+ */
21
+
22
+ const ORG: Organization = create(OrganizationSchema, {
23
+ metadata: {
24
+ id: "acme",
25
+ name: "Acme Corp",
26
+ slug: "acme",
27
+ org: "acme",
28
+ },
29
+ spec: {
30
+ description: "We make everything.",
31
+ logoUrl: "https://acme.example/logo.png",
32
+ preferences: { standingContext: "We deploy to us-east-1." },
33
+ },
34
+ });
35
+
36
+ function createMockStigmer(overrides?: {
37
+ update?: ReturnType<typeof vi.fn>;
38
+ }) {
39
+ return {
40
+ organization: {
41
+ get: vi.fn(async () => ORG),
42
+ update: overrides?.update ?? vi.fn(async () => ORG),
43
+ },
44
+ } as never;
45
+ }
46
+
47
+ // Deployment mode "local" keeps the IdentityProvidersSummary sub-panel
48
+ // inert (identity_provider is cloud-only), so no extra client stubs are
49
+ // needed — the suite tests the form, not the summary.
50
+ function renderPanel(client: unknown) {
51
+ return render(
52
+ <StigmerContext.Provider value={client as never}>
53
+ <DeploymentModeContext.Provider value="local">
54
+ <OrgProfilePanel orgId="acme" />
55
+ </DeploymentModeContext.Provider>
56
+ </StigmerContext.Provider>,
57
+ );
58
+ }
59
+
60
+ afterEach(cleanup);
61
+
62
+ describe("OrgProfilePanel save payload", () => {
63
+ it("round-trips unedited spec fields — preferences survive a profile save", async () => {
64
+ const update = vi.fn(async (_input: OrganizationInput) => ORG);
65
+ const client = createMockStigmer({ update });
66
+ renderPanel(client);
67
+
68
+ // Wait for the server-sync effect to fill the form before editing —
69
+ // interacting on first appearance races the sync.
70
+ const description = await screen.findByLabelText("Description");
71
+ await waitFor(() =>
72
+ expect(description).toHaveProperty("value", "We make everything."),
73
+ );
74
+ fireEvent.change(description, { target: { value: "New description" } });
75
+ fireEvent.click(screen.getByRole("button", { name: "Save changes" }));
76
+
77
+ await waitFor(() => expect(update).toHaveBeenCalledTimes(1));
78
+ const input = update.mock.calls[0]![0];
79
+
80
+ // The edited field.
81
+ expect(input.description).toBe("New description");
82
+ // The unedited fields the form does not render — the wipe-bug guard.
83
+ expect(input.preferences).toEqual({
84
+ standingContext: "We deploy to us-east-1.",
85
+ });
86
+ expect(input.logoUrl).toBe("https://acme.example/logo.png");
87
+ // Addressing fields for the update pipeline's org+slug lookup.
88
+ expect(input.org).toBe("acme");
89
+ expect(input.slug).toBe("acme");
90
+ expect(input.name).toBe("Acme Corp");
91
+ });
92
+
93
+ it("sends the edited name and keeps discard/dirty semantics intact", async () => {
94
+ const update = vi.fn(async (_input: OrganizationInput) => ORG);
95
+ renderPanel(createMockStigmer({ update }));
96
+
97
+ const name = await screen.findByLabelText("Name");
98
+ await waitFor(() => expect(name).toHaveProperty("value", "Acme Corp"));
99
+ fireEvent.change(name, { target: { value: "Acme Corporation" } });
100
+ fireEvent.click(screen.getByRole("button", { name: "Save changes" }));
101
+
102
+ await waitFor(() => expect(update).toHaveBeenCalledTimes(1));
103
+ expect(update.mock.calls[0]![0].name).toBe("Acme Corporation");
104
+ expect(update.mock.calls[0]![0].preferences).toEqual({
105
+ standingContext: "We deploy to us-east-1.",
106
+ });
107
+ });
108
+ });
@@ -16,5 +16,7 @@ export { CreateOrganizationForm } from "./CreateOrganizationForm.js";
16
16
  export type { CreateOrganizationFormProps } from "./CreateOrganizationForm.js";
17
17
  export { OrgProfilePanel } from "./OrgProfilePanel.js";
18
18
  export type { OrgProfilePanelProps } from "./OrgProfilePanel.js";
19
+ export { OrgPreferencesPanel } from "./OrgPreferencesPanel.js";
20
+ export type { OrgPreferencesPanelProps } from "./OrgPreferencesPanel.js";
19
21
  export { OrgSwitcher } from "./OrgSwitcher.js";
20
22
  export type { OrgSwitcherProps } from "./OrgSwitcher.js";
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { type FormEvent, type KeyboardEvent, useCallback, useId, useState } from "react";
4
4
  import { cn } from "@stigmer/theme";
5
+ import { UNSTYLED_FIELDSET } from "../internal/element-resets.js";
5
6
  import { getUserMessage } from "@stigmer/sdk";
6
7
  import { IamRole } from "@stigmer/protos/ai/stigmer/iam/v1/enum_pb";
7
8
  import type { PlatformClientCreateResponse } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/io_pb";
@@ -167,7 +168,7 @@ export function CreatePlatformClientForm({
167
168
  />
168
169
 
169
170
  {/* Expiry */}
170
- <fieldset className="stg:space-y-2" disabled={isCreating}>
171
+ <fieldset className={cn(UNSTYLED_FIELDSET, "stg:space-y-2")} disabled={isCreating}>
171
172
  <legend className="stg:text-xs stg:font-medium stg:text-foreground">
172
173
  Expiry
173
174
  </legend>
@@ -202,7 +203,7 @@ export function CreatePlatformClientForm({
202
203
  </fieldset>
203
204
 
204
205
  {/* JIT provisioning */}
205
- <fieldset className="stg:space-y-2.5" disabled={isCreating}>
206
+ <fieldset className={cn(UNSTYLED_FIELDSET, "stg:space-y-2.5")} disabled={isCreating}>
206
207
  <hr className="stg:border-border-muted" />
207
208
  <legend className="stg:text-xs stg:font-medium stg:text-foreground">
208
209
  JIT provisioning
@@ -263,7 +264,7 @@ export function CreatePlatformClientForm({
263
264
  </fieldset>
264
265
 
265
266
  {/* Allowed origins */}
266
- <fieldset className="stg:space-y-2" disabled={isCreating}>
267
+ <fieldset className={cn(UNSTYLED_FIELDSET, "stg:space-y-2")} disabled={isCreating}>
267
268
  <hr className="stg:border-border-muted" />
268
269
  <legend className="stg:text-xs stg:font-medium stg:text-foreground">
269
270
  Allowed origins
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { type FormEvent, type KeyboardEvent, useCallback, useId, useState } from "react";
4
4
  import { cn } from "@stigmer/theme";
5
+ import { UNSTYLED_FIELDSET } from "../internal/element-resets.js";
5
6
  import { getUserMessage } from "@stigmer/sdk";
6
7
  import { IamRole } from "@stigmer/protos/ai/stigmer/iam/v1/enum_pb";
7
8
  import { timestampDate, type Timestamp } from "@bufbuild/protobuf/wkt";
@@ -302,7 +303,7 @@ export function PlatformClientDetailPanel({
302
303
  </div>
303
304
 
304
305
  {/* Expiry */}
305
- <fieldset className="stg:space-y-2" disabled={isUpdating}>
306
+ <fieldset className={cn(UNSTYLED_FIELDSET, "stg:space-y-2")} disabled={isUpdating}>
306
307
  <legend className="stg:text-xs stg:font-medium stg:text-foreground">
307
308
  Expiry
308
309
  </legend>
@@ -337,7 +338,7 @@ export function PlatformClientDetailPanel({
337
338
  </fieldset>
338
339
 
339
340
  {/* JIT provisioning */}
340
- <fieldset className="stg:space-y-2.5" disabled={isUpdating}>
341
+ <fieldset className={cn(UNSTYLED_FIELDSET, "stg:space-y-2.5")} disabled={isUpdating}>
341
342
  <hr className="stg:border-border-muted" />
342
343
  <legend className="stg:text-xs stg:font-medium stg:text-foreground">
343
344
  JIT provisioning
@@ -393,7 +394,7 @@ export function PlatformClientDetailPanel({
393
394
  </fieldset>
394
395
 
395
396
  {/* Allowed origins */}
396
- <fieldset className="stg:space-y-2" disabled={isUpdating}>
397
+ <fieldset className={cn(UNSTYLED_FIELDSET, "stg:space-y-2")} disabled={isUpdating}>
397
398
  <hr className="stg:border-border-muted" />
398
399
  <legend className="stg:text-xs stg:font-medium stg:text-foreground">
399
400
  Allowed origins
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { useCallback, useState, type FormEvent } from "react";
4
+ import { UNSTYLED_FIELDSET } from "../internal/element-resets.js";
4
5
  import { create } from "@bufbuild/protobuf";
5
6
  import { getUserMessage } from "@stigmer/sdk";
6
7
  import {
@@ -546,7 +547,7 @@ export function BaselineEditor({
546
547
  Featured (appears in the curated default picker list)
547
548
  </label>
548
549
 
549
- <fieldset className="stg:space-y-2">
550
+ <fieldset className={`${UNSTYLED_FIELDSET} stg:space-y-2`}>
550
551
  <legend className="stg:text-xs stg:font-medium stg:text-foreground">
551
552
  Rates ($ per million tokens)
552
553
  </legend>
@@ -565,7 +566,7 @@ export function BaselineEditor({
565
566
  </div>
566
567
  </fieldset>
567
568
 
568
- <fieldset className="stg:space-y-2">
569
+ <fieldset className={`${UNSTYLED_FIELDSET} stg:space-y-2`}>
569
570
  <legend className="stg:text-xs stg:font-medium stg:text-foreground">Pricing variants</legend>
570
571
  {form.variants.map((variant, index) => (
571
572
  <div key={index} className="stg:space-y-2 stg:rounded-md stg:border stg:border-border-muted stg:px-2.5 stg:py-2">
@@ -11,6 +11,7 @@ import { RetireConfirm } from "./RetireConfirm.js";
11
11
  import { ModelGovernanceDetail } from "./ModelGovernanceDetail.js";
12
12
  import { OperatorAccessNotice } from "./OperatorAccessNotice.js";
13
13
  import { GovernanceBadge, PendingOverrideCard, RateCell } from "./governance-primitives.js";
14
+ import { UNSTYLED_LIST } from "../internal/element-resets.js";
14
15
  import { INPUT_CLASSES } from "../internal/form-primitives.js";
15
16
  import { TruncatedText } from "../internal/truncated-text.js";
16
17
  import { ZERO } from "./pricing-format.js";
@@ -328,7 +329,7 @@ function ModelsTab({
328
329
  : "The catalog is empty — has the baseline seed migration run?"}
329
330
  </p>
330
331
  ) : (
331
- <ul role="list" aria-label="Models" className="stg:m-0 stg:list-none stg:p-0">
332
+ <ul role="list" aria-label="Models" className={UNSTYLED_LIST}>
332
333
  {view.models.map((row) => (
333
334
  <ModelRow key={row.key} row={row} onOpen={() => view.openDetail(row.key)} />
334
335
  ))}
@@ -0,0 +1,36 @@
1
+ "use client";
2
+
3
+ import { cn } from "@stigmer/theme";
4
+
5
+ /**
6
+ * The access-denied state for the provider-standing operator surface.
7
+ * Server-side authorization (`can_view_provider_standing` on
8
+ * `platform:stigmer`) is the real gate — this is the honest, designed
9
+ * face of that denial for anyone who navigates to the operator route
10
+ * without the permission, instead of a raw RPC error string.
11
+ */
12
+ export function ProviderStandingAccessNotice({
13
+ className,
14
+ }: {
15
+ readonly className?: string;
16
+ }) {
17
+ return (
18
+ <div
19
+ className={cn(
20
+ "stg:rounded-lg stg:border stg:border-border stg:bg-card stg:px-4 stg:py-6 stg:text-center",
21
+ className,
22
+ )}
23
+ role="status"
24
+ >
25
+ <p className="stg:text-xs stg:font-semibold stg:text-foreground">
26
+ Platform operator access required
27
+ </p>
28
+ <p className="stg:mx-auto stg:mt-1 stg:max-w-md stg:text-xs stg:text-muted-foreground">
29
+ Provider standing reports the health of the platform&apos;s own LLM
30
+ provider accounts, which is Stigmer-internal. It needs the platform
31
+ operator role — organization roles do not grant it. If you believe
32
+ you should have access, contact a platform operator.
33
+ </p>
34
+ </div>
35
+ );
36
+ }
@@ -0,0 +1,216 @@
1
+ "use client";
2
+
3
+ import { cn } from "@stigmer/theme";
4
+ import { getUserMessage, isPermissionDenied } from "@stigmer/sdk";
5
+ import type { Timestamp } from "@bufbuild/protobuf/wkt";
6
+ import type { ProviderStandingEntry } from "@stigmer/protos/ai/stigmer/platform/providerstanding/v1/io_pb";
7
+ import { Button } from "../button/index.js";
8
+ import { ProviderStandingAccessNotice } from "./ProviderStandingAccessNotice.js";
9
+ import { useProviderStanding } from "./useProviderStanding.js";
10
+
11
+ /** Props for {@link ProviderStandingConsole}. */
12
+ export interface ProviderStandingConsoleProps {
13
+ /** Additional CSS class names. */
14
+ readonly className?: string;
15
+ }
16
+
17
+ // BigInt literals (0n) require an ES2020 target, which not every consuming
18
+ // app's tsconfig guarantees — the constructor form is target-agnostic (the
19
+ // cursor-account-format precedent).
20
+ const ZERO = BigInt(0);
21
+
22
+ /**
23
+ * A probe verdict older than this is flagged stale — the probe-silence
24
+ * alert's UI twin: the probe runs hourly and the alert fires at 3h (the
25
+ * 60m interval plus generous grace), so the console goes amber on the
26
+ * same clock operators are paged on.
27
+ */
28
+ const STALE_AFTER_MS = 3 * 60 * 60 * 1000;
29
+
30
+ /**
31
+ * Display metadata for the probe's closed status vocabulary (the
32
+ * lowercase labels StandingStatus persists). An unknown future label
33
+ * degrades to a muted badge showing the raw label — the wire contract is
34
+ * a string precisely so consoles never fail to decode a new status.
35
+ */
36
+ const STATUS_DISPLAY: Record<string, { label: string; tone: "ok" | "warn" | "muted" }> = {
37
+ healthy: { label: "Healthy", tone: "ok" },
38
+ platform_billing: { label: "Billing rejected", tone: "warn" },
39
+ platform_auth: { label: "Auth rejected", tone: "warn" },
40
+ unclassified_error: { label: "Erroring", tone: "warn" },
41
+ unreachable: { label: "Unreachable", tone: "warn" },
42
+ not_configured: { label: "Not configured", tone: "muted" },
43
+ };
44
+
45
+ /**
46
+ * The platform-operator console page for provider standing: one card per
47
+ * platform LLM provider account with the latest canary-probe verdict —
48
+ * status, round-trip latency, upstream HTTP status, a bounded error
49
+ * summary, and when the probe last ran (flagged when stale).
50
+ *
51
+ * Read-only by design: the detection core (hourly probe, standing store,
52
+ * degraded/silence alerts) lives server-side; this surface only makes it
53
+ * visible where operators already look. Requires
54
+ * `can_view_provider_standing` on `platform:stigmer` — non-operators see
55
+ * the designed access notice.
56
+ *
57
+ * @example
58
+ * ```tsx
59
+ * <ProviderStandingConsole />
60
+ * ```
61
+ */
62
+ export function ProviderStandingConsole({ className }: ProviderStandingConsoleProps) {
63
+ const { standing, isLoading, isRefetching, error, refetch } = useProviderStanding();
64
+
65
+ if (isLoading) {
66
+ return (
67
+ <div className={cn("stg:space-y-2", className)} aria-busy="true">
68
+ <div className="stg:h-4 stg:w-40 stg:animate-pulse stg:rounded stg:bg-muted-subtle" />
69
+ {Array.from({ length: 2 }, (_, i) => (
70
+ <div key={i} className="stg:h-16 stg:animate-pulse stg:rounded-lg stg:bg-muted-subtle" />
71
+ ))}
72
+ </div>
73
+ );
74
+ }
75
+
76
+ if (error) {
77
+ // A non-operator landing here is expected (the route is reachable by
78
+ // URL) — show the designed access notice, not a raw RPC error.
79
+ if (isPermissionDenied(error)) {
80
+ return <ProviderStandingAccessNotice className={className} />;
81
+ }
82
+ return (
83
+ <p className={cn("stg:text-destructive stg:text-xs", className)} role="alert">
84
+ {getUserMessage(error)}
85
+ </p>
86
+ );
87
+ }
88
+
89
+ const providers = standing?.providers ?? [];
90
+
91
+ return (
92
+ <div className={cn("stg:space-y-3", className)}>
93
+ <div className="stg:flex stg:items-center stg:justify-between">
94
+ <p className="stg:text-xs stg:text-muted-foreground">
95
+ Latest canary verdict per platform provider account, probed hourly.
96
+ </p>
97
+ <Button
98
+ variant="outline"
99
+ size="sm"
100
+ onClick={refetch}
101
+ disabled={isRefetching}
102
+ >
103
+ {isRefetching ? "Refreshing…" : "Refresh"}
104
+ </Button>
105
+ </div>
106
+
107
+ {providers.length === 0 ? (
108
+ <p className="stg:rounded-lg stg:border stg:border-border stg:bg-card stg:px-4 stg:py-6 stg:text-center stg:text-xs stg:text-muted-foreground">
109
+ No probe verdicts recorded yet — the standing probe has not
110
+ completed a pass since this deployment came up.
111
+ </p>
112
+ ) : (
113
+ <ul className="stg:m-0 stg:grid stg:list-none stg:gap-2 stg:p-0 sm:stg:grid-cols-2">
114
+ {providers.map((entry) => (
115
+ <StandingCard key={entry.provider} entry={entry} />
116
+ ))}
117
+ </ul>
118
+ )}
119
+ </div>
120
+ );
121
+ }
122
+
123
+ function StandingCard({ entry }: { readonly entry: ProviderStandingEntry }) {
124
+ const display = STATUS_DISPLAY[entry.status] ?? {
125
+ label: entry.status || "unknown",
126
+ tone: "muted" as const,
127
+ };
128
+ const stale = isStale(entry.checkedAt);
129
+
130
+ return (
131
+ <li className="stg:rounded-lg stg:border stg:border-border stg:bg-card stg:p-3">
132
+ <div className="stg:flex stg:items-center stg:justify-between stg:gap-2">
133
+ <span className="stg:text-sm stg:font-semibold stg:capitalize stg:text-foreground">
134
+ {entry.provider}
135
+ </span>
136
+ <StatusBadge tone={display.tone} label={display.label} />
137
+ </div>
138
+
139
+ <dl className="stg:mt-2 stg:flex stg:flex-wrap stg:gap-x-4 stg:gap-y-1 stg:text-xs stg:text-muted-foreground">
140
+ {entry.latencyMs > ZERO && (
141
+ <div className="stg:flex stg:gap-1">
142
+ <dt>Latency</dt>
143
+ <dd className="stg:m-0 stg:text-foreground">{`${entry.latencyMs}ms`}</dd>
144
+ </div>
145
+ )}
146
+ {entry.httpStatus > 0 && (
147
+ <div className="stg:flex stg:gap-1">
148
+ <dt>HTTP</dt>
149
+ <dd className="stg:m-0 stg:text-foreground">{entry.httpStatus}</dd>
150
+ </div>
151
+ )}
152
+ <div className="stg:flex stg:gap-1">
153
+ <dt>Probed</dt>
154
+ <dd
155
+ className={cn(
156
+ "stg:m-0",
157
+ stale ? "stg:font-medium stg:text-destructive" : "stg:text-foreground",
158
+ )}
159
+ >
160
+ {formatProbeTime(entry.checkedAt)}
161
+ {stale ? " — stale" : ""}
162
+ </dd>
163
+ </div>
164
+ </dl>
165
+
166
+ {entry.errorSummary && (
167
+ <p className="stg:mt-2 stg:break-words stg:rounded stg:bg-muted-subtle stg:px-2 stg:py-1 stg:text-[11px] stg:text-muted-foreground">
168
+ {entry.errorSummary}
169
+ </p>
170
+ )}
171
+ </li>
172
+ );
173
+ }
174
+
175
+ /**
176
+ * Small tonal status badge — the cursor-accounts StateBadge shape, local
177
+ * to this module (the two operator surfaces evolve independently).
178
+ */
179
+ function StatusBadge({
180
+ tone,
181
+ label,
182
+ }: {
183
+ readonly tone: "ok" | "warn" | "muted";
184
+ readonly label: string;
185
+ }) {
186
+ return (
187
+ <span
188
+ className={cn(
189
+ "stg:inline-block stg:rounded stg:px-1.5 stg:py-0.5 stg:text-[10px] stg:font-medium",
190
+ tone === "ok" && "stg:bg-accent stg:text-primary",
191
+ tone === "warn" && "stg:bg-muted-subtle stg:text-destructive",
192
+ tone === "muted" && "stg:bg-muted-subtle stg:text-muted-foreground",
193
+ )}
194
+ >
195
+ {label}
196
+ </span>
197
+ );
198
+ }
199
+
200
+ function isStale(ts: Timestamp | undefined): boolean {
201
+ if (!ts || ts.seconds === ZERO) return true;
202
+ return Date.now() - Number(ts.seconds) * 1000 > STALE_AFTER_MS;
203
+ }
204
+
205
+ /** Proto Timestamp to a compact local date-time (e.g. "Jul 22, 14:05"). */
206
+ function formatProbeTime(ts: Timestamp | undefined): string {
207
+ if (!ts || ts.seconds === ZERO) {
208
+ return "never";
209
+ }
210
+ return new Date(Number(ts.seconds) * 1000).toLocaleString(undefined, {
211
+ month: "short",
212
+ day: "numeric",
213
+ hour: "2-digit",
214
+ minute: "2-digit",
215
+ });
216
+ }
@@ -0,0 +1,143 @@
1
+ import { describe, it, expect, vi, afterEach } from "vitest";
2
+ import { cleanup, render, screen, waitFor } from "@testing-library/react";
3
+ import type { ReactNode } from "react";
4
+ import { create } from "@bufbuild/protobuf";
5
+ import { timestampFromDate } from "@bufbuild/protobuf/wkt";
6
+ import { StigmerError } from "@stigmer/sdk";
7
+ import {
8
+ ProviderStandingEntrySchema,
9
+ ProviderStandingViewSchema,
10
+ } from "@stigmer/protos/ai/stigmer/platform/providerstanding/v1/io_pb";
11
+ import { StigmerContext } from "../../context";
12
+ import { FetchCacheContext } from "../../internal/FetchCacheProvider";
13
+ import { ProviderStandingConsole } from "../ProviderStandingConsole";
14
+
15
+ function createMockStigmer(getStandingView: ReturnType<typeof vi.fn>) {
16
+ return {
17
+ providerStanding: { getStandingView },
18
+ } as never;
19
+ }
20
+
21
+ function wrapper(client: unknown) {
22
+ return function Wrapper({ children }: { children: ReactNode }) {
23
+ return (
24
+ <FetchCacheContext.Provider value={null}>
25
+ <StigmerContext.Provider value={client as never}>
26
+ {children}
27
+ </StigmerContext.Provider>
28
+ </FetchCacheContext.Provider>
29
+ );
30
+ };
31
+ }
32
+
33
+ function entry(overrides: Record<string, unknown> = {}) {
34
+ return create(ProviderStandingEntrySchema, {
35
+ provider: "anthropic",
36
+ status: "healthy",
37
+ httpStatus: 200,
38
+ latencyMs: BigInt(842),
39
+ errorSummary: "",
40
+ checkedAt: timestampFromDate(new Date()),
41
+ ...overrides,
42
+ });
43
+ }
44
+
45
+ function view(entries: ReturnType<typeof entry>[]) {
46
+ return create(ProviderStandingViewSchema, { providers: entries });
47
+ }
48
+
49
+ afterEach(() => {
50
+ cleanup();
51
+ vi.restoreAllMocks();
52
+ });
53
+
54
+ describe("ProviderStandingConsole", () => {
55
+ it("renders one card per provider with status, latency, and probe time", async () => {
56
+ const client = createMockStigmer(
57
+ vi.fn().mockResolvedValue(
58
+ view([
59
+ entry(),
60
+ entry({
61
+ provider: "openai",
62
+ status: "platform_billing",
63
+ httpStatus: 429,
64
+ latencyMs: BigInt(0),
65
+ errorSummary: "insufficient_quota: billing hard limit reached",
66
+ }),
67
+ ]),
68
+ ),
69
+ );
70
+ render(<ProviderStandingConsole />, { wrapper: wrapper(client) });
71
+
72
+ await waitFor(() => {
73
+ expect(screen.getByText("anthropic")).toBeTruthy();
74
+ });
75
+ expect(screen.getByText("Healthy")).toBeTruthy();
76
+ expect(screen.getByText("842ms")).toBeTruthy();
77
+
78
+ expect(screen.getByText("openai")).toBeTruthy();
79
+ expect(screen.getByText("Billing rejected")).toBeTruthy();
80
+ expect(screen.getByText("429")).toBeTruthy();
81
+ expect(
82
+ screen.getByText("insufficient_quota: billing hard limit reached"),
83
+ ).toBeTruthy();
84
+ });
85
+
86
+ it("flags a verdict older than the probe-silence window as stale", async () => {
87
+ const fourHoursAgo = new Date(Date.now() - 4 * 60 * 60 * 1000);
88
+ const client = createMockStigmer(
89
+ vi.fn().mockResolvedValue(
90
+ view([entry({ checkedAt: timestampFromDate(fourHoursAgo) })]),
91
+ ),
92
+ );
93
+ render(<ProviderStandingConsole />, { wrapper: wrapper(client) });
94
+
95
+ await waitFor(() => {
96
+ expect(screen.getByText(/— stale/)).toBeTruthy();
97
+ });
98
+ });
99
+
100
+ it("degrades an unknown future status label to a muted badge with the raw label", async () => {
101
+ const client = createMockStigmer(
102
+ vi.fn().mockResolvedValue(view([entry({ status: "rate_limited" })])),
103
+ );
104
+ render(<ProviderStandingConsole />, { wrapper: wrapper(client) });
105
+
106
+ await waitFor(() => {
107
+ expect(screen.getByText("rate_limited")).toBeTruthy();
108
+ });
109
+ });
110
+
111
+ it("shows the designed access notice on permission denied, not a raw RPC error", async () => {
112
+ const client = createMockStigmer(
113
+ vi.fn().mockRejectedValue(
114
+ new StigmerError("permission-denied", "only platform operators can view provider standing", 7),
115
+ ),
116
+ );
117
+ render(<ProviderStandingConsole />, { wrapper: wrapper(client) });
118
+
119
+ await waitFor(() => {
120
+ expect(screen.getByText("Platform operator access required")).toBeTruthy();
121
+ });
122
+ });
123
+
124
+ it("shows the error message for non-permission failures", async () => {
125
+ const client = createMockStigmer(
126
+ vi.fn().mockRejectedValue(new StigmerError("internal", "standing lookup failed", 13)),
127
+ );
128
+ render(<ProviderStandingConsole />, { wrapper: wrapper(client) });
129
+
130
+ await waitFor(() => {
131
+ expect(screen.getByRole("alert")).toBeTruthy();
132
+ });
133
+ });
134
+
135
+ it("explains the empty state before the probe's first pass", async () => {
136
+ const client = createMockStigmer(vi.fn().mockResolvedValue(view([])));
137
+ render(<ProviderStandingConsole />, { wrapper: wrapper(client) });
138
+
139
+ await waitFor(() => {
140
+ expect(screen.getByText(/No probe verdicts recorded yet/)).toBeTruthy();
141
+ });
142
+ });
143
+ });
@@ -0,0 +1,8 @@
1
+ // Data hooks
2
+ export { useProviderStanding } from "./useProviderStanding.js";
3
+ export type { UseProviderStandingReturn } from "./useProviderStanding.js";
4
+
5
+ // Styled components
6
+ export { ProviderStandingConsole } from "./ProviderStandingConsole.js";
7
+ export type { ProviderStandingConsoleProps } from "./ProviderStandingConsole.js";
8
+ export { ProviderStandingAccessNotice } from "./ProviderStandingAccessNotice.js";