@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,133 @@
1
+ // Tests for the guarded delete flow (DD-008 SD-6): status-informed
2
+ // counts, slug-typed arming for non-empty datastores, standard confirm
3
+ // for empty ones, and verbatim guard-refusal rendering.
4
+
5
+ import { describe, it, expect, vi, beforeAll, afterEach, beforeEach } from "vitest";
6
+ import { render, screen, cleanup, waitFor } from "@testing-library/react";
7
+ import userEvent from "@testing-library/user-event";
8
+ import type { ReactNode } from "react";
9
+ import { create } from "@bufbuild/protobuf";
10
+ import { DatastoreSchema } from "@stigmer/protos/ai/stigmer/agentic/datastore/v1/api_pb";
11
+ import { StigmerContext } from "../../context";
12
+ import { DeleteDatastoreDialog } from "../DeleteDatastoreDialog";
13
+
14
+ beforeAll(() => {
15
+ HTMLDialogElement.prototype.showModal = function showModal() {
16
+ this.open = true;
17
+ };
18
+ HTMLDialogElement.prototype.close = function close() {
19
+ this.open = false;
20
+ };
21
+ });
22
+
23
+ afterEach(() => cleanup());
24
+ beforeEach(() => vi.restoreAllMocks());
25
+
26
+ function makeDatastore(recordCounts: number[]) {
27
+ return create(DatastoreSchema, {
28
+ metadata: { id: "dst_1", slug: "clinic", name: "Clinic", org: "acme" },
29
+ status: {
30
+ collections: recordCounts.map((count, i) => ({
31
+ name: `coll_${i}`,
32
+ recordCount: BigInt(count),
33
+ })),
34
+ },
35
+ });
36
+ }
37
+
38
+ function renderDialog(overrides: {
39
+ datastore: ReturnType<typeof makeDatastore>;
40
+ del?: (...a: unknown[]) => Promise<unknown>;
41
+ onDeleted?: () => void;
42
+ onOpenChange?: (open: boolean) => void;
43
+ }) {
44
+ const stigmer = { datastore: { delete: overrides.del ?? vi.fn() } };
45
+ function Wrapper({ children }: { children: ReactNode }) {
46
+ return (
47
+ <StigmerContext.Provider value={stigmer as never}>{children}</StigmerContext.Provider>
48
+ );
49
+ }
50
+ return render(
51
+ <DeleteDatastoreDialog
52
+ open
53
+ onOpenChange={overrides.onOpenChange ?? vi.fn()}
54
+ datastore={overrides.datastore}
55
+ onDeleted={overrides.onDeleted}
56
+ />,
57
+ { wrapper: Wrapper },
58
+ );
59
+ }
60
+
61
+ describe("DeleteDatastoreDialog — non-empty datastore", () => {
62
+ it("pre-fills counts from status and requires typing the slug to arm", async () => {
63
+ const user = userEvent.setup();
64
+ const del = vi.fn().mockResolvedValue({});
65
+ renderDialog({ datastore: makeDatastore([200, 14, 0]), del });
66
+
67
+ // Counts from the loaded status: 214 records across 2 non-empty collections.
68
+ expect(screen.getByText(/214 records/)).toBeTruthy();
69
+ expect(screen.getByText(/2 collections/)).toBeTruthy();
70
+
71
+ const deleteButton = screen.getByRole("button", {
72
+ name: "Delete datastore",
73
+ }) as HTMLButtonElement;
74
+ expect(deleteButton.disabled).toBe(true);
75
+
76
+ // A wrong slug keeps it disarmed.
77
+ const input = screen.getByLabelText("Type clinic to confirm deletion");
78
+ await user.type(input, "clinik");
79
+ expect(deleteButton.disabled).toBe(true);
80
+
81
+ // The exact slug arms it; the armed request carries force.
82
+ await user.clear(input);
83
+ await user.type(input, "clinic");
84
+ expect(deleteButton.disabled).toBe(false);
85
+
86
+ await user.click(deleteButton);
87
+ await waitFor(() => expect(del).toHaveBeenCalled());
88
+ expect(del).toHaveBeenCalledWith({ resourceId: "dst_1", force: true });
89
+ });
90
+
91
+ it("renders a guard refusal verbatim inside the dialog — server stays authoritative", async () => {
92
+ const user = userEvent.setup();
93
+ const refusal = new Error(
94
+ 'datastore "clinic" is referenced by 1 agents (clinic-assistant); remove the datastore_usages references before deleting',
95
+ );
96
+ const del = vi.fn().mockRejectedValue(refusal);
97
+ const onOpenChange = vi.fn();
98
+ renderDialog({ datastore: makeDatastore([214]), del, onOpenChange });
99
+
100
+ await user.type(
101
+ screen.getByLabelText("Type clinic to confirm deletion"),
102
+ "clinic",
103
+ );
104
+ await user.click(screen.getByRole("button", { name: "Delete datastore" }));
105
+
106
+ const alert = await screen.findByRole("alert");
107
+ expect(alert.textContent).toContain("clinic-assistant");
108
+ expect(alert.textContent).toContain("remove the datastore_usages references");
109
+ // The dialog stays open — the operator's next step is detaching.
110
+ expect(onOpenChange).not.toHaveBeenCalledWith(false);
111
+ });
112
+ });
113
+
114
+ describe("DeleteDatastoreDialog — empty datastore", () => {
115
+ it("keeps the standard destructive confirm with no slug typing and no force", async () => {
116
+ const user = userEvent.setup();
117
+ const del = vi.fn().mockResolvedValue({});
118
+ const onDeleted = vi.fn();
119
+ renderDialog({ datastore: makeDatastore([0, 0]), del, onDeleted });
120
+
121
+ expect(screen.getByText(/holds no records/)).toBeTruthy();
122
+ expect(screen.queryByLabelText(/to confirm deletion/)).toBeNull();
123
+
124
+ const deleteButton = screen.getByRole("button", {
125
+ name: "Delete datastore",
126
+ }) as HTMLButtonElement;
127
+ expect(deleteButton.disabled).toBe(false);
128
+
129
+ await user.click(deleteButton);
130
+ await waitFor(() => expect(del).toHaveBeenCalledWith({ resourceId: "dst_1", force: false }));
131
+ expect(onDeleted).toHaveBeenCalled();
132
+ });
133
+ });
@@ -0,0 +1,160 @@
1
+ // Tests for the typed value control: per-FieldType dispatch and the
2
+ // canonical encodings it emits.
3
+
4
+ import { describe, it, expect, vi, afterEach } from "vitest";
5
+ import { render, screen, cleanup, fireEvent } from "@testing-library/react";
6
+ import { create } from "@bufbuild/protobuf";
7
+ import {
8
+ FieldDeclarationSchema,
9
+ FieldType,
10
+ } from "@stigmer/protos/ai/stigmer/agentic/datastore/v1/spec_pb";
11
+ import { FieldValueControl } from "../FieldValueControl";
12
+
13
+ afterEach(() => cleanup());
14
+
15
+ function field(type: FieldType, extras: { enumValues?: string[] } = {}) {
16
+ return create(FieldDeclarationSchema, {
17
+ name: "f",
18
+ type,
19
+ enumValues: extras.enumValues ?? [],
20
+ });
21
+ }
22
+
23
+ describe("FieldValueControl dispatch", () => {
24
+ it("renders a text input for plain strings", () => {
25
+ render(
26
+ <FieldValueControl
27
+ field={field(FieldType.string)}
28
+ value={undefined}
29
+ onChange={vi.fn()}
30
+ aria-label="f"
31
+ />,
32
+ );
33
+ expect((screen.getByLabelText("f") as HTMLInputElement).type).toBe("text");
34
+ });
35
+
36
+ it("renders an enum select when enum_values are declared", () => {
37
+ const onChange = vi.fn();
38
+ render(
39
+ <FieldValueControl
40
+ field={field(FieldType.string, { enumValues: ["confirmed", "cancelled"] })}
41
+ value={undefined}
42
+ onChange={onChange}
43
+ aria-label="f"
44
+ />,
45
+ );
46
+ const select = screen.getByLabelText("f") as HTMLSelectElement;
47
+ expect(Array.from(select.options).map((o) => o.value)).toEqual([
48
+ "",
49
+ "confirmed",
50
+ "cancelled",
51
+ ]);
52
+ fireEvent.change(select, { target: { value: "confirmed" } });
53
+ expect(onChange).toHaveBeenCalledWith("confirmed");
54
+ });
55
+
56
+ it("emits numbers (not strings) for numeric fields", () => {
57
+ const onChange = vi.fn();
58
+ render(
59
+ <FieldValueControl
60
+ field={field(FieldType.integer)}
61
+ value={undefined}
62
+ onChange={onChange}
63
+ aria-label="f"
64
+ />,
65
+ );
66
+ fireEvent.change(screen.getByLabelText("f"), { target: { value: "42" } });
67
+ expect(onChange).toHaveBeenCalledWith(42);
68
+ });
69
+
70
+ it("bool renders a tri-state select — unset is distinct from false", () => {
71
+ const onChange = vi.fn();
72
+ render(
73
+ <FieldValueControl
74
+ field={field(FieldType.bool)}
75
+ value={undefined}
76
+ onChange={onChange}
77
+ aria-label="f"
78
+ />,
79
+ );
80
+ const select = screen.getByLabelText("f") as HTMLSelectElement;
81
+ expect(select.value).toBe("");
82
+ fireEvent.change(select, { target: { value: "false" } });
83
+ expect(onChange).toHaveBeenCalledWith(false);
84
+ });
85
+
86
+ it("date and time render native pickers", () => {
87
+ const { rerender } = render(
88
+ <FieldValueControl
89
+ field={field(FieldType.date)}
90
+ value={undefined}
91
+ onChange={vi.fn()}
92
+ aria-label="f"
93
+ />,
94
+ );
95
+ expect((screen.getByLabelText("f") as HTMLInputElement).type).toBe("date");
96
+ rerender(
97
+ <FieldValueControl
98
+ field={field(FieldType.time)}
99
+ value={undefined}
100
+ onChange={vi.fn()}
101
+ aria-label="f"
102
+ />,
103
+ );
104
+ expect((screen.getByLabelText("f") as HTMLInputElement).type).toBe("time");
105
+ });
106
+
107
+ it("timestamp converts the local picker value to RFC 3339 UTC", () => {
108
+ const onChange = vi.fn();
109
+ render(
110
+ <FieldValueControl
111
+ field={field(FieldType.timestamp)}
112
+ value={undefined}
113
+ onChange={onChange}
114
+ aria-label="f"
115
+ />,
116
+ );
117
+ fireEvent.change(screen.getByLabelText("f"), {
118
+ target: { value: "2026-07-22T10:00:00" },
119
+ });
120
+ const emitted = onChange.mock.calls[0][0] as string;
121
+ // Local wall time converts to a UTC instant — exact hour depends on
122
+ // the test host's zone, so assert the canonical UTC shape.
123
+ expect(emitted).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/);
124
+ expect(Date.parse(emitted)).toBe(new Date("2026-07-22T10:00:00").getTime());
125
+ });
126
+
127
+ it("json keeps invalid input local and emits only valid JSON", () => {
128
+ const onChange = vi.fn();
129
+ render(
130
+ <FieldValueControl
131
+ field={field(FieldType.json)}
132
+ value={undefined}
133
+ onChange={onChange}
134
+ aria-label="f"
135
+ />,
136
+ );
137
+ const textarea = screen.getByLabelText("f");
138
+ fireEvent.change(textarea, { target: { value: '{"a":' } });
139
+ expect(onChange).not.toHaveBeenCalled();
140
+ expect(screen.getByRole("alert").textContent).toBe("Invalid JSON");
141
+
142
+ fireEvent.change(textarea, { target: { value: '{"a": 1}' } });
143
+ expect(onChange).toHaveBeenCalledWith({ a: 1 });
144
+ expect(screen.queryByRole("alert")).toBeNull();
145
+ });
146
+
147
+ it("empty input emits undefined (the field will not travel)", () => {
148
+ const onChange = vi.fn();
149
+ render(
150
+ <FieldValueControl
151
+ field={field(FieldType.string)}
152
+ value={"x"}
153
+ onChange={onChange}
154
+ aria-label="f"
155
+ />,
156
+ );
157
+ fireEvent.change(screen.getByLabelText("f"), { target: { value: "" } });
158
+ expect(onChange).toHaveBeenCalledWith(undefined);
159
+ });
160
+ });
@@ -0,0 +1,155 @@
1
+ // Tests for the filter builder (DD-008 SD-3): chips, draft-then-apply,
2
+ // and the structurally-enforced operator matrix.
3
+
4
+ import { describe, it, expect, vi, afterEach } from "vitest";
5
+ import { render, screen, cleanup, fireEvent, waitFor } from "@testing-library/react";
6
+ import { create } from "@bufbuild/protobuf";
7
+ import {
8
+ CollectionDeclarationSchema,
9
+ FieldType,
10
+ } from "@stigmer/protos/ai/stigmer/agentic/datastore/v1/spec_pb";
11
+ import { RecordConditionOp } from "@stigmer/protos/ai/stigmer/agentic/datastore/v1/record_io_pb";
12
+ import { StigmerProvider } from "../../provider";
13
+ import { RecordFilterBuilder } from "../RecordFilterBuilder";
14
+ import type { RecordConditionDraft } from "../recordFilter";
15
+
16
+ afterEach(() => cleanup());
17
+
18
+ const COLLECTION = create(CollectionDeclarationSchema, {
19
+ name: "bookings",
20
+ fields: [
21
+ { name: "slot_date", type: FieldType.date, required: true },
22
+ { name: "status", type: FieldType.string, enumValues: ["confirmed", "cancelled"] },
23
+ { name: "notes", type: FieldType.json },
24
+ ],
25
+ });
26
+
27
+ function renderBuilder(
28
+ conditions: readonly RecordConditionDraft[],
29
+ onChange = vi.fn(),
30
+ readableFields?: readonly string[],
31
+ ) {
32
+ render(
33
+ <StigmerProvider client={{} as never}>
34
+ <RecordFilterBuilder
35
+ collection={COLLECTION}
36
+ readableFields={readableFields}
37
+ conditions={conditions}
38
+ onChange={onChange}
39
+ />
40
+ </StigmerProvider>,
41
+ );
42
+ return onChange;
43
+ }
44
+
45
+ describe("RecordFilterBuilder — chips", () => {
46
+ it("renders active conditions as removable chips", () => {
47
+ const onChange = renderBuilder([
48
+ { field: "status", op: RecordConditionOp.eq, value: "confirmed" },
49
+ { field: "slot_date", op: RecordConditionOp.gte, value: "2026-07-01" },
50
+ ]);
51
+
52
+ expect(screen.getByText("status = confirmed")).toBeTruthy();
53
+ expect(screen.getByText("slot_date ≥ 2026-07-01")).toBeTruthy();
54
+
55
+ fireEvent.click(
56
+ screen.getByRole("button", { name: "Remove filter status = confirmed" }),
57
+ );
58
+ expect(onChange).toHaveBeenCalledWith([
59
+ { field: "slot_date", op: RecordConditionOp.gte, value: "2026-07-01" },
60
+ ]);
61
+ });
62
+
63
+ it("offers clear-all only with more than one condition", () => {
64
+ renderBuilder([{ field: "status", op: RecordConditionOp.is_null }]);
65
+ expect(screen.queryByRole("button", { name: "Clear all" })).toBeNull();
66
+
67
+ cleanup();
68
+ const onChange = renderBuilder([
69
+ { field: "status", op: RecordConditionOp.is_null },
70
+ { field: "slot_date", op: RecordConditionOp.eq, value: "2026-07-01" },
71
+ ]);
72
+ fireEvent.click(screen.getByRole("button", { name: "Clear all" }));
73
+ expect(onChange).toHaveBeenCalledWith([]);
74
+ });
75
+ });
76
+
77
+ describe("RecordFilterBuilder — draft-then-apply popover", () => {
78
+ it("offers only filterable fields and the per-type operator matrix", async () => {
79
+ renderBuilder([]);
80
+ fireEvent.click(screen.getByRole("button", { name: /Add filter/ }));
81
+
82
+ const fieldSelect = (await screen.findByLabelText(/Field/)) as HTMLSelectElement;
83
+ const fieldNames = Array.from(fieldSelect.options).map((o) => o.value);
84
+ // json is structurally unofferable; system fields are present.
85
+ expect(fieldNames).toEqual(["", "slot_date", "status", "id", "created_at", "updated_at"]);
86
+
87
+ // A date field offers range operators only — no in/not_in, and no
88
+ // null tests because it is required.
89
+ fireEvent.change(fieldSelect, { target: { value: "slot_date" } });
90
+ const opSelect = (await screen.findByLabelText(/Operator/)) as HTMLSelectElement;
91
+ const ops = Array.from(opSelect.options)
92
+ .map((o) => o.textContent)
93
+ .filter((t) => t !== "— Select —");
94
+ expect(ops).toEqual(["=", "≠", ">", "≥", "<", "≤"]);
95
+ });
96
+
97
+ it("narrows to the caller's readable fields; system fields stay offered", async () => {
98
+ // A field-restricted read grant (readable_fields on the read verb):
99
+ // the server refuses conditions on unreadable fields, so the
100
+ // builder must not offer them (DD-008 invariant 2).
101
+ renderBuilder([], vi.fn(), ["status"]);
102
+ fireEvent.click(screen.getByRole("button", { name: /Add filter/ }));
103
+
104
+ const fieldSelect = (await screen.findByLabelText(/Field/)) as HTMLSelectElement;
105
+ const fieldNames = Array.from(fieldSelect.options).map((o) => o.value);
106
+ expect(fieldNames).toEqual(["", "status", "id", "created_at", "updated_at"]);
107
+ });
108
+
109
+ it("applies a complete draft and closes; incomplete drafts cannot apply", async () => {
110
+ const onChange = renderBuilder([]);
111
+ fireEvent.click(screen.getByRole("button", { name: /Add filter/ }));
112
+
113
+ const fieldSelect = (await screen.findByLabelText(/Field/)) as HTMLSelectElement;
114
+ fireEvent.change(fieldSelect, { target: { value: "status" } });
115
+ const opSelect = (await screen.findByLabelText(/Operator/)) as HTMLSelectElement;
116
+ fireEvent.change(opSelect, { target: { value: String(RecordConditionOp.eq) } });
117
+
118
+ // No value yet — Apply is disabled (draft-then-apply: nothing fires).
119
+ const apply = screen.getByRole("button", { name: "Apply" }) as HTMLButtonElement;
120
+ expect(apply.disabled).toBe(true);
121
+ expect(onChange).not.toHaveBeenCalled();
122
+
123
+ fireEvent.change(screen.getByLabelText("Filter value for status"), {
124
+ target: { value: "confirmed" },
125
+ });
126
+ expect(apply.disabled).toBe(false);
127
+
128
+ fireEvent.click(apply);
129
+ await waitFor(() =>
130
+ expect(onChange).toHaveBeenCalledWith([
131
+ { field: "status", op: RecordConditionOp.eq, value: "confirmed", values: undefined },
132
+ ]),
133
+ );
134
+ });
135
+
136
+ it("value-less operators apply without a value control", async () => {
137
+ const onChange = renderBuilder([]);
138
+ fireEvent.click(screen.getByRole("button", { name: /Add filter/ }));
139
+
140
+ fireEvent.change(await screen.findByLabelText(/Field/), {
141
+ target: { value: "status" },
142
+ });
143
+ fireEvent.change(await screen.findByLabelText(/Operator/), {
144
+ target: { value: String(RecordConditionOp.is_null) },
145
+ });
146
+
147
+ expect(screen.queryByLabelText("Filter value for status")).toBeNull();
148
+ fireEvent.click(screen.getByRole("button", { name: "Apply" }));
149
+ await waitFor(() =>
150
+ expect(onChange).toHaveBeenCalledWith([
151
+ { field: "status", op: RecordConditionOp.is_null, value: undefined, values: undefined },
152
+ ]),
153
+ );
154
+ });
155
+ });
@@ -0,0 +1,227 @@
1
+ // Tests for the record write surface (DD-008 SD-4): required gating,
2
+ // partial-merge with explicit-null clears, verbatim constraint-error
3
+ // placement, and read-only system fields.
4
+
5
+ import { describe, it, expect, vi, beforeAll, afterEach, beforeEach } from "vitest";
6
+ import { render, screen, cleanup, waitFor } from "@testing-library/react";
7
+ import userEvent from "@testing-library/user-event";
8
+ import type { ReactNode } from "react";
9
+ import { Code, ConnectError } from "@connectrpc/connect";
10
+ import { ErrorInfoSchema } from "@stigmer/protos/google/rpc/error_details_pb";
11
+ import { create } from "@bufbuild/protobuf";
12
+ import {
13
+ CollectionDeclarationSchema,
14
+ FieldType,
15
+ } from "@stigmer/protos/ai/stigmer/agentic/datastore/v1/spec_pb";
16
+ import { RecordEnvelopeSchema } from "@stigmer/protos/ai/stigmer/agentic/datastore/v1/record_io_pb";
17
+ import { StigmerContext } from "../../context";
18
+ import { RecordFormPanel } from "../RecordFormPanel";
19
+
20
+ beforeAll(() => {
21
+ HTMLDialogElement.prototype.showModal = function showModal() {
22
+ this.open = true;
23
+ };
24
+ HTMLDialogElement.prototype.close = function close() {
25
+ this.open = false;
26
+ };
27
+ });
28
+
29
+ afterEach(() => cleanup());
30
+ beforeEach(() => vi.restoreAllMocks());
31
+
32
+ const COLLECTION = create(CollectionDeclarationSchema, {
33
+ name: "bookings",
34
+ fields: [
35
+ { name: "slot_date", type: FieldType.date, required: true },
36
+ { name: "patient_phone", type: FieldType.string },
37
+ { name: "notes", type: FieldType.string },
38
+ ],
39
+ uniques: [
40
+ {
41
+ name: "one_confirmed_per_slot",
42
+ fields: ["slot_date"],
43
+ message: "that slot is already booked",
44
+ },
45
+ ],
46
+ });
47
+
48
+ const SCOPE = {
49
+ org: "acme",
50
+ datastore: "clinic",
51
+ collection: "bookings",
52
+ partition: "default",
53
+ } as const;
54
+
55
+ function renderPanel(overrides: {
56
+ insertRecord?: (...args: unknown[]) => Promise<unknown>;
57
+ updateRecord?: (...args: unknown[]) => Promise<unknown>;
58
+ record?: ReturnType<typeof create<typeof RecordEnvelopeSchema>> | null;
59
+ onSaved?: (envelope: unknown) => void;
60
+ onOpenChange?: (open: boolean) => void;
61
+ }) {
62
+ const client = {
63
+ datastore: {
64
+ insertRecord: overrides.insertRecord ?? vi.fn(),
65
+ updateRecord: overrides.updateRecord ?? vi.fn(),
66
+ },
67
+ };
68
+ function Wrapper({ children }: { children: ReactNode }) {
69
+ return (
70
+ <StigmerContext.Provider value={client as never}>{children}</StigmerContext.Provider>
71
+ );
72
+ }
73
+ return render(
74
+ <RecordFormPanel
75
+ open
76
+ onOpenChange={overrides.onOpenChange ?? vi.fn()}
77
+ scope={SCOPE}
78
+ collection={COLLECTION}
79
+ record={overrides.record ?? null}
80
+ onSaved={overrides.onSaved}
81
+ />,
82
+ { wrapper: Wrapper },
83
+ );
84
+ }
85
+
86
+ describe("RecordFormPanel — insert", () => {
87
+ it("gates the submit on required fields (error prevention)", () => {
88
+ renderPanel({});
89
+ const submit = screen.getByRole("button", { name: "Insert" }) as HTMLButtonElement;
90
+ expect(submit.disabled).toBe(true);
91
+ expect(screen.getByText(/Required: slot_date/)).toBeTruthy();
92
+ });
93
+
94
+ it("submits only the fields the operator set", async () => {
95
+ const user = userEvent.setup();
96
+ const insertRecord = vi.fn().mockResolvedValue(create(RecordEnvelopeSchema, { id: "dsr_1" }));
97
+ const onSaved = vi.fn();
98
+ const onOpenChange = vi.fn();
99
+ renderPanel({ insertRecord, onSaved, onOpenChange });
100
+
101
+ await user.type(screen.getByLabelText(/^slot_date/), "2026-07-22");
102
+ await user.type(screen.getByLabelText("patient_phone"), "+15550100");
103
+ await user.click(screen.getByRole("button", { name: "Insert" }));
104
+
105
+ await waitFor(() => expect(insertRecord).toHaveBeenCalled());
106
+ expect(insertRecord).toHaveBeenCalledWith(
107
+ expect.objectContaining({
108
+ record: { slot_date: "2026-07-22", patient_phone: "+15550100" },
109
+ partition: "default",
110
+ }),
111
+ );
112
+ expect(onSaved).toHaveBeenCalled();
113
+ expect(onOpenChange).toHaveBeenCalledWith(false);
114
+ });
115
+
116
+ it("places a unique-constraint violation next to its declared fields, verbatim", async () => {
117
+ const user = userEvent.setup();
118
+ const violation = new ConnectError(
119
+ "that slot is already booked",
120
+ Code.AlreadyExists,
121
+ undefined,
122
+ [
123
+ {
124
+ desc: ErrorInfoSchema,
125
+ value: {
126
+ domain: "datastore.stigmer.ai",
127
+ reason: "CONSTRAINT_VIOLATION",
128
+ metadata: { constraint: "one_confirmed_per_slot" },
129
+ },
130
+ },
131
+ ],
132
+ );
133
+ renderPanel({ insertRecord: vi.fn().mockRejectedValue(violation) });
134
+
135
+ await user.type(screen.getByLabelText(/^slot_date/), "2026-07-22");
136
+ await user.click(screen.getByRole("button", { name: "Insert" }));
137
+
138
+ // Field-adjacent: the message renders as the slot_date field's error,
139
+ // byte-for-byte — not as a form-level banner.
140
+ const alert = await screen.findByRole("alert");
141
+ expect(alert.textContent).toContain("that slot is already booked");
142
+ const slotDateControl = screen.getByLabelText(/^slot_date/);
143
+ expect(
144
+ slotDateControl.parentElement?.textContent,
145
+ ).toContain("that slot is already booked");
146
+ });
147
+
148
+ it("renders an unmapped violation as a form-level banner, verbatim", async () => {
149
+ const user = userEvent.setup();
150
+ const violation = new ConnectError(
151
+ "bookings must be within opening hours",
152
+ Code.FailedPrecondition,
153
+ undefined,
154
+ [
155
+ {
156
+ desc: ErrorInfoSchema,
157
+ value: {
158
+ domain: "datastore.stigmer.ai",
159
+ reason: "CONSTRAINT_VIOLATION",
160
+ metadata: { constraint: "within_hours" }, // a check — no field mapping
161
+ },
162
+ },
163
+ ],
164
+ );
165
+ renderPanel({ insertRecord: vi.fn().mockRejectedValue(violation) });
166
+
167
+ await user.type(screen.getByLabelText(/^slot_date/), "2026-07-22");
168
+ await user.click(screen.getByRole("button", { name: "Insert" }));
169
+
170
+ const alert = await screen.findByRole("alert");
171
+ expect(alert.textContent).toContain("bookings must be within opening hours");
172
+ });
173
+ });
174
+
175
+ describe("RecordFormPanel — edit (partial merge)", () => {
176
+ const record = create(RecordEnvelopeSchema, {
177
+ id: "dsr_1",
178
+ fields: {
179
+ slot_date: "2026-07-22",
180
+ patient_phone: "+15550100",
181
+ notes: "call first",
182
+ } as never,
183
+ });
184
+
185
+ it("shows read-only system fields", () => {
186
+ renderPanel({ record });
187
+ expect(screen.getByText("dsr_1")).toBeTruthy();
188
+ // The envelope columns are display-only — no input is named "id".
189
+ expect(screen.queryByLabelText("id")).toBeNull();
190
+ });
191
+
192
+ it("submits only dirty fields; explicit clear travels as null", async () => {
193
+ const user = userEvent.setup();
194
+ const updateRecord = vi.fn().mockResolvedValue(create(RecordEnvelopeSchema, { id: "dsr_1" }));
195
+ renderPanel({ record, updateRecord });
196
+
197
+ // Dirty one field…
198
+ const phone = screen.getByLabelText("patient_phone");
199
+ await user.clear(phone);
200
+ await user.type(phone, "+15550199");
201
+ // …and clear another.
202
+ await user.click(screen.getByRole("button", { name: "Clear notes" }));
203
+
204
+ await user.click(screen.getByRole("button", { name: "Save changes" }));
205
+
206
+ await waitFor(() => expect(updateRecord).toHaveBeenCalled());
207
+ expect(updateRecord).toHaveBeenCalledWith(
208
+ expect.objectContaining({
209
+ id: "dsr_1",
210
+ // slot_date untouched — absent from the payload entirely.
211
+ fields: { patient_phone: "+15550199", notes: null },
212
+ }),
213
+ );
214
+ });
215
+
216
+ it("closes without a request when nothing is dirty", async () => {
217
+ const user = userEvent.setup();
218
+ const updateRecord = vi.fn();
219
+ const onOpenChange = vi.fn();
220
+ renderPanel({ record, updateRecord, onOpenChange });
221
+
222
+ await user.click(screen.getByRole("button", { name: "Save changes" }));
223
+
224
+ expect(updateRecord).not.toHaveBeenCalled();
225
+ expect(onOpenChange).toHaveBeenCalledWith(false);
226
+ });
227
+ });