@stigmer/react 0.3.4 → 0.4.1

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 (450) hide show
  1. package/billing/AutoRechargeCard.d.ts +38 -0
  2. package/billing/AutoRechargeCard.d.ts.map +1 -0
  3. package/billing/AutoRechargeCard.js +90 -0
  4. package/billing/AutoRechargeCard.js.map +1 -0
  5. package/billing/BillingSection.d.ts +32 -0
  6. package/billing/BillingSection.d.ts.map +1 -0
  7. package/billing/BillingSection.js +81 -0
  8. package/billing/BillingSection.js.map +1 -0
  9. package/billing/CreditBalanceCard.d.ts +25 -0
  10. package/billing/CreditBalanceCard.d.ts.map +1 -0
  11. package/billing/CreditBalanceCard.js +28 -0
  12. package/billing/CreditBalanceCard.js.map +1 -0
  13. package/billing/CreditLedgerTable.d.ts +22 -0
  14. package/billing/CreditLedgerTable.d.ts.map +1 -0
  15. package/billing/CreditLedgerTable.js +75 -0
  16. package/billing/CreditLedgerTable.js.map +1 -0
  17. package/billing/CreditPackGrid.d.ts +31 -0
  18. package/billing/CreditPackGrid.d.ts.map +1 -0
  19. package/billing/CreditPackGrid.js +35 -0
  20. package/billing/CreditPackGrid.js.map +1 -0
  21. package/billing/LowBalanceBanner.d.ts +26 -0
  22. package/billing/LowBalanceBanner.d.ts.map +1 -0
  23. package/billing/LowBalanceBanner.js +33 -0
  24. package/billing/LowBalanceBanner.js.map +1 -0
  25. package/billing/PaymentMethodCard.d.ts +35 -0
  26. package/billing/PaymentMethodCard.d.ts.map +1 -0
  27. package/billing/PaymentMethodCard.js +48 -0
  28. package/billing/PaymentMethodCard.js.map +1 -0
  29. package/billing/credit-packs.d.ts +25 -0
  30. package/billing/credit-packs.d.ts.map +1 -0
  31. package/billing/credit-packs.js +39 -0
  32. package/billing/credit-packs.js.map +1 -0
  33. package/billing/format.d.ts +39 -0
  34. package/billing/format.d.ts.map +1 -0
  35. package/billing/format.js +90 -0
  36. package/billing/format.js.map +1 -0
  37. package/billing/index.d.ts +32 -0
  38. package/billing/index.d.ts.map +1 -0
  39. package/billing/index.js +21 -0
  40. package/billing/index.js.map +1 -0
  41. package/billing/useBillingAccount.d.ts +40 -0
  42. package/billing/useBillingAccount.d.ts.map +1 -0
  43. package/billing/useBillingAccount.js +35 -0
  44. package/billing/useBillingAccount.js.map +1 -0
  45. package/billing/useBillingUsageReport.d.ts +42 -0
  46. package/billing/useBillingUsageReport.d.ts.map +1 -0
  47. package/billing/useBillingUsageReport.js +43 -0
  48. package/billing/useBillingUsageReport.js.map +1 -0
  49. package/billing/useCreateBillingPortalSession.d.ts +35 -0
  50. package/billing/useCreateBillingPortalSession.d.ts.map +1 -0
  51. package/billing/useCreateBillingPortalSession.js +50 -0
  52. package/billing/useCreateBillingPortalSession.js.map +1 -0
  53. package/billing/useCreateCheckoutSession.d.ts +54 -0
  54. package/billing/useCreateCheckoutSession.d.ts.map +1 -0
  55. package/billing/useCreateCheckoutSession.js +58 -0
  56. package/billing/useCreateCheckoutSession.js.map +1 -0
  57. package/billing/useCreditLedger.d.ts +48 -0
  58. package/billing/useCreditLedger.d.ts.map +1 -0
  59. package/billing/useCreditLedger.js +39 -0
  60. package/billing/useCreditLedger.js.map +1 -0
  61. package/billing/useCustomerModelPricing.d.ts +41 -0
  62. package/billing/useCustomerModelPricing.d.ts.map +1 -0
  63. package/billing/useCustomerModelPricing.js +37 -0
  64. package/billing/useCustomerModelPricing.js.map +1 -0
  65. package/billing/useSetAutoRechargeConfig.d.ts +50 -0
  66. package/billing/useSetAutoRechargeConfig.d.ts.map +1 -0
  67. package/billing/useSetAutoRechargeConfig.js +53 -0
  68. package/billing/useSetAutoRechargeConfig.js.map +1 -0
  69. package/composer/ComposerToolbar.js +1 -1
  70. package/composer/ComposerToolbar.js.map +1 -1
  71. package/composer/SessionComposer.d.ts +1 -1
  72. package/composer/SessionComposer.d.ts.map +1 -1
  73. package/composer/SessionComposer.js +19 -4
  74. package/composer/SessionComposer.js.map +1 -1
  75. package/composer/__tests__/SessionComposer-memo.test.d.ts +2 -0
  76. package/composer/__tests__/SessionComposer-memo.test.d.ts.map +1 -0
  77. package/composer/__tests__/SessionComposer-memo.test.js +23 -0
  78. package/composer/__tests__/SessionComposer-memo.test.js.map +1 -0
  79. package/execution/ApprovalCard.d.ts +5 -1
  80. package/execution/ApprovalCard.d.ts.map +1 -1
  81. package/execution/ApprovalCard.js +7 -3
  82. package/execution/ApprovalCard.js.map +1 -1
  83. package/execution/ExecutionPhaseBadge.d.ts +1 -1
  84. package/execution/ExecutionPhaseBadge.d.ts.map +1 -1
  85. package/execution/ExecutionPhaseBadge.js +3 -2
  86. package/execution/ExecutionPhaseBadge.js.map +1 -1
  87. package/execution/MessageEntry.d.ts +7 -3
  88. package/execution/MessageEntry.d.ts.map +1 -1
  89. package/execution/MessageEntry.js +19 -8
  90. package/execution/MessageEntry.js.map +1 -1
  91. package/execution/MessageThread.d.ts +84 -3
  92. package/execution/MessageThread.d.ts.map +1 -1
  93. package/execution/MessageThread.js +113 -65
  94. package/execution/MessageThread.js.map +1 -1
  95. package/execution/SetupProgress.d.ts +1 -1
  96. package/execution/SetupProgress.d.ts.map +1 -1
  97. package/execution/SetupProgress.js +3 -3
  98. package/execution/SetupProgress.js.map +1 -1
  99. package/execution/SubAgentSection.d.ts +5 -1
  100. package/execution/SubAgentSection.d.ts.map +1 -1
  101. package/execution/SubAgentSection.js +13 -7
  102. package/execution/SubAgentSection.js.map +1 -1
  103. package/execution/ThreadSkeleton.d.ts +22 -0
  104. package/execution/ThreadSkeleton.d.ts.map +1 -0
  105. package/execution/ThreadSkeleton.js +26 -0
  106. package/execution/ThreadSkeleton.js.map +1 -0
  107. package/execution/ToolCallGroup.d.ts +16 -1
  108. package/execution/ToolCallGroup.d.ts.map +1 -1
  109. package/execution/ToolCallGroup.js +31 -3
  110. package/execution/ToolCallGroup.js.map +1 -1
  111. package/execution/UsageWidget.d.ts +1 -1
  112. package/execution/__tests__/message-entry.test.d.ts +2 -0
  113. package/execution/__tests__/message-entry.test.d.ts.map +1 -0
  114. package/execution/__tests__/message-entry.test.js +178 -0
  115. package/execution/__tests__/message-entry.test.js.map +1 -0
  116. package/execution/__tests__/thread-keys.test.d.ts +2 -0
  117. package/execution/__tests__/thread-keys.test.d.ts.map +1 -0
  118. package/execution/__tests__/thread-keys.test.js +289 -0
  119. package/execution/__tests__/thread-keys.test.js.map +1 -0
  120. package/execution/__tests__/thread-memoization.test.d.ts +2 -0
  121. package/execution/__tests__/thread-memoization.test.d.ts.map +1 -0
  122. package/execution/__tests__/thread-memoization.test.js +262 -0
  123. package/execution/__tests__/thread-memoization.test.js.map +1 -0
  124. package/execution/__tests__/thread-skeleton.test.d.ts +2 -0
  125. package/execution/__tests__/thread-skeleton.test.d.ts.map +1 -0
  126. package/execution/__tests__/thread-skeleton.test.js +35 -0
  127. package/execution/__tests__/thread-skeleton.test.js.map +1 -0
  128. package/execution/__tests__/useExecutionStream.test.js +73 -10
  129. package/execution/__tests__/useExecutionStream.test.js.map +1 -1
  130. package/execution/__tests__/useSessionVariables-stability.test.d.ts +2 -0
  131. package/execution/__tests__/useSessionVariables-stability.test.d.ts.map +1 -0
  132. package/execution/__tests__/useSessionVariables-stability.test.js +69 -0
  133. package/execution/__tests__/useSessionVariables-stability.test.js.map +1 -0
  134. package/execution/__tests__/virtualized-thread.test.d.ts +2 -0
  135. package/execution/__tests__/virtualized-thread.test.d.ts.map +1 -0
  136. package/execution/__tests__/virtualized-thread.test.js +274 -0
  137. package/execution/__tests__/virtualized-thread.test.js.map +1 -0
  138. package/execution/index.d.ts +2 -0
  139. package/execution/index.d.ts.map +1 -1
  140. package/execution/index.js +1 -0
  141. package/execution/index.js.map +1 -1
  142. package/execution/useExecutionStream.d.ts +35 -10
  143. package/execution/useExecutionStream.d.ts.map +1 -1
  144. package/execution/useExecutionStream.js +79 -40
  145. package/execution/useExecutionStream.js.map +1 -1
  146. package/execution/useSessionVariables.d.ts.map +1 -1
  147. package/execution/useSessionVariables.js +4 -3
  148. package/execution/useSessionVariables.js.map +1 -1
  149. package/github/useGitHubConnection.d.ts.map +1 -1
  150. package/github/useGitHubConnection.js +5 -4
  151. package/github/useGitHubConnection.js.map +1 -1
  152. package/identity-account/index.d.ts +2 -0
  153. package/identity-account/index.d.ts.map +1 -0
  154. package/identity-account/index.js +2 -0
  155. package/identity-account/index.js.map +1 -0
  156. package/identity-account/useIdentityAccountGate.d.ts +81 -0
  157. package/identity-account/useIdentityAccountGate.d.ts.map +1 -0
  158. package/identity-account/useIdentityAccountGate.js +100 -0
  159. package/identity-account/useIdentityAccountGate.js.map +1 -0
  160. package/index.d.ts +10 -4
  161. package/index.d.ts.map +1 -1
  162. package/index.js +8 -2
  163. package/index.js.map +1 -1
  164. package/internal/FetchCacheProvider.d.ts +44 -0
  165. package/internal/FetchCacheProvider.d.ts.map +1 -0
  166. package/internal/FetchCacheProvider.js +61 -0
  167. package/internal/FetchCacheProvider.js.map +1 -0
  168. package/internal/JumpToLatestButton.d.ts +14 -0
  169. package/internal/JumpToLatestButton.d.ts.map +1 -0
  170. package/internal/JumpToLatestButton.js +19 -0
  171. package/internal/JumpToLatestButton.js.map +1 -0
  172. package/internal/ThreadItemWrapper.d.ts +20 -0
  173. package/internal/ThreadItemWrapper.d.ts.map +1 -0
  174. package/internal/ThreadItemWrapper.js +44 -0
  175. package/internal/ThreadItemWrapper.js.map +1 -0
  176. package/internal/VirtualizedThread.d.ts +25 -0
  177. package/internal/VirtualizedThread.d.ts.map +1 -0
  178. package/internal/VirtualizedThread.js +58 -0
  179. package/internal/VirtualizedThread.js.map +1 -0
  180. package/internal/__tests__/fetch-cache.test.d.ts +2 -0
  181. package/internal/__tests__/fetch-cache.test.d.ts.map +1 -0
  182. package/internal/__tests__/fetch-cache.test.js +182 -0
  183. package/internal/__tests__/fetch-cache.test.js.map +1 -0
  184. package/internal/__tests__/stream-controller.test.d.ts +2 -0
  185. package/internal/__tests__/stream-controller.test.d.ts.map +1 -0
  186. package/internal/__tests__/stream-controller.test.js +294 -0
  187. package/internal/__tests__/stream-controller.test.js.map +1 -0
  188. package/internal/__tests__/thread-animation.test.d.ts +2 -0
  189. package/internal/__tests__/thread-animation.test.d.ts.map +1 -0
  190. package/internal/__tests__/thread-animation.test.js +79 -0
  191. package/internal/__tests__/thread-animation.test.js.map +1 -0
  192. package/internal/__tests__/useAutoScroll.test.d.ts +2 -0
  193. package/internal/__tests__/useAutoScroll.test.d.ts.map +1 -0
  194. package/internal/__tests__/useAutoScroll.test.js +188 -0
  195. package/internal/__tests__/useAutoScroll.test.js.map +1 -0
  196. package/internal/__tests__/useFetch-cache.test.d.ts +2 -0
  197. package/internal/__tests__/useFetch-cache.test.d.ts.map +1 -0
  198. package/internal/__tests__/useFetch-cache.test.js +137 -0
  199. package/internal/__tests__/useFetch-cache.test.js.map +1 -0
  200. package/internal/dev/__tests__/use-key-stability.test.d.ts +2 -0
  201. package/internal/dev/__tests__/use-key-stability.test.d.ts.map +1 -0
  202. package/internal/dev/__tests__/use-key-stability.test.js +72 -0
  203. package/internal/dev/__tests__/use-key-stability.test.js.map +1 -0
  204. package/internal/dev/__tests__/use-render-tracer.test.d.ts +2 -0
  205. package/internal/dev/__tests__/use-render-tracer.test.d.ts.map +1 -0
  206. package/internal/dev/__tests__/use-render-tracer.test.js +55 -0
  207. package/internal/dev/__tests__/use-render-tracer.test.js.map +1 -0
  208. package/internal/dev/dom-counter.d.ts +14 -0
  209. package/internal/dev/dom-counter.d.ts.map +1 -0
  210. package/internal/dev/dom-counter.js +39 -0
  211. package/internal/dev/dom-counter.js.map +1 -0
  212. package/internal/dev/index.d.ts +6 -0
  213. package/internal/dev/index.d.ts.map +1 -0
  214. package/internal/dev/index.js +6 -0
  215. package/internal/dev/index.js.map +1 -0
  216. package/internal/dev/profiler-wrapper.d.ts +16 -0
  217. package/internal/dev/profiler-wrapper.d.ts.map +1 -0
  218. package/internal/dev/profiler-wrapper.js +31 -0
  219. package/internal/dev/profiler-wrapper.js.map +1 -0
  220. package/internal/dev/use-key-stability.d.ts +22 -0
  221. package/internal/dev/use-key-stability.d.ts.map +1 -0
  222. package/internal/dev/use-key-stability.js +67 -0
  223. package/internal/dev/use-key-stability.js.map +1 -0
  224. package/internal/dev/use-render-tracer.d.ts +13 -0
  225. package/internal/dev/use-render-tracer.d.ts.map +1 -0
  226. package/internal/dev/use-render-tracer.js +57 -0
  227. package/internal/dev/use-render-tracer.js.map +1 -0
  228. package/internal/dev/use-stream-rate.d.ts +23 -0
  229. package/internal/dev/use-stream-rate.d.ts.map +1 -0
  230. package/internal/dev/use-stream-rate.js +94 -0
  231. package/internal/dev/use-stream-rate.js.map +1 -0
  232. package/internal/fetch-cache.d.ts +72 -0
  233. package/internal/fetch-cache.d.ts.map +1 -0
  234. package/internal/fetch-cache.js +118 -0
  235. package/internal/fetch-cache.js.map +1 -0
  236. package/internal/store/__tests__/conversation-store.test.d.ts +2 -0
  237. package/internal/store/__tests__/conversation-store.test.d.ts.map +1 -0
  238. package/internal/store/__tests__/conversation-store.test.js +200 -0
  239. package/internal/store/__tests__/conversation-store.test.js.map +1 -0
  240. package/internal/store/__tests__/structural-share.test.d.ts +2 -0
  241. package/internal/store/__tests__/structural-share.test.d.ts.map +1 -0
  242. package/internal/store/__tests__/structural-share.test.js +368 -0
  243. package/internal/store/__tests__/structural-share.test.js.map +1 -0
  244. package/internal/store/conversation-store.d.ts +62 -0
  245. package/internal/store/conversation-store.d.ts.map +1 -0
  246. package/internal/store/conversation-store.js +95 -0
  247. package/internal/store/conversation-store.js.map +1 -0
  248. package/internal/store/index.d.ts +31 -0
  249. package/internal/store/index.d.ts.map +1 -0
  250. package/internal/store/index.js +54 -0
  251. package/internal/store/index.js.map +1 -0
  252. package/internal/store/structural-share.d.ts +13 -0
  253. package/internal/store/structural-share.d.ts.map +1 -0
  254. package/internal/store/structural-share.js +240 -0
  255. package/internal/store/structural-share.js.map +1 -0
  256. package/internal/stream-controller.d.ts +85 -0
  257. package/internal/stream-controller.d.ts.map +1 -0
  258. package/internal/stream-controller.js +146 -0
  259. package/internal/stream-controller.js.map +1 -0
  260. package/internal/useAutoScroll.d.ts +32 -0
  261. package/internal/useAutoScroll.d.ts.map +1 -0
  262. package/internal/useAutoScroll.js +97 -0
  263. package/internal/useAutoScroll.js.map +1 -0
  264. package/internal/useFetch.d.ts +14 -0
  265. package/internal/useFetch.d.ts.map +1 -1
  266. package/internal/useFetch.js +32 -2
  267. package/internal/useFetch.js.map +1 -1
  268. package/mcp-server/McpServerDetailView.d.ts.map +1 -1
  269. package/mcp-server/McpServerDetailView.js +3 -3
  270. package/mcp-server/McpServerDetailView.js.map +1 -1
  271. package/mcp-server/useMcpServerOAuthConnect.d.ts.map +1 -1
  272. package/mcp-server/useMcpServerOAuthConnect.js +37 -9
  273. package/mcp-server/useMcpServerOAuthConnect.js.map +1 -1
  274. package/package.json +7 -5
  275. package/session/__tests__/useNewSessionFlow.test.js +16 -0
  276. package/session/__tests__/useNewSessionFlow.test.js.map +1 -1
  277. package/session/__tests__/usePersistedModel.test.d.ts +2 -0
  278. package/session/__tests__/usePersistedModel.test.d.ts.map +1 -0
  279. package/session/__tests__/usePersistedModel.test.js +82 -0
  280. package/session/__tests__/usePersistedModel.test.js.map +1 -0
  281. package/session/__tests__/useSession.test.d.ts +2 -0
  282. package/session/__tests__/useSession.test.d.ts.map +1 -0
  283. package/session/__tests__/useSession.test.js +130 -0
  284. package/session/__tests__/useSession.test.js.map +1 -0
  285. package/session/useNewSessionFlow.d.ts.map +1 -1
  286. package/session/useNewSessionFlow.js +12 -6
  287. package/session/useNewSessionFlow.js.map +1 -1
  288. package/session/usePersistedModel.d.ts +3 -0
  289. package/session/usePersistedModel.d.ts.map +1 -1
  290. package/session/usePersistedModel.js +27 -2
  291. package/session/usePersistedModel.js.map +1 -1
  292. package/session/useSession.d.ts.map +1 -1
  293. package/session/useSession.js +1 -1
  294. package/session/useSession.js.map +1 -1
  295. package/session/useSessionConversation.d.ts.map +1 -1
  296. package/session/useSessionConversation.js +9 -1
  297. package/session/useSessionConversation.js.map +1 -1
  298. package/session/useSessionExecutions.d.ts.map +1 -1
  299. package/session/useSessionExecutions.js +1 -1
  300. package/session/useSessionExecutions.js.map +1 -1
  301. package/session/useSessionPageFlow.js +1 -1
  302. package/session/useSessionPageFlow.js.map +1 -1
  303. package/session/useSessionUsage.d.ts +24 -40
  304. package/session/useSessionUsage.d.ts.map +1 -1
  305. package/session/useSessionUsage.js +64 -97
  306. package/session/useSessionUsage.js.map +1 -1
  307. package/settings/BillingSection.d.ts +3 -0
  308. package/settings/BillingSection.d.ts.map +1 -0
  309. package/settings/BillingSection.js +3 -0
  310. package/settings/BillingSection.js.map +1 -0
  311. package/settings/index.d.ts +2 -0
  312. package/settings/index.d.ts.map +1 -1
  313. package/settings/index.js +1 -0
  314. package/settings/index.js.map +1 -1
  315. package/settings/settings-nav.js +1 -1
  316. package/settings/settings-nav.js.map +1 -1
  317. package/src/billing/AutoRechargeCard.tsx +274 -0
  318. package/src/billing/BillingSection.tsx +255 -0
  319. package/src/billing/CreditBalanceCard.tsx +81 -0
  320. package/src/billing/CreditLedgerTable.tsx +281 -0
  321. package/src/billing/CreditPackGrid.tsx +132 -0
  322. package/src/billing/LowBalanceBanner.tsx +67 -0
  323. package/src/billing/PaymentMethodCard.tsx +133 -0
  324. package/src/billing/credit-packs.ts +54 -0
  325. package/src/billing/format.ts +97 -0
  326. package/src/billing/index.ts +51 -0
  327. package/src/billing/useBillingAccount.ts +64 -0
  328. package/src/billing/useBillingUsageReport.ts +73 -0
  329. package/src/billing/useCreateBillingPortalSession.ts +76 -0
  330. package/src/billing/useCreateCheckoutSession.ts +101 -0
  331. package/src/billing/useCreditLedger.ts +79 -0
  332. package/src/billing/useCustomerModelPricing.ts +67 -0
  333. package/src/billing/useSetAutoRechargeConfig.ts +90 -0
  334. package/src/composer/ComposerToolbar.tsx +1 -1
  335. package/src/composer/SessionComposer.tsx +22 -4
  336. package/src/composer/__tests__/SessionComposer-memo.test.ts +26 -0
  337. package/src/execution/ApprovalCard.tsx +7 -3
  338. package/src/execution/ExecutionPhaseBadge.tsx +3 -2
  339. package/src/execution/MessageEntry.tsx +27 -16
  340. package/src/execution/MessageThread.tsx +308 -131
  341. package/src/execution/SetupProgress.tsx +3 -3
  342. package/src/execution/SubAgentSection.tsx +14 -6
  343. package/src/execution/ThreadSkeleton.tsx +73 -0
  344. package/src/execution/ToolCallGroup.tsx +36 -3
  345. package/src/execution/UsageWidget.tsx +1 -1
  346. package/src/execution/__tests__/message-entry.test.tsx +236 -0
  347. package/src/execution/__tests__/thread-keys.test.ts +409 -0
  348. package/src/execution/__tests__/thread-memoization.test.ts +320 -0
  349. package/src/execution/__tests__/thread-skeleton.test.tsx +44 -0
  350. package/src/execution/__tests__/useExecutionStream.test.tsx +109 -12
  351. package/src/execution/__tests__/useSessionVariables-stability.test.ts +95 -0
  352. package/src/execution/__tests__/virtualized-thread.test.tsx +401 -0
  353. package/src/execution/index.ts +3 -0
  354. package/src/execution/useExecutionStream.ts +123 -48
  355. package/src/execution/useSessionVariables.ts +17 -12
  356. package/src/github/useGitHubConnection.ts +18 -13
  357. package/src/identity-account/index.ts +5 -0
  358. package/src/identity-account/useIdentityAccountGate.ts +163 -0
  359. package/src/index.ts +73 -0
  360. package/src/internal/FetchCacheProvider.tsx +74 -0
  361. package/src/internal/JumpToLatestButton.tsx +61 -0
  362. package/src/internal/ThreadItemWrapper.tsx +65 -0
  363. package/src/internal/VirtualizedThread.tsx +162 -0
  364. package/src/internal/__tests__/fetch-cache.test.ts +230 -0
  365. package/src/internal/__tests__/stream-controller.test.ts +395 -0
  366. package/src/internal/__tests__/thread-animation.test.tsx +121 -0
  367. package/src/internal/__tests__/useAutoScroll.test.tsx +261 -0
  368. package/src/internal/__tests__/useFetch-cache.test.ts +214 -0
  369. package/src/internal/dev/__tests__/use-key-stability.test.ts +124 -0
  370. package/src/internal/dev/__tests__/use-render-tracer.test.ts +78 -0
  371. package/src/internal/dev/dom-counter.ts +47 -0
  372. package/src/internal/dev/index.ts +5 -0
  373. package/src/internal/dev/profiler-wrapper.tsx +52 -0
  374. package/src/internal/dev/use-key-stability.ts +86 -0
  375. package/src/internal/dev/use-render-tracer.ts +70 -0
  376. package/src/internal/dev/use-stream-rate.ts +138 -0
  377. package/src/internal/fetch-cache.ts +155 -0
  378. package/src/internal/store/__tests__/conversation-store.test.ts +257 -0
  379. package/src/internal/store/__tests__/structural-share.test.ts +454 -0
  380. package/src/internal/store/conversation-store.ts +128 -0
  381. package/src/internal/store/index.ts +68 -0
  382. package/src/internal/store/structural-share.ts +318 -0
  383. package/src/internal/stream-controller.ts +201 -0
  384. package/src/internal/useAutoScroll.ts +121 -0
  385. package/src/internal/useFetch.ts +51 -2
  386. package/src/mcp-server/McpServerDetailView.tsx +15 -0
  387. package/src/mcp-server/useMcpServerOAuthConnect.ts +37 -9
  388. package/src/session/__tests__/useNewSessionFlow.test.tsx +22 -0
  389. package/src/session/__tests__/usePersistedModel.test.tsx +117 -0
  390. package/src/session/__tests__/useSession.test.tsx +187 -0
  391. package/src/session/useNewSessionFlow.ts +12 -6
  392. package/src/session/usePersistedModel.ts +28 -2
  393. package/src/session/useSession.ts +1 -0
  394. package/src/session/useSessionConversation.ts +11 -2
  395. package/src/session/useSessionExecutions.ts +1 -0
  396. package/src/session/useSessionPageFlow.ts +1 -1
  397. package/src/session/useSessionUsage.ts +102 -123
  398. package/src/settings/BillingSection.tsx +4 -0
  399. package/src/settings/index.ts +2 -0
  400. package/src/settings/settings-nav.ts +1 -1
  401. package/src/styles.css +31 -0
  402. package/src/usage/AgentBreakdownList.tsx +147 -0
  403. package/src/usage/CreditRunwayIndicator.tsx +71 -0
  404. package/src/usage/ExportButton.tsx +115 -0
  405. package/src/usage/HarnessSplitCard.tsx +103 -0
  406. package/src/usage/OrgUsagePanel.tsx +109 -45
  407. package/src/usage/index.ts +15 -0
  408. package/src/usage/useExportCSV.ts +115 -0
  409. package/src/usage/useOrgUsageReport.ts +2 -1
  410. package/src/workspace/__tests__/useWorkspaceEntries-stability.test.ts +76 -0
  411. package/src/workspace/useWorkspaceEntries.ts +16 -11
  412. package/styles.css +1 -1
  413. package/usage/AgentBreakdownList.d.ts +21 -0
  414. package/usage/AgentBreakdownList.d.ts.map +1 -0
  415. package/usage/AgentBreakdownList.js +44 -0
  416. package/usage/AgentBreakdownList.js.map +1 -0
  417. package/usage/CreditRunwayIndicator.d.ts +21 -0
  418. package/usage/CreditRunwayIndicator.d.ts.map +1 -0
  419. package/usage/CreditRunwayIndicator.js +38 -0
  420. package/usage/CreditRunwayIndicator.js.map +1 -0
  421. package/usage/ExportButton.d.ts +20 -0
  422. package/usage/ExportButton.d.ts.map +1 -0
  423. package/usage/ExportButton.js +36 -0
  424. package/usage/ExportButton.js.map +1 -0
  425. package/usage/HarnessSplitCard.d.ts +17 -0
  426. package/usage/HarnessSplitCard.d.ts.map +1 -0
  427. package/usage/HarnessSplitCard.js +38 -0
  428. package/usage/HarnessSplitCard.js.map +1 -0
  429. package/usage/OrgUsagePanel.d.ts.map +1 -1
  430. package/usage/OrgUsagePanel.js +30 -22
  431. package/usage/OrgUsagePanel.js.map +1 -1
  432. package/usage/index.d.ts +10 -0
  433. package/usage/index.d.ts.map +1 -1
  434. package/usage/index.js +5 -0
  435. package/usage/index.js.map +1 -1
  436. package/usage/useExportCSV.d.ts +23 -0
  437. package/usage/useExportCSV.d.ts.map +1 -0
  438. package/usage/useExportCSV.js +81 -0
  439. package/usage/useExportCSV.js.map +1 -0
  440. package/usage/useOrgUsageReport.d.ts +2 -1
  441. package/usage/useOrgUsageReport.d.ts.map +1 -1
  442. package/usage/useOrgUsageReport.js +2 -1
  443. package/usage/useOrgUsageReport.js.map +1 -1
  444. package/workspace/__tests__/useWorkspaceEntries-stability.test.d.ts +2 -0
  445. package/workspace/__tests__/useWorkspaceEntries-stability.test.d.ts.map +1 -0
  446. package/workspace/__tests__/useWorkspaceEntries-stability.test.js +57 -0
  447. package/workspace/__tests__/useWorkspaceEntries-stability.test.js.map +1 -0
  448. package/workspace/useWorkspaceEntries.d.ts.map +1 -1
  449. package/workspace/useWorkspaceEntries.js +5 -4
  450. package/workspace/useWorkspaceEntries.js.map +1 -1
@@ -0,0 +1,76 @@
1
+ "use client";
2
+
3
+ import { useCallback, useState } from "react";
4
+ import { useStigmer } from "../hooks";
5
+ import { toError } from "../internal/toError";
6
+
7
+ /** Return value of {@link useCreateBillingPortalSession}. */
8
+ export interface UseCreateBillingPortalSessionReturn {
9
+ /**
10
+ * Open the Stripe Customer Portal for payment method management.
11
+ *
12
+ * On success, redirects the user to the Stripe-hosted portal page.
13
+ * The promise resolves before the redirect occurs.
14
+ */
15
+ readonly openPortal: (orgId: string) => Promise<void>;
16
+ /** `true` while the portal session is being created. */
17
+ readonly isLoading: boolean;
18
+ /** Error from the last failed attempt, or `null` when healthy. */
19
+ readonly error: Error | null;
20
+ /** Reset `error` to `null`. */
21
+ readonly clearError: () => void;
22
+ }
23
+
24
+ /**
25
+ * Behavior hook that opens the Stripe Customer Portal for payment
26
+ * method management.
27
+ *
28
+ * Wraps `billing.createBillingPortalSession` with loading and error
29
+ * state management. On success, redirects the user to the Stripe-hosted
30
+ * portal. Payment method changes are synced back via webhooks.
31
+ *
32
+ * @example
33
+ * ```tsx
34
+ * const { openPortal, isLoading } = useCreateBillingPortalSession();
35
+ *
36
+ * <button onClick={() => openPortal(orgId)} disabled={isLoading}>
37
+ * Manage payment methods
38
+ * </button>
39
+ * ```
40
+ */
41
+ export function useCreateBillingPortalSession(): UseCreateBillingPortalSessionReturn {
42
+ const stigmer = useStigmer();
43
+ const [isLoading, setIsLoading] = useState(false);
44
+ const [error, setError] = useState<Error | null>(null);
45
+
46
+ const clearError = useCallback(() => setError(null), []);
47
+
48
+ const openPortal = useCallback(
49
+ async (orgId: string): Promise<void> => {
50
+ setIsLoading(true);
51
+ setError(null);
52
+
53
+ try {
54
+ const returnUrl =
55
+ typeof window !== "undefined" ? window.location.href : "";
56
+
57
+ const response = await stigmer.billing.createBillingPortalSession({
58
+ orgId,
59
+ returnUrl,
60
+ });
61
+
62
+ if (response.portalUrl) {
63
+ window.location.href = response.portalUrl;
64
+ }
65
+ } catch (err) {
66
+ setError(toError(err));
67
+ throw err;
68
+ } finally {
69
+ setIsLoading(false);
70
+ }
71
+ },
72
+ [stigmer],
73
+ );
74
+
75
+ return { openPortal, isLoading, error, clearError };
76
+ }
@@ -0,0 +1,101 @@
1
+ "use client";
2
+
3
+ import { useCallback, useState } from "react";
4
+ import type { CreateCreditCheckoutSessionResponse } from "@stigmer/protos/ai/stigmer/billing/v1/io_pb";
5
+ import { useStigmer } from "../hooks";
6
+ import { toError } from "../internal/toError";
7
+
8
+ /** Parameters for {@link useCreateCheckoutSession}'s `createSession` callback. */
9
+ export interface CreateCheckoutSessionInput {
10
+ /** Organization purchasing credits. */
11
+ readonly orgId: string;
12
+ /** Credit pack to purchase (e.g., "starter", "growth", "team"). */
13
+ readonly packId: string;
14
+ /** URL to redirect to after successful payment. */
15
+ readonly successUrl: string;
16
+ /** URL to redirect to if the user cancels checkout. */
17
+ readonly cancelUrl: string;
18
+ }
19
+
20
+ /** Return value of {@link useCreateCheckoutSession}. */
21
+ export interface UseCreateCheckoutSessionReturn {
22
+ /**
23
+ * Create a Stripe Checkout Session and redirect the user.
24
+ *
25
+ * On success, sets `window.location.href` to the Stripe-hosted
26
+ * checkout page URL. The promise resolves with the response before
27
+ * the redirect occurs, allowing callers to perform cleanup if needed.
28
+ */
29
+ readonly createSession: (
30
+ input: CreateCheckoutSessionInput,
31
+ ) => Promise<CreateCreditCheckoutSessionResponse>;
32
+ /** `true` while the checkout session is being created. */
33
+ readonly isSubmitting: boolean;
34
+ /** Error from the last failed attempt, or `null` when healthy. */
35
+ readonly error: Error | null;
36
+ /** Reset `error` to `null`. */
37
+ readonly clearError: () => void;
38
+ }
39
+
40
+ /**
41
+ * Behavior hook that creates a Stripe Checkout Session for credit
42
+ * pack purchases.
43
+ *
44
+ * Wraps `billing.createCreditCheckoutSession` with loading and error
45
+ * state management. On success, redirects the user to the Stripe-hosted
46
+ * checkout page. Credits are provisioned asynchronously via webhook
47
+ * after payment succeeds.
48
+ *
49
+ * @example
50
+ * ```tsx
51
+ * const { createSession, isSubmitting, error } = useCreateCheckoutSession();
52
+ *
53
+ * const handleBuy = () => {
54
+ * createSession({
55
+ * orgId,
56
+ * packId: "growth",
57
+ * successUrl: `${window.location.origin}/settings/billing?checkout=success`,
58
+ * cancelUrl: `${window.location.origin}/settings/billing`,
59
+ * });
60
+ * };
61
+ * ```
62
+ */
63
+ export function useCreateCheckoutSession(): UseCreateCheckoutSessionReturn {
64
+ const stigmer = useStigmer();
65
+ const [isSubmitting, setIsSubmitting] = useState(false);
66
+ const [error, setError] = useState<Error | null>(null);
67
+
68
+ const clearError = useCallback(() => setError(null), []);
69
+
70
+ const createSession = useCallback(
71
+ async (
72
+ input: CreateCheckoutSessionInput,
73
+ ): Promise<CreateCreditCheckoutSessionResponse> => {
74
+ setIsSubmitting(true);
75
+ setError(null);
76
+
77
+ try {
78
+ const response = await stigmer.billing.createCreditCheckoutSession({
79
+ orgId: input.orgId,
80
+ packId: input.packId,
81
+ successUrl: input.successUrl,
82
+ cancelUrl: input.cancelUrl,
83
+ });
84
+
85
+ if (response.checkoutUrl) {
86
+ window.location.href = response.checkoutUrl;
87
+ }
88
+
89
+ return response;
90
+ } catch (err) {
91
+ setError(toError(err));
92
+ throw err;
93
+ } finally {
94
+ setIsSubmitting(false);
95
+ }
96
+ },
97
+ [stigmer],
98
+ );
99
+
100
+ return { createSession, isSubmitting, error, clearError };
101
+ }
@@ -0,0 +1,79 @@
1
+ "use client";
2
+
3
+ import type {
4
+ CreditLedgerResponse,
5
+ } from "@stigmer/protos/ai/stigmer/billing/v1/io_pb";
6
+ import type { LedgerEntryType } from "@stigmer/protos/ai/stigmer/billing/v1/enum_pb";
7
+ import { useStigmer } from "../hooks";
8
+ import { useFetch } from "../internal/useFetch";
9
+
10
+ /** Return value of {@link useCreditLedger}. */
11
+ export interface UseCreditLedgerReturn {
12
+ /** The ledger response, or `null` before the first successful fetch. */
13
+ readonly ledger: CreditLedgerResponse | null;
14
+ /** `true` while the initial fetch is in flight. */
15
+ readonly isLoading: boolean;
16
+ /** `true` while a background refetch is in flight and stale data is shown. */
17
+ readonly isRefetching: boolean;
18
+ /** Error from the last failed request, or `null` when healthy. */
19
+ readonly error: Error | null;
20
+ /** Discard cached data and re-fetch from the server. */
21
+ readonly refetch: () => void;
22
+ }
23
+
24
+ /** Options for {@link useCreditLedger}. */
25
+ export interface UseCreditLedgerOptions {
26
+ /** 1-based page number. Defaults to 1. */
27
+ readonly pageNum?: number;
28
+ /** Page size. Defaults to 20. */
29
+ readonly pageSize?: number;
30
+ /** Filter to specific ledger entry types. Empty means all types. */
31
+ readonly typeFilter?: LedgerEntryType[];
32
+ }
33
+
34
+ /**
35
+ * Data hook that fetches paginated credit ledger entries for an organization.
36
+ *
37
+ * Calls `billing.getCreditLedger` with optional pagination and type
38
+ * filtering. Returns the entries and total page count for building
39
+ * pagination controls.
40
+ *
41
+ * Pass `null` as `orgId` to skip fetching (stable no-op).
42
+ *
43
+ * @param orgId - Organization ID, or `null` to skip.
44
+ * @param options - Pagination and filter options.
45
+ *
46
+ * @example
47
+ * ```tsx
48
+ * const { ledger, isLoading } = useCreditLedger(orgId, { pageNum: 1 });
49
+ *
50
+ * if (isLoading) return <Skeleton />;
51
+ * if (!ledger) return null;
52
+ *
53
+ * return ledger.entries.map(entry => <LedgerRow key={entry.entryId} entry={entry} />);
54
+ * ```
55
+ */
56
+ export function useCreditLedger(
57
+ orgId: string | null,
58
+ options: UseCreditLedgerOptions = {},
59
+ ): UseCreditLedgerReturn {
60
+ const stigmer = useStigmer();
61
+ const { pageNum = 1, pageSize = 20, typeFilter } = options;
62
+
63
+ const typeFilterKey = typeFilter?.join(",") ?? "";
64
+
65
+ const { data: ledger, isLoading, isRefetching, error, refetch } = useFetch(
66
+ orgId
67
+ ? () =>
68
+ stigmer.billing.getCreditLedger({
69
+ orgId,
70
+ page: { num: pageNum, size: pageSize },
71
+ typeFilter,
72
+ })
73
+ : null,
74
+ [orgId, pageNum, pageSize, typeFilterKey, stigmer],
75
+ null as CreditLedgerResponse | null,
76
+ );
77
+
78
+ return { ledger, isLoading, isRefetching, error, refetch };
79
+ }
@@ -0,0 +1,67 @@
1
+ "use client";
2
+
3
+ import type {
4
+ CustomerModelPricingResponse,
5
+ } from "@stigmer/protos/ai/stigmer/billing/v1/io_pb";
6
+ import { useStigmer } from "../hooks";
7
+ import { useFetch } from "../internal/useFetch";
8
+
9
+ /** Return value of {@link useCustomerModelPricing}. */
10
+ export interface UseCustomerModelPricingReturn {
11
+ /** The pricing response, or `null` before the first successful fetch. */
12
+ readonly pricing: CustomerModelPricingResponse | null;
13
+ /** `true` while the initial fetch is in flight. */
14
+ readonly isLoading: boolean;
15
+ /** `true` while a background refetch is in flight and stale data is shown. */
16
+ readonly isRefetching: boolean;
17
+ /** Error from the last failed request, or `null` when healthy. */
18
+ readonly error: Error | null;
19
+ /** Discard cached data and re-fetch from the server. */
20
+ readonly refetch: () => void;
21
+ }
22
+
23
+ /**
24
+ * Data hook that fetches the customer-facing model price list.
25
+ *
26
+ * Returns per-million-token prices for all billable models with
27
+ * billing policy markup already applied, organized by harness
28
+ * and cost tier.
29
+ *
30
+ * Pass `null` as `orgId` to skip fetching (stable no-op).
31
+ * Pass `undefined` to fetch default pricing (no org override).
32
+ *
33
+ * @param orgId - Organization ID for org-specific overrides,
34
+ * `undefined` for default pricing, or `null` to skip.
35
+ *
36
+ * @example
37
+ * ```tsx
38
+ * const { pricing, isLoading } = useCustomerModelPricing(orgId);
39
+ *
40
+ * if (isLoading) return <Skeleton />;
41
+ * if (!pricing) return null;
42
+ *
43
+ * return pricing.entries.map(entry => (
44
+ * <PricingRow key={entry.modelId} entry={entry} />
45
+ * ));
46
+ * ```
47
+ */
48
+ export function useCustomerModelPricing(
49
+ orgId: string | undefined | null,
50
+ ): UseCustomerModelPricingReturn {
51
+ const stigmer = useStigmer();
52
+
53
+ const skip = orgId === null;
54
+
55
+ const { data: pricing, isLoading, isRefetching, error, refetch } = useFetch(
56
+ skip
57
+ ? null
58
+ : () =>
59
+ stigmer.billing.getCustomerModelPricing(
60
+ orgId ? { orgId } : undefined,
61
+ ),
62
+ [orgId, stigmer],
63
+ null as CustomerModelPricingResponse | null,
64
+ );
65
+
66
+ return { pricing, isLoading, isRefetching, error, refetch };
67
+ }
@@ -0,0 +1,90 @@
1
+ "use client";
2
+
3
+ import { useCallback, useState } from "react";
4
+ import { useStigmer } from "../hooks";
5
+ import { toError } from "../internal/toError";
6
+ import type { BillingAccount } from "@stigmer/protos/ai/stigmer/billing/v1/billing_account_pb";
7
+
8
+ /** Input for {@link useSetAutoRechargeConfig}. */
9
+ export interface SetAutoRechargeConfigInput {
10
+ /** Organization ID to configure auto-recharge for. */
11
+ readonly orgId: string;
12
+ /** Whether auto-recharge is enabled. */
13
+ readonly enabled: boolean;
14
+ /** Trigger threshold in micro-USD. */
15
+ readonly thresholdMicros: bigint;
16
+ /** Fixed charge per recharge event in micro-USD. */
17
+ readonly rechargeAmountMicros: bigint;
18
+ /** Maximum monthly auto-recharge spend in micro-USD. */
19
+ readonly monthlyCapMicros: bigint;
20
+ }
21
+
22
+ /** Return value of {@link useSetAutoRechargeConfig}. */
23
+ export interface UseSetAutoRechargeConfigReturn {
24
+ /**
25
+ * Save auto-recharge configuration. Returns the updated BillingAccount
26
+ * on success.
27
+ */
28
+ readonly setConfig: (input: SetAutoRechargeConfigInput) => Promise<BillingAccount>;
29
+ /** `true` while the configuration is being saved. */
30
+ readonly isSubmitting: boolean;
31
+ /** Error from the last failed attempt, or `null` when healthy. */
32
+ readonly error: Error | null;
33
+ /** Reset `error` to `null`. */
34
+ readonly clearError: () => void;
35
+ }
36
+
37
+ /**
38
+ * Behavior hook for configuring auto-recharge settings.
39
+ *
40
+ * Wraps `billing.setAutoRechargeConfig` with loading and error state
41
+ * management. Returns the updated `BillingAccount` on success so the
42
+ * parent can refresh its cached data.
43
+ *
44
+ * @example
45
+ * ```tsx
46
+ * const { setConfig, isSubmitting, error } = useSetAutoRechargeConfig();
47
+ *
48
+ * await setConfig({
49
+ * orgId,
50
+ * enabled: true,
51
+ * thresholdMicros: BigInt(5_000_000),
52
+ * rechargeAmountMicros: BigInt(50_000_000),
53
+ * monthlyCapMicros: BigInt(200_000_000),
54
+ * });
55
+ * ```
56
+ */
57
+ export function useSetAutoRechargeConfig(): UseSetAutoRechargeConfigReturn {
58
+ const stigmer = useStigmer();
59
+ const [isSubmitting, setIsSubmitting] = useState(false);
60
+ const [error, setError] = useState<Error | null>(null);
61
+
62
+ const clearError = useCallback(() => setError(null), []);
63
+
64
+ const setConfig = useCallback(
65
+ async (input: SetAutoRechargeConfigInput): Promise<BillingAccount> => {
66
+ setIsSubmitting(true);
67
+ setError(null);
68
+
69
+ try {
70
+ const account = await stigmer.billing.setAutoRechargeConfig({
71
+ orgId: input.orgId,
72
+ enabled: input.enabled,
73
+ thresholdMicros: input.thresholdMicros,
74
+ rechargeAmountMicros: input.rechargeAmountMicros,
75
+ monthlyCapMicros: input.monthlyCapMicros,
76
+ });
77
+
78
+ return account;
79
+ } catch (err) {
80
+ setError(toError(err));
81
+ throw err;
82
+ } finally {
83
+ setIsSubmitting(false);
84
+ }
85
+ },
86
+ [stigmer],
87
+ );
88
+
89
+ return { setConfig, isSubmitting, error, clearError };
90
+ }
@@ -97,7 +97,7 @@ export function ComposerToolbar({
97
97
  const hasTier2 = configureItems.length > 0;
98
98
  const showHarnessSeparate = showHarnessSelector && !showModelSelector;
99
99
  const hasExecParams = showHarnessSeparate || showModelSelector;
100
- const harnessLocked = harness !== undefined && !showHarnessSelector;
100
+ const harnessLocked = harness !== undefined;
101
101
 
102
102
  return (
103
103
  <div className="flex items-center justify-between gap-2 border-t border-border-muted px-3 py-2">
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
 
3
- import { useCallback, useEffect, useMemo, useRef, useState, type DragEvent, type KeyboardEvent } from "react";
3
+ import { memo, useCallback, useEffect, useMemo, useRef, useState, type DragEvent, type KeyboardEvent } from "react";
4
4
  import { cn } from "@stigmer/theme";
5
5
  import { getUserMessage, type AttachmentInput, type EnvVarInput, type McpServerUsageInput, type ResourceRef } from "@stigmer/sdk";
6
6
  import { useComposer } from "./useComposer";
@@ -33,6 +33,7 @@ import {
33
33
  SYSTEM_ENV_VAR_KEYS,
34
34
  resolveSystemEnvVarValues,
35
35
  } from "../environment/systemEnvVars";
36
+ import { useRenderTracer } from "../internal/dev";
36
37
  import { RunnerPhase } from "@stigmer/protos/ai/stigmer/agentic/runner/v1/enum_pb";
37
38
  import {
38
39
  isActivePhase,
@@ -371,7 +372,7 @@ export interface SessionComposerProps {
371
372
  * />
372
373
  * ```
373
374
  */
374
- export function SessionComposer({
375
+ export const SessionComposer = memo(function SessionComposer({
375
376
  onSubmit,
376
377
  isSubmitting = false,
377
378
  disabled = false,
@@ -407,7 +408,24 @@ export function SessionComposer({
407
408
  ariaLabel = "Send message",
408
409
  className,
409
410
  }: SessionComposerProps) {
410
- const [modelId, setModelId] = useState<string | undefined>(defaultModelId);
411
+ useRenderTracer("SessionComposer", { disabled, isSubmitting });
412
+
413
+ const [modelId, setModelIdRaw] = useState<string | undefined>(defaultModelId);
414
+ const userOverrodeModel = useRef(false);
415
+
416
+ // Sync internal modelId when the external defaultModelId prop changes
417
+ // (e.g., lastExecModelId resolves after executions load). Only sync if the
418
+ // user hasn't made a local selection in this composer instance.
419
+ useEffect(() => {
420
+ if (!userOverrodeModel.current && defaultModelId !== undefined) {
421
+ setModelIdRaw(defaultModelId);
422
+ }
423
+ }, [defaultModelId]);
424
+
425
+ const setModelId = useCallback((id: string | undefined) => {
426
+ userOverrodeModel.current = true;
427
+ setModelIdRaw(id);
428
+ }, []);
411
429
 
412
430
  const [displayNames, setDisplayNames] = useState<Map<string, string>>(
413
431
  () => new Map(),
@@ -1418,7 +1436,7 @@ export function SessionComposer({
1418
1436
  </div>
1419
1437
  </div>
1420
1438
  );
1421
- }
1439
+ });
1422
1440
 
1423
1441
  // ---------------------------------------------------------------------------
1424
1442
  // Agent setup error — secret-flow guidance or generic fallback
@@ -0,0 +1,26 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { SessionComposer } from "../SessionComposer";
3
+
4
+ describe("SessionComposer — memo isolation", () => {
5
+ it("is wrapped in React.memo (shallow prop comparison)", () => {
6
+ // React.memo returns an exotic component with $$typeof set to
7
+ // Symbol.for("react.memo") and a `type` field pointing at the
8
+ // inner function. Regular function components have neither.
9
+ const memoSymbol = Symbol.for("react.memo");
10
+ const typed = SessionComposer as unknown as { $$typeof: symbol; type: Function; compare: unknown };
11
+
12
+ expect(typed.$$typeof).toBe(memoSymbol);
13
+ expect(typeof typed.type).toBe("function");
14
+ // The transform may rename the inner function to avoid shadowing
15
+ // the outer `const SessionComposer` binding (e.g. "SessionComposer2").
16
+ expect(typed.type.name).toMatch(/^SessionComposer/);
17
+ });
18
+
19
+ it("uses default shallow comparison (no custom areEqual)", () => {
20
+ const typed = SessionComposer as unknown as { compare: unknown };
21
+
22
+ // When React.memo is called without a second argument, `compare`
23
+ // is null — React falls back to shallow prop comparison.
24
+ expect(typed.compare).toBeNull();
25
+ });
26
+ });
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
 
3
- import { useCallback, useEffect, useMemo, useState } from "react";
3
+ import { memo, useCallback, useEffect, useMemo, useState } from "react";
4
4
  import type { PendingApproval } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/approval_pb";
5
5
  import { ApprovalAction } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
6
6
  import { cn } from "@stigmer/theme";
@@ -39,6 +39,10 @@ export interface ApprovalCardProps {
39
39
  * dispatch, ensuring pixel-level parity between the approval
40
40
  * preview and the post-execution detail view.
41
41
  *
42
+ * Wrapped in `React.memo` — structural sharing (T04) preserves the
43
+ * `PendingApproval` reference when unchanged, so approval cards
44
+ * skip re-renders during unrelated stream updates.
45
+ *
42
46
  * @example
43
47
  * ```tsx
44
48
  * <ApprovalCard
@@ -48,7 +52,7 @@ export interface ApprovalCardProps {
48
52
  * />
49
53
  * ```
50
54
  */
51
- export function ApprovalCard({
55
+ export const ApprovalCard = memo(function ApprovalCard({
52
56
  pendingApproval,
53
57
  onSubmit,
54
58
  isSubmitting = false,
@@ -198,7 +202,7 @@ export function ApprovalCard({
198
202
  </div>
199
203
  </div>
200
204
  );
201
- }
205
+ });
202
206
 
203
207
  // ---------------------------------------------------------------------------
204
208
  // Internal sub-components
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
 
3
+ import { memo } from "react";
3
4
  import { ExecutionPhase } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
4
5
  import { cn } from "@stigmer/theme";
5
6
 
@@ -97,7 +98,7 @@ const PHASE_CONFIG: ReadonlyMap<ExecutionPhase, PhaseConfig> = new Map([
97
98
  * <ExecutionPhaseBadge phase={ExecutionPhase.EXECUTION_COMPLETED} />
98
99
  * ```
99
100
  */
100
- export function ExecutionPhaseBadge({
101
+ export const ExecutionPhaseBadge = memo(function ExecutionPhaseBadge({
101
102
  phase,
102
103
  className,
103
104
  }: ExecutionPhaseBadgeProps) {
@@ -122,7 +123,7 @@ export function ExecutionPhaseBadge({
122
123
  {config.label}
123
124
  </span>
124
125
  );
125
- }
126
+ });
126
127
 
127
128
  function DotIcon() {
128
129
  return (
@@ -1,11 +1,12 @@
1
1
  "use client";
2
2
 
3
- import { useState } from "react";
4
- import Markdown from "react-markdown";
3
+ import { memo, useState } from "react";
4
+ import { Streamdown } from "streamdown";
5
5
  import type { AgentMessage } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
6
6
  import { MessageType } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
7
7
  import { cn } from "@stigmer/theme";
8
- import { MARKDOWN_COMPONENTS, REMARK_PLUGINS } from "../internal/markdown-components";
8
+ import { MARKDOWN_COMPONENTS } from "../internal/markdown-components";
9
+ import { useRenderTracer } from "../internal/dev";
9
10
 
10
11
  /** Props for {@link MessageEntry}. */
11
12
  export interface MessageEntryProps {
@@ -19,14 +20,18 @@ export interface MessageEntryProps {
19
20
  * Renders a single message in the conversation thread.
20
21
  *
21
22
  * - `MESSAGE_HUMAN` — plain text with muted background
22
- * - `MESSAGE_AI` — markdown-rendered via `react-markdown` + `remark-gfm`,
23
- * with a blinking cursor while streaming
23
+ * - `MESSAGE_AI` — markdown-rendered via Streamdown with block-level
24
+ * memoization and streaming-aware incomplete-syntax healing
24
25
  * - `MESSAGE_THINKING` — collapsible thinking block with subdued styling,
25
26
  * collapsed by default showing a brief summary
26
27
  * - `MESSAGE_SYSTEM` — small muted text
27
28
  * - `MESSAGE_TOOL` / `UNSPECIFIED` — renders nothing (tool results are
28
29
  * consumed by {@link ToolCallGroup})
29
30
  *
31
+ * Wrapped in `React.memo` — structural sharing (T04) guarantees that
32
+ * unchanged messages keep the same object reference, so completed
33
+ * messages skip re-renders entirely during streaming.
34
+ *
30
35
  * Purely presentational — no data fetching, no state.
31
36
  * All visual properties flow through `--stgm-*` tokens.
32
37
  *
@@ -35,7 +40,16 @@ export interface MessageEntryProps {
35
40
  * <MessageEntry message={agentMessage} />
36
41
  * ```
37
42
  */
38
- export function MessageEntry({ message, className }: MessageEntryProps) {
43
+ export const MessageEntry = memo(function MessageEntry({
44
+ message,
45
+ className,
46
+ }: MessageEntryProps) {
47
+ useRenderTracer("MessageEntry", {
48
+ messageType: message.type,
49
+ contentLength: message.content.length,
50
+ isStreaming: message.isStreaming,
51
+ });
52
+
39
53
  switch (message.type) {
40
54
  case MessageType.MESSAGE_HUMAN:
41
55
  return <HumanMessage content={message.content} className={className} />;
@@ -60,7 +74,7 @@ export function MessageEntry({ message, className }: MessageEntryProps) {
60
74
  default:
61
75
  return null;
62
76
  }
63
- }
77
+ });
64
78
 
65
79
  function HumanMessage({
66
80
  content,
@@ -89,6 +103,8 @@ function AiMessage({
89
103
  isStreaming: boolean;
90
104
  className?: string;
91
105
  }) {
106
+ useRenderTracer("AiMessage", { contentLength: content.length, isStreaming });
107
+
92
108
  return (
93
109
  <div
94
110
  role="article"
@@ -97,18 +113,13 @@ function AiMessage({
97
113
  className={cn("px-4 py-3", className)}
98
114
  >
99
115
  <div className="stgm-prose">
100
- <Markdown
101
- remarkPlugins={REMARK_PLUGINS}
116
+ <Streamdown
102
117
  components={MARKDOWN_COMPONENTS}
118
+ isAnimating={isStreaming}
119
+ caret="block"
103
120
  >
104
121
  {content}
105
- </Markdown>
106
- {isStreaming && (
107
- <span
108
- className="inline-block w-[2px] h-[1em] bg-foreground align-text-bottom animate-pulse ml-0.5"
109
- aria-hidden="true"
110
- />
111
- )}
122
+ </Streamdown>
112
123
  </div>
113
124
  </div>
114
125
  );