@stigmer/react 3.2.3 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (402) hide show
  1. package/agent/steps/ReviewStep.d.ts.map +1 -1
  2. package/agent/steps/ReviewStep.js +2 -3
  3. package/agent/steps/ReviewStep.js.map +1 -1
  4. package/billing/BaselineEditor.d.ts +9 -0
  5. package/billing/BaselineEditor.d.ts.map +1 -0
  6. package/billing/BaselineEditor.js +253 -0
  7. package/billing/BaselineEditor.js.map +1 -0
  8. package/billing/ModelCatalogPanel.d.ts +28 -0
  9. package/billing/ModelCatalogPanel.d.ts.map +1 -0
  10. package/billing/ModelCatalogPanel.js +102 -0
  11. package/billing/ModelCatalogPanel.js.map +1 -0
  12. package/billing/ModelGovernanceDetail.d.ts +8 -0
  13. package/billing/ModelGovernanceDetail.d.ts.map +1 -0
  14. package/billing/ModelGovernanceDetail.js +64 -0
  15. package/billing/ModelGovernanceDetail.js.map +1 -0
  16. package/billing/OperatorAccessNotice.d.ts +12 -0
  17. package/billing/OperatorAccessNotice.d.ts.map +1 -0
  18. package/billing/OperatorAccessNotice.js +15 -0
  19. package/billing/OperatorAccessNotice.js.map +1 -0
  20. package/billing/PricingGovernanceConsole.d.ts +57 -0
  21. package/billing/PricingGovernanceConsole.d.ts.map +1 -0
  22. package/billing/PricingGovernanceConsole.js +164 -0
  23. package/billing/PricingGovernanceConsole.js.map +1 -0
  24. package/billing/PricingGovernancePanel.d.ts +26 -0
  25. package/billing/PricingGovernancePanel.d.ts.map +1 -0
  26. package/billing/PricingGovernancePanel.js +64 -0
  27. package/billing/PricingGovernancePanel.js.map +1 -0
  28. package/billing/RetireConfirm.d.ts +9 -0
  29. package/billing/RetireConfirm.d.ts.map +1 -0
  30. package/billing/RetireConfirm.js +19 -0
  31. package/billing/RetireConfirm.js.map +1 -0
  32. package/billing/form-primitives.d.ts +18 -0
  33. package/billing/form-primitives.d.ts.map +1 -0
  34. package/billing/form-primitives.js +19 -0
  35. package/billing/form-primitives.js.map +1 -0
  36. package/billing/governance-primitives.d.ts +33 -0
  37. package/billing/governance-primitives.d.ts.map +1 -0
  38. package/billing/governance-primitives.js +43 -0
  39. package/billing/governance-primitives.js.map +1 -0
  40. package/billing/index.d.ts +18 -0
  41. package/billing/index.d.ts.map +1 -1
  42. package/billing/index.js +9 -0
  43. package/billing/index.js.map +1 -1
  44. package/billing/pricing-format.d.ts +12 -0
  45. package/billing/pricing-format.d.ts.map +1 -0
  46. package/billing/pricing-format.js +50 -0
  47. package/billing/pricing-format.js.map +1 -0
  48. package/billing/useDecidePricingOverride.d.ts +46 -0
  49. package/billing/useDecidePricingOverride.d.ts.map +1 -0
  50. package/billing/useDecidePricingOverride.js +44 -0
  51. package/billing/useDecidePricingOverride.js.map +1 -0
  52. package/billing/useModelGovernanceView.d.ts +103 -0
  53. package/billing/useModelGovernanceView.d.ts.map +1 -0
  54. package/billing/useModelGovernanceView.js +144 -0
  55. package/billing/useModelGovernanceView.js.map +1 -0
  56. package/billing/useModelPricingBaselines.d.ts +40 -0
  57. package/billing/useModelPricingBaselines.d.ts.map +1 -0
  58. package/billing/useModelPricingBaselines.js +32 -0
  59. package/billing/useModelPricingBaselines.js.map +1 -0
  60. package/billing/usePricingGovernance.d.ts +39 -0
  61. package/billing/usePricingGovernance.d.ts.map +1 -0
  62. package/billing/usePricingGovernance.js +31 -0
  63. package/billing/usePricingGovernance.js.map +1 -0
  64. package/billing/useRetireModelPricingBaseline.d.ts +31 -0
  65. package/billing/useRetireModelPricingBaseline.d.ts.map +1 -0
  66. package/billing/useRetireModelPricingBaseline.js +36 -0
  67. package/billing/useRetireModelPricingBaseline.js.map +1 -0
  68. package/billing/useUpsertModelPricingBaseline.d.ts +38 -0
  69. package/billing/useUpsertModelPricingBaseline.d.ts.map +1 -0
  70. package/billing/useUpsertModelPricingBaseline.js +37 -0
  71. package/billing/useUpsertModelPricingBaseline.js.map +1 -0
  72. package/channel/AgentChannelsPanel.d.ts.map +1 -1
  73. package/channel/AgentChannelsPanel.js +11 -5
  74. package/channel/AgentChannelsPanel.js.map +1 -1
  75. package/composer/SessionComposer.d.ts +1 -1
  76. package/cursor-accounts/CursorAccountEditor.d.ts +28 -0
  77. package/cursor-accounts/CursorAccountEditor.d.ts.map +1 -0
  78. package/cursor-accounts/CursorAccountEditor.js +51 -0
  79. package/cursor-accounts/CursorAccountEditor.js.map +1 -0
  80. package/cursor-accounts/CursorAccountsAccessNotice.d.ts +11 -0
  81. package/cursor-accounts/CursorAccountsAccessNotice.d.ts.map +1 -0
  82. package/cursor-accounts/CursorAccountsAccessNotice.js +14 -0
  83. package/cursor-accounts/CursorAccountsAccessNotice.js.map +1 -0
  84. package/cursor-accounts/CursorAccountsConsole.d.ts +28 -0
  85. package/cursor-accounts/CursorAccountsConsole.d.ts.map +1 -0
  86. package/cursor-accounts/CursorAccountsConsole.js +275 -0
  87. package/cursor-accounts/CursorAccountsConsole.js.map +1 -0
  88. package/cursor-accounts/cursor-account-format.d.ts +10 -0
  89. package/cursor-accounts/cursor-account-format.d.ts.map +1 -0
  90. package/cursor-accounts/cursor-account-format.js +26 -0
  91. package/cursor-accounts/cursor-account-format.js.map +1 -0
  92. package/cursor-accounts/index.d.ts +18 -0
  93. package/cursor-accounts/index.d.ts.map +1 -0
  94. package/cursor-accounts/index.js +13 -0
  95. package/cursor-accounts/index.js.map +1 -0
  96. package/cursor-accounts/useCursorAccountView.d.ts +30 -0
  97. package/cursor-accounts/useCursorAccountView.d.ts.map +1 -0
  98. package/cursor-accounts/useCursorAccountView.js +25 -0
  99. package/cursor-accounts/useCursorAccountView.js.map +1 -0
  100. package/cursor-accounts/useCursorAccounts.d.ts +33 -0
  101. package/cursor-accounts/useCursorAccounts.d.ts.map +1 -0
  102. package/cursor-accounts/useCursorAccounts.js +25 -0
  103. package/cursor-accounts/useCursorAccounts.js.map +1 -0
  104. package/cursor-accounts/useCursorMemberKeyActions.d.ts +49 -0
  105. package/cursor-accounts/useCursorMemberKeyActions.d.ts.map +1 -0
  106. package/cursor-accounts/useCursorMemberKeyActions.js +38 -0
  107. package/cursor-accounts/useCursorMemberKeyActions.js.map +1 -0
  108. package/cursor-accounts/useDeleteCursorAccount.d.ts +30 -0
  109. package/cursor-accounts/useDeleteCursorAccount.d.ts.map +1 -0
  110. package/cursor-accounts/useDeleteCursorAccount.js +32 -0
  111. package/cursor-accounts/useDeleteCursorAccount.js.map +1 -0
  112. package/cursor-accounts/useSyncCursorAccount.d.ts +23 -0
  113. package/cursor-accounts/useSyncCursorAccount.d.ts.map +1 -0
  114. package/cursor-accounts/useSyncCursorAccount.js +31 -0
  115. package/cursor-accounts/useSyncCursorAccount.js.map +1 -0
  116. package/cursor-accounts/useUpsertCursorAccount.d.ts +26 -0
  117. package/cursor-accounts/useUpsertCursorAccount.d.ts.map +1 -0
  118. package/cursor-accounts/useUpsertCursorAccount.js +34 -0
  119. package/cursor-accounts/useUpsertCursorAccount.js.map +1 -0
  120. package/datastore/CollectionRecordsBrowser.d.ts +34 -0
  121. package/datastore/CollectionRecordsBrowser.d.ts.map +1 -0
  122. package/datastore/CollectionRecordsBrowser.js +209 -0
  123. package/datastore/CollectionRecordsBrowser.js.map +1 -0
  124. package/datastore/CollectionSchemaView.d.ts +29 -0
  125. package/datastore/CollectionSchemaView.d.ts.map +1 -0
  126. package/datastore/CollectionSchemaView.js +93 -0
  127. package/datastore/CollectionSchemaView.js.map +1 -0
  128. package/datastore/DatastoreDetailView.d.ts +44 -0
  129. package/datastore/DatastoreDetailView.d.ts.map +1 -0
  130. package/datastore/DatastoreDetailView.js +76 -0
  131. package/datastore/DatastoreDetailView.js.map +1 -0
  132. package/datastore/DatastoreSyncReport.d.ts +19 -0
  133. package/datastore/DatastoreSyncReport.d.ts.map +1 -0
  134. package/datastore/DatastoreSyncReport.js +45 -0
  135. package/datastore/DatastoreSyncReport.js.map +1 -0
  136. package/datastore/DeleteDatastoreDialog.d.ts +35 -0
  137. package/datastore/DeleteDatastoreDialog.d.ts.map +1 -0
  138. package/datastore/DeleteDatastoreDialog.js +90 -0
  139. package/datastore/DeleteDatastoreDialog.js.map +1 -0
  140. package/datastore/FieldValueControl.d.ts +55 -0
  141. package/datastore/FieldValueControl.d.ts.map +1 -0
  142. package/datastore/FieldValueControl.js +138 -0
  143. package/datastore/FieldValueControl.js.map +1 -0
  144. package/datastore/RecordFilterBuilder.d.ts +30 -0
  145. package/datastore/RecordFilterBuilder.d.ts.map +1 -0
  146. package/datastore/RecordFilterBuilder.js +122 -0
  147. package/datastore/RecordFilterBuilder.js.map +1 -0
  148. package/datastore/RecordFormPanel.d.ts +45 -0
  149. package/datastore/RecordFormPanel.d.ts.map +1 -0
  150. package/datastore/RecordFormPanel.js +215 -0
  151. package/datastore/RecordFormPanel.js.map +1 -0
  152. package/datastore/index.d.ts +43 -0
  153. package/datastore/index.d.ts.map +1 -0
  154. package/datastore/index.js +22 -0
  155. package/datastore/index.js.map +1 -0
  156. package/datastore/recordFilter.d.ts +54 -0
  157. package/datastore/recordFilter.d.ts.map +1 -0
  158. package/datastore/recordFilter.js +93 -0
  159. package/datastore/recordFilter.js.map +1 -0
  160. package/datastore/recordValues.d.ts +85 -0
  161. package/datastore/recordValues.d.ts.map +1 -0
  162. package/datastore/recordValues.js +282 -0
  163. package/datastore/recordValues.js.map +1 -0
  164. package/datastore/useDatastore.d.ts +39 -0
  165. package/datastore/useDatastore.d.ts.map +1 -0
  166. package/datastore/useDatastore.js +44 -0
  167. package/datastore/useDatastore.js.map +1 -0
  168. package/datastore/useDatastoreCount.d.ts +48 -0
  169. package/datastore/useDatastoreCount.d.ts.map +1 -0
  170. package/datastore/useDatastoreCount.js +27 -0
  171. package/datastore/useDatastoreCount.js.map +1 -0
  172. package/datastore/useDatastoreDescription.d.ts +54 -0
  173. package/datastore/useDatastoreDescription.d.ts.map +1 -0
  174. package/datastore/useDatastoreDescription.js +50 -0
  175. package/datastore/useDatastoreDescription.js.map +1 -0
  176. package/datastore/useDatastoreList.d.ts +57 -0
  177. package/datastore/useDatastoreList.d.ts.map +1 -0
  178. package/datastore/useDatastoreList.js +29 -0
  179. package/datastore/useDatastoreList.js.map +1 -0
  180. package/datastore/useDatastoreSearch.d.ts +19 -0
  181. package/datastore/useDatastoreSearch.d.ts.map +1 -0
  182. package/datastore/useDatastoreSearch.js +22 -0
  183. package/datastore/useDatastoreSearch.js.map +1 -0
  184. package/datastore/useDeleteDatastore.d.ts +53 -0
  185. package/datastore/useDeleteDatastore.d.ts.map +1 -0
  186. package/datastore/useDeleteDatastore.js +40 -0
  187. package/datastore/useDeleteDatastore.js.map +1 -0
  188. package/datastore/useDeleteRecord.d.ts +34 -0
  189. package/datastore/useDeleteRecord.d.ts.map +1 -0
  190. package/datastore/useDeleteRecord.js +43 -0
  191. package/datastore/useDeleteRecord.js.map +1 -0
  192. package/datastore/useInsertRecord.d.ts +50 -0
  193. package/datastore/useInsertRecord.d.ts.map +1 -0
  194. package/datastore/useInsertRecord.js +55 -0
  195. package/datastore/useInsertRecord.js.map +1 -0
  196. package/datastore/useRecordCollection.d.ts +104 -0
  197. package/datastore/useRecordCollection.d.ts.map +1 -0
  198. package/datastore/useRecordCollection.js +136 -0
  199. package/datastore/useRecordCollection.js.map +1 -0
  200. package/datastore/useRecordList.d.ts +66 -0
  201. package/datastore/useRecordList.d.ts.map +1 -0
  202. package/datastore/useRecordList.js +58 -0
  203. package/datastore/useRecordList.js.map +1 -0
  204. package/datastore/useUpdateRecord.d.ts +52 -0
  205. package/datastore/useUpdateRecord.d.ts.map +1 -0
  206. package/datastore/useUpdateRecord.js +55 -0
  207. package/datastore/useUpdateRecord.js.map +1 -0
  208. package/environment/EnvironmentListPanel.d.ts.map +1 -1
  209. package/environment/EnvironmentListPanel.js +12 -7
  210. package/environment/EnvironmentListPanel.js.map +1 -1
  211. package/iam-policy/index.d.ts +1 -1
  212. package/iam-policy/index.d.ts.map +1 -1
  213. package/iam-policy/index.js.map +1 -1
  214. package/iam-policy/useCheckPermission.d.ts +32 -11
  215. package/iam-policy/useCheckPermission.d.ts.map +1 -1
  216. package/iam-policy/useCheckPermission.js +24 -19
  217. package/iam-policy/useCheckPermission.js.map +1 -1
  218. package/index.d.ts +12 -6
  219. package/index.d.ts.map +1 -1
  220. package/index.js +14 -3
  221. package/index.js.map +1 -1
  222. package/library/index.d.ts +0 -7
  223. package/library/index.d.ts.map +1 -1
  224. package/library/index.js +0 -4
  225. package/library/index.js.map +1 -1
  226. package/library/useApplyResource.d.ts +12 -12
  227. package/library/useApplyResource.d.ts.map +1 -1
  228. package/library/useApplyResource.js +16 -25
  229. package/library/useApplyResource.js.map +1 -1
  230. package/library/useExportResource.d.ts +13 -9
  231. package/library/useExportResource.d.ts.map +1 -1
  232. package/library/useExportResource.js +18 -36
  233. package/library/useExportResource.js.map +1 -1
  234. package/manifest/ApplyManifestDialog.d.ts +48 -0
  235. package/manifest/ApplyManifestDialog.d.ts.map +1 -0
  236. package/manifest/ApplyManifestDialog.js +135 -0
  237. package/manifest/ApplyManifestDialog.js.map +1 -0
  238. package/manifest/EditResourceYamlDialog.d.ts +51 -0
  239. package/manifest/EditResourceYamlDialog.d.ts.map +1 -0
  240. package/manifest/EditResourceYamlDialog.js +104 -0
  241. package/manifest/EditResourceYamlDialog.js.map +1 -0
  242. package/manifest/RedactedSecretsNotice.d.ts +14 -0
  243. package/manifest/RedactedSecretsNotice.d.ts.map +1 -0
  244. package/manifest/RedactedSecretsNotice.js +14 -0
  245. package/manifest/RedactedSecretsNotice.js.map +1 -0
  246. package/manifest/YamlEditor.d.ts +44 -0
  247. package/manifest/YamlEditor.d.ts.map +1 -0
  248. package/manifest/YamlEditor.js +216 -0
  249. package/manifest/YamlEditor.js.map +1 -0
  250. package/manifest/index.d.ts +13 -0
  251. package/manifest/index.d.ts.map +1 -0
  252. package/manifest/index.js +13 -0
  253. package/manifest/index.js.map +1 -0
  254. package/manifest/useApplyManifest.d.ts +82 -0
  255. package/manifest/useApplyManifest.d.ts.map +1 -0
  256. package/manifest/useApplyManifest.js +180 -0
  257. package/manifest/useApplyManifest.js.map +1 -0
  258. package/manifest/useEditResourceYaml.d.ts +106 -0
  259. package/manifest/useEditResourceYaml.d.ts.map +1 -0
  260. package/manifest/useEditResourceYaml.js +168 -0
  261. package/manifest/useEditResourceYaml.js.map +1 -0
  262. package/mcp-server/steps/ReviewStep.d.ts.map +1 -1
  263. package/mcp-server/steps/ReviewStep.js +2 -3
  264. package/mcp-server/steps/ReviewStep.js.map +1 -1
  265. package/package.json +4 -4
  266. package/resource-creation/templates/workflow-templates.js +4 -4
  267. package/session/useEditSessionPrep.js +3 -3
  268. package/session/useEditSessionPrep.js.map +1 -1
  269. package/settings/index.d.ts +2 -1
  270. package/settings/index.d.ts.map +1 -1
  271. package/settings/index.js +2 -1
  272. package/settings/index.js.map +1 -1
  273. package/settings/settings-nav.d.ts +22 -0
  274. package/settings/settings-nav.d.ts.map +1 -1
  275. package/settings/settings-nav.js +32 -1
  276. package/settings/settings-nav.js.map +1 -1
  277. package/settings/useSettingsNavGroups.d.ts +29 -0
  278. package/settings/useSettingsNavGroups.d.ts.map +1 -0
  279. package/settings/useSettingsNavGroups.js +61 -0
  280. package/settings/useSettingsNavGroups.js.map +1 -0
  281. package/src/agent/steps/ReviewStep.tsx +2 -3
  282. package/src/billing/BaselineEditor.tsx +619 -0
  283. package/src/billing/ModelCatalogPanel.tsx +283 -0
  284. package/src/billing/ModelGovernanceDetail.tsx +289 -0
  285. package/src/billing/OperatorAccessNotice.tsx +32 -0
  286. package/src/billing/PricingGovernanceConsole.tsx +465 -0
  287. package/src/billing/PricingGovernancePanel.tsx +166 -0
  288. package/src/billing/RetireConfirm.tsx +82 -0
  289. package/src/billing/__tests__/ModelCatalogPanel.test.tsx +253 -0
  290. package/src/billing/__tests__/PricingGovernanceConsole.test.tsx +410 -0
  291. package/src/billing/__tests__/useModelGovernanceView.test.tsx +308 -0
  292. package/src/billing/form-primitives.tsx +68 -0
  293. package/src/billing/governance-primitives.tsx +133 -0
  294. package/src/billing/index.ts +37 -0
  295. package/src/billing/pricing-format.ts +53 -0
  296. package/src/billing/useDecidePricingOverride.ts +78 -0
  297. package/src/billing/useModelGovernanceView.ts +290 -0
  298. package/src/billing/useModelPricingBaselines.ts +69 -0
  299. package/src/billing/usePricingGovernance.ts +57 -0
  300. package/src/billing/useRetireModelPricingBaseline.ts +65 -0
  301. package/src/billing/useUpsertModelPricingBaseline.ts +72 -0
  302. package/src/channel/AgentChannelsPanel.tsx +27 -1
  303. package/src/composer/SessionComposer.tsx +1 -1
  304. package/src/cursor-accounts/CursorAccountEditor.tsx +181 -0
  305. package/src/cursor-accounts/CursorAccountsAccessNotice.tsx +36 -0
  306. package/src/cursor-accounts/CursorAccountsConsole.tsx +810 -0
  307. package/src/cursor-accounts/__tests__/CursorAccountsConsole.test.tsx +384 -0
  308. package/src/cursor-accounts/cursor-account-format.ts +29 -0
  309. package/src/cursor-accounts/index.ts +29 -0
  310. package/src/cursor-accounts/useCursorAccountView.ts +49 -0
  311. package/src/cursor-accounts/useCursorAccounts.ts +49 -0
  312. package/src/cursor-accounts/useCursorMemberKeyActions.ts +106 -0
  313. package/src/cursor-accounts/useDeleteCursorAccount.ts +62 -0
  314. package/src/cursor-accounts/useSyncCursorAccount.ts +54 -0
  315. package/src/cursor-accounts/useUpsertCursorAccount.ts +57 -0
  316. package/src/datastore/CollectionRecordsBrowser.tsx +544 -0
  317. package/src/datastore/CollectionSchemaView.tsx +247 -0
  318. package/src/datastore/DatastoreDetailView.tsx +201 -0
  319. package/src/datastore/DatastoreSyncReport.tsx +105 -0
  320. package/src/datastore/DeleteDatastoreDialog.tsx +202 -0
  321. package/src/datastore/FieldValueControl.tsx +289 -0
  322. package/src/datastore/RecordFilterBuilder.tsx +411 -0
  323. package/src/datastore/RecordFormPanel.tsx +432 -0
  324. package/src/datastore/__tests__/CollectionRecordsBrowser.test.tsx +207 -0
  325. package/src/datastore/__tests__/DeleteDatastoreDialog.test.tsx +133 -0
  326. package/src/datastore/__tests__/FieldValueControl.test.tsx +160 -0
  327. package/src/datastore/__tests__/RecordFilterBuilder.test.tsx +141 -0
  328. package/src/datastore/__tests__/RecordFormPanel.test.tsx +227 -0
  329. package/src/datastore/__tests__/recordFilter.test.ts +156 -0
  330. package/src/datastore/__tests__/recordMutations.test.tsx +195 -0
  331. package/src/datastore/__tests__/recordValues.test.ts +359 -0
  332. package/src/datastore/__tests__/schemaAndSyncViews.test.tsx +142 -0
  333. package/src/datastore/__tests__/useDatastoreDescription.test.tsx +143 -0
  334. package/src/datastore/__tests__/useRecordCollection.test.tsx +250 -0
  335. package/src/datastore/__tests__/useRecordList.test.tsx +85 -0
  336. package/src/datastore/index.ts +111 -0
  337. package/src/datastore/recordFilter.ts +140 -0
  338. package/src/datastore/recordValues.ts +327 -0
  339. package/src/datastore/useDatastore.ts +67 -0
  340. package/src/datastore/useDatastoreCount.ts +67 -0
  341. package/src/datastore/useDatastoreDescription.ts +90 -0
  342. package/src/datastore/useDatastoreList.ts +79 -0
  343. package/src/datastore/useDatastoreSearch.ts +39 -0
  344. package/src/datastore/useDeleteDatastore.ts +87 -0
  345. package/src/datastore/useDeleteRecord.ts +77 -0
  346. package/src/datastore/useInsertRecord.ts +92 -0
  347. package/src/datastore/useRecordCollection.ts +256 -0
  348. package/src/datastore/useRecordList.ts +131 -0
  349. package/src/datastore/useUpdateRecord.ts +95 -0
  350. package/src/environment/EnvironmentListPanel.tsx +24 -0
  351. package/src/execution/__tests__/useArtifactInspection.test.tsx +12 -2
  352. package/src/iam-policy/__tests__/useCheckPermission.test.tsx +221 -0
  353. package/src/iam-policy/index.ts +1 -0
  354. package/src/iam-policy/useCheckPermission.ts +45 -19
  355. package/src/index.ts +163 -13
  356. package/src/library/index.ts +0 -20
  357. package/src/library/useApplyResource.ts +27 -34
  358. package/src/library/useExportResource.ts +21 -40
  359. package/src/manifest/ApplyManifestDialog.tsx +315 -0
  360. package/src/manifest/EditResourceYamlDialog.tsx +265 -0
  361. package/src/manifest/RedactedSecretsNotice.tsx +30 -0
  362. package/src/manifest/YamlEditor.tsx +272 -0
  363. package/src/manifest/__tests__/EditResourceYamlDialog.test.tsx +118 -0
  364. package/src/manifest/__tests__/useApplyManifest.test.tsx +237 -0
  365. package/src/manifest/__tests__/useEditResourceYaml.test.tsx +252 -0
  366. package/src/manifest/index.ts +33 -0
  367. package/src/manifest/useApplyManifest.ts +270 -0
  368. package/src/manifest/useEditResourceYaml.ts +273 -0
  369. package/src/mcp-server/steps/ReviewStep.tsx +2 -3
  370. package/src/resource-creation/templates/workflow-templates.ts +4 -4
  371. package/src/session/useEditSessionPrep.ts +3 -3
  372. package/src/settings/__tests__/useSettingsNavGroups.test.tsx +138 -0
  373. package/src/settings/index.ts +5 -1
  374. package/src/settings/settings-nav.ts +43 -0
  375. package/src/settings/useSettingsNavGroups.ts +81 -0
  376. package/src/workflow/WorkflowYamlEditor.tsx +6 -270
  377. package/styles.css +1 -1
  378. package/workflow/CanvasTransitionEdge.d.ts +1 -1
  379. package/workflow/WorkflowYamlEditor.d.ts +2 -42
  380. package/workflow/WorkflowYamlEditor.d.ts.map +1 -1
  381. package/workflow/WorkflowYamlEditor.js +5 -214
  382. package/workflow/WorkflowYamlEditor.js.map +1 -1
  383. package/library/ImportResourceDialog.d.ts +0 -43
  384. package/library/ImportResourceDialog.d.ts.map +0 -1
  385. package/library/ImportResourceDialog.js +0 -90
  386. package/library/ImportResourceDialog.js.map +0 -1
  387. package/library/parse-resource-yaml.d.ts +0 -63
  388. package/library/parse-resource-yaml.d.ts.map +0 -1
  389. package/library/parse-resource-yaml.js +0 -407
  390. package/library/parse-resource-yaml.js.map +0 -1
  391. package/library/serialize-resource-yaml.d.ts +0 -103
  392. package/library/serialize-resource-yaml.d.ts.map +0 -1
  393. package/library/serialize-resource-yaml.js +0 -565
  394. package/library/serialize-resource-yaml.js.map +0 -1
  395. package/library/useImportResource.d.ts +0 -84
  396. package/library/useImportResource.d.ts.map +0 -1
  397. package/library/useImportResource.js +0 -188
  398. package/library/useImportResource.js.map +0 -1
  399. package/src/library/ImportResourceDialog.tsx +0 -227
  400. package/src/library/parse-resource-yaml.ts +0 -630
  401. package/src/library/serialize-resource-yaml.ts +0 -756
  402. package/src/library/useImportResource.ts +0 -253
@@ -0,0 +1,273 @@
1
+ "use client";
2
+
3
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
+ import type { Message } from "@bufbuild/protobuf";
5
+ import {
6
+ containsRedactedSecrets,
7
+ parseManifest,
8
+ serializeManifest,
9
+ type AppliedManifest,
10
+ type ManifestDocument,
11
+ } from "@stigmer/sdk";
12
+ import { useStigmer } from "../hooks.js";
13
+ import { toError } from "../internal/toError.js";
14
+
15
+ /**
16
+ * Result of validating the edited YAML against the source resource.
17
+ *
18
+ * Discriminated on `status` so consumers can render exactly one of:
19
+ * a disabled state (`empty`), an inline error (`invalid`), or the
20
+ * apply affordance (`valid`).
21
+ */
22
+ export type EditYamlValidation =
23
+ | { readonly status: "empty" }
24
+ | { readonly status: "invalid"; readonly message: string }
25
+ | { readonly status: "valid"; readonly document: ManifestDocument };
26
+
27
+ /**
28
+ * What applying the current content will do — resolved against live
29
+ * server state so renames surface as "create" before the user commits.
30
+ */
31
+ export type EditYamlTarget =
32
+ | { readonly action: "update"; readonly slug: string }
33
+ | { readonly action: "create"; readonly slug: string }
34
+ /** Existence lookup failed (offline, permissions) — wording degrades. */
35
+ | { readonly action: "unknown"; readonly slug: string };
36
+
37
+ /** Options for {@link useEditResourceYaml}. */
38
+ export interface UseEditResourceYamlOptions {
39
+ /**
40
+ * The resource to edit, as returned by the server (e.g. from `useAgent`
41
+ * or a list panel row). `null` while the resource is loading — the hook
42
+ * seeds the editor when it arrives.
43
+ */
44
+ readonly resource: Message | null;
45
+ }
46
+
47
+ /** Return value of {@link useEditResourceYaml}. */
48
+ export interface UseEditResourceYamlReturn {
49
+ /** Current editor content. */
50
+ readonly yaml: string;
51
+ /** Update the editor content (wire to the YAML editor's `onChange`). */
52
+ readonly setYaml: (value: string) => void;
53
+ /** `true` when the content differs from the seeded serialization. */
54
+ readonly isDirty: boolean;
55
+ /** Live validation of the current content. */
56
+ readonly validation: EditYamlValidation;
57
+ /**
58
+ * Create-vs-update preview for the current content, or `null` while
59
+ * validation fails or the existence check is in flight.
60
+ */
61
+ readonly target: EditYamlTarget | null;
62
+ /** `true` when the content contains `***REDACTED***` secret markers. */
63
+ readonly hasRedactedSecrets: boolean;
64
+ /**
65
+ * Apply the edited YAML. Resolves with the applied resource, or `null`
66
+ * when the content is not currently valid (check `validation`).
67
+ */
68
+ readonly apply: () => Promise<AppliedManifest | null>;
69
+ /** `true` while the apply RPC is in flight. */
70
+ readonly isApplying: boolean;
71
+ /** Error from the last failed apply, or `null` when healthy. */
72
+ readonly error: Error | null;
73
+ /** Clear the apply error (e.g. when the user edits again). */
74
+ readonly clearError: () => void;
75
+ /** Discard edits and reseed from the resource. */
76
+ readonly reset: () => void;
77
+ }
78
+
79
+ // Existence checks run against live server state; wait for a typing pause.
80
+ const TARGET_CHECK_DEBOUNCE_MS = 400;
81
+
82
+ /**
83
+ * Behavior hook for editing an existing resource as YAML and applying the
84
+ * result — the console's kind-agnostic "Edit YAML" flow.
85
+ *
86
+ * Seeds the editor from {@link serializeManifest} (full fidelity minus
87
+ * system-managed state), validates every edit against the kind's generated
88
+ * proto schema via {@link parseManifest} (strict — unknown fields fail
89
+ * loudly), previews create-vs-update by resolving the edited document's
90
+ * reference against the server, and applies through
91
+ * `stigmer.manifest.apply()`.
92
+ *
93
+ * Guardrails beyond the raw engine:
94
+ * - the edited document must stay the same kind as the source resource
95
+ * (pasting an Environment into an Agent editor is an error, not an apply)
96
+ * - the edited manifest must contain exactly one document
97
+ *
98
+ * @example
99
+ * ```tsx
100
+ * const edit = useEditResourceYaml({ resource: agent });
101
+ *
102
+ * <YamlEditor value={edit.yaml} onChange={edit.setYaml} />
103
+ * {edit.validation.status === "invalid" && <p>{edit.validation.message}</p>}
104
+ * <button disabled={edit.validation.status !== "valid"} onClick={edit.apply}>
105
+ * {edit.target?.action === "create" ? "Create" : "Apply changes"}
106
+ * </button>
107
+ * ```
108
+ *
109
+ * @see {@link EditResourceYamlDialog} for the styled component composing this hook
110
+ * @see {@link useApplyManifest} for the paste/upload counterpart
111
+ */
112
+ export function useEditResourceYaml({
113
+ resource,
114
+ }: UseEditResourceYamlOptions): UseEditResourceYamlReturn {
115
+ const stigmer = useStigmer();
116
+
117
+ // The serialized source of truth. Recomputed when the resource changes;
118
+ // only reseeds the editor while the user hasn't started editing.
119
+ const seeded = useMemo(
120
+ () => (resource ? serializeManifest(resource) : ""),
121
+ [resource],
122
+ );
123
+
124
+ const [yaml, setYamlState] = useState(seeded);
125
+ const [isDirty, setIsDirty] = useState(false);
126
+ const [isApplying, setIsApplying] = useState(false);
127
+ const [error, setError] = useState<Error | null>(null);
128
+ const [target, setTarget] = useState<EditYamlTarget | null>(null);
129
+
130
+ useEffect(() => {
131
+ if (!isDirty) setYamlState(seeded);
132
+ }, [seeded, isDirty]);
133
+
134
+ const setYaml = useCallback(
135
+ (value: string) => {
136
+ setYamlState(value);
137
+ setIsDirty(value !== seeded);
138
+ },
139
+ [seeded],
140
+ );
141
+
142
+ const reset = useCallback(() => {
143
+ setYamlState(seeded);
144
+ setIsDirty(false);
145
+ setError(null);
146
+ }, [seeded]);
147
+
148
+ const clearError = useCallback(() => setError(null), []);
149
+
150
+ const sourceKind = useMemo(() => {
151
+ if (!resource) return null;
152
+ // The seeded YAML's kind is authoritative for the source resource; a
153
+ // parse of our own serialization cannot fail.
154
+ return seeded ? parseManifest(seeded)[0].handler.yamlKind : null;
155
+ }, [resource, seeded]);
156
+
157
+ const validation = useMemo<EditYamlValidation>(() => {
158
+ if (!yaml.trim()) return { status: "empty" };
159
+
160
+ let documents: ManifestDocument[];
161
+ try {
162
+ documents = parseManifest(yaml);
163
+ } catch (err) {
164
+ return { status: "invalid", message: toError(err).message };
165
+ }
166
+
167
+ if (documents.length > 1) {
168
+ return {
169
+ status: "invalid",
170
+ message:
171
+ "This editor applies a single resource. Remove the extra " +
172
+ "documents, or use Apply YAML in the Library for multi-document manifests.",
173
+ };
174
+ }
175
+
176
+ const document = documents[0];
177
+ if (sourceKind !== null && document.handler.yamlKind !== sourceKind) {
178
+ return {
179
+ status: "invalid",
180
+ message:
181
+ `The document's kind changed from ${sourceKind} to ` +
182
+ `${document.handler.yamlKind}. To apply a different resource kind, ` +
183
+ "use Apply YAML in the Library instead.",
184
+ };
185
+ }
186
+
187
+ return { status: "valid", document };
188
+ }, [yaml, sourceKind]);
189
+
190
+ // Resolve create-vs-update against live state whenever the content
191
+ // becomes valid, debounced across keystrokes and cancellation-safe.
192
+ const checkSeq = useRef(0);
193
+ useEffect(() => {
194
+ if (validation.status !== "valid") {
195
+ setTarget(null);
196
+ return;
197
+ }
198
+
199
+ const { document } = validation;
200
+ const seq = ++checkSeq.current;
201
+ setTarget(null);
202
+
203
+ const timer = setTimeout(async () => {
204
+ let action: EditYamlTarget["action"];
205
+ try {
206
+ const existing = await stigmer.manifest.getByReference(
207
+ document.handler.yamlKind,
208
+ document.org,
209
+ document.slug,
210
+ );
211
+ action = existing !== null ? "update" : "create";
212
+ } catch {
213
+ action = "unknown";
214
+ }
215
+ if (checkSeq.current === seq) {
216
+ setTarget({ action, slug: document.slug });
217
+ }
218
+ }, TARGET_CHECK_DEBOUNCE_MS);
219
+
220
+ return () => clearTimeout(timer);
221
+ }, [validation, stigmer]);
222
+
223
+ const apply = useCallback(async (): Promise<AppliedManifest | null> => {
224
+ if (validation.status !== "valid") return null;
225
+
226
+ setIsApplying(true);
227
+ setError(null);
228
+ try {
229
+ const applied = await stigmer.manifest.apply(validation.document);
230
+ setIsDirty(false);
231
+ return applied;
232
+ } catch (err) {
233
+ setError(toError(err));
234
+ throw err;
235
+ } finally {
236
+ setIsApplying(false);
237
+ }
238
+ }, [validation, stigmer]);
239
+
240
+ const hasRedactedSecrets = useMemo(
241
+ () => containsRedactedSecrets(yaml),
242
+ [yaml],
243
+ );
244
+
245
+ return useMemo(
246
+ () => ({
247
+ yaml,
248
+ setYaml,
249
+ isDirty,
250
+ validation,
251
+ target,
252
+ hasRedactedSecrets,
253
+ apply,
254
+ isApplying,
255
+ error,
256
+ clearError,
257
+ reset,
258
+ }),
259
+ [
260
+ yaml,
261
+ setYaml,
262
+ isDirty,
263
+ validation,
264
+ target,
265
+ hasRedactedSecrets,
266
+ apply,
267
+ isApplying,
268
+ error,
269
+ clearError,
270
+ reset,
271
+ ],
272
+ );
273
+ }
@@ -2,9 +2,8 @@
2
2
 
3
3
  import { useMemo } from "react";
4
4
  import { cn } from "@stigmer/theme";
5
- import { getUserMessage } from "@stigmer/sdk";
5
+ import { buildMcpServerProto, getUserMessage, serializeManifest } from "@stigmer/sdk";
6
6
  import type { McpServerInput } from "@stigmer/sdk";
7
- import { serializeMcpServerInputYaml } from "../../library/serialize-resource-yaml.js";
8
7
  import type { McpServerWizardData } from "./types.js";
9
8
 
10
9
  /** Props for {@link ReviewStep}. */
@@ -33,7 +32,7 @@ export function ReviewStep({
33
32
  [org, data],
34
33
  );
35
34
  const yamlPreview = useMemo(
36
- () => serializeMcpServerInputYaml(mcpServerInput),
35
+ () => serializeManifest(buildMcpServerProto(mcpServerInput)),
37
36
  [mcpServerInput],
38
37
  );
39
38
 
@@ -122,7 +122,7 @@ spec:
122
122
  - name: extract_findings
123
123
  kind: llm_call
124
124
  task_config:
125
- model: "gpt-4o-mini"
125
+ model: "claude-haiku-4.5"
126
126
  system_prompt: "Extract structured key findings from the analysis."
127
127
  prompt: >
128
128
  Extract the key findings from this analysis as structured data:
@@ -280,7 +280,7 @@ spec:
280
280
  - name: classify_ticket
281
281
  kind: llm_call
282
282
  task_config:
283
- model: "gpt-4o-mini"
283
+ model: "claude-haiku-4.5"
284
284
  system_prompt: >
285
285
  You are a support ticket classifier. Analyze the ticket and classify
286
286
  it by severity and category. Be conservative with critical/high
@@ -923,7 +923,7 @@ spec:
923
923
  - name: diagnose_error
924
924
  kind: llm_call
925
925
  task_config:
926
- model: "gpt-4o-mini"
926
+ model: "claude-haiku-4.5"
927
927
  system_prompt: >
928
928
  You are an API integration specialist. Given an error from
929
929
  an HTTP call, provide a brief, actionable diagnosis.
@@ -1010,7 +1010,7 @@ spec:
1010
1010
  description: "Comma-separated list of topics the response should cover"
1011
1011
  optional: true
1012
1012
  MODEL_UNDER_TEST:
1013
- description: "Model to evaluate (default: gpt-4o-mini)"
1013
+ description: "Model to evaluate (default: claude-haiku-4.5)"
1014
1014
  optional: true
1015
1015
  budget:
1016
1016
  max_cost_micros: 2000000
@@ -7,7 +7,7 @@ import { useStigmer } from "../hooks.js";
7
7
  import { useAgent } from "../agent/index.js";
8
8
  import { useMcpServer } from "../mcp-server/index.js";
9
9
  import { useSkill } from "../skill/index.js";
10
- import { serializeAgentYaml, serializeMcpServerYaml } from "../library/index.js";
10
+ import { serializeManifest } from "@stigmer/sdk";
11
11
  import type { DraftResourceType } from "./draft.js";
12
12
 
13
13
  /** Return value of {@link useEditSessionPrep}. */
@@ -64,7 +64,7 @@ export function useEditSessionPrep(
64
64
  if (draftType === "agent" && editAgent) {
65
65
  built.current = true;
66
66
  try {
67
- const yaml = serializeAgentYaml(editAgent);
67
+ const yaml = serializeManifest(editAgent);
68
68
  const slug = editAgent.metadata?.slug ?? "agent";
69
69
  setFiles([new File([yaml], `${slug}.yaml`, { type: "text/yaml" })]);
70
70
  } catch {
@@ -75,7 +75,7 @@ export function useEditSessionPrep(
75
75
  if (draftType === "mcp-server" && editMcpServer) {
76
76
  built.current = true;
77
77
  try {
78
- const yaml = serializeMcpServerYaml(editMcpServer);
78
+ const yaml = serializeManifest(editMcpServer);
79
79
  const slug = editMcpServer.metadata?.slug ?? "mcp-server";
80
80
  setFiles([new File([yaml], `${slug}.yaml`, { type: "text/yaml" })]);
81
81
  } catch {
@@ -0,0 +1,138 @@
1
+ import { describe, it, expect, vi, afterEach } from "vitest";
2
+ import { cleanup, fireEvent, render, screen } from "@testing-library/react";
3
+ import { useRef, useState } from "react";
4
+ import { SETTINGS_NAV_GROUPS, PLATFORM_SETTINGS_NAV_GROUP } from "../settings-nav";
5
+ import { useSettingsNavGroups } from "../useSettingsNavGroups";
6
+
7
+ // Drive the nav gate through the permission hook. The fail-closed
8
+ // semantics themselves are covered by useCheckPermission's own suite —
9
+ // here we assert the nav hook's per-item composition of the verdicts.
10
+ // Verdicts are keyed by relation so each platform permission can be
11
+ // granted independently.
12
+ let verdicts: Record<string, boolean> = {};
13
+ let checkedRelations: string[] = [];
14
+ let lastCheckArgs: readonly unknown[] = [];
15
+
16
+ vi.mock("../../iam-policy/useCheckPermission", () => ({
17
+ useCheckPermission: (
18
+ ...args: [unknown, string, { fail?: string } | undefined]
19
+ ) => {
20
+ lastCheckArgs = args;
21
+ checkedRelations.push(args[1]);
22
+ return { allowed: verdicts[args[1]] === true, isLoading: false, error: null };
23
+ },
24
+ }));
25
+
26
+ afterEach(() => {
27
+ cleanup();
28
+ verdicts = {};
29
+ checkedRelations = [];
30
+ });
31
+
32
+ function GroupsProbe() {
33
+ const groups = useSettingsNavGroups();
34
+ const platform = groups.find((g) => g.label === PLATFORM_SETTINGS_NAV_GROUP.label);
35
+ return (
36
+ <div
37
+ data-testid="groups"
38
+ data-labels={groups.map((g) => g.label).join(",")}
39
+ data-platform-items={platform?.items.map((i) => i.label).join(",") ?? ""}
40
+ />
41
+ );
42
+ }
43
+
44
+ const BASE_LABELS = SETTINGS_NAV_GROUPS.map((g) => g.label).join(",");
45
+
46
+ describe("useSettingsNavGroups", () => {
47
+ it("appends the full Platform group when the operator holds every platform permission", () => {
48
+ verdicts = {
49
+ can_manage_model_pricing: true,
50
+ can_manage_cursor_accounts: true,
51
+ };
52
+ render(<GroupsProbe />);
53
+
54
+ const el = screen.getByTestId("groups");
55
+ expect(el.getAttribute("data-labels")).toBe(
56
+ `${BASE_LABELS},${PLATFORM_SETTINGS_NAV_GROUP.label}`,
57
+ );
58
+ expect(el.getAttribute("data-platform-items")).toBe(
59
+ "Pricing Governance,Cursor Accounts",
60
+ );
61
+ });
62
+
63
+ it("filters per item: pricing-only operator sees only Pricing Governance", () => {
64
+ verdicts = { can_manage_model_pricing: true };
65
+ render(<GroupsProbe />);
66
+
67
+ expect(screen.getByTestId("groups").getAttribute("data-platform-items")).toBe(
68
+ "Pricing Governance",
69
+ );
70
+ });
71
+
72
+ it("filters per item: cursor-accounts-only operator sees only Cursor Accounts", () => {
73
+ verdicts = { can_manage_cursor_accounts: true };
74
+ render(<GroupsProbe />);
75
+
76
+ expect(screen.getByTestId("groups").getAttribute("data-platform-items")).toBe(
77
+ "Cursor Accounts",
78
+ );
79
+ });
80
+
81
+ it("returns exactly the base groups when not authorized", () => {
82
+ function IdentityProbe() {
83
+ const groups = useSettingsNavGroups();
84
+ return (
85
+ <div
86
+ data-testid="identity"
87
+ data-is-base={String(groups === SETTINGS_NAV_GROUPS)}
88
+ />
89
+ );
90
+ }
91
+ render(<IdentityProbe />);
92
+
93
+ // Not just equivalent — the same array, no needless copy.
94
+ expect(screen.getByTestId("identity").getAttribute("data-is-base")).toBe("true");
95
+ });
96
+
97
+ it("checks every declared platform permission on platform:stigmer, fail-closed", () => {
98
+ render(<GroupsProbe />);
99
+
100
+ // Exhaustiveness guard: every requiredPermission declared on the
101
+ // platform group must have a corresponding hook check — a new
102
+ // platform surface that forgets to add its check fails here.
103
+ const declared = PLATFORM_SETTINGS_NAV_GROUP.items.map(
104
+ (item) => item.requiredPermission,
105
+ );
106
+ for (const permission of declared) {
107
+ expect(permission, "platform items must declare requiredPermission").toBeDefined();
108
+ expect(checkedRelations).toContain(permission);
109
+ }
110
+ expect(lastCheckArgs[0]).toEqual({ kind: "platform", id: "stigmer" });
111
+ expect(lastCheckArgs[2]).toEqual({ fail: "closed" });
112
+ });
113
+
114
+ it("keeps the array reference stable across re-renders", () => {
115
+ verdicts = {
116
+ can_manage_model_pricing: true,
117
+ can_manage_cursor_accounts: true,
118
+ };
119
+
120
+ function StabilityProbe() {
121
+ const groups = useSettingsNavGroups();
122
+ const first = useRef(groups);
123
+ const [, force] = useState(0);
124
+ return (
125
+ <button
126
+ data-testid="stability"
127
+ data-stable={String(first.current === groups)}
128
+ onClick={() => force((n) => n + 1)}
129
+ />
130
+ );
131
+ }
132
+
133
+ render(<StabilityProbe />);
134
+ const el = screen.getByTestId("stability");
135
+ fireEvent.click(el);
136
+ expect(el.getAttribute("data-stable")).toBe("true");
137
+ });
138
+ });
@@ -1,5 +1,9 @@
1
- export { SETTINGS_NAV_GROUPS } from "./settings-nav.js";
1
+ export {
2
+ SETTINGS_NAV_GROUPS,
3
+ PLATFORM_SETTINGS_NAV_GROUP,
4
+ } from "./settings-nav.js";
2
5
  export type { SettingsNavItem, SettingsNavGroup } from "./settings-nav.js";
6
+ export { useSettingsNavGroups } from "./useSettingsNavGroups.js";
3
7
 
4
8
  export { ApiKeysSection } from "./ApiKeysSection.js";
5
9
  export { BillingSection } from "./BillingSection.js";
@@ -8,7 +8,9 @@ import {
8
8
  KeyRound,
9
9
  Link,
10
10
  MessageSquare,
11
+ MousePointerClick,
11
12
  Plug,
13
+ Scale,
12
14
  ShieldCheck,
13
15
  Users,
14
16
  } from "lucide-react";
@@ -24,6 +26,14 @@ export interface SettingsNavItem {
24
26
  /** Optional CSS class name applied to the icon. */
25
27
  className?: string;
26
28
  }>;
29
+ /**
30
+ * Platform-level permission (on `platform:stigmer`) required for this
31
+ * entry to appear. Only meaningful inside
32
+ * {@link PLATFORM_SETTINGS_NAV_GROUP} — items in the base groups are
33
+ * visible to every signed-in user and leave this unset.
34
+ * {@link useSettingsNavGroups} checks it fail-closed.
35
+ */
36
+ readonly requiredPermission?: string;
27
37
  }
28
38
 
29
39
  /** Grouping model used to render settings navigation sections. */
@@ -82,3 +92,36 @@ export const SETTINGS_NAV_GROUPS: readonly SettingsNavGroup[] = [
82
92
  ],
83
93
  },
84
94
  ];
95
+
96
+ /**
97
+ * Stigmer-internal platform-operator navigation group.
98
+ *
99
+ * Deliberately NOT part of {@link SETTINGS_NAV_GROUPS}: that constant
100
+ * means "groups every signed-in user sees", and its members must never
101
+ * change based on who is looking. This group is appended for platform
102
+ * operators only — use {@link useSettingsNavGroups} to get the
103
+ * permission-aware list instead of composing the two by hand.
104
+ *
105
+ * Every item here declares its `requiredPermission`; the hook filters
106
+ * per item (fail-closed), so an operator holding one platform permission
107
+ * but not another sees exactly their slice of this group.
108
+ */
109
+ export const PLATFORM_SETTINGS_NAV_GROUP: SettingsNavGroup = {
110
+ label: "Platform",
111
+ description:
112
+ "Stigmer-internal operations. Visible to platform operators only.",
113
+ items: [
114
+ {
115
+ href: "/settings/pricing-governance",
116
+ label: "Pricing Governance",
117
+ icon: Scale,
118
+ requiredPermission: "can_manage_model_pricing",
119
+ },
120
+ {
121
+ href: "/settings/cursor-accounts",
122
+ label: "Cursor Accounts",
123
+ icon: MousePointerClick,
124
+ requiredPermission: "can_manage_cursor_accounts",
125
+ },
126
+ ],
127
+ };
@@ -0,0 +1,81 @@
1
+ "use client";
2
+
3
+ import { useMemo } from "react";
4
+ import { useCheckPermission } from "../iam-policy/useCheckPermission.js";
5
+ import {
6
+ PLATFORM_SETTINGS_NAV_GROUP,
7
+ SETTINGS_NAV_GROUPS,
8
+ type SettingsNavGroup,
9
+ } from "./settings-nav.js";
10
+
11
+ /**
12
+ * The `platform` singleton — platform-level permissions are always
13
+ * checked against this one resource (mirrors the server-side
14
+ * `resource_id = "stigmer"` RPC config).
15
+ */
16
+ const PLATFORM_RESOURCE = { kind: "platform", id: "stigmer" } as const;
17
+
18
+ /**
19
+ * Permission-aware settings navigation: {@link SETTINGS_NAV_GROUPS}
20
+ * for everyone, plus the {@link PLATFORM_SETTINGS_NAV_GROUP} items the
21
+ * caller's platform permissions unlock (per-item `requiredPermission`,
22
+ * checked against `platform:stigmer`). The group appears when at least
23
+ * one of its items is visible.
24
+ *
25
+ * The checks run fail-closed — the opposite of the `useCheckPermission`
26
+ * default — because navigation is *discoverability*, not *capability*:
27
+ * an action gate can safely fail open (the server re-checks every
28
+ * request), but an operator-only nav entry must not appear while the
29
+ * check is loading, when it errors, or on deployments where the IAM
30
+ * service (and the surface it points to) does not exist.
31
+ *
32
+ * Implementation note: hooks must be called unconditionally, so each
33
+ * distinct platform permission gets its own static check below. When a
34
+ * new platform surface adds a new permission, add its check to
35
+ * PLATFORM_PERMISSION_CHECKS' shape here — the exhaustiveness guard in
36
+ * the tests catches a forgotten one.
37
+ *
38
+ * @example
39
+ * ```tsx
40
+ * const groups = useSettingsNavGroups();
41
+ * return groups.map((group) => <NavGroup key={group.label} group={group} />);
42
+ * ```
43
+ */
44
+ export function useSettingsNavGroups(): readonly SettingsNavGroup[] {
45
+ const pricing = useCheckPermission(
46
+ PLATFORM_RESOURCE,
47
+ "can_manage_model_pricing",
48
+ { fail: "closed" },
49
+ );
50
+ const cursorAccounts = useCheckPermission(
51
+ PLATFORM_RESOURCE,
52
+ "can_manage_cursor_accounts",
53
+ { fail: "closed" },
54
+ );
55
+
56
+ const pricingAllowed = pricing.allowed;
57
+ const cursorAccountsAllowed = cursorAccounts.allowed;
58
+
59
+ return useMemo(() => {
60
+ const verdicts: Record<string, boolean> = {
61
+ can_manage_model_pricing: pricingAllowed,
62
+ can_manage_cursor_accounts: cursorAccountsAllowed,
63
+ };
64
+
65
+ const visibleItems = PLATFORM_SETTINGS_NAV_GROUP.items.filter(
66
+ // Fail closed on both axes: an item with no declared permission or
67
+ // with a permission this hook does not check yet stays hidden.
68
+ (item) =>
69
+ item.requiredPermission !== undefined &&
70
+ verdicts[item.requiredPermission] === true,
71
+ );
72
+
73
+ if (visibleItems.length === 0) {
74
+ return SETTINGS_NAV_GROUPS;
75
+ }
76
+ return [
77
+ ...SETTINGS_NAV_GROUPS,
78
+ { ...PLATFORM_SETTINGS_NAV_GROUP, items: visibleItems },
79
+ ];
80
+ }, [pricingAllowed, cursorAccountsAllowed]);
81
+ }