@stigmer/react 3.12.4 → 3.12.6

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 (369) hide show
  1. package/agent/AgentDetailView.js +2 -2
  2. package/agent/AgentDetailView.js.map +1 -1
  3. package/agent/index.d.ts +0 -1
  4. package/agent/index.d.ts.map +1 -1
  5. package/agent/index.js +0 -1
  6. package/agent/index.js.map +1 -1
  7. package/agent/useUpdateAgent.d.ts +3 -3
  8. package/agent/useUpdateAgent.js +3 -3
  9. package/agent-instance/AgentInstanceDetailPanel.d.ts.map +1 -1
  10. package/agent-instance/AgentInstanceDetailPanel.js +5 -7
  11. package/agent-instance/AgentInstanceDetailPanel.js.map +1 -1
  12. package/agent-instance/useUpdateAgentInstance.d.ts +1 -1
  13. package/agent-instance/useUpdateAgentInstance.js +1 -1
  14. package/attachment/AttachmentChipList.js +16 -6
  15. package/attachment/AttachmentChipList.js.map +1 -1
  16. package/attachment/index.d.ts +1 -1
  17. package/attachment/index.d.ts.map +1 -1
  18. package/attachment/index.js.map +1 -1
  19. package/attachment/useAttachments.d.ts +51 -8
  20. package/attachment/useAttachments.d.ts.map +1 -1
  21. package/attachment/useAttachments.js +74 -3
  22. package/attachment/useAttachments.js.map +1 -1
  23. package/channel/AgentChannelsPanel.d.ts.map +1 -1
  24. package/channel/AgentChannelsPanel.js +10 -5
  25. package/channel/AgentChannelsPanel.js.map +1 -1
  26. package/channel/ChannelRunConfigDialog.d.ts +51 -0
  27. package/channel/ChannelRunConfigDialog.d.ts.map +1 -0
  28. package/channel/ChannelRunConfigDialog.js +121 -0
  29. package/channel/ChannelRunConfigDialog.js.map +1 -0
  30. package/channel/index.d.ts +2 -0
  31. package/channel/index.d.ts.map +1 -1
  32. package/channel/index.js +1 -0
  33. package/channel/index.js.map +1 -1
  34. package/channel/useSaveAgentChannel.d.ts.map +1 -1
  35. package/channel/useSaveAgentChannel.js +6 -3
  36. package/channel/useSaveAgentChannel.js.map +1 -1
  37. package/channel-app/ChannelAppDetailPanel.js +9 -9
  38. package/channel-app/ChannelAppDetailPanel.js.map +1 -1
  39. package/composer/ComposerToolbar.d.ts +6 -1
  40. package/composer/ComposerToolbar.d.ts.map +1 -1
  41. package/composer/ComposerToolbar.js +2 -2
  42. package/composer/ComposerToolbar.js.map +1 -1
  43. package/composer/SessionComposer.d.ts +9 -0
  44. package/composer/SessionComposer.d.ts.map +1 -1
  45. package/composer/SessionComposer.js +39 -13
  46. package/composer/SessionComposer.js.map +1 -1
  47. package/conversation/ConversationComposer.d.ts +19 -3
  48. package/conversation/ConversationComposer.d.ts.map +1 -1
  49. package/conversation/ConversationComposer.js +4 -4
  50. package/conversation/ConversationComposer.js.map +1 -1
  51. package/conversation/ConversationTemplatePickerDialog.d.ts +63 -0
  52. package/conversation/ConversationTemplatePickerDialog.d.ts.map +1 -0
  53. package/conversation/ConversationTemplatePickerDialog.js +151 -0
  54. package/conversation/ConversationTemplatePickerDialog.js.map +1 -0
  55. package/conversation/ConversationsWorkbench.d.ts.map +1 -1
  56. package/conversation/ConversationsWorkbench.js +16 -5
  57. package/conversation/ConversationsWorkbench.js.map +1 -1
  58. package/conversation/index.d.ts +3 -1
  59. package/conversation/index.d.ts.map +1 -1
  60. package/conversation/index.js +1 -0
  61. package/conversation/index.js.map +1 -1
  62. package/conversation/useConversationParticipation.d.ts +41 -4
  63. package/conversation/useConversationParticipation.d.ts.map +1 -1
  64. package/conversation/useConversationParticipation.js +13 -2
  65. package/conversation/useConversationParticipation.js.map +1 -1
  66. package/cursor-accounts/CursorAccountsConsole.d.ts.map +1 -1
  67. package/cursor-accounts/CursorAccountsConsole.js +12 -3
  68. package/cursor-accounts/CursorAccountsConsole.js.map +1 -1
  69. package/cursor-accounts/cursor-account-coverage.d.ts +30 -1
  70. package/cursor-accounts/cursor-account-coverage.d.ts.map +1 -1
  71. package/cursor-accounts/cursor-account-coverage.js +21 -0
  72. package/cursor-accounts/cursor-account-coverage.js.map +1 -1
  73. package/execution/useCreateAgentExecution.d.ts +15 -0
  74. package/execution/useCreateAgentExecution.d.ts.map +1 -1
  75. package/execution/useCreateAgentExecution.js +5 -0
  76. package/execution/useCreateAgentExecution.js.map +1 -1
  77. package/identity-account/AccountPreferencesPanel.d.ts +36 -0
  78. package/identity-account/AccountPreferencesPanel.d.ts.map +1 -0
  79. package/identity-account/AccountPreferencesPanel.js +202 -0
  80. package/identity-account/AccountPreferencesPanel.js.map +1 -0
  81. package/identity-account/index.d.ts +8 -0
  82. package/identity-account/index.d.ts.map +1 -1
  83. package/identity-account/index.js +4 -0
  84. package/identity-account/index.js.map +1 -1
  85. package/identity-account/useAccountExecutionDefaults.d.ts +36 -0
  86. package/identity-account/useAccountExecutionDefaults.d.ts.map +1 -0
  87. package/identity-account/useAccountExecutionDefaults.js +46 -0
  88. package/identity-account/useAccountExecutionDefaults.js.map +1 -0
  89. package/identity-account/useMyIdentityAccount.d.ts +55 -0
  90. package/identity-account/useMyIdentityAccount.d.ts.map +1 -0
  91. package/identity-account/useMyIdentityAccount.js +37 -0
  92. package/identity-account/useMyIdentityAccount.js.map +1 -0
  93. package/identity-account/useUpdateIdentityAccount.d.ts +42 -0
  94. package/identity-account/useUpdateIdentityAccount.d.ts.map +1 -0
  95. package/identity-account/useUpdateIdentityAccount.js +53 -0
  96. package/identity-account/useUpdateIdentityAccount.js.map +1 -0
  97. package/identity-provider/IdentityProviderDetailPanel.d.ts.map +1 -1
  98. package/identity-provider/IdentityProviderDetailPanel.js +16 -15
  99. package/identity-provider/IdentityProviderDetailPanel.js.map +1 -1
  100. package/index.d.ts +15 -13
  101. package/index.d.ts.map +1 -1
  102. package/index.js +11 -8
  103. package/index.js.map +1 -1
  104. package/internal/StandingContextField.d.ts +37 -0
  105. package/internal/StandingContextField.d.ts.map +1 -0
  106. package/internal/StandingContextField.js +24 -0
  107. package/internal/StandingContextField.js.map +1 -0
  108. package/library/InstanceVisibilitySelector.d.ts.map +1 -1
  109. package/library/InstanceVisibilitySelector.js +9 -2
  110. package/library/InstanceVisibilitySelector.js.map +1 -1
  111. package/library/ResourceVisibilityControl.d.ts +13 -3
  112. package/library/ResourceVisibilityControl.d.ts.map +1 -1
  113. package/library/ResourceVisibilityControl.js +33 -5
  114. package/library/ResourceVisibilityControl.js.map +1 -1
  115. package/library/VisibilityOptionRow.d.ts +6 -0
  116. package/library/VisibilityOptionRow.d.ts.map +1 -1
  117. package/library/VisibilityOptionRow.js +11 -1
  118. package/library/VisibilityOptionRow.js.map +1 -1
  119. package/library/VisibilitySelector.d.ts.map +1 -1
  120. package/library/VisibilitySelector.js +14 -2
  121. package/library/VisibilitySelector.js.map +1 -1
  122. package/library/index.d.ts +4 -2
  123. package/library/index.d.ts.map +1 -1
  124. package/library/index.js +2 -1
  125. package/library/index.js.map +1 -1
  126. package/library/useCanSetPublicVisibility.d.ts +30 -0
  127. package/library/useCanSetPublicVisibility.d.ts.map +1 -0
  128. package/library/useCanSetPublicVisibility.js +41 -0
  129. package/library/useCanSetPublicVisibility.js.map +1 -0
  130. package/library/visibilityLevels.d.ts +32 -1
  131. package/library/visibilityLevels.d.ts.map +1 -1
  132. package/library/visibilityLevels.js +34 -14
  133. package/library/visibilityLevels.js.map +1 -1
  134. package/mcp-server/McpServerDetailView.js +2 -2
  135. package/mcp-server/McpServerDetailView.js.map +1 -1
  136. package/mcp-server/index.d.ts +0 -1
  137. package/mcp-server/index.d.ts.map +1 -1
  138. package/mcp-server/index.js +0 -1
  139. package/mcp-server/index.js.map +1 -1
  140. package/mcp-server/internal/definitionDrift.d.ts +14 -12
  141. package/mcp-server/internal/definitionDrift.d.ts.map +1 -1
  142. package/mcp-server/internal/definitionDrift.js +22 -19
  143. package/mcp-server/internal/definitionDrift.js.map +1 -1
  144. package/mcp-server/useMcpServerConnect.d.ts +8 -5
  145. package/mcp-server/useMcpServerConnect.d.ts.map +1 -1
  146. package/mcp-server/useMcpServerConnect.js +2 -1
  147. package/mcp-server/useMcpServerConnect.js.map +1 -1
  148. package/mcp-server/useMcpServerOAuthConnect.d.ts.map +1 -1
  149. package/mcp-server/useMcpServerOAuthConnect.js +7 -2
  150. package/mcp-server/useMcpServerOAuthConnect.js.map +1 -1
  151. package/mcp-server/useUpdateMcpServer.d.ts +3 -3
  152. package/mcp-server/useUpdateMcpServer.js +3 -3
  153. package/models/ModelSelector.d.ts +33 -1
  154. package/models/ModelSelector.d.ts.map +1 -1
  155. package/models/ModelSelector.js +37 -4
  156. package/models/ModelSelector.js.map +1 -1
  157. package/models/index.d.ts +2 -0
  158. package/models/index.d.ts.map +1 -1
  159. package/models/index.js +1 -0
  160. package/models/index.js.map +1 -1
  161. package/models/registry.d.ts +16 -0
  162. package/models/registry.d.ts.map +1 -1
  163. package/models/registry.js +13 -0
  164. package/models/registry.js.map +1 -1
  165. package/models/thinking-mode.d.ts +27 -0
  166. package/models/thinking-mode.d.ts.map +1 -0
  167. package/models/thinking-mode.js +32 -0
  168. package/models/thinking-mode.js.map +1 -0
  169. package/oauth-app/CreateOAuthAppForm.d.ts +2 -2
  170. package/oauth-app/CreateOAuthAppForm.d.ts.map +1 -1
  171. package/oauth-app/CreateOAuthAppForm.js +13 -4
  172. package/oauth-app/CreateOAuthAppForm.js.map +1 -1
  173. package/oauth-app/OAuthAppDetailPanel.d.ts.map +1 -1
  174. package/oauth-app/OAuthAppDetailPanel.js +50 -22
  175. package/oauth-app/OAuthAppDetailPanel.js.map +1 -1
  176. package/organization/OrgPreferencesPanel.d.ts +32 -0
  177. package/organization/OrgPreferencesPanel.d.ts.map +1 -0
  178. package/organization/OrgPreferencesPanel.js +90 -0
  179. package/organization/OrgPreferencesPanel.js.map +1 -0
  180. package/organization/OrgProfilePanel.d.ts.map +1 -1
  181. package/organization/OrgProfilePanel.js +5 -4
  182. package/organization/OrgProfilePanel.js.map +1 -1
  183. package/organization/index.d.ts +2 -0
  184. package/organization/index.d.ts.map +1 -1
  185. package/organization/index.js +1 -0
  186. package/organization/index.js.map +1 -1
  187. package/package.json +4 -4
  188. package/platform-client/PlatformClientDetailPanel.d.ts.map +1 -1
  189. package/platform-client/PlatformClientDetailPanel.js +12 -13
  190. package/platform-client/PlatformClientDetailPanel.js.map +1 -1
  191. package/schedule/ScheduleDetailView.d.ts.map +1 -1
  192. package/schedule/ScheduleDetailView.js +21 -10
  193. package/schedule/ScheduleDetailView.js.map +1 -1
  194. package/schedule/ScheduleForm.d.ts.map +1 -1
  195. package/schedule/ScheduleForm.js +13 -3
  196. package/schedule/ScheduleForm.js.map +1 -1
  197. package/session/NewSessionViewer.d.ts +10 -1
  198. package/session/NewSessionViewer.d.ts.map +1 -1
  199. package/session/NewSessionViewer.js +2 -1
  200. package/session/NewSessionViewer.js.map +1 -1
  201. package/session/run-config.d.ts +9 -0
  202. package/session/run-config.d.ts.map +1 -1
  203. package/session/run-config.js +5 -0
  204. package/session/run-config.js.map +1 -1
  205. package/session/useNewSessionFlow.d.ts +27 -1
  206. package/session/useNewSessionFlow.d.ts.map +1 -1
  207. package/session/useNewSessionFlow.js +51 -8
  208. package/session/useNewSessionFlow.js.map +1 -1
  209. package/session/useSessionConversation.d.ts +12 -1
  210. package/session/useSessionConversation.d.ts.map +1 -1
  211. package/session/useSessionConversation.js +15 -24
  212. package/session/useSessionConversation.js.map +1 -1
  213. package/session/useSessionPageFlow.d.ts.map +1 -1
  214. package/session/useSessionPageFlow.js +14 -10
  215. package/session/useSessionPageFlow.js.map +1 -1
  216. package/session/useUpdateSession.d.ts +18 -24
  217. package/session/useUpdateSession.d.ts.map +1 -1
  218. package/session/useUpdateSession.js +18 -24
  219. package/session/useUpdateSession.js.map +1 -1
  220. package/settings/AccountPreferencesSection.d.ts +3 -0
  221. package/settings/AccountPreferencesSection.d.ts.map +1 -0
  222. package/settings/AccountPreferencesSection.js +10 -0
  223. package/settings/AccountPreferencesSection.js.map +1 -0
  224. package/settings/OrgPreferencesSection.d.ts +3 -0
  225. package/settings/OrgPreferencesSection.d.ts.map +1 -0
  226. package/settings/OrgPreferencesSection.js +13 -0
  227. package/settings/OrgPreferencesSection.js.map +1 -0
  228. package/settings/index.d.ts +2 -0
  229. package/settings/index.d.ts.map +1 -1
  230. package/settings/index.js +2 -0
  231. package/settings/index.js.map +1 -1
  232. package/settings/settings-nav.d.ts.map +1 -1
  233. package/settings/settings-nav.js +17 -1
  234. package/settings/settings-nav.js.map +1 -1
  235. package/src/agent/AgentDetailView.tsx +2 -2
  236. package/src/agent/index.ts +0 -2
  237. package/src/agent/useUpdateAgent.ts +3 -3
  238. package/src/agent-instance/AgentInstanceDetailPanel.tsx +5 -7
  239. package/src/agent-instance/__tests__/AgentInstanceDetailPanel.test.tsx +75 -0
  240. package/src/agent-instance/useUpdateAgentInstance.ts +1 -1
  241. package/src/attachment/AttachmentChipList.tsx +19 -8
  242. package/src/attachment/__tests__/AttachmentChipList.test.tsx +12 -0
  243. package/src/attachment/__tests__/useAttachments.test.ts +293 -0
  244. package/src/attachment/index.ts +1 -0
  245. package/src/attachment/useAttachments.ts +143 -11
  246. package/src/channel/AgentChannelsPanel.tsx +26 -1
  247. package/src/channel/ChannelRunConfigDialog.tsx +351 -0
  248. package/src/channel/__tests__/ChannelRunConfigDialog.test.tsx +210 -0
  249. package/src/channel/index.ts +2 -0
  250. package/src/channel/useSaveAgentChannel.ts +6 -3
  251. package/src/channel-app/ChannelAppDetailPanel.tsx +9 -9
  252. package/src/channel-app/__tests__/channelAppWhatsApp.test.tsx +2 -0
  253. package/src/channel-app/__tests__/channelAppWipeSafety.test.tsx +87 -0
  254. package/src/composer/ComposerToolbar.tsx +9 -0
  255. package/src/composer/SessionComposer.tsx +59 -16
  256. package/src/composer/__tests__/SessionComposer-thinkingMode.test.tsx +176 -0
  257. package/src/conversation/ConversationComposer.tsx +38 -4
  258. package/src/conversation/ConversationTemplatePickerDialog.tsx +441 -0
  259. package/src/conversation/ConversationsWorkbench.tsx +40 -4
  260. package/src/conversation/__tests__/ConversationComposer.test.tsx +56 -1
  261. package/src/conversation/__tests__/ConversationTemplatePickerDialog.test.tsx +224 -0
  262. package/src/conversation/__tests__/useConversationParticipation.test.tsx +45 -4
  263. package/src/conversation/index.ts +4 -0
  264. package/src/conversation/useConversationParticipation.ts +58 -6
  265. package/src/cursor-accounts/CursorAccountsConsole.tsx +40 -5
  266. package/src/cursor-accounts/__tests__/CursorAccountsConsole.test.tsx +32 -0
  267. package/src/cursor-accounts/__tests__/cursor-account-coverage.test.ts +58 -1
  268. package/src/cursor-accounts/cursor-account-coverage.ts +55 -0
  269. package/src/execution/useCreateAgentExecution.ts +19 -0
  270. package/src/identity-account/AccountPreferencesPanel.tsx +532 -0
  271. package/src/identity-account/__tests__/AccountPreferencesPanel.test.tsx +267 -0
  272. package/src/identity-account/__tests__/useAccountExecutionDefaults.test.tsx +116 -0
  273. package/src/identity-account/__tests__/useMyIdentityAccount.test.tsx +121 -0
  274. package/src/identity-account/index.ts +11 -0
  275. package/src/identity-account/useAccountExecutionDefaults.ts +63 -0
  276. package/src/identity-account/useMyIdentityAccount.ts +75 -0
  277. package/src/identity-account/useUpdateIdentityAccount.ts +73 -0
  278. package/src/identity-provider/IdentityProviderDetailPanel.tsx +18 -15
  279. package/src/identity-provider/__tests__/IdentityProviderDetailPanel.test.tsx +80 -0
  280. package/src/index.ts +26 -4
  281. package/src/internal/StandingContextField.tsx +92 -0
  282. package/src/library/InstanceVisibilitySelector.tsx +9 -2
  283. package/src/library/ResourceVisibilityControl.tsx +46 -13
  284. package/src/library/VisibilityOptionRow.tsx +28 -6
  285. package/src/library/VisibilitySelector.tsx +14 -2
  286. package/src/library/__tests__/VisibilitySelector.test.tsx +48 -1
  287. package/src/library/__tests__/visibilityLevels.test.ts +67 -1
  288. package/src/library/index.ts +6 -1
  289. package/src/library/useCanSetPublicVisibility.ts +56 -0
  290. package/src/library/visibilityLevels.ts +65 -14
  291. package/src/mcp-server/McpServerDetailView.tsx +2 -2
  292. package/src/mcp-server/__tests__/useMcpServerConnect.asyncLane.test.tsx +117 -0
  293. package/src/mcp-server/index.ts +0 -2
  294. package/src/mcp-server/internal/definitionDrift.ts +22 -20
  295. package/src/mcp-server/useMcpServerConnect.ts +9 -6
  296. package/src/mcp-server/useMcpServerOAuthConnect.ts +7 -2
  297. package/src/mcp-server/useUpdateMcpServer.ts +3 -3
  298. package/src/models/ModelSelector.tsx +94 -1
  299. package/src/models/index.ts +2 -0
  300. package/src/models/registry.ts +29 -0
  301. package/src/models/thinking-mode.ts +48 -0
  302. package/src/oauth-app/CreateOAuthAppForm.tsx +58 -3
  303. package/src/oauth-app/OAuthAppDetailPanel.tsx +103 -20
  304. package/src/oauth-app/__tests__/OAuthAppDetailPanel.test.tsx +94 -0
  305. package/src/organization/OrgPreferencesPanel.tsx +221 -0
  306. package/src/organization/OrgProfilePanel.tsx +5 -4
  307. package/src/organization/__tests__/OrgPreferencesPanel.test.tsx +145 -0
  308. package/src/organization/__tests__/OrgProfilePanel.test.tsx +108 -0
  309. package/src/organization/index.ts +2 -0
  310. package/src/platform-client/PlatformClientDetailPanel.tsx +14 -13
  311. package/src/platform-client/__tests__/PlatformClientDetailPanel.test.tsx +82 -0
  312. package/src/schedule/ScheduleDetailView.tsx +29 -4
  313. package/src/schedule/ScheduleForm.tsx +14 -1
  314. package/src/session/NewSessionViewer.tsx +12 -0
  315. package/src/session/__tests__/useNewSessionFlow.test.tsx +151 -0
  316. package/src/session/run-config.ts +16 -0
  317. package/src/session/useNewSessionFlow.ts +82 -9
  318. package/src/session/useSessionConversation.ts +32 -40
  319. package/src/session/useSessionPageFlow.ts +14 -11
  320. package/src/session/useUpdateSession.ts +18 -24
  321. package/src/settings/AccountPreferencesSection.tsx +25 -0
  322. package/src/settings/OrgPreferencesSection.tsx +35 -0
  323. package/src/settings/__tests__/settings-nav.test.ts +27 -0
  324. package/src/settings/index.ts +2 -0
  325. package/src/settings/settings-nav.ts +18 -0
  326. package/src/workflow/ApprovalFormBuilder.tsx +20 -27
  327. package/src/workflow/WorkflowDetailView.tsx +2 -2
  328. package/src/workflow/__tests__/WorkflowInstanceDetailPanel.test.tsx +83 -0
  329. package/src/workflow/index.ts +0 -2
  330. package/src/workflow/instance/WorkflowInstanceDetailPanel.tsx +6 -7
  331. package/src/workflow/useUpdateWorkflow.ts +3 -3
  332. package/styles.css +1 -1
  333. package/workflow/ApprovalFormBuilder.d.ts.map +1 -1
  334. package/workflow/ApprovalFormBuilder.js +10 -5
  335. package/workflow/ApprovalFormBuilder.js.map +1 -1
  336. package/workflow/WorkflowDetailView.js +2 -2
  337. package/workflow/WorkflowDetailView.js.map +1 -1
  338. package/workflow/index.d.ts +0 -1
  339. package/workflow/index.d.ts.map +1 -1
  340. package/workflow/index.js +0 -1
  341. package/workflow/index.js.map +1 -1
  342. package/workflow/instance/WorkflowInstanceDetailPanel.d.ts.map +1 -1
  343. package/workflow/instance/WorkflowInstanceDetailPanel.js +6 -7
  344. package/workflow/instance/WorkflowInstanceDetailPanel.js.map +1 -1
  345. package/workflow/useUpdateWorkflow.d.ts +3 -3
  346. package/workflow/useUpdateWorkflow.js +3 -3
  347. package/agent/internal/agentToInput.d.ts +0 -15
  348. package/agent/internal/agentToInput.d.ts.map +0 -1
  349. package/agent/internal/agentToInput.js +0 -84
  350. package/agent/internal/agentToInput.js.map +0 -1
  351. package/mcp-server/internal/mcpServerToInput.d.ts +0 -12
  352. package/mcp-server/internal/mcpServerToInput.d.ts.map +0 -1
  353. package/mcp-server/internal/mcpServerToInput.js +0 -84
  354. package/mcp-server/internal/mcpServerToInput.js.map +0 -1
  355. package/session/session-spec-converters.d.ts +0 -24
  356. package/session/session-spec-converters.d.ts.map +0 -1
  357. package/session/session-spec-converters.js +0 -72
  358. package/session/session-spec-converters.js.map +0 -1
  359. package/src/agent/internal/__tests__/agentToInput.test.ts +0 -145
  360. package/src/agent/internal/agentToInput.ts +0 -102
  361. package/src/mcp-server/__tests__/mcpServerToInput.test.ts +0 -201
  362. package/src/mcp-server/internal/mcpServerToInput.ts +0 -98
  363. package/src/session/__tests__/session-spec-converters.test.ts +0 -183
  364. package/src/session/session-spec-converters.ts +0 -86
  365. package/src/workflow/internal/workflowToInput.ts +0 -96
  366. package/workflow/internal/workflowToInput.d.ts +0 -12
  367. package/workflow/internal/workflowToInput.d.ts.map +0 -1
  368. package/workflow/internal/workflowToInput.js +0 -73
  369. package/workflow/internal/workflowToInput.js.map +0 -1
@@ -0,0 +1,73 @@
1
+ "use client";
2
+
3
+ import { useCallback, useState } from "react";
4
+ import type { IdentityAccountInput } from "@stigmer/sdk";
5
+ import type { IdentityAccount } from "@stigmer/protos/ai/stigmer/iam/identityaccount/v1/api_pb";
6
+ import { useStigmer } from "../hooks.js";
7
+ import { toError } from "../internal/toError.js";
8
+
9
+ /** Return value of {@link useUpdateIdentityAccount}. */
10
+ export interface UseUpdateIdentityAccountReturn {
11
+ /** Submit an {@link IdentityAccountInput} to update an identity account. Resolves with the updated resource. */
12
+ readonly update: (input: IdentityAccountInput) => Promise<IdentityAccount>;
13
+ /** `true` while the update request is in flight. */
14
+ readonly isUpdating: boolean;
15
+ /** Error from the last failed update, or `null` when healthy. */
16
+ readonly error: Error | null;
17
+ /** Reset `error` to `null`. */
18
+ readonly clearError: () => void;
19
+ }
20
+
21
+ /**
22
+ * Mutation hook that wraps `identityAccount.update()` with loading and
23
+ * error state.
24
+ *
25
+ * The update RPC is a full-spec replacement addressed by `org` + `slug`
26
+ * (the generated input carries no id). Build the input from the loaded
27
+ * resource with `toIdentityAccountUpdateInput` from `@stigmer/sdk` and
28
+ * override only the fields being edited — sending a partial input wipes
29
+ * the unsent spec fields. The same rule applies INSIDE nested messages:
30
+ * override `preferences` by spreading the mapper's complete value first,
31
+ * or the untouched preference fields wipe.
32
+ *
33
+ * Self-service: a user can always update their own account (`can_edit`
34
+ * via the self-ownership relationship written at account creation).
35
+ *
36
+ * @example
37
+ * ```tsx
38
+ * const { update, isUpdating, error } = useUpdateIdentityAccount();
39
+ *
40
+ * const mapped = toIdentityAccountUpdateInput(account);
41
+ * await update({
42
+ * ...mapped,
43
+ * preferences: { ...mapped.preferences, standingContext: "Keep answers terse." },
44
+ * });
45
+ * refetch(); // re-sync the editor
46
+ * ```
47
+ */
48
+ export function useUpdateIdentityAccount(): UseUpdateIdentityAccountReturn {
49
+ const stigmer = useStigmer();
50
+ const [isUpdating, setIsUpdating] = useState(false);
51
+ const [error, setError] = useState<Error | null>(null);
52
+
53
+ const clearError = useCallback(() => setError(null), []);
54
+
55
+ const update = useCallback(
56
+ async (input: IdentityAccountInput): Promise<IdentityAccount> => {
57
+ setIsUpdating(true);
58
+ setError(null);
59
+
60
+ try {
61
+ return await stigmer.identityAccount.update(input);
62
+ } catch (err) {
63
+ setError(toError(err));
64
+ throw err;
65
+ } finally {
66
+ setIsUpdating(false);
67
+ }
68
+ },
69
+ [stigmer],
70
+ );
71
+
72
+ return { update, isUpdating, error, clearError };
73
+ }
@@ -3,7 +3,7 @@
3
3
  import { type FormEvent, useCallback, useId, useRef, useState } from "react";
4
4
  import { cn } from "@stigmer/theme";
5
5
  import { UNSTYLED_FIELDSET } from "../internal/element-resets.js";
6
- import { getUserMessage } from "@stigmer/sdk";
6
+ import { getUserMessage, toIdentityProviderUpdateInput } from "@stigmer/sdk";
7
7
  import type { IdentityProvider } from "@stigmer/protos/ai/stigmer/iam/identityprovider/v1/api_pb";
8
8
  import { IamRole } from "@stigmer/protos/ai/stigmer/iam/v1/enum_pb";
9
9
  import { timestampDate, type Timestamp } from "@bufbuild/protobuf/wkt";
@@ -132,10 +132,13 @@ export function IdentityProviderDetailPanel({
132
132
  e.preventDefault();
133
133
  clearError();
134
134
  try {
135
+ // Full-spec-replace safety: spread the complete mapped input and
136
+ // override only the edited fields, so unlisted spec fields (e.g.
137
+ // rate_limit_budget) survive the save. Fields the form clears
138
+ // (SSO providers have no JIT settings) are set to undefined
139
+ // explicitly — omitting them would carry the stale mapped value.
135
140
  const updated = await update({
136
- name: meta?.name ?? "",
137
- slug: meta?.slug,
138
- org: meta?.org ?? "",
141
+ ...toIdentityProviderUpdateInput(identityProvider),
139
142
  displayName: displayName.trim(),
140
143
  jwksUri: jwksUri.trim(),
141
144
  allowedIssuers: issuers
@@ -146,16 +149,16 @@ export function IdentityProviderDetailPanel({
146
149
  userinfoEndpoint: userinfoEndpoint.trim() || undefined,
147
150
  isSsoProvider: isSso,
148
151
  oidcClientId: isSso ? oidcClientId.trim() : undefined,
149
- ...(!isSso && {
150
- autoProvisionAccounts: autoProvision,
151
- autoGrantOnOrg: autoGrant,
152
- ...(autoGrant && autoGrantRole !== IamRole.iam_role_unspecified && {
153
- autoGrantRole,
154
- }),
155
- ...(autoGrant && tenantOrgClaim.trim() && {
156
- tenantOrgClaim: tenantOrgClaim.trim(),
157
- }),
158
- }),
152
+ autoProvisionAccounts: !isSso && autoProvision ? true : undefined,
153
+ autoGrantOnOrg: !isSso && autoGrant ? true : undefined,
154
+ autoGrantRole:
155
+ !isSso && autoGrant && autoGrantRole !== IamRole.iam_role_unspecified
156
+ ? autoGrantRole
157
+ : undefined,
158
+ tenantOrgClaim:
159
+ !isSso && autoGrant && tenantOrgClaim.trim()
160
+ ? tenantOrgClaim.trim()
161
+ : undefined,
159
162
  });
160
163
  setMode("view");
161
164
  onUpdated?.(updated);
@@ -164,7 +167,7 @@ export function IdentityProviderDetailPanel({
164
167
  }
165
168
  },
166
169
  [
167
- meta, displayName, jwksUri, issuers, audience,
170
+ identityProvider, displayName, jwksUri, issuers, audience,
168
171
  userinfoEndpoint, isSso, oidcClientId, autoProvision, autoGrant,
169
172
  autoGrantRole, tenantOrgClaim, update, clearError, onUpdated,
170
173
  ],
@@ -0,0 +1,80 @@
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
+ IdentityProviderSchema,
6
+ type IdentityProvider,
7
+ } from "@stigmer/protos/ai/stigmer/iam/identityprovider/v1/api_pb";
8
+ import { IamRole } from "@stigmer/protos/ai/stigmer/iam/v1/enum_pb";
9
+ import type { IdentityProviderInput } from "@stigmer/sdk";
10
+ import { StigmerContext } from "../../context";
11
+ import { IdentityProviderDetailPanel } from "../IdentityProviderDetailPanel";
12
+
13
+ /**
14
+ * Regression suite for the full-spec-replace wipe bug: before the
15
+ * generated-mapper migration, this panel hand-built its update input and
16
+ * NEVER sent `rate_limit_budget` — editing any IdP field zeroed the
17
+ * provider's rate limit. The panel must spread
18
+ * `toIdentityProviderUpdateInput` and override only what it edits.
19
+ */
20
+
21
+ const IDP: IdentityProvider = create(IdentityProviderSchema, {
22
+ metadata: {
23
+ id: "idp-1",
24
+ name: "Acme Okta",
25
+ slug: "acme-okta",
26
+ org: "acme",
27
+ },
28
+ spec: {
29
+ displayName: "Acme Okta",
30
+ jwksUri: "https://acme.okta.example/jwks",
31
+ allowedIssuers: ["https://acme.okta.example"],
32
+ expectedAudience: "stigmer",
33
+ rateLimitBudget: 120,
34
+ isSsoProvider: false,
35
+ autoProvisionAccounts: true,
36
+ autoGrantOnOrg: true,
37
+ autoGrantRole: IamRole.admin,
38
+ tenantOrgClaim: "org_slug",
39
+ },
40
+ });
41
+
42
+ function renderPanel(update: ReturnType<typeof vi.fn>) {
43
+ const client = {
44
+ identityProvider: { update },
45
+ } as never;
46
+ return render(
47
+ <StigmerContext.Provider value={client}>
48
+ <IdentityProviderDetailPanel identityProvider={IDP} />
49
+ </StigmerContext.Provider>,
50
+ );
51
+ }
52
+
53
+ afterEach(cleanup);
54
+
55
+ describe("IdentityProviderDetailPanel save payload", () => {
56
+ it("round-trips rate_limit_budget on a display-name edit (the wipe bug)", async () => {
57
+ const update = vi.fn(async (_input: IdentityProviderInput) => IDP);
58
+ renderPanel(update);
59
+
60
+ fireEvent.click(screen.getByRole("button", { name: "Edit" }));
61
+ const displayName = await screen.findByLabelText("Display name");
62
+ fireEvent.change(displayName, { target: { value: "Acme Okta (renamed)" } });
63
+ fireEvent.click(screen.getByRole("button", { name: "Save changes" }));
64
+
65
+ await waitFor(() => expect(update).toHaveBeenCalledTimes(1));
66
+ const input = update.mock.calls[0]![0];
67
+
68
+ // The wipe-bug guard: the form does not render the rate limit, yet it
69
+ // must survive the save.
70
+ expect(input.rateLimitBudget).toBe(120);
71
+ // The edited field.
72
+ expect(input.displayName).toBe("Acme Okta (renamed)");
73
+ // JIT settings round-trip from the edit state.
74
+ expect(input.autoGrantRole).toBe(IamRole.admin);
75
+ expect(input.tenantOrgClaim).toBe("org_slug");
76
+ // Addressing fields.
77
+ expect(input.org).toBe("acme");
78
+ expect(input.slug).toBe("acme-okta");
79
+ });
80
+ });
package/src/index.ts CHANGED
@@ -502,6 +502,7 @@ export {
502
502
  visionPreflightMessage,
503
503
  } from "./attachment/index.js";
504
504
  export type {
505
+ AddFilesOptions,
505
506
  AttachmentPhase,
506
507
  AttachmentEntry,
507
508
  AssessVisionPreflightOptions,
@@ -566,7 +567,6 @@ export {
566
567
  McpToolSelector,
567
568
  useCreateMcpServer,
568
569
  useUpdateMcpServer,
569
- mcpServerToInput,
570
570
  toServerKey,
571
571
  IdentityTransportStep,
572
572
  EnvironmentAuthStep,
@@ -695,7 +695,6 @@ export {
695
695
  useDefaultAgent,
696
696
  useCreateAgent,
697
697
  useUpdateAgent,
698
- agentToInput,
699
698
  AgentCreationWizard,
700
699
  } from "./agent/index.js";
701
700
  export type {
@@ -833,13 +832,23 @@ export type {
833
832
  ToolCredentialsReadiness,
834
833
  } from "./environment/index.js";
835
834
 
836
- // Identity Account — gate hook for ensuring the caller's identity account exists before app render
835
+ // Identity Account — gate hook, self-account data/mutation hooks, the
836
+ // account preferences editor, and the composer-seeding execution defaults
837
837
  export {
838
838
  useIdentityAccountGate,
839
+ useMyIdentityAccount,
840
+ useAccountExecutionDefaults,
841
+ useUpdateIdentityAccount,
842
+ AccountPreferencesPanel,
839
843
  } from "./identity-account/index.js";
840
844
  export type {
841
845
  IdentityAccountGateState,
842
846
  UseIdentityAccountGateReturn,
847
+ UseMyIdentityAccountOptions,
848
+ UseMyIdentityAccountReturn,
849
+ AccountExecutionDefaults,
850
+ UseUpdateIdentityAccountReturn,
851
+ AccountPreferencesPanelProps,
843
852
  } from "./identity-account/index.js";
844
853
 
845
854
  // IAM Policy — data hooks, behavior hooks, headless hook, and styled components for access management
@@ -921,6 +930,7 @@ export {
921
930
  useUpdateOrganization,
922
931
  CreateOrganizationForm,
923
932
  OrgProfilePanel,
933
+ OrgPreferencesPanel,
924
934
  OrgSwitcher,
925
935
  } from "./organization/index.js";
926
936
  export type {
@@ -933,6 +943,7 @@ export type {
933
943
  UseUpdateOrganizationReturn,
934
944
  CreateOrganizationFormProps,
935
945
  OrgProfilePanelProps,
946
+ OrgPreferencesPanelProps,
936
947
  OrgSwitcherProps,
937
948
  } from "./organization/index.js";
938
949
 
@@ -1065,6 +1076,8 @@ export type { SettingsNavItem, SettingsNavGroup } from "./settings/index.js";
1065
1076
  export { ApiKeysSection } from "./settings/index.js";
1066
1077
  export { MembersSection } from "./settings/index.js";
1067
1078
  export { OrgProfileSection } from "./settings/index.js";
1079
+ export { OrgPreferencesSection } from "./settings/index.js";
1080
+ export { AccountPreferencesSection } from "./settings/index.js";
1068
1081
  export { EnvironmentsSection } from "./settings/index.js";
1069
1082
  export { InvitationsSection } from "./settings/index.js";
1070
1083
  export { IdentityProvidersSection } from "./settings/index.js";
@@ -1296,6 +1309,7 @@ export {
1296
1309
  ConnectSlackDialog,
1297
1310
  ConnectWhatsAppDialog,
1298
1311
  ChannelCredentialsDialog,
1312
+ ChannelRunConfigDialog,
1299
1313
  ChannelToolCredentials,
1300
1314
  ChannelConversationsDialog,
1301
1315
  ChannelTemplatesDialog,
@@ -1319,6 +1333,7 @@ export type {
1319
1333
  ConnectSlackDialogProps,
1320
1334
  ConnectWhatsAppDialogProps,
1321
1335
  ChannelCredentialsDialogProps,
1336
+ ChannelRunConfigDialogProps,
1322
1337
  ChannelToolCredentialsProps,
1323
1338
  ChannelConversationsDialogProps,
1324
1339
  ChannelTemplatesDialogProps,
@@ -1335,6 +1350,7 @@ export {
1335
1350
  ConversationControlBanner,
1336
1351
  ConversationAttentionBanner,
1337
1352
  ConversationComposer,
1353
+ ConversationTemplatePickerDialog,
1338
1354
  useConversation,
1339
1355
  useConversationList,
1340
1356
  useConversationMediaUrl,
@@ -1364,6 +1380,8 @@ export type {
1364
1380
  ConversationControlBannerProps,
1365
1381
  ConversationAttentionBannerProps,
1366
1382
  ConversationComposerProps,
1383
+ ConversationTemplatePickerDialogProps,
1384
+ ConversationReplyPayload,
1367
1385
  UseConversationOptions,
1368
1386
  UseConversationReturn,
1369
1387
  UseConversationListOptions,
@@ -1422,9 +1440,11 @@ export {
1422
1440
  VisibilitySelector,
1423
1441
  VisibilityBadge,
1424
1442
  blueprintVisibilityLevels,
1425
- INSTANCE_VISIBILITY_LEVELS,
1443
+ instanceVisibilityLevels,
1444
+ PUBLIC_LOCKED_REASON,
1426
1445
  visibilityLabel,
1427
1446
  useUpdateVisibility,
1447
+ useCanSetPublicVisibility,
1428
1448
  } from "./library/index.js";
1429
1449
  export type {
1430
1450
  ScopeToggleProps,
@@ -1444,8 +1464,10 @@ export type {
1444
1464
  VisibilityBadgeProps,
1445
1465
  VisibilityLevelOption,
1446
1466
  BlueprintVisibilityLevelsContext,
1467
+ InstanceVisibilityLevelsContext,
1447
1468
  VisibilityResourceKind,
1448
1469
  UseUpdateVisibilityReturn,
1470
+ UseCanSetPublicVisibilityReturn,
1449
1471
  } from "./library/index.js";
1450
1472
 
1451
1473
  // Manifest — kind-agnostic YAML edit/apply (editor, hooks, dialogs)
@@ -0,0 +1,92 @@
1
+ "use client";
2
+
3
+ import type { ReactNode } from "react";
4
+ import { cn } from "@stigmer/theme";
5
+
6
+ /**
7
+ * Both `OrganizationPreferences.standing_context` and
8
+ * `IdentityAccountPreferences.standing_context` pin `max_len = 2000`
9
+ * (buf.validate, spec.proto in each package).
10
+ */
11
+ export const STANDING_CONTEXT_MAX_LEN = 2000;
12
+ /** Show the remaining-characters counter once within this margin. */
13
+ const COUNTER_THRESHOLD = 200;
14
+
15
+ /** Props for {@link StandingContextField}. */
16
+ export interface StandingContextFieldProps {
17
+ /** DOM id for the textarea (label association). */
18
+ readonly id: string;
19
+ /** Current field value. */
20
+ readonly value: string;
21
+ /** Fired with the new value on every edit. */
22
+ readonly onChange: (value: string) => void;
23
+ /** Disables editing while a save is in flight. */
24
+ readonly disabled?: boolean;
25
+ /**
26
+ * Renders the field as read-only (still selectable/copyable) for
27
+ * viewers without edit permission.
28
+ */
29
+ readonly readOnly?: boolean;
30
+ /** Example text shown when the field is empty. */
31
+ readonly placeholder?: string;
32
+ /** Helper line rendered under the textarea. */
33
+ readonly helperText?: ReactNode;
34
+ }
35
+
36
+ /**
37
+ * Shared textarea for declared-preference standing context — the one
38
+ * free-text field both the organization and account preference editors
39
+ * render. Owns the proto length cap and the remaining-characters
40
+ * counter; the parent panel owns label, load/save, and dirty state.
41
+ *
42
+ * Internal to `@stigmer/react` — the public surface is the panels.
43
+ */
44
+ export function StandingContextField({
45
+ id,
46
+ value,
47
+ onChange,
48
+ disabled,
49
+ readOnly,
50
+ placeholder,
51
+ helperText,
52
+ }: StandingContextFieldProps) {
53
+ const remaining = STANDING_CONTEXT_MAX_LEN - value.length;
54
+
55
+ return (
56
+ <div className="stg:space-y-1">
57
+ <label
58
+ htmlFor={id}
59
+ className="stg:text-xs stg:font-medium stg:text-foreground"
60
+ >
61
+ Standing context
62
+ </label>
63
+ <textarea
64
+ id={id}
65
+ value={value}
66
+ onChange={(e) => onChange(e.target.value)}
67
+ maxLength={STANDING_CONTEXT_MAX_LEN}
68
+ rows={6}
69
+ disabled={disabled}
70
+ readOnly={readOnly}
71
+ placeholder={readOnly ? undefined : placeholder}
72
+ className={cn(
73
+ "stg:w-full stg:resize-y stg:rounded-md stg:border stg:border-input stg:bg-background stg:px-2.5 stg:py-1.5 stg:text-xs stg:text-foreground",
74
+ "stg:placeholder:text-muted-foreground",
75
+ "stg:focus-visible:outline-none stg:focus-visible:ring-1 stg:focus-visible:ring-ring",
76
+ "stg:disabled:pointer-events-none stg:disabled:opacity-50",
77
+ readOnly && "stg:bg-muted-subtle stg:text-muted-foreground",
78
+ )}
79
+ />
80
+ {helperText && (
81
+ <p className="stg:text-[0.65rem] stg:text-muted-foreground">
82
+ {helperText}
83
+ </p>
84
+ )}
85
+ {!readOnly && remaining <= COUNTER_THRESHOLD && (
86
+ <p className="stg:text-[0.65rem] stg:text-muted-foreground">
87
+ {remaining} characters left
88
+ </p>
89
+ )}
90
+ </div>
91
+ );
92
+ }
@@ -5,7 +5,8 @@ import {
5
5
  VisibilitySelector,
6
6
  type VisibilitySelectorMode,
7
7
  } from "./VisibilitySelector.js";
8
- import { INSTANCE_VISIBILITY_LEVELS } from "./visibilityLevels.js";
8
+ import { useCanSetPublicVisibility } from "./useCanSetPublicVisibility.js";
9
+ import { instanceVisibilityLevels } from "./visibilityLevels.js";
9
10
 
10
11
  /** Props for {@link InstanceVisibilitySelector}. */
11
12
  export interface InstanceVisibilitySelectorProps {
@@ -69,10 +70,16 @@ export function InstanceVisibilitySelector({
69
70
  disabled = false,
70
71
  className,
71
72
  }: InstanceVisibilitySelectorProps) {
73
+ // Public is operator-granted in the cloud edition; the hook resolves the
74
+ // caller's grant here so every consumer (create dialogs, list rows) gets
75
+ // the locked Public row without wiring anything.
76
+ const canSetPublic = useCanSetPublicVisibility();
72
77
  return (
73
78
  <VisibilitySelector
74
79
  visibility={visibility}
75
- options={INSTANCE_VISIBILITY_LEVELS}
80
+ options={instanceVisibilityLevels({
81
+ canSetPublicVisibility: canSetPublic.allowed,
82
+ })}
76
83
  onVisibilityChange={onVisibilityChange}
77
84
  mode={mode}
78
85
  isPending={isPending}
@@ -1,15 +1,16 @@
1
1
  "use client";
2
2
 
3
3
  import { useCallback } from "react";
4
- import type { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
4
+ import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
5
5
  import { useDeploymentMode } from "../deployment-mode.js";
6
6
  import { PermissionGate } from "../iam-policy/PermissionGate.js";
7
7
  import { useSsoProvider } from "../identity-provider/useSsoProvider.js";
8
+ import { useCanSetPublicVisibility } from "./useCanSetPublicVisibility.js";
8
9
  import { VisibilityBadge, VisibilitySelector } from "./VisibilitySelector.js";
9
10
  import {
10
11
  blueprintVisibilityLevels,
11
12
  environmentVisibilityLevels,
12
- INSTANCE_VISIBILITY_LEVELS,
13
+ instanceVisibilityLevels,
13
14
  } from "./visibilityLevels.js";
14
15
  import {
15
16
  useUpdateVisibility,
@@ -85,9 +86,19 @@ export interface ResourceVisibilityControlProps {
85
86
  * the org boundary. In `local` mode there are no levels to choose (the
86
87
  * OSS backend is single-user), so the control degrades to a badge.
87
88
  *
89
+ * The Public level is operator-gated in the cloud edition (publishing is a
90
+ * curation decision): for callers without `can_set_public_visibility`
91
+ * ({@link useCanSetPublicVisibility}) the option renders locked with copy
92
+ * naming the path forward. The same permission opens the operator takedown
93
+ * lane — on a currently-public resource, an operator gets the interactive
94
+ * selector even without resource `can_edit`, so noisy public resources can
95
+ * be un-published directly from the console (every offered move off PUBLIC
96
+ * is a downgrade, which the backend allows operators unilaterally).
97
+ *
88
98
  * The backend remains the enforcer (`ValidateVisibilityStep` rejects
89
- * platform without an IdP); the gate here only prevents offering an option
90
- * that is guaranteed to fail.
99
+ * platform without an IdP; `AuthorizeVisibilityTransitionStep` gates the
100
+ * PUBLIC level); the gates here only prevent offering an option that is
101
+ * guaranteed to fail.
91
102
  */
92
103
  export function ResourceVisibilityControl({
93
104
  kind,
@@ -99,6 +110,7 @@ export function ResourceVisibilityControl({
99
110
  }: ResourceVisibilityControlProps) {
100
111
  const { updateVisibility, isPending } = useUpdateVisibility(kind, resourceId);
101
112
  const deploymentMode = useDeploymentMode();
113
+ const canSetPublic = useCanSetPublicVisibility();
102
114
 
103
115
  const isInstance = INSTANCE_KINDS.has(kind);
104
116
  const isEnvironment = kind === "environment";
@@ -113,10 +125,13 @@ export function ResourceVisibilityControl({
113
125
  const options = isEnvironment
114
126
  ? environmentVisibilityLevels(deploymentMode)
115
127
  : isInstance
116
- ? INSTANCE_VISIBILITY_LEVELS
128
+ ? instanceVisibilityLevels({
129
+ canSetPublicVisibility: canSetPublic.allowed,
130
+ })
117
131
  : blueprintVisibilityLevels({
118
132
  deploymentMode,
119
133
  hasIdentityProvider: ssoProvider !== null,
134
+ canSetPublicVisibility: canSetPublic.allowed,
120
135
  });
121
136
 
122
137
  const handleChange = useCallback(
@@ -141,6 +156,31 @@ export function ResourceVisibilityControl({
141
156
  return badge;
142
157
  }
143
158
 
159
+ const selector = (
160
+ <VisibilitySelector
161
+ visibility={visibility}
162
+ options={options}
163
+ onVisibilityChange={handleChange}
164
+ isPending={isPending}
165
+ ariaLabel={isInstance ? "Instance visibility" : "Resource visibility"}
166
+ className={className}
167
+ />
168
+ );
169
+
170
+ // Operator takedown lane: on a currently-public resource, a caller holding
171
+ // can_set_public_visibility gets the control without resource can_edit —
172
+ // every offered move is a downgrade off PUBLIC, which the backend allows
173
+ // operators unilaterally (AuthorizeVisibilityTransitionStep), so no
174
+ // offered action can fail authorization. Scoped to the public state on
175
+ // purpose: on non-public foreign resources most transitions would be
176
+ // denied, and publishing those stays a request-to-operator flow.
177
+ if (
178
+ canSetPublic.allowed &&
179
+ visibility === ApiResourceVisibility.visibility_public
180
+ ) {
181
+ return selector;
182
+ }
183
+
144
184
  return (
145
185
  <PermissionGate
146
186
  resource={{ kind: FGA_KIND[kind], id: resourceId }}
@@ -148,14 +188,7 @@ export function ResourceVisibilityControl({
148
188
  fallback={badge}
149
189
  loading={badge}
150
190
  >
151
- <VisibilitySelector
152
- visibility={visibility}
153
- options={options}
154
- onVisibilityChange={handleChange}
155
- isPending={isPending}
156
- ariaLabel={isInstance ? "Instance visibility" : "Resource visibility"}
157
- className={className}
158
- />
191
+ {selector}
159
192
  </PermissionGate>
160
193
  );
161
194
  }
@@ -53,6 +53,12 @@ export interface VisibilityOptionRowProps {
53
53
  * label, one-line description, and a check when current. Shared by the
54
54
  * {@link VisibilitySelector} popover and its create-mode inline list so
55
55
  * option presentation has exactly one source.
56
+ *
57
+ * A level carrying {@link VisibilityLevelOption.lockedReason} renders
58
+ * non-interactive with a lock affordance and the reason as its detail line
59
+ * — the level stays discoverable (the reason names the path forward)
60
+ * without being selectable. The locked row keeps its normal opacity: it is
61
+ * informative, not in a transient disabled state.
56
62
  */
57
63
  export const VisibilityOptionRow = forwardRef<
58
64
  HTMLButtonElement,
@@ -71,6 +77,7 @@ export const VisibilityOptionRow = forwardRef<
71
77
  },
72
78
  ref,
73
79
  ) {
80
+ const locked = option.lockedReason !== undefined;
74
81
  const selectionAttr =
75
82
  role === "radio"
76
83
  ? { "aria-checked": isSelected }
@@ -82,16 +89,20 @@ export const VisibilityOptionRow = forwardRef<
82
89
  type="button"
83
90
  role={role}
84
91
  {...selectionAttr}
92
+ aria-disabled={locked || undefined}
85
93
  tabIndex={tabIndex}
86
- disabled={disabled}
94
+ disabled={disabled || locked}
87
95
  onClick={onSelect}
88
96
  onMouseEnter={onMouseEnter}
89
97
  onKeyDown={onKeyDown}
90
98
  className={cn(
91
99
  "stg:flex stg:w-full stg:items-start stg:gap-2 stg:rounded-md stg:px-2.5 stg:py-2 stg:text-left stg:transition-colors",
92
100
  "stg:focus-visible:outline-none stg:focus-visible:ring-2 stg:focus-visible:ring-ring",
93
- "stg:disabled:pointer-events-none stg:disabled:opacity-50",
94
- isHighlighted ? "stg:bg-accent stg:text-foreground" : "stg:hover:bg-accent-hover",
101
+ locked
102
+ ? "stg:disabled:pointer-events-none stg:cursor-default"
103
+ : "stg:disabled:pointer-events-none stg:disabled:opacity-50",
104
+ !locked &&
105
+ (isHighlighted ? "stg:bg-accent stg:text-foreground" : "stg:hover:bg-accent-hover"),
95
106
  )}
96
107
  >
97
108
  <VisibilityIcon
@@ -99,14 +110,25 @@ export const VisibilityOptionRow = forwardRef<
99
110
  className="stg:mt-0.5 stg:size-3.5 stg:shrink-0 stg:text-muted-foreground"
100
111
  />
101
112
  <span className="stg:min-w-0 stg:flex-1">
102
- <span className="stg:block stg:text-xs stg:font-medium stg:text-foreground">
113
+ <span
114
+ className={cn(
115
+ "stg:block stg:text-xs stg:font-medium",
116
+ locked ? "stg:text-muted-foreground" : "stg:text-foreground",
117
+ )}
118
+ >
103
119
  {option.label}
104
120
  </span>
105
121
  <span className="stg:block stg:text-[0.65rem] stg:leading-snug stg:text-muted-foreground">
106
- {option.description}
122
+ {locked ? option.lockedReason : option.description}
107
123
  </span>
108
124
  </span>
109
- {isSelected && <CheckIcon className="stg:mt-0.5 stg:size-3.5 stg:shrink-0 stg:text-primary" />}
125
+ {locked ? (
126
+ <LockIcon className="stg:mt-0.5 stg:size-3.5 stg:shrink-0 stg:text-muted-foreground" />
127
+ ) : (
128
+ isSelected && (
129
+ <CheckIcon className="stg:mt-0.5 stg:size-3.5 stg:shrink-0 stg:text-primary" />
130
+ )
131
+ )}
110
132
  </button>
111
133
  );
112
134
  });
@@ -150,6 +150,12 @@ export function VisibilitySelector({
150
150
 
151
151
  const handleSelect = useCallback(
152
152
  (option: VisibilityLevelOption) => {
153
+ // Locked levels are not selectable; the row is disabled, this guard
154
+ // is defense in depth for programmatic activation.
155
+ if (option.lockedReason !== undefined) {
156
+ return;
157
+ }
158
+
153
159
  const value = option.value;
154
160
  if (value === visibility) {
155
161
  setPendingInline(null);
@@ -193,11 +199,17 @@ export function VisibilitySelector({
193
199
  const moveFocus = useCallback(
194
200
  (from: number, delta: number) => {
195
201
  const count = effectiveOptions.length;
196
- const next = (from + delta + count) % count;
202
+ // Skip locked rows: their buttons are disabled and cannot take focus,
203
+ // so landing on one would strand the roving tabindex.
204
+ let next = from;
205
+ for (let step = 0; step < count; step++) {
206
+ next = (next + delta + count) % count;
207
+ if (effectiveOptions[next]?.lockedReason === undefined) break;
208
+ }
197
209
  setHighlightIdx(next);
198
210
  optionRefs.current[next]?.focus();
199
211
  },
200
- [effectiveOptions.length],
212
+ [effectiveOptions],
201
213
  );
202
214
 
203
215
  const handleRowKeyDown = useCallback(