@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
@@ -0,0 +1,432 @@
1
+ "use client";
2
+
3
+ import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
4
+ import { cn } from "@stigmer/theme";
5
+ import type { JsonObject, JsonValue } from "@bufbuild/protobuf";
6
+ import { timestampDate } from "@bufbuild/protobuf/wkt";
7
+ import { getRecordConstraint, getUserMessage } from "@stigmer/sdk";
8
+ import type {
9
+ CollectionDeclaration,
10
+ FieldDeclaration,
11
+ } from "@stigmer/protos/ai/stigmer/agentic/datastore/v1/spec_pb";
12
+ import type { RecordEnvelope } from "@stigmer/protos/ai/stigmer/agentic/datastore/v1/record_io_pb";
13
+ import { FieldValueControl } from "./FieldValueControl.js";
14
+ import {
15
+ buildUpdateFields,
16
+ coerceFieldValue,
17
+ formatSystemTimestamp,
18
+ } from "./recordValues.js";
19
+ import { formatSubject } from "./CollectionSchemaView.js";
20
+ import { useInsertRecord } from "./useInsertRecord.js";
21
+ import { useUpdateRecord } from "./useUpdateRecord.js";
22
+ import type { RecordScope } from "./useRecordList.js";
23
+
24
+ /** Props for {@link RecordFormPanel}. */
25
+ export interface RecordFormPanelProps {
26
+ /** Whether the panel is open. */
27
+ readonly open: boolean;
28
+ /** Called when the panel requests to close (Escape, Cancel, or save). */
29
+ readonly onOpenChange: (open: boolean) => void;
30
+ /** Record addressing: org, datastore, collection, partition. */
31
+ readonly scope: RecordScope;
32
+ /** The collection declaration driving the form's typed controls. */
33
+ readonly collection: CollectionDeclaration;
34
+ /**
35
+ * The record to edit, or `null`/`undefined` to insert a new one.
36
+ * Edit submits a partial merge of dirty fields only.
37
+ */
38
+ readonly record?: RecordEnvelope | null;
39
+ /** Called with the server-stamped envelope after a successful save. */
40
+ readonly onSaved?: (envelope: RecordEnvelope) => void;
41
+ }
42
+
43
+ /**
44
+ * Per-field edit state. `unset` distinguishes "never touched" from an
45
+ * explicit clear — the honest projection of the update RPC's tri-state
46
+ * (absent = unchanged, null = clear, value = replace).
47
+ */
48
+ type FieldEdit =
49
+ | { readonly kind: "value"; readonly value: JsonValue }
50
+ | { readonly kind: "cleared" };
51
+
52
+ /**
53
+ * The record write surface (DD-008 SD-4): one schema-generated panel
54
+ * form serving insert and edit, with typed controls dispatched from
55
+ * `FieldDeclaration`.
56
+ *
57
+ * - **Insert** submits every field the operator set; required fields
58
+ * gate the submit (error prevention — the server remains the
59
+ * enforcer).
60
+ * - **Edit** submits a **partial merge**: only dirty fields travel, and
61
+ * the per-field clear affordance sends an explicit `null` — the form
62
+ * is honest about the RPC's tri-state. System fields render read-only
63
+ * and never travel.
64
+ * - **Errors replay the DD-002/DD-005 contract for humans**: a
65
+ * constraint violation renders the declared message **verbatim** —
66
+ * field-adjacent when the `ErrorInfo` constraint name maps through
67
+ * the spec to declared fields (unique constraints), as a form-level
68
+ * banner otherwise. The console operator and the WhatsApp patient
69
+ * read the same bytes.
70
+ *
71
+ * All visual properties flow through `--stgm-*` design tokens.
72
+ * Zero Console dependencies — safe for platform builder embedding.
73
+ */
74
+ export function RecordFormPanel({
75
+ open,
76
+ onOpenChange,
77
+ scope,
78
+ collection,
79
+ record,
80
+ onSaved,
81
+ }: RecordFormPanelProps) {
82
+ const dialogRef = useRef<HTMLDialogElement>(null);
83
+ const isEdit = record != null;
84
+
85
+ const insert = useInsertRecord();
86
+ const update = useUpdateRecord();
87
+ const isSaving = insert.isInserting || update.isUpdating;
88
+
89
+ const [edits, setEdits] = useState<ReadonlyMap<string, FieldEdit>>(new Map());
90
+ const [fieldErrors, setFieldErrors] = useState<ReadonlyMap<string, string>>(new Map());
91
+ const [bannerError, setBannerError] = useState<string | null>(null);
92
+
93
+ // Reset form state whenever the panel opens (fresh draft per open).
94
+ // Deps narrowed to the stable clearError callbacks (DD-010).
95
+ const clearInsertError = insert.clearError;
96
+ const clearUpdateError = update.clearError;
97
+ useEffect(() => {
98
+ if (open) {
99
+ setEdits(new Map());
100
+ setFieldErrors(new Map());
101
+ setBannerError(null);
102
+ clearInsertError();
103
+ clearUpdateError();
104
+ }
105
+ }, [open, record?.id, clearInsertError, clearUpdateError]);
106
+
107
+ // Native <dialog> open/close sync (the ConfirmDialog pattern).
108
+ useEffect(() => {
109
+ const dialog = dialogRef.current;
110
+ if (!dialog) return;
111
+ if (open && !dialog.open) dialog.showModal();
112
+ else if (!open && dialog.open) dialog.close();
113
+ }, [open]);
114
+
115
+ const handleDialogCancel = useCallback(
116
+ (e: React.SyntheticEvent) => {
117
+ e.preventDefault();
118
+ onOpenChange(false);
119
+ },
120
+ [onOpenChange],
121
+ );
122
+
123
+ /** The effective value a field's control shows: edit > stored > empty. */
124
+ const effectiveValue = useCallback(
125
+ (field: FieldDeclaration): JsonValue | undefined => {
126
+ const edit = edits.get(field.name);
127
+ if (edit) return edit.kind === "cleared" ? undefined : edit.value;
128
+ if (isEdit) {
129
+ const stored = record?.fields?.[field.name];
130
+ return stored === null ? undefined : stored;
131
+ }
132
+ return undefined;
133
+ },
134
+ [edits, isEdit, record],
135
+ );
136
+
137
+ const setFieldValue = useCallback((name: string, value: JsonValue | undefined) => {
138
+ setEdits((prev) => {
139
+ const next = new Map(prev);
140
+ if (value === undefined) {
141
+ // Emptying the control: in insert mode the field simply won't
142
+ // travel; in edit mode an emptied control is an explicit clear.
143
+ next.set(name, { kind: "cleared" });
144
+ } else {
145
+ next.set(name, { kind: "value", value });
146
+ }
147
+ return next;
148
+ });
149
+ setFieldErrors((prev) => {
150
+ if (!prev.has(name)) return prev;
151
+ const next = new Map(prev);
152
+ next.delete(name);
153
+ return next;
154
+ });
155
+ }, []);
156
+
157
+ const clearField = useCallback((name: string) => {
158
+ setEdits((prev) => new Map(prev).set(name, { kind: "cleared" }));
159
+ }, []);
160
+
161
+ // Required fields gate the submit in insert mode (error prevention).
162
+ const missingRequired = useMemo(() => {
163
+ if (isEdit) return [];
164
+ return collection.fields
165
+ .filter((f) => f.required && f.default === undefined)
166
+ .filter((f) => {
167
+ const edit = edits.get(f.name);
168
+ return !edit || edit.kind === "cleared";
169
+ })
170
+ .map((f) => f.name);
171
+ }, [collection, edits, isEdit]);
172
+
173
+ const handleSubmit = useCallback(async () => {
174
+ setBannerError(null);
175
+
176
+ // Client-side coercion (a projection of the server contract): fail
177
+ // fast with field-adjacent messages; the server stays the enforcer.
178
+ const coerced = new Map<string, JsonValue | null>();
179
+ const errors = new Map<string, string>();
180
+ for (const field of collection.fields) {
181
+ const edit = edits.get(field.name);
182
+ if (!edit) continue;
183
+ if (edit.kind === "cleared") {
184
+ if (isEdit) coerced.set(field.name, null);
185
+ continue;
186
+ }
187
+ const result = coerceFieldValue(field, edit.value);
188
+ if (result.ok) {
189
+ coerced.set(field.name, result.value);
190
+ } else {
191
+ errors.set(field.name, result.error);
192
+ }
193
+ }
194
+ if (errors.size > 0) {
195
+ setFieldErrors(errors);
196
+ return;
197
+ }
198
+
199
+ try {
200
+ let saved: RecordEnvelope;
201
+ if (isEdit) {
202
+ if (coerced.size === 0) {
203
+ onOpenChange(false); // nothing dirty — an honest no-op
204
+ return;
205
+ }
206
+ saved = await update.updateRecord({
207
+ ...scope,
208
+ id: record.id,
209
+ fields: buildUpdateFields(coerced),
210
+ });
211
+ } else {
212
+ const payload: JsonObject = {};
213
+ for (const [name, value] of coerced) {
214
+ if (value !== null) payload[name] = value;
215
+ }
216
+ saved = await insert.insertRecord({ ...scope, record: payload });
217
+ }
218
+ onSaved?.(saved);
219
+ onOpenChange(false);
220
+ } catch (err) {
221
+ placeServerError(err, collection, setFieldErrors, setBannerError);
222
+ }
223
+ }, [collection, edits, isEdit, record, scope, insert, update, onSaved, onOpenChange]);
224
+
225
+ if (!open) return null;
226
+
227
+ return (
228
+ <dialog
229
+ ref={dialogRef}
230
+ onCancel={handleDialogCancel}
231
+ aria-label={
232
+ isEdit
233
+ ? `Edit record in ${collection.name}`
234
+ : `Insert record into ${collection.name}`
235
+ }
236
+ className={cn(
237
+ "fixed inset-0 z-50 m-auto w-full max-w-md rounded-lg border border-border bg-popover p-0 text-popover-foreground shadow-lg",
238
+ "backdrop:bg-black/50",
239
+ "open:animate-in open:fade-in-0 open:zoom-in-95",
240
+ )}
241
+ >
242
+ <div className="flex max-h-[80vh] flex-col gap-4 overflow-y-auto p-6">
243
+ <h3 className="text-base font-semibold text-foreground">
244
+ {isEdit ? "Edit record" : "Insert record"}
245
+ <span className="ml-2 font-mono text-xs font-normal text-muted-foreground">
246
+ {collection.name}
247
+ </span>
248
+ </h3>
249
+
250
+ {bannerError && (
251
+ <div
252
+ role="alert"
253
+ className="rounded-md border border-destructive bg-card px-3 py-2 text-sm text-destructive"
254
+ >
255
+ {bannerError}
256
+ </div>
257
+ )}
258
+
259
+ {isEdit && record && <SystemFieldsSummary record={record} />}
260
+
261
+ <div className="flex flex-col gap-3">
262
+ {collection.fields.map((field) => (
263
+ <FieldRow
264
+ key={field.name}
265
+ field={field}
266
+ value={effectiveValue(field)}
267
+ error={fieldErrors.get(field.name)}
268
+ isEdit={isEdit}
269
+ cleared={edits.get(field.name)?.kind === "cleared"}
270
+ onChange={(v) => setFieldValue(field.name, v)}
271
+ onClear={() => clearField(field.name)}
272
+ />
273
+ ))}
274
+ </div>
275
+
276
+ <div className="flex items-center justify-end gap-2">
277
+ {missingRequired.length > 0 && (
278
+ <span className="mr-auto text-xs text-muted-foreground">
279
+ Required: {missingRequired.join(", ")}
280
+ </span>
281
+ )}
282
+ <button
283
+ type="button"
284
+ onClick={() => onOpenChange(false)}
285
+ className={cn(
286
+ "rounded-md border border-input bg-background px-3 py-1.5 text-sm font-medium text-foreground",
287
+ "hover:bg-accent hover:text-accent-foreground",
288
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
289
+ )}
290
+ >
291
+ Cancel
292
+ </button>
293
+ <button
294
+ type="button"
295
+ onClick={handleSubmit}
296
+ disabled={isSaving || missingRequired.length > 0}
297
+ className={cn(
298
+ "rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground",
299
+ "hover:bg-primary-hover",
300
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
301
+ "disabled:pointer-events-none disabled:opacity-50",
302
+ )}
303
+ >
304
+ {isSaving ? "Saving…" : isEdit ? "Save changes" : "Insert"}
305
+ </button>
306
+ </div>
307
+ </div>
308
+ </dialog>
309
+ );
310
+ }
311
+
312
+ // ---------------------------------------------------------------------------
313
+ // Server-error placement (DD-008 SD-4)
314
+ // ---------------------------------------------------------------------------
315
+
316
+ /**
317
+ * Place a record-write error: field-adjacent when the ErrorInfo
318
+ * constraint name maps to a unique constraint's declared fields, a
319
+ * form-level banner otherwise. Either way the message is the server's
320
+ * bytes, verbatim.
321
+ */
322
+ function placeServerError(
323
+ err: unknown,
324
+ collection: CollectionDeclaration,
325
+ setFieldErrors: (errors: ReadonlyMap<string, string>) => void,
326
+ setBannerError: (message: string) => void,
327
+ ): void {
328
+ const message = getUserMessage(err);
329
+ const constraintName = getRecordConstraint(err);
330
+ if (constraintName) {
331
+ const unique = collection.uniques.find((u) => u.name === constraintName);
332
+ if (unique && unique.fields.length > 0) {
333
+ setFieldErrors(new Map(unique.fields.map((f) => [f, message])));
334
+ return;
335
+ }
336
+ }
337
+ setBannerError(message);
338
+ }
339
+
340
+ // ---------------------------------------------------------------------------
341
+ // Rows
342
+ // ---------------------------------------------------------------------------
343
+
344
+ function FieldRow({
345
+ field,
346
+ value,
347
+ error,
348
+ isEdit,
349
+ cleared,
350
+ onChange,
351
+ onClear,
352
+ }: {
353
+ readonly field: FieldDeclaration;
354
+ readonly value: JsonValue | undefined;
355
+ readonly error: string | undefined;
356
+ readonly isEdit: boolean;
357
+ readonly cleared: boolean;
358
+ readonly onChange: (value: JsonValue | undefined) => void;
359
+ readonly onClear: () => void;
360
+ }) {
361
+ const controlId = useId();
362
+ const showClear = isEdit && !field.required && !cleared && value !== undefined;
363
+
364
+ return (
365
+ <div className="flex flex-col gap-1">
366
+ <div className="flex items-baseline gap-1">
367
+ <label htmlFor={controlId} className="text-xs font-medium text-muted-foreground">
368
+ {field.name}
369
+ {field.required && (
370
+ <span className="text-destructive" aria-label="required">
371
+ {" "}
372
+ *
373
+ </span>
374
+ )}
375
+ </label>
376
+ {showClear && (
377
+ <button
378
+ type="button"
379
+ onClick={onClear}
380
+ aria-label={`Clear ${field.name}`}
381
+ className={cn(
382
+ "ml-auto text-xs text-muted-foreground underline-offset-2 hover:text-foreground hover:underline",
383
+ "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
384
+ )}
385
+ >
386
+ Clear
387
+ </button>
388
+ )}
389
+ {cleared && (
390
+ <span className="ml-auto text-xs italic text-muted-foreground">
391
+ will be cleared
392
+ </span>
393
+ )}
394
+ </div>
395
+ {field.description && (
396
+ <p className="text-xs leading-tight text-muted-foreground">{field.description}</p>
397
+ )}
398
+ <FieldValueControl id={controlId} field={field} value={value} onChange={onChange} />
399
+ {error && (
400
+ <span role="alert" className="text-xs text-destructive">
401
+ {error}
402
+ </span>
403
+ )}
404
+ </div>
405
+ );
406
+ }
407
+
408
+ /** Read-only envelope columns shown in edit mode; never editable, never submitted. */
409
+ function SystemFieldsSummary({ record }: { readonly record: RecordEnvelope }) {
410
+ return (
411
+ <dl className="grid grid-cols-[auto_1fr] gap-x-3 gap-y-1 rounded-md bg-muted p-3 text-xs">
412
+ <dt className="font-medium text-muted-foreground">id</dt>
413
+ <dd className="truncate font-mono text-foreground">{record.id}</dd>
414
+ <dt className="font-medium text-muted-foreground">created_at</dt>
415
+ <dd className="font-mono text-foreground">
416
+ {record.createdAt
417
+ ? formatSystemTimestamp(timestampDate(record.createdAt).toISOString())
418
+ : ""}
419
+ </dd>
420
+ <dt className="font-medium text-muted-foreground">updated_at</dt>
421
+ <dd className="font-mono text-foreground">
422
+ {record.updatedAt
423
+ ? formatSystemTimestamp(timestampDate(record.updatedAt).toISOString())
424
+ : ""}
425
+ </dd>
426
+ <dt className="font-medium text-muted-foreground">created_by</dt>
427
+ <dd className="truncate font-mono text-foreground">
428
+ {formatSubject(record.createdBy)}
429
+ </dd>
430
+ </dl>
431
+ );
432
+ }
@@ -0,0 +1,245 @@
1
+ // Tests for the records browser (DD-008 SD-3): the two denied-state
2
+ // branches, verb-gated write affordances, typed grid rendering, and the
3
+ // partition picker fed by the describe projection unioned with default.
4
+
5
+ import { describe, it, expect, vi, beforeAll, afterEach, beforeEach } from "vitest";
6
+ import { render, screen, cleanup, waitFor } from "@testing-library/react";
7
+ import type { ReactNode } from "react";
8
+ import { create } from "@bufbuild/protobuf";
9
+ import { Code, ConnectError } from "@connectrpc/connect";
10
+ import {
11
+ DatastoreSpecSchema,
12
+ DatastoreVerb,
13
+ } from "@stigmer/protos/ai/stigmer/agentic/datastore/v1/spec_pb";
14
+ import {
15
+ DatastoreDescriptionSchema,
16
+ RecordEnvelopeSchema,
17
+ RecordListSchema,
18
+ } from "@stigmer/protos/ai/stigmer/agentic/datastore/v1/record_io_pb";
19
+ import { StigmerError } from "@stigmer/sdk";
20
+ import { StigmerContext } from "../../context";
21
+ import { FetchCacheContext } from "../../internal/FetchCacheProvider";
22
+ import { CollectionRecordsBrowser } from "../CollectionRecordsBrowser";
23
+
24
+ beforeAll(() => {
25
+ HTMLDialogElement.prototype.showModal = function showModal() {
26
+ this.open = true;
27
+ };
28
+ HTMLDialogElement.prototype.close = function close() {
29
+ this.open = false;
30
+ };
31
+ });
32
+
33
+ afterEach(() => cleanup());
34
+ beforeEach(() => vi.restoreAllMocks());
35
+
36
+ const SPEC = create(DatastoreSpecSchema, {
37
+ collections: [
38
+ {
39
+ name: "bookings",
40
+ fields: [
41
+ { name: "slot_date", type: 6 /* date */, required: true },
42
+ { name: "status", type: 1 /* string */, enumValues: ["confirmed", "cancelled"] },
43
+ ],
44
+ },
45
+ ],
46
+ });
47
+
48
+ function fullAccessDescription(partitions: string[] = []) {
49
+ return create(DatastoreDescriptionSchema, {
50
+ datastore: "clinic",
51
+ partitions,
52
+ collections: [
53
+ {
54
+ name: "bookings",
55
+ access: [
56
+ { verb: DatastoreVerb.read },
57
+ { verb: DatastoreVerb.insert },
58
+ { verb: DatastoreVerb.update },
59
+ { verb: DatastoreVerb.delete },
60
+ ],
61
+ },
62
+ ],
63
+ });
64
+ }
65
+
66
+ function renderBrowser(client: {
67
+ describeDatastore: (...a: unknown[]) => Promise<unknown>;
68
+ findRecords?: (...a: unknown[]) => Promise<unknown>;
69
+ deleteRecord?: (...a: unknown[]) => Promise<unknown>;
70
+ }) {
71
+ const stigmer = {
72
+ datastore: {
73
+ describeDatastore: client.describeDatastore,
74
+ findRecords: client.findRecords ?? vi.fn().mockResolvedValue(create(RecordListSchema, {})),
75
+ deleteRecord: client.deleteRecord ?? vi.fn(),
76
+ insertRecord: vi.fn(),
77
+ updateRecord: vi.fn(),
78
+ },
79
+ };
80
+ function Wrapper({ children }: { children: ReactNode }) {
81
+ return (
82
+ <FetchCacheContext.Provider value={null}>
83
+ <StigmerContext.Provider value={stigmer as never}>{children}</StigmerContext.Provider>
84
+ </FetchCacheContext.Provider>
85
+ );
86
+ }
87
+ return render(
88
+ <CollectionRecordsBrowser org="acme" datastoreSlug="clinic" spec={SPEC} />,
89
+ { wrapper: Wrapper },
90
+ );
91
+ }
92
+
93
+ describe("CollectionRecordsBrowser — denied states (first-class renders)", () => {
94
+ it("renders the denied panel on empty access lists — the primary branch", async () => {
95
+ // Deny-by-default renders as empty access, never an error: a viewer
96
+ // with no record grants gets a successful describe with no verbs.
97
+ const describeDatastore = vi.fn().mockResolvedValue(
98
+ create(DatastoreDescriptionSchema, {
99
+ datastore: "clinic",
100
+ collections: [{ name: "bookings", access: [] }],
101
+ }),
102
+ );
103
+ const findRecords = vi.fn();
104
+ renderBrowser({ describeDatastore, findRecords });
105
+
106
+ await waitFor(() =>
107
+ expect(
108
+ screen.getByText(/You do not have record access to “bookings”/),
109
+ ).toBeTruthy(),
110
+ );
111
+ // Operator guidance, not a silently empty grid.
112
+ expect(screen.getByText(/default_role/)).toBeTruthy();
113
+ // No query is fired without read access.
114
+ expect(findRecords).not.toHaveBeenCalled();
115
+ });
116
+
117
+ it("renders the denied panel on a reach-level PERMISSION_DENIED — the defensive branch", async () => {
118
+ const denial = new StigmerError(
119
+ "permission-denied",
120
+ "unauthorized to use records in this datastore",
121
+ Code.PermissionDenied,
122
+ {
123
+ cause: new ConnectError(
124
+ "unauthorized to use records in this datastore",
125
+ Code.PermissionDenied,
126
+ ),
127
+ },
128
+ );
129
+ const describeDatastore = vi.fn().mockRejectedValue(denial);
130
+ renderBrowser({ describeDatastore });
131
+
132
+ await waitFor(() =>
133
+ expect(
134
+ screen.getByText("unauthorized to use records in this datastore"),
135
+ ).toBeTruthy(),
136
+ );
137
+ });
138
+ });
139
+
140
+ describe("CollectionRecordsBrowser — grid and affordances", () => {
141
+ it("renders typed cells and verb-gated write affordances", async () => {
142
+ const describeDatastore = vi.fn().mockResolvedValue(fullAccessDescription());
143
+ const findRecords = vi.fn().mockResolvedValue(
144
+ create(RecordListSchema, {
145
+ records: [
146
+ create(RecordEnvelopeSchema, {
147
+ id: "dsr_1",
148
+ fields: { slot_date: "2026-07-22", status: "confirmed" } as never,
149
+ }),
150
+ ],
151
+ total: 1,
152
+ }),
153
+ );
154
+ renderBrowser({ describeDatastore, findRecords });
155
+
156
+ await waitFor(() => expect(screen.getByText("2026-07-22")).toBeTruthy());
157
+ expect(screen.getByText("confirmed")).toBeTruthy();
158
+ expect(screen.getByRole("button", { name: "Insert record" })).toBeTruthy();
159
+ expect(screen.getByRole("button", { name: "Edit record dsr_1" })).toBeTruthy();
160
+ expect(screen.getByRole("button", { name: "Delete record dsr_1" })).toBeTruthy();
161
+ });
162
+
163
+ it("hides write affordances the caller's verbs do not include", async () => {
164
+ const describeDatastore = vi.fn().mockResolvedValue(
165
+ create(DatastoreDescriptionSchema, {
166
+ datastore: "clinic",
167
+ collections: [
168
+ { name: "bookings", access: [{ verb: DatastoreVerb.read }] },
169
+ ],
170
+ }),
171
+ );
172
+ const findRecords = vi.fn().mockResolvedValue(
173
+ create(RecordListSchema, {
174
+ records: [create(RecordEnvelopeSchema, { id: "dsr_1" })],
175
+ total: 1,
176
+ }),
177
+ );
178
+ renderBrowser({ describeDatastore, findRecords });
179
+
180
+ await waitFor(() => expect(findRecords).toHaveBeenCalled());
181
+ expect(screen.queryByRole("button", { name: "Insert record" })).toBeNull();
182
+ expect(screen.queryByRole("button", { name: "Edit record dsr_1" })).toBeNull();
183
+ expect(screen.queryByRole("button", { name: "Delete record dsr_1" })).toBeNull();
184
+ });
185
+
186
+ it("offers the partition catalog unioned with default and threads the selection", async () => {
187
+ const describeDatastore = vi
188
+ .fn()
189
+ .mockResolvedValue(fullAccessDescription(["dr-alt"]));
190
+ const findRecords = vi.fn().mockResolvedValue(create(RecordListSchema, {}));
191
+ renderBrowser({ describeDatastore, findRecords });
192
+
193
+ const picker = (await screen.findByLabelText(/Partition/)) as HTMLSelectElement;
194
+ const options = Array.from(picker.options).map((o) => o.value);
195
+ expect(options).toEqual(["default", "dr-alt"]);
196
+ });
197
+
198
+ it("renders the filtered-empty state distinctly from the no-records state", async () => {
199
+ const describeDatastore = vi.fn().mockResolvedValue(fullAccessDescription());
200
+ const findRecords = vi.fn().mockResolvedValue(create(RecordListSchema, {}));
201
+ renderBrowser({ describeDatastore, findRecords });
202
+
203
+ await waitFor(() =>
204
+ expect(screen.getByText(/No records in “bookings” yet/)).toBeTruthy(),
205
+ );
206
+ });
207
+
208
+ it("narrows the grid and filter builder to the caller's readable fields", async () => {
209
+ // A field-restricted read grant (readable_fields on the read verb):
210
+ // the server projects hidden fields out of every envelope and
211
+ // refuses conditions on them, so the grid must not show a
212
+ // permanently-empty column and the builder must not offer an
213
+ // unservable filter (DD-008 invariant 2).
214
+ const describeDatastore = vi.fn().mockResolvedValue(
215
+ create(DatastoreDescriptionSchema, {
216
+ datastore: "clinic",
217
+ collections: [
218
+ {
219
+ name: "bookings",
220
+ access: [{ verb: DatastoreVerb.read, readableFields: ["slot_date"] }],
221
+ },
222
+ ],
223
+ }),
224
+ );
225
+ const findRecords = vi.fn().mockResolvedValue(
226
+ create(RecordListSchema, {
227
+ records: [
228
+ create(RecordEnvelopeSchema, {
229
+ id: "dsr_1",
230
+ fields: { slot_date: "2026-07-22" } as never,
231
+ }),
232
+ ],
233
+ total: 1,
234
+ }),
235
+ );
236
+ renderBrowser({ describeDatastore, findRecords });
237
+
238
+ await waitFor(() => expect(screen.getByText("2026-07-22")).toBeTruthy());
239
+ expect(screen.getByRole("columnheader", { name: "slot_date" })).toBeTruthy();
240
+ expect(screen.queryByRole("columnheader", { name: "status" })).toBeNull();
241
+ // The filter builder's field narrowing under the same prop is
242
+ // pinned in RecordFilterBuilder.test.tsx (its harness mounts the
243
+ // provider the popover portal needs).
244
+ });
245
+ });