@stigmer/react 3.2.3 → 3.4.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 (460) 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/index.d.ts.map +1 -1
  5. package/billing/index.js +4 -0
  6. package/billing/index.js.map +1 -1
  7. package/channel/AgentChannelsPanel.d.ts.map +1 -1
  8. package/channel/AgentChannelsPanel.js +11 -5
  9. package/channel/AgentChannelsPanel.js.map +1 -1
  10. package/composer/SessionComposer.d.ts +1 -1
  11. package/cursor-accounts/CursorAccountEditor.d.ts +34 -0
  12. package/cursor-accounts/CursorAccountEditor.d.ts.map +1 -0
  13. package/cursor-accounts/CursorAccountEditor.js +67 -0
  14. package/cursor-accounts/CursorAccountEditor.js.map +1 -0
  15. package/cursor-accounts/CursorAccountsAccessNotice.d.ts +11 -0
  16. package/cursor-accounts/CursorAccountsAccessNotice.d.ts.map +1 -0
  17. package/cursor-accounts/CursorAccountsAccessNotice.js +14 -0
  18. package/cursor-accounts/CursorAccountsAccessNotice.js.map +1 -0
  19. package/cursor-accounts/CursorAccountsConsole.d.ts +32 -0
  20. package/cursor-accounts/CursorAccountsConsole.d.ts.map +1 -0
  21. package/cursor-accounts/CursorAccountsConsole.js +169 -0
  22. package/cursor-accounts/CursorAccountsConsole.js.map +1 -0
  23. package/cursor-accounts/MemberCoverageTable.d.ts +29 -0
  24. package/cursor-accounts/MemberCoverageTable.d.ts.map +1 -0
  25. package/cursor-accounts/MemberCoverageTable.js +150 -0
  26. package/cursor-accounts/MemberCoverageTable.js.map +1 -0
  27. package/cursor-accounts/MemberKeysPanel.d.ts +25 -0
  28. package/cursor-accounts/MemberKeysPanel.d.ts.map +1 -0
  29. package/cursor-accounts/MemberKeysPanel.js +115 -0
  30. package/cursor-accounts/MemberKeysPanel.js.map +1 -0
  31. package/cursor-accounts/badges.d.ts +9 -0
  32. package/cursor-accounts/badges.d.ts.map +1 -0
  33. package/cursor-accounts/badges.js +10 -0
  34. package/cursor-accounts/badges.js.map +1 -0
  35. package/cursor-accounts/cursor-account-coverage.d.ts +41 -0
  36. package/cursor-accounts/cursor-account-coverage.d.ts.map +1 -0
  37. package/cursor-accounts/cursor-account-coverage.js +26 -0
  38. package/cursor-accounts/cursor-account-coverage.js.map +1 -0
  39. package/cursor-accounts/cursor-account-format.d.ts +23 -0
  40. package/cursor-accounts/cursor-account-format.d.ts.map +1 -0
  41. package/cursor-accounts/cursor-account-format.js +41 -0
  42. package/cursor-accounts/cursor-account-format.js.map +1 -0
  43. package/cursor-accounts/index.d.ts +18 -0
  44. package/cursor-accounts/index.d.ts.map +1 -0
  45. package/cursor-accounts/index.js +13 -0
  46. package/cursor-accounts/index.js.map +1 -0
  47. package/cursor-accounts/useCursorAccountView.d.ts +30 -0
  48. package/cursor-accounts/useCursorAccountView.d.ts.map +1 -0
  49. package/cursor-accounts/useCursorAccountView.js +25 -0
  50. package/cursor-accounts/useCursorAccountView.js.map +1 -0
  51. package/cursor-accounts/useCursorAccounts.d.ts +33 -0
  52. package/cursor-accounts/useCursorAccounts.d.ts.map +1 -0
  53. package/cursor-accounts/useCursorAccounts.js +25 -0
  54. package/cursor-accounts/useCursorAccounts.js.map +1 -0
  55. package/cursor-accounts/useCursorMemberKeyActions.d.ts +49 -0
  56. package/cursor-accounts/useCursorMemberKeyActions.d.ts.map +1 -0
  57. package/cursor-accounts/useCursorMemberKeyActions.js +38 -0
  58. package/cursor-accounts/useCursorMemberKeyActions.js.map +1 -0
  59. package/cursor-accounts/useDeleteCursorAccount.d.ts +30 -0
  60. package/cursor-accounts/useDeleteCursorAccount.d.ts.map +1 -0
  61. package/cursor-accounts/useDeleteCursorAccount.js +32 -0
  62. package/cursor-accounts/useDeleteCursorAccount.js.map +1 -0
  63. package/cursor-accounts/useSyncCursorAccount.d.ts +23 -0
  64. package/cursor-accounts/useSyncCursorAccount.d.ts.map +1 -0
  65. package/cursor-accounts/useSyncCursorAccount.js +31 -0
  66. package/cursor-accounts/useSyncCursorAccount.js.map +1 -0
  67. package/cursor-accounts/useUpsertCursorAccount.d.ts +26 -0
  68. package/cursor-accounts/useUpsertCursorAccount.d.ts.map +1 -0
  69. package/cursor-accounts/useUpsertCursorAccount.js +34 -0
  70. package/cursor-accounts/useUpsertCursorAccount.js.map +1 -0
  71. package/datastore/CollectionRecordsBrowser.d.ts +34 -0
  72. package/datastore/CollectionRecordsBrowser.d.ts.map +1 -0
  73. package/datastore/CollectionRecordsBrowser.js +223 -0
  74. package/datastore/CollectionRecordsBrowser.js.map +1 -0
  75. package/datastore/CollectionSchemaView.d.ts +29 -0
  76. package/datastore/CollectionSchemaView.d.ts.map +1 -0
  77. package/datastore/CollectionSchemaView.js +93 -0
  78. package/datastore/CollectionSchemaView.js.map +1 -0
  79. package/datastore/DatastoreDetailView.d.ts +44 -0
  80. package/datastore/DatastoreDetailView.d.ts.map +1 -0
  81. package/datastore/DatastoreDetailView.js +76 -0
  82. package/datastore/DatastoreDetailView.js.map +1 -0
  83. package/datastore/DatastoreSyncReport.d.ts +19 -0
  84. package/datastore/DatastoreSyncReport.d.ts.map +1 -0
  85. package/datastore/DatastoreSyncReport.js +45 -0
  86. package/datastore/DatastoreSyncReport.js.map +1 -0
  87. package/datastore/DeleteDatastoreDialog.d.ts +35 -0
  88. package/datastore/DeleteDatastoreDialog.d.ts.map +1 -0
  89. package/datastore/DeleteDatastoreDialog.js +90 -0
  90. package/datastore/DeleteDatastoreDialog.js.map +1 -0
  91. package/datastore/FieldValueControl.d.ts +55 -0
  92. package/datastore/FieldValueControl.d.ts.map +1 -0
  93. package/datastore/FieldValueControl.js +138 -0
  94. package/datastore/FieldValueControl.js.map +1 -0
  95. package/datastore/RecordFilterBuilder.d.ts +37 -0
  96. package/datastore/RecordFilterBuilder.d.ts.map +1 -0
  97. package/datastore/RecordFilterBuilder.js +122 -0
  98. package/datastore/RecordFilterBuilder.js.map +1 -0
  99. package/datastore/RecordFormPanel.d.ts +45 -0
  100. package/datastore/RecordFormPanel.d.ts.map +1 -0
  101. package/datastore/RecordFormPanel.js +215 -0
  102. package/datastore/RecordFormPanel.js.map +1 -0
  103. package/datastore/index.d.ts +43 -0
  104. package/datastore/index.d.ts.map +1 -0
  105. package/datastore/index.js +22 -0
  106. package/datastore/index.js.map +1 -0
  107. package/datastore/recordFilter.d.ts +62 -0
  108. package/datastore/recordFilter.d.ts.map +1 -0
  109. package/datastore/recordFilter.js +104 -0
  110. package/datastore/recordFilter.js.map +1 -0
  111. package/datastore/recordValues.d.ts +85 -0
  112. package/datastore/recordValues.d.ts.map +1 -0
  113. package/datastore/recordValues.js +282 -0
  114. package/datastore/recordValues.js.map +1 -0
  115. package/datastore/useDatastore.d.ts +39 -0
  116. package/datastore/useDatastore.d.ts.map +1 -0
  117. package/datastore/useDatastore.js +44 -0
  118. package/datastore/useDatastore.js.map +1 -0
  119. package/datastore/useDatastoreCount.d.ts +48 -0
  120. package/datastore/useDatastoreCount.d.ts.map +1 -0
  121. package/datastore/useDatastoreCount.js +27 -0
  122. package/datastore/useDatastoreCount.js.map +1 -0
  123. package/datastore/useDatastoreDescription.d.ts +54 -0
  124. package/datastore/useDatastoreDescription.d.ts.map +1 -0
  125. package/datastore/useDatastoreDescription.js +50 -0
  126. package/datastore/useDatastoreDescription.js.map +1 -0
  127. package/datastore/useDatastoreList.d.ts +57 -0
  128. package/datastore/useDatastoreList.d.ts.map +1 -0
  129. package/datastore/useDatastoreList.js +29 -0
  130. package/datastore/useDatastoreList.js.map +1 -0
  131. package/datastore/useDatastoreSearch.d.ts +19 -0
  132. package/datastore/useDatastoreSearch.d.ts.map +1 -0
  133. package/datastore/useDatastoreSearch.js +22 -0
  134. package/datastore/useDatastoreSearch.js.map +1 -0
  135. package/datastore/useDeleteDatastore.d.ts +53 -0
  136. package/datastore/useDeleteDatastore.d.ts.map +1 -0
  137. package/datastore/useDeleteDatastore.js +40 -0
  138. package/datastore/useDeleteDatastore.js.map +1 -0
  139. package/datastore/useDeleteRecord.d.ts +34 -0
  140. package/datastore/useDeleteRecord.d.ts.map +1 -0
  141. package/datastore/useDeleteRecord.js +43 -0
  142. package/datastore/useDeleteRecord.js.map +1 -0
  143. package/datastore/useInsertRecord.d.ts +50 -0
  144. package/datastore/useInsertRecord.d.ts.map +1 -0
  145. package/datastore/useInsertRecord.js +55 -0
  146. package/datastore/useInsertRecord.js.map +1 -0
  147. package/datastore/useRecordCollection.d.ts +104 -0
  148. package/datastore/useRecordCollection.d.ts.map +1 -0
  149. package/datastore/useRecordCollection.js +136 -0
  150. package/datastore/useRecordCollection.js.map +1 -0
  151. package/datastore/useRecordList.d.ts +66 -0
  152. package/datastore/useRecordList.d.ts.map +1 -0
  153. package/datastore/useRecordList.js +58 -0
  154. package/datastore/useRecordList.js.map +1 -0
  155. package/datastore/useUpdateRecord.d.ts +52 -0
  156. package/datastore/useUpdateRecord.d.ts.map +1 -0
  157. package/datastore/useUpdateRecord.js +55 -0
  158. package/datastore/useUpdateRecord.js.map +1 -0
  159. package/environment/EnvironmentListPanel.d.ts.map +1 -1
  160. package/environment/EnvironmentListPanel.js +12 -7
  161. package/environment/EnvironmentListPanel.js.map +1 -1
  162. package/execution/ExecutionProgress.d.ts.map +1 -1
  163. package/execution/ExecutionProgress.js +13 -4
  164. package/execution/ExecutionProgress.js.map +1 -1
  165. package/execution/MessageThread.d.ts.map +1 -1
  166. package/execution/MessageThread.js +10 -2
  167. package/execution/MessageThread.js.map +1 -1
  168. package/execution/useCreateAgentExecution.d.ts +31 -1
  169. package/execution/useCreateAgentExecution.d.ts.map +1 -1
  170. package/execution/useCreateAgentExecution.js +2 -0
  171. package/execution/useCreateAgentExecution.js.map +1 -1
  172. package/iam-policy/index.d.ts +1 -1
  173. package/iam-policy/index.d.ts.map +1 -1
  174. package/iam-policy/index.js.map +1 -1
  175. package/iam-policy/useCheckPermission.d.ts +32 -11
  176. package/iam-policy/useCheckPermission.d.ts.map +1 -1
  177. package/iam-policy/useCheckPermission.js +24 -19
  178. package/iam-policy/useCheckPermission.js.map +1 -1
  179. package/index.d.ts +12 -4
  180. package/index.d.ts.map +1 -1
  181. package/index.js +18 -3
  182. package/index.js.map +1 -1
  183. package/internal/form-primitives.d.ts +10 -0
  184. package/internal/form-primitives.d.ts.map +1 -0
  185. package/internal/form-primitives.js +15 -0
  186. package/internal/form-primitives.js.map +1 -0
  187. package/library/index.d.ts +0 -7
  188. package/library/index.d.ts.map +1 -1
  189. package/library/index.js +0 -4
  190. package/library/index.js.map +1 -1
  191. package/library/useApplyResource.d.ts +12 -12
  192. package/library/useApplyResource.d.ts.map +1 -1
  193. package/library/useApplyResource.js +16 -25
  194. package/library/useApplyResource.js.map +1 -1
  195. package/library/useExportResource.d.ts +13 -9
  196. package/library/useExportResource.d.ts.map +1 -1
  197. package/library/useExportResource.js +18 -36
  198. package/library/useExportResource.js.map +1 -1
  199. package/manifest/ApplyManifestDialog.d.ts +48 -0
  200. package/manifest/ApplyManifestDialog.d.ts.map +1 -0
  201. package/manifest/ApplyManifestDialog.js +135 -0
  202. package/manifest/ApplyManifestDialog.js.map +1 -0
  203. package/manifest/EditResourceYamlDialog.d.ts +51 -0
  204. package/manifest/EditResourceYamlDialog.d.ts.map +1 -0
  205. package/manifest/EditResourceYamlDialog.js +104 -0
  206. package/manifest/EditResourceYamlDialog.js.map +1 -0
  207. package/manifest/RedactedSecretsNotice.d.ts +14 -0
  208. package/manifest/RedactedSecretsNotice.d.ts.map +1 -0
  209. package/manifest/RedactedSecretsNotice.js +14 -0
  210. package/manifest/RedactedSecretsNotice.js.map +1 -0
  211. package/manifest/YamlEditor.d.ts +44 -0
  212. package/manifest/YamlEditor.d.ts.map +1 -0
  213. package/manifest/YamlEditor.js +216 -0
  214. package/manifest/YamlEditor.js.map +1 -0
  215. package/manifest/index.d.ts +13 -0
  216. package/manifest/index.d.ts.map +1 -0
  217. package/manifest/index.js +13 -0
  218. package/manifest/index.js.map +1 -0
  219. package/manifest/useApplyManifest.d.ts +82 -0
  220. package/manifest/useApplyManifest.d.ts.map +1 -0
  221. package/manifest/useApplyManifest.js +180 -0
  222. package/manifest/useApplyManifest.js.map +1 -0
  223. package/manifest/useEditResourceYaml.d.ts +106 -0
  224. package/manifest/useEditResourceYaml.d.ts.map +1 -0
  225. package/manifest/useEditResourceYaml.js +168 -0
  226. package/manifest/useEditResourceYaml.js.map +1 -0
  227. package/mcp-server/steps/ReviewStep.d.ts.map +1 -1
  228. package/mcp-server/steps/ReviewStep.js +2 -3
  229. package/mcp-server/steps/ReviewStep.js.map +1 -1
  230. package/package.json +4 -4
  231. package/pricing-governance/BaselineEditor.d.ts +9 -0
  232. package/pricing-governance/BaselineEditor.d.ts.map +1 -0
  233. package/pricing-governance/BaselineEditor.js +270 -0
  234. package/pricing-governance/BaselineEditor.js.map +1 -0
  235. package/pricing-governance/ModelCatalogPanel.d.ts +28 -0
  236. package/pricing-governance/ModelCatalogPanel.d.ts.map +1 -0
  237. package/pricing-governance/ModelCatalogPanel.js +102 -0
  238. package/pricing-governance/ModelCatalogPanel.js.map +1 -0
  239. package/pricing-governance/ModelGovernanceDetail.d.ts +8 -0
  240. package/pricing-governance/ModelGovernanceDetail.d.ts.map +1 -0
  241. package/pricing-governance/ModelGovernanceDetail.js +64 -0
  242. package/pricing-governance/ModelGovernanceDetail.js.map +1 -0
  243. package/pricing-governance/OperatorAccessNotice.d.ts +12 -0
  244. package/pricing-governance/OperatorAccessNotice.d.ts.map +1 -0
  245. package/pricing-governance/OperatorAccessNotice.js +15 -0
  246. package/pricing-governance/OperatorAccessNotice.js.map +1 -0
  247. package/pricing-governance/PricingGovernanceConsole.d.ts +57 -0
  248. package/pricing-governance/PricingGovernanceConsole.d.ts.map +1 -0
  249. package/pricing-governance/PricingGovernanceConsole.js +164 -0
  250. package/pricing-governance/PricingGovernanceConsole.js.map +1 -0
  251. package/pricing-governance/PricingGovernancePanel.d.ts +26 -0
  252. package/pricing-governance/PricingGovernancePanel.d.ts.map +1 -0
  253. package/pricing-governance/PricingGovernancePanel.js +64 -0
  254. package/pricing-governance/PricingGovernancePanel.js.map +1 -0
  255. package/pricing-governance/RetireConfirm.d.ts +9 -0
  256. package/pricing-governance/RetireConfirm.d.ts.map +1 -0
  257. package/pricing-governance/RetireConfirm.js +19 -0
  258. package/pricing-governance/RetireConfirm.js.map +1 -0
  259. package/pricing-governance/governance-primitives.d.ts +33 -0
  260. package/pricing-governance/governance-primitives.d.ts.map +1 -0
  261. package/pricing-governance/governance-primitives.js +43 -0
  262. package/pricing-governance/governance-primitives.js.map +1 -0
  263. package/pricing-governance/index.d.ts +19 -0
  264. package/pricing-governance/index.d.ts.map +1 -0
  265. package/pricing-governance/index.js +20 -0
  266. package/pricing-governance/index.js.map +1 -0
  267. package/pricing-governance/pricing-format.d.ts +12 -0
  268. package/pricing-governance/pricing-format.d.ts.map +1 -0
  269. package/pricing-governance/pricing-format.js +50 -0
  270. package/pricing-governance/pricing-format.js.map +1 -0
  271. package/pricing-governance/useDecidePricingOverride.d.ts +46 -0
  272. package/pricing-governance/useDecidePricingOverride.d.ts.map +1 -0
  273. package/pricing-governance/useDecidePricingOverride.js +44 -0
  274. package/pricing-governance/useDecidePricingOverride.js.map +1 -0
  275. package/pricing-governance/useModelGovernanceView.d.ts +103 -0
  276. package/pricing-governance/useModelGovernanceView.d.ts.map +1 -0
  277. package/pricing-governance/useModelGovernanceView.js +144 -0
  278. package/pricing-governance/useModelGovernanceView.js.map +1 -0
  279. package/pricing-governance/useModelPricingBaselines.d.ts +40 -0
  280. package/pricing-governance/useModelPricingBaselines.d.ts.map +1 -0
  281. package/pricing-governance/useModelPricingBaselines.js +32 -0
  282. package/pricing-governance/useModelPricingBaselines.js.map +1 -0
  283. package/pricing-governance/usePricingGovernance.d.ts +39 -0
  284. package/pricing-governance/usePricingGovernance.d.ts.map +1 -0
  285. package/pricing-governance/usePricingGovernance.js +31 -0
  286. package/pricing-governance/usePricingGovernance.js.map +1 -0
  287. package/pricing-governance/useRetireModelPricingBaseline.d.ts +31 -0
  288. package/pricing-governance/useRetireModelPricingBaseline.d.ts.map +1 -0
  289. package/pricing-governance/useRetireModelPricingBaseline.js +36 -0
  290. package/pricing-governance/useRetireModelPricingBaseline.js.map +1 -0
  291. package/pricing-governance/useUpsertModelPricingBaseline.d.ts +38 -0
  292. package/pricing-governance/useUpsertModelPricingBaseline.d.ts.map +1 -0
  293. package/pricing-governance/useUpsertModelPricingBaseline.js +37 -0
  294. package/pricing-governance/useUpsertModelPricingBaseline.js.map +1 -0
  295. package/resource-creation/templates/workflow-templates.js +4 -4
  296. package/session/useCreateSession.d.ts +28 -0
  297. package/session/useCreateSession.d.ts.map +1 -1
  298. package/session/useCreateSession.js +2 -1
  299. package/session/useCreateSession.js.map +1 -1
  300. package/session/useEditSessionPrep.js +3 -3
  301. package/session/useEditSessionPrep.js.map +1 -1
  302. package/session/useNewSessionFlow.d.ts +30 -0
  303. package/session/useNewSessionFlow.d.ts.map +1 -1
  304. package/session/useNewSessionFlow.js +7 -1
  305. package/session/useNewSessionFlow.js.map +1 -1
  306. package/session/useUpdateSession.d.ts +29 -10
  307. package/session/useUpdateSession.d.ts.map +1 -1
  308. package/session/useUpdateSession.js +29 -10
  309. package/session/useUpdateSession.js.map +1 -1
  310. package/settings/index.d.ts +2 -1
  311. package/settings/index.d.ts.map +1 -1
  312. package/settings/index.js +2 -1
  313. package/settings/index.js.map +1 -1
  314. package/settings/settings-nav.d.ts +22 -0
  315. package/settings/settings-nav.d.ts.map +1 -1
  316. package/settings/settings-nav.js +32 -1
  317. package/settings/settings-nav.js.map +1 -1
  318. package/settings/useSettingsNavGroups.d.ts +29 -0
  319. package/settings/useSettingsNavGroups.d.ts.map +1 -0
  320. package/settings/useSettingsNavGroups.js +61 -0
  321. package/settings/useSettingsNavGroups.js.map +1 -0
  322. package/src/agent/steps/ReviewStep.tsx +2 -3
  323. package/src/billing/index.ts +5 -0
  324. package/src/channel/AgentChannelsPanel.tsx +27 -1
  325. package/src/composer/SessionComposer.tsx +1 -1
  326. package/src/cursor-accounts/CursorAccountEditor.tsx +229 -0
  327. package/src/cursor-accounts/CursorAccountsAccessNotice.tsx +36 -0
  328. package/src/cursor-accounts/CursorAccountsConsole.tsx +454 -0
  329. package/src/cursor-accounts/MemberCoverageTable.tsx +439 -0
  330. package/src/cursor-accounts/MemberKeysPanel.tsx +278 -0
  331. package/src/cursor-accounts/__tests__/CursorAccountsConsole.test.tsx +680 -0
  332. package/src/cursor-accounts/__tests__/cursor-account-coverage.test.ts +73 -0
  333. package/src/cursor-accounts/badges.tsx +26 -0
  334. package/src/cursor-accounts/cursor-account-coverage.ts +71 -0
  335. package/src/cursor-accounts/cursor-account-format.ts +45 -0
  336. package/src/cursor-accounts/index.ts +29 -0
  337. package/src/cursor-accounts/useCursorAccountView.ts +49 -0
  338. package/src/cursor-accounts/useCursorAccounts.ts +49 -0
  339. package/src/cursor-accounts/useCursorMemberKeyActions.ts +106 -0
  340. package/src/cursor-accounts/useDeleteCursorAccount.ts +62 -0
  341. package/src/cursor-accounts/useSyncCursorAccount.ts +54 -0
  342. package/src/cursor-accounts/useUpsertCursorAccount.ts +57 -0
  343. package/src/datastore/CollectionRecordsBrowser.tsx +570 -0
  344. package/src/datastore/CollectionSchemaView.tsx +247 -0
  345. package/src/datastore/DatastoreDetailView.tsx +201 -0
  346. package/src/datastore/DatastoreSyncReport.tsx +105 -0
  347. package/src/datastore/DeleteDatastoreDialog.tsx +202 -0
  348. package/src/datastore/FieldValueControl.tsx +289 -0
  349. package/src/datastore/RecordFilterBuilder.tsx +422 -0
  350. package/src/datastore/RecordFormPanel.tsx +432 -0
  351. package/src/datastore/__tests__/CollectionRecordsBrowser.test.tsx +245 -0
  352. package/src/datastore/__tests__/DeleteDatastoreDialog.test.tsx +133 -0
  353. package/src/datastore/__tests__/FieldValueControl.test.tsx +160 -0
  354. package/src/datastore/__tests__/RecordFilterBuilder.test.tsx +155 -0
  355. package/src/datastore/__tests__/RecordFormPanel.test.tsx +227 -0
  356. package/src/datastore/__tests__/recordFilter.test.ts +169 -0
  357. package/src/datastore/__tests__/recordMutations.test.tsx +195 -0
  358. package/src/datastore/__tests__/recordValues.test.ts +359 -0
  359. package/src/datastore/__tests__/schemaAndSyncViews.test.tsx +142 -0
  360. package/src/datastore/__tests__/useDatastoreDescription.test.tsx +143 -0
  361. package/src/datastore/__tests__/useRecordCollection.test.tsx +250 -0
  362. package/src/datastore/__tests__/useRecordList.test.tsx +85 -0
  363. package/src/datastore/index.ts +111 -0
  364. package/src/datastore/recordFilter.ts +154 -0
  365. package/src/datastore/recordValues.ts +327 -0
  366. package/src/datastore/useDatastore.ts +67 -0
  367. package/src/datastore/useDatastoreCount.ts +67 -0
  368. package/src/datastore/useDatastoreDescription.ts +90 -0
  369. package/src/datastore/useDatastoreList.ts +79 -0
  370. package/src/datastore/useDatastoreSearch.ts +39 -0
  371. package/src/datastore/useDeleteDatastore.ts +87 -0
  372. package/src/datastore/useDeleteRecord.ts +77 -0
  373. package/src/datastore/useInsertRecord.ts +92 -0
  374. package/src/datastore/useRecordCollection.ts +256 -0
  375. package/src/datastore/useRecordList.ts +131 -0
  376. package/src/datastore/useUpdateRecord.ts +95 -0
  377. package/src/environment/EnvironmentListPanel.tsx +24 -0
  378. package/src/execution/ExecutionProgress.tsx +14 -4
  379. package/src/execution/MessageThread.tsx +10 -2
  380. package/src/execution/__tests__/ExecutionProgress.test.tsx +67 -0
  381. package/src/execution/__tests__/MessageThread.test.tsx +44 -0
  382. package/src/execution/__tests__/useArtifactInspection.test.tsx +12 -2
  383. package/src/execution/__tests__/useCreateAgentExecution.test.tsx +86 -0
  384. package/src/execution/useCreateAgentExecution.ts +41 -6
  385. package/src/iam-policy/__tests__/useCheckPermission.test.tsx +221 -0
  386. package/src/iam-policy/index.ts +1 -0
  387. package/src/iam-policy/useCheckPermission.ts +45 -19
  388. package/src/index.ts +172 -14
  389. package/src/internal/form-primitives.tsx +40 -0
  390. package/src/library/index.ts +0 -20
  391. package/src/library/useApplyResource.ts +27 -34
  392. package/src/library/useExportResource.ts +21 -40
  393. package/src/manifest/ApplyManifestDialog.tsx +315 -0
  394. package/src/manifest/EditResourceYamlDialog.tsx +265 -0
  395. package/src/manifest/RedactedSecretsNotice.tsx +30 -0
  396. package/src/manifest/YamlEditor.tsx +272 -0
  397. package/src/manifest/__tests__/EditResourceYamlDialog.test.tsx +118 -0
  398. package/src/manifest/__tests__/useApplyManifest.test.tsx +237 -0
  399. package/src/manifest/__tests__/useEditResourceYaml.test.tsx +252 -0
  400. package/src/manifest/index.ts +33 -0
  401. package/src/manifest/useApplyManifest.ts +270 -0
  402. package/src/manifest/useEditResourceYaml.ts +273 -0
  403. package/src/mcp-server/steps/ReviewStep.tsx +2 -3
  404. package/src/pricing-governance/BaselineEditor.tsx +652 -0
  405. package/src/pricing-governance/ModelCatalogPanel.tsx +283 -0
  406. package/src/pricing-governance/ModelGovernanceDetail.tsx +289 -0
  407. package/src/pricing-governance/OperatorAccessNotice.tsx +32 -0
  408. package/src/pricing-governance/PricingGovernanceConsole.tsx +465 -0
  409. package/src/pricing-governance/PricingGovernancePanel.tsx +166 -0
  410. package/src/pricing-governance/RetireConfirm.tsx +82 -0
  411. package/src/pricing-governance/__tests__/ModelCatalogPanel.test.tsx +253 -0
  412. package/src/pricing-governance/__tests__/PricingGovernanceConsole.test.tsx +410 -0
  413. package/src/pricing-governance/__tests__/useModelGovernanceView.test.tsx +308 -0
  414. package/src/pricing-governance/governance-primitives.tsx +133 -0
  415. package/src/pricing-governance/index.ts +50 -0
  416. package/src/pricing-governance/pricing-format.ts +53 -0
  417. package/src/pricing-governance/useDecidePricingOverride.ts +78 -0
  418. package/src/pricing-governance/useModelGovernanceView.ts +290 -0
  419. package/src/pricing-governance/useModelPricingBaselines.ts +69 -0
  420. package/src/pricing-governance/usePricingGovernance.ts +57 -0
  421. package/src/pricing-governance/useRetireModelPricingBaseline.ts +65 -0
  422. package/src/pricing-governance/useUpsertModelPricingBaseline.ts +72 -0
  423. package/src/resource-creation/templates/workflow-templates.ts +4 -4
  424. package/src/session/__tests__/useCreateSession.test.tsx +94 -0
  425. package/src/session/__tests__/useNewSessionFlow.test.tsx +33 -0
  426. package/src/session/useCreateSession.ts +30 -0
  427. package/src/session/useEditSessionPrep.ts +3 -3
  428. package/src/session/useNewSessionFlow.ts +45 -1
  429. package/src/session/useUpdateSession.ts +29 -10
  430. package/src/settings/__tests__/useSettingsNavGroups.test.tsx +138 -0
  431. package/src/settings/index.ts +5 -1
  432. package/src/settings/settings-nav.ts +43 -0
  433. package/src/settings/useSettingsNavGroups.ts +81 -0
  434. package/src/workflow/WorkflowYamlEditor.tsx +6 -270
  435. package/styles.css +1 -1
  436. package/workflow/CanvasTransitionEdge.d.ts +1 -1
  437. package/workflow/WorkflowYamlEditor.d.ts +2 -42
  438. package/workflow/WorkflowYamlEditor.d.ts.map +1 -1
  439. package/workflow/WorkflowYamlEditor.js +5 -214
  440. package/workflow/WorkflowYamlEditor.js.map +1 -1
  441. package/library/ImportResourceDialog.d.ts +0 -43
  442. package/library/ImportResourceDialog.d.ts.map +0 -1
  443. package/library/ImportResourceDialog.js +0 -90
  444. package/library/ImportResourceDialog.js.map +0 -1
  445. package/library/parse-resource-yaml.d.ts +0 -63
  446. package/library/parse-resource-yaml.d.ts.map +0 -1
  447. package/library/parse-resource-yaml.js +0 -407
  448. package/library/parse-resource-yaml.js.map +0 -1
  449. package/library/serialize-resource-yaml.d.ts +0 -103
  450. package/library/serialize-resource-yaml.d.ts.map +0 -1
  451. package/library/serialize-resource-yaml.js +0 -565
  452. package/library/serialize-resource-yaml.js.map +0 -1
  453. package/library/useImportResource.d.ts +0 -84
  454. package/library/useImportResource.d.ts.map +0 -1
  455. package/library/useImportResource.js +0 -188
  456. package/library/useImportResource.js.map +0 -1
  457. package/src/library/ImportResourceDialog.tsx +0 -227
  458. package/src/library/parse-resource-yaml.ts +0 -630
  459. package/src/library/serialize-resource-yaml.ts +0 -756
  460. package/src/library/useImportResource.ts +0 -253
@@ -334,6 +334,39 @@ describe("useNewSessionFlow", () => {
334
334
  expect(execInput.sessionSpec.harness).toBe("cursor");
335
335
  });
336
336
 
337
+ it("forwards metadata and sessionContext verbatim in the sessionSpec", async () => {
338
+ // The typed-wins merge onto the reserved key happens downstream in
339
+ // useCreateAgentExecution (covered by its own tests); the flow's job
340
+ // is faithful forwarding.
341
+ const opts = {
342
+ ...defaultOptions(),
343
+ metadata: { "acme/tenant": "t-1" },
344
+ sessionContext: "Role: platform admin",
345
+ };
346
+ const { result } = renderHook(() => useNewSessionFlow(opts), { wrapper: createWrapper() });
347
+
348
+ await act(async () => {
349
+ await result.current.submit("Hello");
350
+ });
351
+
352
+ const execInput = mockCreateExecution.mock.calls[0][0];
353
+ expect(execInput.sessionSpec.metadata).toEqual({ "acme/tenant": "t-1" });
354
+ expect(execInput.sessionSpec.sessionContext).toBe("Role: platform admin");
355
+ });
356
+
357
+ it("leaves metadata and sessionContext undefined when not provided", async () => {
358
+ const opts = defaultOptions();
359
+ const { result } = renderHook(() => useNewSessionFlow(opts), { wrapper: createWrapper() });
360
+
361
+ await act(async () => {
362
+ await result.current.submit("Hello");
363
+ });
364
+
365
+ const execInput = mockCreateExecution.mock.calls[0][0];
366
+ expect(execInput.sessionSpec.metadata).toBeUndefined();
367
+ expect(execInput.sessionSpec.sessionContext).toBeUndefined();
368
+ });
369
+
337
370
  it("calls onSessionCreated with the server-assigned session id", async () => {
338
371
  const opts = defaultOptions();
339
372
  const { result } = renderHook(() => useNewSessionFlow(opts), { wrapper: createWrapper() });
@@ -3,6 +3,7 @@
3
3
  import { useCallback, useState } from "react";
4
4
  import {
5
5
  PENDING_SUBJECT,
6
+ mergeSessionContext,
6
7
  type McpServerUsageInput,
7
8
  type ResourceRef,
8
9
  type WorkspaceEntryInput,
@@ -25,6 +26,34 @@ export interface SharedSessionFields {
25
26
  readonly mcpServerUsages?: McpServerUsageInput[];
26
27
  /** Skill references to enable for executions in this session. */
27
28
  readonly skillRefs?: ResourceRef[];
29
+ /**
30
+ * Custom key-value pairs stored on `SessionSpec.metadata`.
31
+ *
32
+ * A passthrough for embedder-owned keys (correlation IDs, tenant tags)
33
+ * and for platform-reserved `stigmer.ai/*` keys set explicitly. For the
34
+ * common case — standing user context injected into the agent's prompt —
35
+ * prefer the typed {@link sessionContext} field, which maps onto the
36
+ * reserved `stigmer.ai/session-context` key and wins over a raw entry
37
+ * under that key when both are provided.
38
+ */
39
+ readonly metadata?: Record<string, string>;
40
+ /**
41
+ * Standing, per-user context the agent receives on every turn but the
42
+ * conversation UI never renders — who the caller is, their experience
43
+ * level, their standing instructions (stigmer/stigmer#286).
44
+ *
45
+ * Stored on `SessionSpec.metadata` under `stigmer.ai/session-context`;
46
+ * the agent runner injects it into the system prompt as already-known
47
+ * background, so agents can greet the user by name and calibrate
48
+ * depth/defaults from the first turn without a visible context preamble.
49
+ *
50
+ * Personalization, not authorization: anyone who can create the session
51
+ * can set this (the same trust level as authoring the first message).
52
+ * Hidden from the conversation thread, not from the API — `session.get`
53
+ * returns it, so never put secrets here; secrets belong in `runtimeEnv`
54
+ * or Environment resources. Large values bloat every prompt.
55
+ */
56
+ readonly sessionContext?: string;
28
57
  /**
29
58
  * Execution harness for this session.
30
59
  *
@@ -172,6 +201,7 @@ export function useCreateSession(): UseCreateSessionReturn {
172
201
  workspaceEntries: input.workspaceEntries,
173
202
  mcpServerUsages: input.mcpServerUsages,
174
203
  skillRefs: input.skillRefs,
204
+ metadata: mergeSessionContext(input.metadata, input.sessionContext),
175
205
  agentInstanceId: resolvedInstanceId,
176
206
  harness: input.harness ? toProtoHarness(input.harness) : undefined,
177
207
  executionTarget: resolvedTarget
@@ -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 {
@@ -111,6 +111,36 @@ export interface UseNewSessionFlowOptions {
111
111
  * @default "integrator"
112
112
  */
113
113
  readonly audience?: SessionAudience;
114
+ /**
115
+ * Custom key-value pairs stored on the created session's
116
+ * `SessionSpec.metadata`.
117
+ *
118
+ * A passthrough for embedder-owned keys (correlation IDs, tenant tags)
119
+ * and for platform-reserved `stigmer.ai/*` keys set explicitly. For the
120
+ * common case — standing user context injected into the agent's prompt —
121
+ * prefer the typed {@link sessionContext} option, which maps onto the
122
+ * reserved `stigmer.ai/session-context` key and wins over a raw entry
123
+ * under that key when both are provided.
124
+ */
125
+ readonly metadata?: Record<string, string>;
126
+ /**
127
+ * Standing, per-user context the agent receives on every turn but the
128
+ * conversation UI never renders — who the caller is, their experience
129
+ * level, their standing instructions (stigmer/stigmer#286).
130
+ *
131
+ * Stored on the created session's `SessionSpec.metadata` under
132
+ * `stigmer.ai/session-context`; the agent runner injects it into the
133
+ * system prompt as already-known background, so agents can greet the
134
+ * user by name and calibrate depth/defaults from the first turn without
135
+ * a visible context preamble.
136
+ *
137
+ * Personalization, not authorization: anyone who can create the session
138
+ * can set this (the same trust level as authoring the first message).
139
+ * Hidden from the conversation thread, not from the API — `session.get`
140
+ * returns it, so never put secrets here; secrets belong in `runtimeEnv`
141
+ * or Environment resources. Large values bloat every prompt.
142
+ */
143
+ readonly sessionContext?: string;
114
144
  }
115
145
 
116
146
  /** Return value of {@link useNewSessionFlow}. */
@@ -217,7 +247,15 @@ export interface UseNewSessionFlowReturn {
217
247
  export function useNewSessionFlow(
218
248
  options: UseNewSessionFlowOptions,
219
249
  ): UseNewSessionFlowReturn {
220
- const { org, onSessionCreated, onError, getRuntimeEnv, defaultHarness } = options;
250
+ const {
251
+ org,
252
+ onSessionCreated,
253
+ onError,
254
+ getRuntimeEnv,
255
+ defaultHarness,
256
+ metadata,
257
+ sessionContext,
258
+ } = options;
221
259
  const isGuest = options.audience === "guest";
222
260
  const contextTarget = useExecutionTarget();
223
261
  const executionTarget = options.executionTarget ?? contextTarget;
@@ -336,6 +374,10 @@ export function useNewSessionFlow(
336
374
  : undefined,
337
375
  mcpServerUsages: mcpServerUsages.length > 0 ? mcpServerUsages : undefined,
338
376
  skillRefs: skillRefs.length > 0 ? skillRefs : undefined,
377
+ // The typed-wins merge happens downstream in useCreateAgentExecution;
378
+ // both fields are forwarded verbatim here.
379
+ metadata,
380
+ sessionContext,
339
381
  harness,
340
382
  executionTarget,
341
383
  };
@@ -440,6 +482,8 @@ export function useNewSessionFlow(
440
482
  workspace,
441
483
  mcpServerUsages,
442
484
  skillRefs,
485
+ metadata,
486
+ sessionContext,
443
487
  agentRef,
444
488
  resolution,
445
489
  defaultAgent,
@@ -24,28 +24,47 @@ export interface UseUpdateSessionReturn {
24
24
  * {@link SessionInput} including the session's `name` and `org` (used
25
25
  * by the backend to identify the resource) and all spec fields.
26
26
  *
27
+ * **Full-replace semantics.** The backend replaces the entire spec with
28
+ * what you send — there is no field mask. Round-trip every spec field
29
+ * you do not intend to change, *including `metadata`*: an update built
30
+ * from a reconstructed partial spec silently wipes the fields it omits
31
+ * (workspace entries, MCP servers, and `stigmer.ai/*` metadata keys such
32
+ * as the embedder session context). To change only the subject, prefer
33
+ * the field-level `session.updateSubject()` RPC instead of this hook.
34
+ *
27
35
  * Typically composed into higher-level hooks like
28
- * {@link useSessionConversation} rather than used directly. Platform
29
- * builders who need direct session mutation can use this hook
30
- * standalone.
36
+ * {@link useSessionConversation} (whose update path round-trips the
37
+ * fetched spec) rather than used directly. Platform builders who need
38
+ * direct session mutation can use this hook standalone.
31
39
  *
32
40
  * @example
33
41
  * ```tsx
34
- * function RenameSession({ session }: { session: Session }) {
35
- * const { update, isUpdating, error } = useUpdateSession();
42
+ * function TagSession({ session }: { session: Session }) {
43
+ * const { update, isUpdating } = useUpdateSession();
36
44
  *
37
- * async function handleRename(newSubject: string) {
45
+ * async function handleTag() {
46
+ * const spec = session.spec;
38
47
  * await update({
39
48
  * name: session.metadata!.name,
40
49
  * org: session.metadata!.org,
41
- * agentInstanceId: session.spec?.agentInstanceId,
42
- * subject: newSubject,
50
+ * // Full replace: carry over every fetched spec field...
51
+ * agentInstanceId: spec?.agentInstanceId,
52
+ * subject: spec?.subject,
53
+ * harnessStateId: spec?.harnessStateId,
54
+ * harness: spec?.harness,
55
+ * cursorMode: spec?.cursorMode,
56
+ * executionTarget: spec?.executionTarget,
57
+ * // ...including the spec lists (workspaceEntries, mcpServerUsages,
58
+ * // skillRefs), converted from the fetched proto exactly as
59
+ * // useSessionConversation's update path does...
60
+ * // ...then apply the one change on top of the carried metadata.
61
+ * metadata: { ...spec?.metadata, "acme/reviewed": "true" },
43
62
  * });
44
63
  * }
45
64
  *
46
65
  * return (
47
- * <button onClick={() => handleRename("New subject")} disabled={isUpdating}>
48
- * Rename
66
+ * <button onClick={handleTag} disabled={isUpdating}>
67
+ * Mark reviewed
49
68
  * </button>
50
69
  * );
51
70
  * }
@@ -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
+ }