@stigmer/react 3.12.4 → 3.12.6

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 (369) hide show
  1. package/agent/AgentDetailView.js +2 -2
  2. package/agent/AgentDetailView.js.map +1 -1
  3. package/agent/index.d.ts +0 -1
  4. package/agent/index.d.ts.map +1 -1
  5. package/agent/index.js +0 -1
  6. package/agent/index.js.map +1 -1
  7. package/agent/useUpdateAgent.d.ts +3 -3
  8. package/agent/useUpdateAgent.js +3 -3
  9. package/agent-instance/AgentInstanceDetailPanel.d.ts.map +1 -1
  10. package/agent-instance/AgentInstanceDetailPanel.js +5 -7
  11. package/agent-instance/AgentInstanceDetailPanel.js.map +1 -1
  12. package/agent-instance/useUpdateAgentInstance.d.ts +1 -1
  13. package/agent-instance/useUpdateAgentInstance.js +1 -1
  14. package/attachment/AttachmentChipList.js +16 -6
  15. package/attachment/AttachmentChipList.js.map +1 -1
  16. package/attachment/index.d.ts +1 -1
  17. package/attachment/index.d.ts.map +1 -1
  18. package/attachment/index.js.map +1 -1
  19. package/attachment/useAttachments.d.ts +51 -8
  20. package/attachment/useAttachments.d.ts.map +1 -1
  21. package/attachment/useAttachments.js +74 -3
  22. package/attachment/useAttachments.js.map +1 -1
  23. package/channel/AgentChannelsPanel.d.ts.map +1 -1
  24. package/channel/AgentChannelsPanel.js +10 -5
  25. package/channel/AgentChannelsPanel.js.map +1 -1
  26. package/channel/ChannelRunConfigDialog.d.ts +51 -0
  27. package/channel/ChannelRunConfigDialog.d.ts.map +1 -0
  28. package/channel/ChannelRunConfigDialog.js +121 -0
  29. package/channel/ChannelRunConfigDialog.js.map +1 -0
  30. package/channel/index.d.ts +2 -0
  31. package/channel/index.d.ts.map +1 -1
  32. package/channel/index.js +1 -0
  33. package/channel/index.js.map +1 -1
  34. package/channel/useSaveAgentChannel.d.ts.map +1 -1
  35. package/channel/useSaveAgentChannel.js +6 -3
  36. package/channel/useSaveAgentChannel.js.map +1 -1
  37. package/channel-app/ChannelAppDetailPanel.js +9 -9
  38. package/channel-app/ChannelAppDetailPanel.js.map +1 -1
  39. package/composer/ComposerToolbar.d.ts +6 -1
  40. package/composer/ComposerToolbar.d.ts.map +1 -1
  41. package/composer/ComposerToolbar.js +2 -2
  42. package/composer/ComposerToolbar.js.map +1 -1
  43. package/composer/SessionComposer.d.ts +9 -0
  44. package/composer/SessionComposer.d.ts.map +1 -1
  45. package/composer/SessionComposer.js +39 -13
  46. package/composer/SessionComposer.js.map +1 -1
  47. package/conversation/ConversationComposer.d.ts +19 -3
  48. package/conversation/ConversationComposer.d.ts.map +1 -1
  49. package/conversation/ConversationComposer.js +4 -4
  50. package/conversation/ConversationComposer.js.map +1 -1
  51. package/conversation/ConversationTemplatePickerDialog.d.ts +63 -0
  52. package/conversation/ConversationTemplatePickerDialog.d.ts.map +1 -0
  53. package/conversation/ConversationTemplatePickerDialog.js +151 -0
  54. package/conversation/ConversationTemplatePickerDialog.js.map +1 -0
  55. package/conversation/ConversationsWorkbench.d.ts.map +1 -1
  56. package/conversation/ConversationsWorkbench.js +16 -5
  57. package/conversation/ConversationsWorkbench.js.map +1 -1
  58. package/conversation/index.d.ts +3 -1
  59. package/conversation/index.d.ts.map +1 -1
  60. package/conversation/index.js +1 -0
  61. package/conversation/index.js.map +1 -1
  62. package/conversation/useConversationParticipation.d.ts +41 -4
  63. package/conversation/useConversationParticipation.d.ts.map +1 -1
  64. package/conversation/useConversationParticipation.js +13 -2
  65. package/conversation/useConversationParticipation.js.map +1 -1
  66. package/cursor-accounts/CursorAccountsConsole.d.ts.map +1 -1
  67. package/cursor-accounts/CursorAccountsConsole.js +12 -3
  68. package/cursor-accounts/CursorAccountsConsole.js.map +1 -1
  69. package/cursor-accounts/cursor-account-coverage.d.ts +30 -1
  70. package/cursor-accounts/cursor-account-coverage.d.ts.map +1 -1
  71. package/cursor-accounts/cursor-account-coverage.js +21 -0
  72. package/cursor-accounts/cursor-account-coverage.js.map +1 -1
  73. package/execution/useCreateAgentExecution.d.ts +15 -0
  74. package/execution/useCreateAgentExecution.d.ts.map +1 -1
  75. package/execution/useCreateAgentExecution.js +5 -0
  76. package/execution/useCreateAgentExecution.js.map +1 -1
  77. package/identity-account/AccountPreferencesPanel.d.ts +36 -0
  78. package/identity-account/AccountPreferencesPanel.d.ts.map +1 -0
  79. package/identity-account/AccountPreferencesPanel.js +202 -0
  80. package/identity-account/AccountPreferencesPanel.js.map +1 -0
  81. package/identity-account/index.d.ts +8 -0
  82. package/identity-account/index.d.ts.map +1 -1
  83. package/identity-account/index.js +4 -0
  84. package/identity-account/index.js.map +1 -1
  85. package/identity-account/useAccountExecutionDefaults.d.ts +36 -0
  86. package/identity-account/useAccountExecutionDefaults.d.ts.map +1 -0
  87. package/identity-account/useAccountExecutionDefaults.js +46 -0
  88. package/identity-account/useAccountExecutionDefaults.js.map +1 -0
  89. package/identity-account/useMyIdentityAccount.d.ts +55 -0
  90. package/identity-account/useMyIdentityAccount.d.ts.map +1 -0
  91. package/identity-account/useMyIdentityAccount.js +37 -0
  92. package/identity-account/useMyIdentityAccount.js.map +1 -0
  93. package/identity-account/useUpdateIdentityAccount.d.ts +42 -0
  94. package/identity-account/useUpdateIdentityAccount.d.ts.map +1 -0
  95. package/identity-account/useUpdateIdentityAccount.js +53 -0
  96. package/identity-account/useUpdateIdentityAccount.js.map +1 -0
  97. package/identity-provider/IdentityProviderDetailPanel.d.ts.map +1 -1
  98. package/identity-provider/IdentityProviderDetailPanel.js +16 -15
  99. package/identity-provider/IdentityProviderDetailPanel.js.map +1 -1
  100. package/index.d.ts +15 -13
  101. package/index.d.ts.map +1 -1
  102. package/index.js +11 -8
  103. package/index.js.map +1 -1
  104. package/internal/StandingContextField.d.ts +37 -0
  105. package/internal/StandingContextField.d.ts.map +1 -0
  106. package/internal/StandingContextField.js +24 -0
  107. package/internal/StandingContextField.js.map +1 -0
  108. package/library/InstanceVisibilitySelector.d.ts.map +1 -1
  109. package/library/InstanceVisibilitySelector.js +9 -2
  110. package/library/InstanceVisibilitySelector.js.map +1 -1
  111. package/library/ResourceVisibilityControl.d.ts +13 -3
  112. package/library/ResourceVisibilityControl.d.ts.map +1 -1
  113. package/library/ResourceVisibilityControl.js +33 -5
  114. package/library/ResourceVisibilityControl.js.map +1 -1
  115. package/library/VisibilityOptionRow.d.ts +6 -0
  116. package/library/VisibilityOptionRow.d.ts.map +1 -1
  117. package/library/VisibilityOptionRow.js +11 -1
  118. package/library/VisibilityOptionRow.js.map +1 -1
  119. package/library/VisibilitySelector.d.ts.map +1 -1
  120. package/library/VisibilitySelector.js +14 -2
  121. package/library/VisibilitySelector.js.map +1 -1
  122. package/library/index.d.ts +4 -2
  123. package/library/index.d.ts.map +1 -1
  124. package/library/index.js +2 -1
  125. package/library/index.js.map +1 -1
  126. package/library/useCanSetPublicVisibility.d.ts +30 -0
  127. package/library/useCanSetPublicVisibility.d.ts.map +1 -0
  128. package/library/useCanSetPublicVisibility.js +41 -0
  129. package/library/useCanSetPublicVisibility.js.map +1 -0
  130. package/library/visibilityLevels.d.ts +32 -1
  131. package/library/visibilityLevels.d.ts.map +1 -1
  132. package/library/visibilityLevels.js +34 -14
  133. package/library/visibilityLevels.js.map +1 -1
  134. package/mcp-server/McpServerDetailView.js +2 -2
  135. package/mcp-server/McpServerDetailView.js.map +1 -1
  136. package/mcp-server/index.d.ts +0 -1
  137. package/mcp-server/index.d.ts.map +1 -1
  138. package/mcp-server/index.js +0 -1
  139. package/mcp-server/index.js.map +1 -1
  140. package/mcp-server/internal/definitionDrift.d.ts +14 -12
  141. package/mcp-server/internal/definitionDrift.d.ts.map +1 -1
  142. package/mcp-server/internal/definitionDrift.js +22 -19
  143. package/mcp-server/internal/definitionDrift.js.map +1 -1
  144. package/mcp-server/useMcpServerConnect.d.ts +8 -5
  145. package/mcp-server/useMcpServerConnect.d.ts.map +1 -1
  146. package/mcp-server/useMcpServerConnect.js +2 -1
  147. package/mcp-server/useMcpServerConnect.js.map +1 -1
  148. package/mcp-server/useMcpServerOAuthConnect.d.ts.map +1 -1
  149. package/mcp-server/useMcpServerOAuthConnect.js +7 -2
  150. package/mcp-server/useMcpServerOAuthConnect.js.map +1 -1
  151. package/mcp-server/useUpdateMcpServer.d.ts +3 -3
  152. package/mcp-server/useUpdateMcpServer.js +3 -3
  153. package/models/ModelSelector.d.ts +33 -1
  154. package/models/ModelSelector.d.ts.map +1 -1
  155. package/models/ModelSelector.js +37 -4
  156. package/models/ModelSelector.js.map +1 -1
  157. package/models/index.d.ts +2 -0
  158. package/models/index.d.ts.map +1 -1
  159. package/models/index.js +1 -0
  160. package/models/index.js.map +1 -1
  161. package/models/registry.d.ts +16 -0
  162. package/models/registry.d.ts.map +1 -1
  163. package/models/registry.js +13 -0
  164. package/models/registry.js.map +1 -1
  165. package/models/thinking-mode.d.ts +27 -0
  166. package/models/thinking-mode.d.ts.map +1 -0
  167. package/models/thinking-mode.js +32 -0
  168. package/models/thinking-mode.js.map +1 -0
  169. package/oauth-app/CreateOAuthAppForm.d.ts +2 -2
  170. package/oauth-app/CreateOAuthAppForm.d.ts.map +1 -1
  171. package/oauth-app/CreateOAuthAppForm.js +13 -4
  172. package/oauth-app/CreateOAuthAppForm.js.map +1 -1
  173. package/oauth-app/OAuthAppDetailPanel.d.ts.map +1 -1
  174. package/oauth-app/OAuthAppDetailPanel.js +50 -22
  175. package/oauth-app/OAuthAppDetailPanel.js.map +1 -1
  176. package/organization/OrgPreferencesPanel.d.ts +32 -0
  177. package/organization/OrgPreferencesPanel.d.ts.map +1 -0
  178. package/organization/OrgPreferencesPanel.js +90 -0
  179. package/organization/OrgPreferencesPanel.js.map +1 -0
  180. package/organization/OrgProfilePanel.d.ts.map +1 -1
  181. package/organization/OrgProfilePanel.js +5 -4
  182. package/organization/OrgProfilePanel.js.map +1 -1
  183. package/organization/index.d.ts +2 -0
  184. package/organization/index.d.ts.map +1 -1
  185. package/organization/index.js +1 -0
  186. package/organization/index.js.map +1 -1
  187. package/package.json +4 -4
  188. package/platform-client/PlatformClientDetailPanel.d.ts.map +1 -1
  189. package/platform-client/PlatformClientDetailPanel.js +12 -13
  190. package/platform-client/PlatformClientDetailPanel.js.map +1 -1
  191. package/schedule/ScheduleDetailView.d.ts.map +1 -1
  192. package/schedule/ScheduleDetailView.js +21 -10
  193. package/schedule/ScheduleDetailView.js.map +1 -1
  194. package/schedule/ScheduleForm.d.ts.map +1 -1
  195. package/schedule/ScheduleForm.js +13 -3
  196. package/schedule/ScheduleForm.js.map +1 -1
  197. package/session/NewSessionViewer.d.ts +10 -1
  198. package/session/NewSessionViewer.d.ts.map +1 -1
  199. package/session/NewSessionViewer.js +2 -1
  200. package/session/NewSessionViewer.js.map +1 -1
  201. package/session/run-config.d.ts +9 -0
  202. package/session/run-config.d.ts.map +1 -1
  203. package/session/run-config.js +5 -0
  204. package/session/run-config.js.map +1 -1
  205. package/session/useNewSessionFlow.d.ts +27 -1
  206. package/session/useNewSessionFlow.d.ts.map +1 -1
  207. package/session/useNewSessionFlow.js +51 -8
  208. package/session/useNewSessionFlow.js.map +1 -1
  209. package/session/useSessionConversation.d.ts +12 -1
  210. package/session/useSessionConversation.d.ts.map +1 -1
  211. package/session/useSessionConversation.js +15 -24
  212. package/session/useSessionConversation.js.map +1 -1
  213. package/session/useSessionPageFlow.d.ts.map +1 -1
  214. package/session/useSessionPageFlow.js +14 -10
  215. package/session/useSessionPageFlow.js.map +1 -1
  216. package/session/useUpdateSession.d.ts +18 -24
  217. package/session/useUpdateSession.d.ts.map +1 -1
  218. package/session/useUpdateSession.js +18 -24
  219. package/session/useUpdateSession.js.map +1 -1
  220. package/settings/AccountPreferencesSection.d.ts +3 -0
  221. package/settings/AccountPreferencesSection.d.ts.map +1 -0
  222. package/settings/AccountPreferencesSection.js +10 -0
  223. package/settings/AccountPreferencesSection.js.map +1 -0
  224. package/settings/OrgPreferencesSection.d.ts +3 -0
  225. package/settings/OrgPreferencesSection.d.ts.map +1 -0
  226. package/settings/OrgPreferencesSection.js +13 -0
  227. package/settings/OrgPreferencesSection.js.map +1 -0
  228. package/settings/index.d.ts +2 -0
  229. package/settings/index.d.ts.map +1 -1
  230. package/settings/index.js +2 -0
  231. package/settings/index.js.map +1 -1
  232. package/settings/settings-nav.d.ts.map +1 -1
  233. package/settings/settings-nav.js +17 -1
  234. package/settings/settings-nav.js.map +1 -1
  235. package/src/agent/AgentDetailView.tsx +2 -2
  236. package/src/agent/index.ts +0 -2
  237. package/src/agent/useUpdateAgent.ts +3 -3
  238. package/src/agent-instance/AgentInstanceDetailPanel.tsx +5 -7
  239. package/src/agent-instance/__tests__/AgentInstanceDetailPanel.test.tsx +75 -0
  240. package/src/agent-instance/useUpdateAgentInstance.ts +1 -1
  241. package/src/attachment/AttachmentChipList.tsx +19 -8
  242. package/src/attachment/__tests__/AttachmentChipList.test.tsx +12 -0
  243. package/src/attachment/__tests__/useAttachments.test.ts +293 -0
  244. package/src/attachment/index.ts +1 -0
  245. package/src/attachment/useAttachments.ts +143 -11
  246. package/src/channel/AgentChannelsPanel.tsx +26 -1
  247. package/src/channel/ChannelRunConfigDialog.tsx +351 -0
  248. package/src/channel/__tests__/ChannelRunConfigDialog.test.tsx +210 -0
  249. package/src/channel/index.ts +2 -0
  250. package/src/channel/useSaveAgentChannel.ts +6 -3
  251. package/src/channel-app/ChannelAppDetailPanel.tsx +9 -9
  252. package/src/channel-app/__tests__/channelAppWhatsApp.test.tsx +2 -0
  253. package/src/channel-app/__tests__/channelAppWipeSafety.test.tsx +87 -0
  254. package/src/composer/ComposerToolbar.tsx +9 -0
  255. package/src/composer/SessionComposer.tsx +59 -16
  256. package/src/composer/__tests__/SessionComposer-thinkingMode.test.tsx +176 -0
  257. package/src/conversation/ConversationComposer.tsx +38 -4
  258. package/src/conversation/ConversationTemplatePickerDialog.tsx +441 -0
  259. package/src/conversation/ConversationsWorkbench.tsx +40 -4
  260. package/src/conversation/__tests__/ConversationComposer.test.tsx +56 -1
  261. package/src/conversation/__tests__/ConversationTemplatePickerDialog.test.tsx +224 -0
  262. package/src/conversation/__tests__/useConversationParticipation.test.tsx +45 -4
  263. package/src/conversation/index.ts +4 -0
  264. package/src/conversation/useConversationParticipation.ts +58 -6
  265. package/src/cursor-accounts/CursorAccountsConsole.tsx +40 -5
  266. package/src/cursor-accounts/__tests__/CursorAccountsConsole.test.tsx +32 -0
  267. package/src/cursor-accounts/__tests__/cursor-account-coverage.test.ts +58 -1
  268. package/src/cursor-accounts/cursor-account-coverage.ts +55 -0
  269. package/src/execution/useCreateAgentExecution.ts +19 -0
  270. package/src/identity-account/AccountPreferencesPanel.tsx +532 -0
  271. package/src/identity-account/__tests__/AccountPreferencesPanel.test.tsx +267 -0
  272. package/src/identity-account/__tests__/useAccountExecutionDefaults.test.tsx +116 -0
  273. package/src/identity-account/__tests__/useMyIdentityAccount.test.tsx +121 -0
  274. package/src/identity-account/index.ts +11 -0
  275. package/src/identity-account/useAccountExecutionDefaults.ts +63 -0
  276. package/src/identity-account/useMyIdentityAccount.ts +75 -0
  277. package/src/identity-account/useUpdateIdentityAccount.ts +73 -0
  278. package/src/identity-provider/IdentityProviderDetailPanel.tsx +18 -15
  279. package/src/identity-provider/__tests__/IdentityProviderDetailPanel.test.tsx +80 -0
  280. package/src/index.ts +26 -4
  281. package/src/internal/StandingContextField.tsx +92 -0
  282. package/src/library/InstanceVisibilitySelector.tsx +9 -2
  283. package/src/library/ResourceVisibilityControl.tsx +46 -13
  284. package/src/library/VisibilityOptionRow.tsx +28 -6
  285. package/src/library/VisibilitySelector.tsx +14 -2
  286. package/src/library/__tests__/VisibilitySelector.test.tsx +48 -1
  287. package/src/library/__tests__/visibilityLevels.test.ts +67 -1
  288. package/src/library/index.ts +6 -1
  289. package/src/library/useCanSetPublicVisibility.ts +56 -0
  290. package/src/library/visibilityLevels.ts +65 -14
  291. package/src/mcp-server/McpServerDetailView.tsx +2 -2
  292. package/src/mcp-server/__tests__/useMcpServerConnect.asyncLane.test.tsx +117 -0
  293. package/src/mcp-server/index.ts +0 -2
  294. package/src/mcp-server/internal/definitionDrift.ts +22 -20
  295. package/src/mcp-server/useMcpServerConnect.ts +9 -6
  296. package/src/mcp-server/useMcpServerOAuthConnect.ts +7 -2
  297. package/src/mcp-server/useUpdateMcpServer.ts +3 -3
  298. package/src/models/ModelSelector.tsx +94 -1
  299. package/src/models/index.ts +2 -0
  300. package/src/models/registry.ts +29 -0
  301. package/src/models/thinking-mode.ts +48 -0
  302. package/src/oauth-app/CreateOAuthAppForm.tsx +58 -3
  303. package/src/oauth-app/OAuthAppDetailPanel.tsx +103 -20
  304. package/src/oauth-app/__tests__/OAuthAppDetailPanel.test.tsx +94 -0
  305. package/src/organization/OrgPreferencesPanel.tsx +221 -0
  306. package/src/organization/OrgProfilePanel.tsx +5 -4
  307. package/src/organization/__tests__/OrgPreferencesPanel.test.tsx +145 -0
  308. package/src/organization/__tests__/OrgProfilePanel.test.tsx +108 -0
  309. package/src/organization/index.ts +2 -0
  310. package/src/platform-client/PlatformClientDetailPanel.tsx +14 -13
  311. package/src/platform-client/__tests__/PlatformClientDetailPanel.test.tsx +82 -0
  312. package/src/schedule/ScheduleDetailView.tsx +29 -4
  313. package/src/schedule/ScheduleForm.tsx +14 -1
  314. package/src/session/NewSessionViewer.tsx +12 -0
  315. package/src/session/__tests__/useNewSessionFlow.test.tsx +151 -0
  316. package/src/session/run-config.ts +16 -0
  317. package/src/session/useNewSessionFlow.ts +82 -9
  318. package/src/session/useSessionConversation.ts +32 -40
  319. package/src/session/useSessionPageFlow.ts +14 -11
  320. package/src/session/useUpdateSession.ts +18 -24
  321. package/src/settings/AccountPreferencesSection.tsx +25 -0
  322. package/src/settings/OrgPreferencesSection.tsx +35 -0
  323. package/src/settings/__tests__/settings-nav.test.ts +27 -0
  324. package/src/settings/index.ts +2 -0
  325. package/src/settings/settings-nav.ts +18 -0
  326. package/src/workflow/ApprovalFormBuilder.tsx +20 -27
  327. package/src/workflow/WorkflowDetailView.tsx +2 -2
  328. package/src/workflow/__tests__/WorkflowInstanceDetailPanel.test.tsx +83 -0
  329. package/src/workflow/index.ts +0 -2
  330. package/src/workflow/instance/WorkflowInstanceDetailPanel.tsx +6 -7
  331. package/src/workflow/useUpdateWorkflow.ts +3 -3
  332. package/styles.css +1 -1
  333. package/workflow/ApprovalFormBuilder.d.ts.map +1 -1
  334. package/workflow/ApprovalFormBuilder.js +10 -5
  335. package/workflow/ApprovalFormBuilder.js.map +1 -1
  336. package/workflow/WorkflowDetailView.js +2 -2
  337. package/workflow/WorkflowDetailView.js.map +1 -1
  338. package/workflow/index.d.ts +0 -1
  339. package/workflow/index.d.ts.map +1 -1
  340. package/workflow/index.js +0 -1
  341. package/workflow/index.js.map +1 -1
  342. package/workflow/instance/WorkflowInstanceDetailPanel.d.ts.map +1 -1
  343. package/workflow/instance/WorkflowInstanceDetailPanel.js +6 -7
  344. package/workflow/instance/WorkflowInstanceDetailPanel.js.map +1 -1
  345. package/workflow/useUpdateWorkflow.d.ts +3 -3
  346. package/workflow/useUpdateWorkflow.js +3 -3
  347. package/agent/internal/agentToInput.d.ts +0 -15
  348. package/agent/internal/agentToInput.d.ts.map +0 -1
  349. package/agent/internal/agentToInput.js +0 -84
  350. package/agent/internal/agentToInput.js.map +0 -1
  351. package/mcp-server/internal/mcpServerToInput.d.ts +0 -12
  352. package/mcp-server/internal/mcpServerToInput.d.ts.map +0 -1
  353. package/mcp-server/internal/mcpServerToInput.js +0 -84
  354. package/mcp-server/internal/mcpServerToInput.js.map +0 -1
  355. package/session/session-spec-converters.d.ts +0 -24
  356. package/session/session-spec-converters.d.ts.map +0 -1
  357. package/session/session-spec-converters.js +0 -72
  358. package/session/session-spec-converters.js.map +0 -1
  359. package/src/agent/internal/__tests__/agentToInput.test.ts +0 -145
  360. package/src/agent/internal/agentToInput.ts +0 -102
  361. package/src/mcp-server/__tests__/mcpServerToInput.test.ts +0 -201
  362. package/src/mcp-server/internal/mcpServerToInput.ts +0 -98
  363. package/src/session/__tests__/session-spec-converters.test.ts +0 -183
  364. package/src/session/session-spec-converters.ts +0 -86
  365. package/src/workflow/internal/workflowToInput.ts +0 -96
  366. package/workflow/internal/workflowToInput.d.ts +0 -12
  367. package/workflow/internal/workflowToInput.d.ts.map +0 -1
  368. package/workflow/internal/workflowToInput.js +0 -73
  369. package/workflow/internal/workflowToInput.js.map +0 -1
@@ -0,0 +1,87 @@
1
+ import { describe, it, expect, vi, afterEach } from "vitest";
2
+ import { render, screen, fireEvent, waitFor, cleanup } from "@testing-library/react";
3
+ import { create } from "@bufbuild/protobuf";
4
+ import {
5
+ ChannelAppSchema,
6
+ type ChannelApp,
7
+ } from "@stigmer/protos/ai/stigmer/agentic/channelapp/v1/api_pb";
8
+ import type { ChannelAppInput } from "@stigmer/sdk";
9
+ import { StigmerContext } from "../../context";
10
+ import { ChannelAppDetailPanel } from "../ChannelAppDetailPanel";
11
+
12
+ /**
13
+ * Regression suite for the full-spec-replace wipe bug: both detail forms
14
+ * must spread `toChannelAppUpdateInput` and override only their provider
15
+ * arm, so identity fields and any future spec field survive the save.
16
+ * Secrets follow the redaction contract: the form is seeded with the
17
+ * server's redaction marker, and sending the marker back means "keep the
18
+ * stored value" (per-field, verified by the backend's
19
+ * TestRedactAndEncryptCoverEveryProviderArm).
20
+ */
21
+
22
+ const REDACTED = "***REDACTED***";
23
+
24
+ const SLACK_APP: ChannelApp = create(ChannelAppSchema, {
25
+ metadata: {
26
+ id: "chapp_1",
27
+ name: "Acme Slack",
28
+ slug: "acme-slack",
29
+ org: "acme",
30
+ labels: { team: "support" },
31
+ },
32
+ spec: {
33
+ providerConfig: {
34
+ case: "slack",
35
+ value: {
36
+ clientId: "1234.5678",
37
+ clientSecret: REDACTED,
38
+ signingSecret: REDACTED,
39
+ },
40
+ },
41
+ },
42
+ });
43
+
44
+ function renderPanel(update: ReturnType<typeof vi.fn>) {
45
+ const client = {
46
+ baseUrl: "https://api.stigmer.ai",
47
+ channelapp: { update, delete: vi.fn(async () => ({})) },
48
+ } as never;
49
+ return render(
50
+ <StigmerContext.Provider value={client}>
51
+ <ChannelAppDetailPanel
52
+ channelApp={SLACK_APP}
53
+ consoleOrigin="https://console.acme.example"
54
+ />
55
+ </StigmerContext.Provider>,
56
+ );
57
+ }
58
+
59
+ afterEach(cleanup);
60
+
61
+ describe("ChannelAppDetailPanel save payload (slack arm)", () => {
62
+ it("spreads the complete mapped input and overrides only the slack config", async () => {
63
+ const update = vi.fn(async (_input: ChannelAppInput) => SLACK_APP);
64
+ renderPanel(update);
65
+
66
+ const clientId = await screen.findByLabelText(/client id/i);
67
+ fireEvent.change(clientId, { target: { value: "9999.0000" } });
68
+ fireEvent.click(screen.getByRole("button", { name: /save credentials/i }));
69
+
70
+ await waitFor(() => expect(update).toHaveBeenCalledTimes(1));
71
+ const input = update.mock.calls[0]![0];
72
+
73
+ // The edited field, plus per-field keep-markers for untouched secrets.
74
+ expect(input.slack).toEqual({
75
+ clientId: "9999.0000",
76
+ clientSecret: REDACTED,
77
+ signingSecret: REDACTED,
78
+ });
79
+ // Identity and unrendered metadata survive via the mapper spread.
80
+ expect(input.name).toBe("Acme Slack");
81
+ expect(input.org).toBe("acme");
82
+ expect(input.slug).toBe("acme-slack");
83
+ expect(input.labels).toEqual({ team: "support" });
84
+ // The other provider arm stays absent — the oneof is preserved.
85
+ expect(input.whatsapp).toBeUndefined();
86
+ });
87
+ });
@@ -13,6 +13,7 @@ import { ModelSelector } from "../models/ModelSelector.js";
13
13
  import { HarnessSelector } from "../models/HarnessSelector.js";
14
14
  import type { HarnessOption } from "../models/harness.js";
15
15
  import type { ServiceTierOption } from "../models/service-tier.js";
16
+ import type { ThinkingModeOption } from "../models/thinking-mode.js";
16
17
  import { InteractionModePicker, type InteractionModeOption } from "./InteractionModePicker.js";
17
18
  import {
18
19
  PaperclipIcon,
@@ -53,6 +54,10 @@ export interface ComposerToolbarProps {
53
54
  readonly serviceTier?: ServiceTierOption;
54
55
  /** Enables the fast-tier switch inside the model selector's options area. */
55
56
  readonly onServiceTierChange?: (tier: ServiceTierOption) => void;
57
+ /** Current thinking mode for the selected model (#772). */
58
+ readonly thinkingMode?: ThinkingModeOption;
59
+ /** Enables the thinking switch inside the model selector's options area. */
60
+ readonly onThinkingModeChange?: (mode: ThinkingModeOption) => void;
56
61
 
57
62
  // -- Right group: Secondary actions (icon-only) ---------------------------
58
63
 
@@ -128,6 +133,8 @@ export function ComposerToolbar({
128
133
  onModelChange,
129
134
  serviceTier,
130
135
  onServiceTierChange,
136
+ thinkingMode,
137
+ onThinkingModeChange,
131
138
  }: ComposerToolbarProps) {
132
139
  const showHarnessSeparate = showHarnessSelector && !showModelSelector;
133
140
 
@@ -161,6 +168,8 @@ export function ComposerToolbar({
161
168
  onHarnessChange={showHarnessSelector ? onHarnessChange : undefined}
162
169
  serviceTier={serviceTier}
163
170
  onServiceTierChange={onServiceTierChange}
171
+ thinkingMode={thinkingMode}
172
+ onThinkingModeChange={onThinkingModeChange}
164
173
  disabled={disabled}
165
174
  />
166
175
  )}
@@ -8,6 +8,7 @@ import { ComposerToolbar } from "./ComposerToolbar.js";
8
8
  import { type ConfigureMenuItem } from "./ConfigureMenu.js";
9
9
  import type { HarnessOption } from "../models/harness.js";
10
10
  import { FAST_SERVICE_TIER, type ServiceTierOption } from "../models/service-tier.js";
11
+ import type { ThinkingModeOption } from "../models/thinking-mode.js";
11
12
  import type { InteractionModeOption } from "./InteractionModePicker.js";
12
13
  import { parseModelKey } from "../models/registry.js";
13
14
  import { useModelRegistry } from "../models/useModelRegistry.js";
@@ -31,7 +32,6 @@ import type { UseGitHubConnectionReturn } from "../github/useGitHubConnection.js
31
32
  import { useAttachments } from "../attachment/useAttachments.js";
32
33
  import { AttachmentChipList } from "../attachment/AttachmentChipList.js";
33
34
  import { extractClipboardFiles } from "../attachment/clipboard.js";
34
- import { prepareImageForVision } from "../attachment/prepare-image.js";
35
35
  import { useFileReferences } from "../file-reference/useFileReferences.js";
36
36
  import { FileReferenceChipList } from "../file-reference/FileReferenceChipList.js";
37
37
  import { FILE_REF_MIME } from "../internal/file-tree/index.js";
@@ -165,6 +165,14 @@ export interface SessionComposerSubmitContext {
165
165
  * Pass to execution creation as `execution_config.service_tier`.
166
166
  */
167
167
  readonly serviceTier?: ServiceTierOption;
168
+ /**
169
+ * Thinking mode the user actively selected for this execution
170
+ * (stigmer/stigmer#772). Only ever `"enabled"` — an untouched mode means
171
+ * "platform default" and is carried as `undefined`, the same
172
+ * unspecified-vs-explicit contract as {@link serviceTier}.
173
+ * Pass to execution creation as `execution_config.thinking_mode`.
174
+ */
175
+ readonly thinkingMode?: ThinkingModeOption;
168
176
  /**
169
177
  * Workspace-relative file paths the user referenced via drag-to-reference.
170
178
  *
@@ -664,6 +672,9 @@ const SessionComposerInner = forwardRef<SessionComposerHandle, SessionComposerPr
664
672
  // selection below (#663) — state here records the user's intent only.
665
673
  const [serviceTier, setServiceTier] = useState<ServiceTierOption>("standard");
666
674
 
675
+ // Thinking mode (#772): the tier's twin, same intent-vs-effective split.
676
+ const [thinkingMode, setThinkingMode] = useState<ThinkingModeOption>("disabled");
677
+
667
678
  // Active harness mirror: controlled hosts (both viewers) keep the
668
679
  // `harness` prop current, but with `showHarnessSelector` the dropdown
669
680
  // lives inside ModelSelector and an uncontrolled host would leave the
@@ -838,10 +849,26 @@ const SessionComposerInner = forwardRef<SessionComposerHandle, SessionComposerPr
838
849
  ? "fast"
839
850
  : "standard";
840
851
 
841
- return { modelId: effectiveModelId, model: effectiveModel, serviceTier: effectiveServiceTier };
852
+ // "enabled" rides under the identical rule, keyed on the capability
853
+ // (cursor-harness models only — v1 has no native thinking mapping, #772).
854
+ const effectiveThinkingMode: ThinkingModeOption =
855
+ thinkingMode === "enabled"
856
+ && effectiveModel !== undefined
857
+ && effectiveModel.harness === "cursor"
858
+ && effectiveModel.thinkingCapable === true
859
+ ? "enabled"
860
+ : "disabled";
861
+
862
+ return {
863
+ modelId: effectiveModelId,
864
+ model: effectiveModel,
865
+ serviceTier: effectiveServiceTier,
866
+ thinkingMode: effectiveThinkingMode,
867
+ };
842
868
  }, [
843
869
  modelId,
844
870
  serviceTier,
871
+ thinkingMode,
845
872
  showModelSelector,
846
873
  isRegistryLoading,
847
874
  registryGetByKey,
@@ -855,11 +882,17 @@ const SessionComposerInner = forwardRef<SessionComposerHandle, SessionComposerPr
855
882
  const visionNotice = useMemo(() => {
856
883
  if (!enableAttachments || attachments.entries.length === 0) return null;
857
884
  const preflight = assessVisionPreflight(
858
- attachments.entries.map((e) => ({
859
- name: e.file.name,
860
- sizeBytes: e.file.size,
861
- contentType: e.contentType,
862
- })),
885
+ attachments.entries
886
+ // A `preparing` entry still carries pre-downscale bytes — judging
887
+ // those would flash a false "too large" that vanishes when
888
+ // preparation lands. Same tri-state discipline as the module
889
+ // itself: silent while the real size is unknown.
890
+ .filter((e) => e.phase !== "preparing")
891
+ .map((e) => ({
892
+ name: e.file.name,
893
+ sizeBytes: e.file.size,
894
+ contentType: e.contentType,
895
+ })),
863
896
  { model: effective.model, limits: visionLimits },
864
897
  );
865
898
  return visionPreflightMessage(preflight, {
@@ -963,12 +996,11 @@ const SessionComposerInner = forwardRef<SessionComposerHandle, SessionComposerPr
963
996
  e.preventDefault();
964
997
 
965
998
  // Pasted images (and only pasted — see prepare-image.ts) are bounded
966
- // to provider resolution before upload. prepareImageForVision never
967
- // throws; every failure path yields the original file.
999
+ // to provider resolution before upload. The hook owns the whole
1000
+ // pipeline: chips appear immediately in the `preparing` phase
1001
+ // (stigmer/stigmer#369), then upload.
968
1002
  const addFiles = attachments.addFiles;
969
- void Promise.all(files.map(prepareImageForVision)).then((prepared) => {
970
- addFiles(prepared);
971
- });
1003
+ addFiles(files, { prepareImages: true });
972
1004
  },
973
1005
  [enableAttachments, isDisabled, attachments.addFiles],
974
1006
  );
@@ -1050,15 +1082,19 @@ const SessionComposerInner = forwardRef<SessionComposerHandle, SessionComposerPr
1050
1082
  // explicit distinction is load-bearing telemetry (#357).
1051
1083
  const submitServiceTier =
1052
1084
  effective.serviceTier === "fast" ? effective.serviceTier : undefined;
1085
+ // The tier's #772 twin: only an in-effect "enabled" is carried.
1086
+ const submitThinkingMode =
1087
+ effective.thinkingMode === "enabled" ? effective.thinkingMode : undefined;
1053
1088
 
1054
1089
  const context: SessionComposerSubmitContext | undefined =
1055
1090
  hasEnv || hasAttachments || effectiveMode || hasFileRefs || buildFromPlan
1056
- || submitServiceTier
1091
+ || submitServiceTier || submitThinkingMode
1057
1092
  ? {
1058
1093
  runtimeEnv: hasEnv ? env : undefined,
1059
1094
  attachments: hasAttachments ? attachmentInputs : undefined,
1060
1095
  interactionMode: effectiveMode,
1061
1096
  serviceTier: submitServiceTier,
1097
+ thinkingMode: submitThinkingMode,
1062
1098
  workspaceFileRefs: hasFileRefs ? [...fileRefs.refs] : undefined,
1063
1099
  buildFromPlan,
1064
1100
  }
@@ -1376,11 +1412,12 @@ const SessionComposerInner = forwardRef<SessionComposerHandle, SessionComposerPr
1376
1412
  // ---------------------------------------------------------------------------
1377
1413
 
1378
1414
  const mcpBlocked = showMcp && !mcpSetup.allReady;
1379
- // Send waits for in-flight uploads: toAttachmentInputs() carries only
1415
+ // Send waits for in-flight work: toAttachmentInputs() carries only
1380
1416
  // "ready" entries, so a send racing an upload would silently drop the
1381
1417
  // file the user just pasted — the worst failure for "what's wrong in
1382
1418
  // this screenshot?". Errored entries never gate (their chip offers
1383
- // retry/remove); only the uploading phase blocks.
1419
+ // retry/remove); isUploading covers both the preparing and uploading
1420
+ // phases (see its doc — same drop class).
1384
1421
  const uploadsBlocked = enableAttachments && attachments.isUploading;
1385
1422
  const canSend = composer.canSubmit && !mcpBlocked && !uploadsBlocked;
1386
1423
 
@@ -1758,7 +1795,11 @@ const SessionComposerInner = forwardRef<SessionComposerHandle, SessionComposerPr
1758
1795
  className="stg:mx-3 stg:mb-2 stg:flex stg:items-center stg:gap-2 stg:rounded-md stg:bg-muted stg:px-2.5 stg:py-1.5 stg:text-xs stg:text-muted-foreground"
1759
1796
  >
1760
1797
  <ChipSpinner />
1761
- <span>Waiting for attachments to finish uploading…</span>
1798
+ <span>
1799
+ {attachments.isPreparing
1800
+ ? "Waiting for attachments to finish preparing…"
1801
+ : "Waiting for attachments to finish uploading…"}
1802
+ </span>
1762
1803
  </div>
1763
1804
  )}
1764
1805
 
@@ -1885,6 +1926,8 @@ const SessionComposerInner = forwardRef<SessionComposerHandle, SessionComposerPr
1885
1926
  onModelChange={handleModelChange}
1886
1927
  serviceTier={serviceTier}
1887
1928
  onServiceTierChange={setServiceTier}
1929
+ thinkingMode={thinkingMode}
1930
+ onThinkingModeChange={setThinkingMode}
1888
1931
  />
1889
1932
  </div>
1890
1933
  </div>
@@ -0,0 +1,176 @@
1
+ import { describe, it, expect, vi, afterEach, beforeAll } from "vitest";
2
+ import { render, screen, cleanup, fireEvent, waitFor } from "@testing-library/react";
3
+ import { type ReactNode } from "react";
4
+ import type { Stigmer } from "@stigmer/sdk";
5
+ import { StigmerContext } from "../../context";
6
+ import { ModelRegistryContext } from "../../models/ModelRegistryContext";
7
+ import type { ModelInfo } from "../../models/registry";
8
+ import { SessionComposer } from "../SessionComposer";
9
+
10
+ // Shim ResizeObserver for Base UI's positioner (the
11
+ // scheduleCreation.test.tsx pattern).
12
+ beforeAll(() => {
13
+ if (!("ResizeObserver" in globalThis)) {
14
+ (globalThis as unknown as { ResizeObserver: unknown }).ResizeObserver =
15
+ class {
16
+ observe() {}
17
+ unobserve() {}
18
+ disconnect() {}
19
+ };
20
+ }
21
+ });
22
+
23
+ /**
24
+ * End-to-end composer wiring for the thinking mode (stigmer/stigmer#772) —
25
+ * the SessionComposer-serviceTier twin. The toolbar's ModelSelector toggle
26
+ * must reach composer state, and an active enabled selection must ride the
27
+ * submit context to execution create. This seam (SessionComposer →
28
+ * ComposerToolbar props → submit context) is exactly where the #357 tier
29
+ * toggle originally shipped dead while every layer unit-tested green.
30
+ */
31
+
32
+ const THINKING_CAPABLE: ModelInfo = {
33
+ modelId: "claude-haiku-4-5",
34
+ provider: "anthropic",
35
+ displayName: "Haiku 4.5",
36
+ shortDescription: "Fast Claude with optional thinking",
37
+ speedTier: "fast",
38
+ costTier: "economy",
39
+ harness: "cursor",
40
+ featured: true,
41
+ serviceTiers: [],
42
+ thinkingCapable: true,
43
+ };
44
+
45
+ const NOT_CAPABLE: ModelInfo = {
46
+ modelId: "composer-2.5",
47
+ provider: "cursor",
48
+ displayName: "Composer 2.5",
49
+ shortDescription: "Fast agentic model",
50
+ speedTier: "fastest",
51
+ costTier: "economy",
52
+ harness: "cursor",
53
+ featured: true,
54
+ serviceTiers: ["fast"],
55
+ thinkingCapable: false,
56
+ };
57
+
58
+ function createMinimalStigmerMock(): Stigmer {
59
+ return {
60
+ agentExecution: { uploadAttachment: vi.fn() },
61
+ environment: { getPersonal: vi.fn().mockResolvedValue(null) },
62
+ baseUrl: "http://localhost:8080",
63
+ getAuthCredential: vi.fn().mockResolvedValue("test-token"),
64
+ config: {
65
+ baseUrl: "http://localhost:8080",
66
+ getAccessToken: vi.fn().mockResolvedValue(""),
67
+ },
68
+ } as unknown as Stigmer;
69
+ }
70
+
71
+ function createWrapper(client: Stigmer) {
72
+ return function Wrapper({ children }: { children: ReactNode }) {
73
+ return (
74
+ <StigmerContext.Provider value={client}>
75
+ <ModelRegistryContext.Provider
76
+ value={{
77
+ models: [THINKING_CAPABLE, NOT_CAPABLE],
78
+ isLoading: false,
79
+ error: null,
80
+ refetch: vi.fn(),
81
+ }}
82
+ >
83
+ {children}
84
+ </ModelRegistryContext.Provider>
85
+ </StigmerContext.Provider>
86
+ );
87
+ };
88
+ }
89
+
90
+ function renderComposer(defaultModelId = "claude-haiku-4-5") {
91
+ const client = createMinimalStigmerMock();
92
+ const onSubmit = vi.fn();
93
+
94
+ const result = render(
95
+ <SessionComposer
96
+ onSubmit={onSubmit}
97
+ harness="cursor"
98
+ defaultModelId={defaultModelId}
99
+ />,
100
+ { wrapper: createWrapper(client) },
101
+ );
102
+
103
+ return { ...result, onSubmit };
104
+ }
105
+
106
+ function submitMessage(message: string) {
107
+ const textarea = screen.getByRole("textbox");
108
+ fireEvent.change(textarea, { target: { value: message } });
109
+ fireEvent.keyDown(textarea, { key: "Enter", shiftKey: false });
110
+ }
111
+
112
+ /**
113
+ * Open the model selector popover (Base UI portals mount asynchronously in
114
+ * jsdom — the switch is awaited) and flip the thinking switch in the
115
+ * options area, then close the popover (toggling keeps it open).
116
+ */
117
+ async function toggleThinking(triggerName: RegExp = /Haiku 4\.5/) {
118
+ fireEvent.click(screen.getByRole("button", { name: triggerName }));
119
+ const toggle = await screen.findByRole("switch", { name: "Thinking" });
120
+ fireEvent.click(toggle);
121
+ fireEvent.keyDown(document.activeElement ?? document.body, { key: "Escape" });
122
+ }
123
+
124
+ afterEach(cleanup);
125
+
126
+ describe("SessionComposer — thinking mode submit contract", () => {
127
+ it("renders the thinking switch through the toolbar for a capable model", async () => {
128
+ renderComposer();
129
+
130
+ fireEvent.click(screen.getByRole("button", { name: /Haiku 4\.5/ }));
131
+ expect(await screen.findByRole("switch", { name: "Thinking" })).toBeTruthy();
132
+ });
133
+
134
+ it("renders no thinking switch for a model without the capability", async () => {
135
+ renderComposer("composer-2.5");
136
+
137
+ fireEvent.click(screen.getByRole("button", { name: /Composer 2\.5/ }));
138
+ // The fast switch proves the options area itself rendered — only the
139
+ // thinking row must be absent (no dead controls).
140
+ await screen.findByRole("switch", { name: "Fast tier" });
141
+ expect(screen.queryByRole("switch", { name: "Thinking" })).toBeNull();
142
+ });
143
+
144
+ it("carries thinkingMode 'enabled' on the submit context after toggling", async () => {
145
+ const { onSubmit } = renderComposer();
146
+
147
+ await toggleThinking();
148
+ submitMessage("Think hard about this");
149
+
150
+ await waitFor(() => expect(onSubmit).toHaveBeenCalledOnce());
151
+ const context = onSubmit.mock.calls[0][2];
152
+ expect(context?.thinkingMode).toBe("enabled");
153
+ });
154
+
155
+ it("leaves thinkingMode undefined when the switch is untouched", async () => {
156
+ const { onSubmit } = renderComposer();
157
+
158
+ submitMessage("Just answer");
159
+
160
+ await waitFor(() => expect(onSubmit).toHaveBeenCalledOnce());
161
+ const context = onSubmit.mock.calls[0][2];
162
+ expect(context?.thinkingMode).toBeUndefined();
163
+ });
164
+
165
+ it("leaves thinkingMode undefined after toggling on and back off", async () => {
166
+ const { onSubmit } = renderComposer();
167
+
168
+ await toggleThinking();
169
+ await toggleThinking();
170
+ submitMessage("Changed my mind");
171
+
172
+ await waitFor(() => expect(onSubmit).toHaveBeenCalledOnce());
173
+ const context = onSubmit.mock.calls[0][2];
174
+ expect(context?.thinkingMode).toBeUndefined();
175
+ });
176
+ });
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { useId, useState } from "react";
4
- import { Send, TriangleAlert } from "lucide-react";
4
+ import { LayoutTemplate, Send, TriangleAlert } from "lucide-react";
5
5
  import { cn } from "@stigmer/theme";
6
6
  import { getUserMessage } from "@stigmer/sdk";
7
7
  import { ChannelSendOutcome } from "@stigmer/protos/ai/stigmer/agentic/agentchannel/v1/message_io_pb";
@@ -9,13 +9,29 @@ import type { SendChannelMessageOutput } from "@stigmer/protos/ai/stigmer/agenti
9
9
  import { Button } from "../button/Button.js";
10
10
  import { SpinnerIcon } from "../internal/SpinnerIcon.js";
11
11
  import { useComposer } from "../composer/useComposer.js";
12
+ import type { ConversationReplyPayload } from "./useConversationParticipation.js";
12
13
 
13
14
  /** Props for {@link ConversationComposer}. */
14
15
  export interface ConversationComposerProps {
15
- /** Send a staff reply (from `useConversationParticipation().reply`). */
16
- readonly onSend: (text: string) => Promise<SendChannelMessageOutput>;
16
+ /**
17
+ * Send a staff reply (from `useConversationParticipation().reply`).
18
+ * The composer's own textarea always submits the `text` lane; hosts
19
+ * wiring a template picker send the `template` lane through the same
20
+ * seam — one command, one busy state, one outcome contract.
21
+ */
22
+ readonly onSend: (payload: ConversationReplyPayload) => Promise<SendChannelMessageOutput>;
17
23
  /** `true` while a reply is in flight. */
18
24
  readonly isSending: boolean;
25
+ /**
26
+ * Opens the host's template picker. When present, a persistent
27
+ * template affordance renders beside Send — templates are the one
28
+ * lane WhatsApp offers for a closed window, so the surface must
29
+ * offer the door, not just the advisory's warning (hosts should
30
+ * point at the button in the advisory copy; the advisory itself
31
+ * stays interaction-free — it is the input's description, F-18).
32
+ * Omit on channels whose provider has no template registry.
33
+ */
34
+ readonly onOpenTemplatePicker?: () => void;
19
35
  /**
20
36
  * Why replying is unavailable, when it is — rendered in place of the
21
37
  * input (a senderless provider, or a conversation the customer has
@@ -52,6 +68,7 @@ export interface ConversationComposerProps {
52
68
  export function ConversationComposer({
53
69
  onSend,
54
70
  isSending,
71
+ onOpenTemplatePicker,
55
72
  disabledReason,
56
73
  advisory,
57
74
  className,
@@ -66,7 +83,7 @@ export function ConversationComposer({
66
83
  const composer = useComposer({
67
84
  onSubmit: (message) => {
68
85
  setNotice(null);
69
- onSend(message).then(
86
+ onSend({ kind: "text", body: message }).then(
70
87
  (output) => {
71
88
  if (output.outcome === ChannelSendOutcome.refused) {
72
89
  // The words never left; keep them so the user can adjust.
@@ -110,6 +127,12 @@ export function ConversationComposer({
110
127
  className="stg:mb-2 stg:flex stg:items-start stg:gap-1.5 stg:rounded-md stg:bg-status-degraded-subtle stg:px-2.5 stg:py-1.5 stg:text-xs stg:text-status-degraded"
111
128
  >
112
129
  <TriangleAlert aria-hidden="true" className="stg:mt-0.5 stg:size-3.5 stg:shrink-0" />
130
+ {/* Interactive content is deliberately NOT allowed in here:
131
+ this element is the input's aria-describedby target with
132
+ zero tab stops (F-18). The way forward the advisory names
133
+ (the template lane) lives on the persistent template
134
+ button beside Send — hosts point at it in the advisory
135
+ COPY, never with an embedded control. */}
113
136
  <span className="stg:min-w-0">{advisory}</span>
114
137
  </p>
115
138
  )}
@@ -137,6 +160,17 @@ export function ConversationComposer({
137
160
  "stg:focus-visible:outline-none stg:focus-visible:ring-2 stg:focus-visible:ring-ring",
138
161
  )}
139
162
  />
163
+ {onOpenTemplatePicker && (
164
+ <Button
165
+ variant="outline"
166
+ size="sm"
167
+ onClick={onOpenTemplatePicker}
168
+ disabled={isSending}
169
+ aria-label="Send a template reply"
170
+ >
171
+ <LayoutTemplate aria-hidden="true" className="stg:size-4" />
172
+ </Button>
173
+ )}
140
174
  <Button
141
175
  variant="primary"
142
176
  size="sm"