@stigmer/react 3.12.5 → 3.12.7

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 (329) 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/channel/AgentChannelsPanel.d.ts.map +1 -1
  15. package/channel/AgentChannelsPanel.js +10 -5
  16. package/channel/AgentChannelsPanel.js.map +1 -1
  17. package/channel/ChannelRunConfigDialog.d.ts +51 -0
  18. package/channel/ChannelRunConfigDialog.d.ts.map +1 -0
  19. package/channel/ChannelRunConfigDialog.js +121 -0
  20. package/channel/ChannelRunConfigDialog.js.map +1 -0
  21. package/channel/index.d.ts +2 -0
  22. package/channel/index.d.ts.map +1 -1
  23. package/channel/index.js +1 -0
  24. package/channel/index.js.map +1 -1
  25. package/channel/useSaveAgentChannel.d.ts.map +1 -1
  26. package/channel/useSaveAgentChannel.js +6 -3
  27. package/channel/useSaveAgentChannel.js.map +1 -1
  28. package/channel-app/ChannelAppDetailPanel.js +9 -9
  29. package/channel-app/ChannelAppDetailPanel.js.map +1 -1
  30. package/composer/ComposerToolbar.d.ts +24 -1
  31. package/composer/ComposerToolbar.d.ts.map +1 -1
  32. package/composer/ComposerToolbar.js +7 -2
  33. package/composer/ComposerToolbar.js.map +1 -1
  34. package/composer/SessionComposer.d.ts +19 -0
  35. package/composer/SessionComposer.d.ts.map +1 -1
  36. package/composer/SessionComposer.js +24 -5
  37. package/composer/SessionComposer.js.map +1 -1
  38. package/composer/index.d.ts +1 -0
  39. package/composer/index.d.ts.map +1 -1
  40. package/composer/index.js.map +1 -1
  41. package/cursor-accounts/CursorAccountsConsole.d.ts.map +1 -1
  42. package/cursor-accounts/CursorAccountsConsole.js +12 -3
  43. package/cursor-accounts/CursorAccountsConsole.js.map +1 -1
  44. package/cursor-accounts/cursor-account-coverage.d.ts +30 -1
  45. package/cursor-accounts/cursor-account-coverage.d.ts.map +1 -1
  46. package/cursor-accounts/cursor-account-coverage.js +21 -0
  47. package/cursor-accounts/cursor-account-coverage.js.map +1 -1
  48. package/execution/MessageThread.d.ts.map +1 -1
  49. package/execution/MessageThread.js +8 -11
  50. package/execution/MessageThread.js.map +1 -1
  51. package/execution/useCreateAgentExecution.d.ts +15 -0
  52. package/execution/useCreateAgentExecution.d.ts.map +1 -1
  53. package/execution/useCreateAgentExecution.js +5 -0
  54. package/execution/useCreateAgentExecution.js.map +1 -1
  55. package/execution/useFileChangeContent.d.ts +2 -6
  56. package/execution/useFileChangeContent.d.ts.map +1 -1
  57. package/execution/useFileChangeContent.js +4 -12
  58. package/execution/useFileChangeContent.js.map +1 -1
  59. package/identity-account/AccountPreferencesPanel.d.ts +2 -1
  60. package/identity-account/AccountPreferencesPanel.d.ts.map +1 -1
  61. package/identity-account/AccountPreferencesPanel.js +33 -18
  62. package/identity-account/AccountPreferencesPanel.js.map +1 -1
  63. package/identity-provider/IdentityProviderDetailPanel.d.ts.map +1 -1
  64. package/identity-provider/IdentityProviderDetailPanel.js +16 -15
  65. package/identity-provider/IdentityProviderDetailPanel.js.map +1 -1
  66. package/index.d.ts +10 -7
  67. package/index.d.ts.map +1 -1
  68. package/index.js +8 -4
  69. package/index.js.map +1 -1
  70. package/internal/download.d.ts +8 -0
  71. package/internal/download.d.ts.map +1 -0
  72. package/internal/download.js +25 -0
  73. package/internal/download.js.map +1 -0
  74. package/library/useExportResource.d.ts.map +1 -1
  75. package/library/useExportResource.js +3 -17
  76. package/library/useExportResource.js.map +1 -1
  77. package/mcp-server/McpServerDetailView.js +2 -2
  78. package/mcp-server/McpServerDetailView.js.map +1 -1
  79. package/mcp-server/index.d.ts +0 -1
  80. package/mcp-server/index.d.ts.map +1 -1
  81. package/mcp-server/index.js +0 -1
  82. package/mcp-server/index.js.map +1 -1
  83. package/mcp-server/internal/definitionDrift.d.ts +14 -12
  84. package/mcp-server/internal/definitionDrift.d.ts.map +1 -1
  85. package/mcp-server/internal/definitionDrift.js +22 -19
  86. package/mcp-server/internal/definitionDrift.js.map +1 -1
  87. package/mcp-server/useUpdateMcpServer.d.ts +3 -3
  88. package/mcp-server/useUpdateMcpServer.js +3 -3
  89. package/memory/MemoryListPanel.d.ts +35 -0
  90. package/memory/MemoryListPanel.d.ts.map +1 -0
  91. package/memory/MemoryListPanel.js +141 -0
  92. package/memory/MemoryListPanel.js.map +1 -0
  93. package/memory/index.d.ts +8 -0
  94. package/memory/index.d.ts.map +1 -0
  95. package/memory/index.js +12 -0
  96. package/memory/index.js.map +1 -0
  97. package/memory/memoryGroups.d.ts +31 -0
  98. package/memory/memoryGroups.d.ts.map +1 -0
  99. package/memory/memoryGroups.js +52 -0
  100. package/memory/memoryGroups.js.map +1 -0
  101. package/memory/useConfirmMemory.d.ts +31 -0
  102. package/memory/useConfirmMemory.d.ts.map +1 -0
  103. package/memory/useConfirmMemory.js +43 -0
  104. package/memory/useConfirmMemory.js.map +1 -0
  105. package/memory/useDeleteMemory.d.ts +30 -0
  106. package/memory/useDeleteMemory.d.ts.map +1 -0
  107. package/memory/useDeleteMemory.js +42 -0
  108. package/memory/useDeleteMemory.js.map +1 -0
  109. package/memory/useMemories.d.ts +33 -0
  110. package/memory/useMemories.d.ts.map +1 -0
  111. package/memory/useMemories.js +42 -0
  112. package/memory/useMemories.js.map +1 -0
  113. package/memory/useRejectMemory.d.ts +34 -0
  114. package/memory/useRejectMemory.d.ts.map +1 -0
  115. package/memory/useRejectMemory.js +46 -0
  116. package/memory/useRejectMemory.js.map +1 -0
  117. package/memory/useUpdateMemoryContent.d.ts +32 -0
  118. package/memory/useUpdateMemoryContent.d.ts.map +1 -0
  119. package/memory/useUpdateMemoryContent.js +48 -0
  120. package/memory/useUpdateMemoryContent.js.map +1 -0
  121. package/models/ModelSelector.d.ts +33 -1
  122. package/models/ModelSelector.d.ts.map +1 -1
  123. package/models/ModelSelector.js +37 -4
  124. package/models/ModelSelector.js.map +1 -1
  125. package/models/index.d.ts +2 -0
  126. package/models/index.d.ts.map +1 -1
  127. package/models/index.js +1 -0
  128. package/models/index.js.map +1 -1
  129. package/models/registry.d.ts +16 -0
  130. package/models/registry.d.ts.map +1 -1
  131. package/models/registry.js +13 -0
  132. package/models/registry.js.map +1 -1
  133. package/models/thinking-mode.d.ts +27 -0
  134. package/models/thinking-mode.d.ts.map +1 -0
  135. package/models/thinking-mode.js +32 -0
  136. package/models/thinking-mode.js.map +1 -0
  137. package/oauth-app/OAuthAppDetailPanel.d.ts.map +1 -1
  138. package/oauth-app/OAuthAppDetailPanel.js +21 -20
  139. package/oauth-app/OAuthAppDetailPanel.js.map +1 -1
  140. package/package.json +4 -4
  141. package/platform-client/PlatformClientDetailPanel.d.ts.map +1 -1
  142. package/platform-client/PlatformClientDetailPanel.js +12 -13
  143. package/platform-client/PlatformClientDetailPanel.js.map +1 -1
  144. package/schedule/ScheduleDetailView.d.ts.map +1 -1
  145. package/schedule/ScheduleDetailView.js +21 -10
  146. package/schedule/ScheduleDetailView.js.map +1 -1
  147. package/schedule/ScheduleForm.d.ts.map +1 -1
  148. package/schedule/ScheduleForm.js +13 -3
  149. package/schedule/ScheduleForm.js.map +1 -1
  150. package/session/NewSessionViewer.d.ts.map +1 -1
  151. package/session/NewSessionViewer.js +9 -1
  152. package/session/NewSessionViewer.js.map +1 -1
  153. package/session/SessionViewer.d.ts +14 -1
  154. package/session/SessionViewer.d.ts.map +1 -1
  155. package/session/SessionViewer.js +21 -25
  156. package/session/SessionViewer.js.map +1 -1
  157. package/session/TranscriptExportMenu.d.ts +24 -0
  158. package/session/TranscriptExportMenu.d.ts.map +1 -0
  159. package/session/TranscriptExportMenu.js +26 -0
  160. package/session/TranscriptExportMenu.js.map +1 -0
  161. package/session/index.d.ts +4 -0
  162. package/session/index.d.ts.map +1 -1
  163. package/session/index.js +2 -0
  164. package/session/index.js.map +1 -1
  165. package/session/run-config.d.ts +9 -0
  166. package/session/run-config.d.ts.map +1 -1
  167. package/session/run-config.js +5 -0
  168. package/session/run-config.js.map +1 -1
  169. package/session/useExportTranscript.d.ts +44 -0
  170. package/session/useExportTranscript.d.ts.map +1 -0
  171. package/session/useExportTranscript.js +109 -0
  172. package/session/useExportTranscript.js.map +1 -0
  173. package/session/useNewSessionFlow.d.ts +11 -0
  174. package/session/useNewSessionFlow.d.ts.map +1 -1
  175. package/session/useNewSessionFlow.js +20 -6
  176. package/session/useNewSessionFlow.js.map +1 -1
  177. package/session/useSessionConversation.d.ts +12 -1
  178. package/session/useSessionConversation.d.ts.map +1 -1
  179. package/session/useSessionConversation.js +21 -42
  180. package/session/useSessionConversation.js.map +1 -1
  181. package/session/useSessionExecutions.d.ts +2 -14
  182. package/session/useSessionExecutions.d.ts.map +1 -1
  183. package/session/useSessionExecutions.js +4 -26
  184. package/session/useSessionExecutions.js.map +1 -1
  185. package/session/useSessionPageFlow.d.ts +23 -10
  186. package/session/useSessionPageFlow.d.ts.map +1 -1
  187. package/session/useSessionPageFlow.js +75 -19
  188. package/session/useSessionPageFlow.js.map +1 -1
  189. package/session/useUpdateSession.d.ts +18 -24
  190. package/session/useUpdateSession.d.ts.map +1 -1
  191. package/session/useUpdateSession.js +18 -24
  192. package/session/useUpdateSession.js.map +1 -1
  193. package/settings/MemorySection.d.ts +12 -0
  194. package/settings/MemorySection.d.ts.map +1 -0
  195. package/settings/MemorySection.js +21 -0
  196. package/settings/MemorySection.js.map +1 -0
  197. package/settings/index.d.ts +1 -0
  198. package/settings/index.d.ts.map +1 -1
  199. package/settings/index.js +1 -0
  200. package/settings/index.js.map +1 -1
  201. package/settings/settings-nav.d.ts.map +1 -1
  202. package/settings/settings-nav.js +2 -1
  203. package/settings/settings-nav.js.map +1 -1
  204. package/src/__tests__/public-api.test.ts +21 -0
  205. package/src/agent/AgentDetailView.tsx +2 -2
  206. package/src/agent/index.ts +0 -2
  207. package/src/agent/useUpdateAgent.ts +3 -3
  208. package/src/agent-instance/AgentInstanceDetailPanel.tsx +5 -7
  209. package/src/agent-instance/__tests__/AgentInstanceDetailPanel.test.tsx +75 -0
  210. package/src/agent-instance/useUpdateAgentInstance.ts +1 -1
  211. package/src/channel/AgentChannelsPanel.tsx +26 -1
  212. package/src/channel/ChannelRunConfigDialog.tsx +351 -0
  213. package/src/channel/__tests__/ChannelRunConfigDialog.test.tsx +210 -0
  214. package/src/channel/index.ts +2 -0
  215. package/src/channel/useSaveAgentChannel.ts +6 -3
  216. package/src/channel-app/ChannelAppDetailPanel.tsx +9 -9
  217. package/src/channel-app/__tests__/channelAppWhatsApp.test.tsx +2 -0
  218. package/src/channel-app/__tests__/channelAppWipeSafety.test.tsx +87 -0
  219. package/src/composer/ComposerToolbar.tsx +67 -0
  220. package/src/composer/SessionComposer.tsx +49 -3
  221. package/src/composer/__tests__/ComposerToolbar-autoApprove.test.tsx +80 -0
  222. package/src/composer/__tests__/SessionComposer-thinkingMode.test.tsx +176 -0
  223. package/src/composer/index.ts +1 -0
  224. package/src/cursor-accounts/CursorAccountsConsole.tsx +40 -5
  225. package/src/cursor-accounts/__tests__/CursorAccountsConsole.test.tsx +32 -0
  226. package/src/cursor-accounts/__tests__/cursor-account-coverage.test.ts +58 -1
  227. package/src/cursor-accounts/cursor-account-coverage.ts +55 -0
  228. package/src/execution/MessageThread.tsx +8 -11
  229. package/src/execution/useCreateAgentExecution.ts +19 -0
  230. package/src/execution/useFileChangeContent.ts +5 -12
  231. package/src/identity-account/AccountPreferencesPanel.tsx +154 -68
  232. package/src/identity-account/__tests__/AccountPreferencesPanel.test.tsx +36 -2
  233. package/src/identity-provider/IdentityProviderDetailPanel.tsx +18 -15
  234. package/src/identity-provider/__tests__/IdentityProviderDetailPanel.test.tsx +80 -0
  235. package/src/index.ts +36 -2
  236. package/src/internal/download.ts +29 -0
  237. package/src/library/useExportResource.ts +3 -19
  238. package/src/mcp-server/McpServerDetailView.tsx +2 -2
  239. package/src/mcp-server/index.ts +0 -2
  240. package/src/mcp-server/internal/definitionDrift.ts +22 -20
  241. package/src/mcp-server/useUpdateMcpServer.ts +3 -3
  242. package/src/memory/MemoryListPanel.tsx +532 -0
  243. package/src/memory/__tests__/MemoryListPanel.test.tsx +259 -0
  244. package/src/memory/__tests__/memoryHooks.test.tsx +220 -0
  245. package/src/memory/index.ts +19 -0
  246. package/src/memory/memoryGroups.ts +64 -0
  247. package/src/memory/useConfirmMemory.ts +62 -0
  248. package/src/memory/useDeleteMemory.ts +61 -0
  249. package/src/memory/useMemories.ts +66 -0
  250. package/src/memory/useRejectMemory.ts +65 -0
  251. package/src/memory/useUpdateMemoryContent.ts +67 -0
  252. package/src/models/ModelSelector.tsx +94 -1
  253. package/src/models/index.ts +2 -0
  254. package/src/models/registry.ts +29 -0
  255. package/src/models/thinking-mode.ts +48 -0
  256. package/src/oauth-app/OAuthAppDetailPanel.tsx +23 -21
  257. package/src/oauth-app/__tests__/OAuthAppDetailPanel.test.tsx +94 -0
  258. package/src/platform-client/PlatformClientDetailPanel.tsx +14 -13
  259. package/src/platform-client/__tests__/PlatformClientDetailPanel.test.tsx +82 -0
  260. package/src/schedule/ScheduleDetailView.tsx +29 -4
  261. package/src/schedule/ScheduleForm.tsx +14 -1
  262. package/src/session/NewSessionViewer.tsx +15 -1
  263. package/src/session/SessionViewer.tsx +48 -49
  264. package/src/session/TranscriptExportMenu.tsx +78 -0
  265. package/src/session/__tests__/SessionViewer-autoApproveToggle.test.tsx +178 -0
  266. package/src/session/__tests__/SessionViewer-transcriptExport.test.tsx +151 -0
  267. package/src/session/__tests__/TranscriptExportMenu.test.tsx +97 -0
  268. package/src/session/__tests__/useExportTranscript.test.tsx +187 -0
  269. package/src/session/__tests__/useNewSessionFlow.test.tsx +33 -0
  270. package/src/session/__tests__/useSessionPageFlow.autoApprove.test.tsx +176 -2
  271. package/src/session/index.ts +9 -0
  272. package/src/session/run-config.ts +16 -0
  273. package/src/session/useExportTranscript.ts +171 -0
  274. package/src/session/useNewSessionFlow.ts +34 -7
  275. package/src/session/useSessionConversation.ts +42 -56
  276. package/src/session/useSessionExecutions.ts +5 -26
  277. package/src/session/useSessionPageFlow.ts +98 -32
  278. package/src/session/useUpdateSession.ts +18 -24
  279. package/src/settings/MemorySection.tsx +45 -0
  280. package/src/settings/index.ts +1 -0
  281. package/src/settings/settings-nav.ts +2 -0
  282. package/src/usage/useExportCSV.ts +2 -14
  283. package/src/workflow/ApprovalFormBuilder.tsx +20 -5
  284. package/src/workflow/WorkflowDetailView.tsx +2 -2
  285. package/src/workflow/__tests__/WorkflowInstanceDetailPanel.test.tsx +83 -0
  286. package/src/workflow/index.ts +0 -2
  287. package/src/workflow/instance/WorkflowInstanceDetailPanel.tsx +6 -7
  288. package/src/workflow/useUpdateWorkflow.ts +3 -3
  289. package/styles.css +1 -1
  290. package/usage/useExportCSV.d.ts.map +1 -1
  291. package/usage/useExportCSV.js +2 -13
  292. package/usage/useExportCSV.js.map +1 -1
  293. package/workflow/ApprovalFormBuilder.d.ts.map +1 -1
  294. package/workflow/ApprovalFormBuilder.js +10 -8
  295. package/workflow/ApprovalFormBuilder.js.map +1 -1
  296. package/workflow/WorkflowDetailView.js +2 -2
  297. package/workflow/WorkflowDetailView.js.map +1 -1
  298. package/workflow/index.d.ts +0 -1
  299. package/workflow/index.d.ts.map +1 -1
  300. package/workflow/index.js +0 -1
  301. package/workflow/index.js.map +1 -1
  302. package/workflow/instance/WorkflowInstanceDetailPanel.d.ts.map +1 -1
  303. package/workflow/instance/WorkflowInstanceDetailPanel.js +6 -7
  304. package/workflow/instance/WorkflowInstanceDetailPanel.js.map +1 -1
  305. package/workflow/useUpdateWorkflow.d.ts +3 -3
  306. package/workflow/useUpdateWorkflow.js +3 -3
  307. package/agent/internal/agentToInput.d.ts +0 -15
  308. package/agent/internal/agentToInput.d.ts.map +0 -1
  309. package/agent/internal/agentToInput.js +0 -84
  310. package/agent/internal/agentToInput.js.map +0 -1
  311. package/mcp-server/internal/mcpServerToInput.d.ts +0 -12
  312. package/mcp-server/internal/mcpServerToInput.d.ts.map +0 -1
  313. package/mcp-server/internal/mcpServerToInput.js +0 -84
  314. package/mcp-server/internal/mcpServerToInput.js.map +0 -1
  315. package/session/session-spec-converters.d.ts +0 -24
  316. package/session/session-spec-converters.d.ts.map +0 -1
  317. package/session/session-spec-converters.js +0 -72
  318. package/session/session-spec-converters.js.map +0 -1
  319. package/src/agent/internal/__tests__/agentToInput.test.ts +0 -145
  320. package/src/agent/internal/agentToInput.ts +0 -102
  321. package/src/mcp-server/__tests__/mcpServerToInput.test.ts +0 -201
  322. package/src/mcp-server/internal/mcpServerToInput.ts +0 -98
  323. package/src/session/__tests__/session-spec-converters.test.ts +0 -183
  324. package/src/session/session-spec-converters.ts +0 -86
  325. package/src/workflow/internal/workflowToInput.ts +0 -96
  326. package/workflow/internal/workflowToInput.d.ts +0 -12
  327. package/workflow/internal/workflowToInput.d.ts.map +0 -1
  328. package/workflow/internal/workflowToInput.js +0 -73
  329. package/workflow/internal/workflowToInput.js.map +0 -1
@@ -1,6 +1,14 @@
1
1
  "use client";
2
2
 
3
- import { type FormEvent, useCallback, useEffect, useId, useMemo, useState } from "react";
3
+ import {
4
+ type FormEvent,
5
+ type ReactNode,
6
+ useCallback,
7
+ useEffect,
8
+ useId,
9
+ useMemo,
10
+ useState,
11
+ } from "react";
4
12
  import { cn } from "@stigmer/theme";
5
13
  import { getUserMessage, toIdentityAccountUpdateInput } from "@stigmer/sdk";
6
14
  import type { IdentityAccount } from "@stigmer/protos/ai/stigmer/iam/identityaccount/v1/api_pb";
@@ -23,7 +31,8 @@ export interface AccountPreferencesPanelProps {
23
31
 
24
32
  /**
25
33
  * Self-contained editor for the current user's declared preferences
26
- * (`IdentityAccountSpec.preferences.standing_context`).
34
+ * (`IdentityAccountSpec.preferences`): standing context plus the
35
+ * execution defaults (default harness and per-harness default models).
27
36
  *
28
37
  * The declared text is snapshotted into the user's own eligible agent
29
38
  * executions and delivered to the agent as background context.
@@ -267,31 +276,60 @@ function AccountPreferencesForm({
267
276
  </p>
268
277
  </div>
269
278
 
270
- <DefaultHarnessRadioGroup
271
- baseId={baseId}
272
- value={defaultHarness}
273
- onChange={setDefaultHarness}
274
- disabled={isUpdating}
275
- />
276
-
277
- <div className="stg:grid stg:gap-3 stg:sm:grid-cols-2">
278
- <DefaultModelSelect
279
- id={`${baseId}-default-native-model`}
280
- harness="native"
281
- label={`Default model — ${HARNESS_META.native.label}`}
282
- value={defaultNativeModel}
283
- onChange={setDefaultNativeModel}
279
+ <fieldset className="stg:space-y-2" disabled={isUpdating}>
280
+ <legend className="stg:text-xs stg:font-medium stg:text-foreground stg:mb-1">
281
+ Default harness
282
+ </legend>
283
+ <HarnessOptionRow
284
+ radioName={`${baseId}-default-harness`}
285
+ value=""
286
+ label="Platform default"
287
+ description="Stigmer picks the harness for new sessions."
288
+ checked={defaultHarness === ""}
289
+ onSelect={setDefaultHarness}
284
290
  disabled={isUpdating}
285
291
  />
286
- <DefaultModelSelect
287
- id={`${baseId}-default-cursor-model`}
288
- harness="cursor"
289
- label={`Default model — ${HARNESS_META.cursor.label}`}
290
- value={defaultCursorModel}
291
- onChange={setDefaultCursorModel}
292
+ <HarnessOptionRow
293
+ radioName={`${baseId}-default-harness`}
294
+ value="native"
295
+ label={HARNESS_META.native.label}
296
+ description={HARNESS_META.native.description}
297
+ checked={defaultHarness === "native"}
298
+ onSelect={setDefaultHarness}
292
299
  disabled={isUpdating}
293
- />
294
- </div>
300
+ >
301
+ <DefaultModelSelect
302
+ id={`${baseId}-default-native-model`}
303
+ harness="native"
304
+ label="Default model"
305
+ value={defaultNativeModel}
306
+ onChange={setDefaultNativeModel}
307
+ disabled={isUpdating}
308
+ />
309
+ </HarnessOptionRow>
310
+ <HarnessOptionRow
311
+ radioName={`${baseId}-default-harness`}
312
+ value="cursor"
313
+ label={HARNESS_META.cursor.label}
314
+ description={HARNESS_META.cursor.description}
315
+ checked={defaultHarness === "cursor"}
316
+ onSelect={setDefaultHarness}
317
+ disabled={isUpdating}
318
+ >
319
+ <DefaultModelSelect
320
+ id={`${baseId}-default-cursor-model`}
321
+ harness="cursor"
322
+ label="Default model"
323
+ value={defaultCursorModel}
324
+ onChange={setDefaultCursorModel}
325
+ disabled={isUpdating}
326
+ />
327
+ </HarnessOptionRow>
328
+ <p className="stg:text-[0.65rem] stg:text-muted-foreground">
329
+ Each harness&apos;s default model applies whenever a session runs
330
+ on that harness — even when it isn&apos;t your default.
331
+ </p>
332
+ </fieldset>
295
333
  </div>
296
334
 
297
335
  {updateError && (
@@ -332,60 +370,104 @@ function AccountPreferencesForm({
332
370
  }
333
371
 
334
372
  /**
335
- * Only harnesses with a shipped runtime are offerable as a default — must
336
- * stay in lockstep with the proto's `default_harness` in-list validation.
337
- */
338
- const OFFERABLE_HARNESSES: readonly HarnessOption[] = ["native", "cursor"];
339
-
340
- /**
341
- * Default-harness choice: Platform default plus each shipped harness.
342
- * A mutually exclusive, always-visible set radio group, not a select.
373
+ * One selectable default-harness choice, presented as a bordered row
374
+ * (the `VisibilityOptionRow` presentation shape): radio + label +
375
+ * one-line description, with the harness's own controls nested below.
376
+ *
377
+ * The rows for shipped harnesses embed that harness's default-model
378
+ * select as `children` — the pairing IS the information architecture:
379
+ * a harness and its model default belong to each other, and each model
380
+ * default applies whenever a session runs on that harness, independent
381
+ * of which harness is the default. The non-selected harness's select
382
+ * therefore stays interactive (visually secondary, never disabled).
383
+ *
384
+ * The nested controls live OUTSIDE the radio's `<label>` on purpose:
385
+ * a `<select>` inside the label would toggle the radio on every
386
+ * interaction with it.
387
+ *
388
+ * The shipped-harness rows must stay in lockstep with the proto's
389
+ * `default_harness` in-list validation (`["native", "cursor"]`) — each
390
+ * shipped harness binds to its own proto model field, so a new harness
391
+ * adds a field, a row, and a validation entry together.
343
392
  */
344
- function DefaultHarnessRadioGroup({
345
- baseId,
393
+ function HarnessOptionRow({
394
+ radioName,
346
395
  value,
347
- onChange,
396
+ label,
397
+ description,
398
+ checked,
399
+ onSelect,
348
400
  disabled,
401
+ children,
349
402
  }: {
350
- readonly baseId: string;
403
+ readonly radioName: string;
351
404
  readonly value: string;
352
- readonly onChange: (value: string) => void;
405
+ readonly label: string;
406
+ readonly description: string;
407
+ readonly checked: boolean;
408
+ readonly onSelect: (value: string) => void;
353
409
  readonly disabled: boolean;
410
+ readonly children?: ReactNode;
354
411
  }) {
355
- const name = `${baseId}-default-harness`;
356
- const options: readonly { value: string; label: string }[] = [
357
- { value: "", label: "Platform default" },
358
- ...OFFERABLE_HARNESSES.map((h) => ({ value: h, label: HARNESS_META[h].label })),
359
- ];
412
+ // The radio's accessible name is the title alone (aria-labelledby), with
413
+ // the description attached as aria-describedby the wrapping label keeps
414
+ // the whole text block clickable without bloating the announced name.
415
+ const titleId = `${radioName}-${value || "platform"}-title`;
416
+ const descriptionId = `${radioName}-${value || "platform"}-description`;
360
417
 
361
418
  return (
362
- <fieldset className="stg:space-y-1" disabled={disabled}>
363
- <legend className="stg:text-xs stg:font-medium stg:text-foreground">
364
- Default harness
365
- </legend>
366
- <div className="stg:flex stg:flex-wrap stg:gap-x-4 stg:gap-y-1">
367
- {options.map((option) => (
368
- <label
369
- key={option.value}
370
- className={cn(
371
- "stg:inline-flex stg:cursor-pointer stg:items-center stg:gap-1.5 stg:text-xs stg:text-foreground",
372
- disabled && "stg:cursor-default stg:opacity-50",
373
- )}
419
+ <div
420
+ className={cn(
421
+ "stg:rounded-md stg:border stg:px-3 stg:py-2.5 stg:transition-colors",
422
+ checked
423
+ ? "stg:border-primary stg:bg-accent"
424
+ : "stg:border-input stg:hover:bg-accent-hover",
425
+ disabled && "stg:opacity-50",
426
+ )}
427
+ >
428
+ <label
429
+ className={cn(
430
+ "stg:flex stg:cursor-pointer stg:items-start stg:gap-2",
431
+ disabled && "stg:cursor-default",
432
+ )}
433
+ >
434
+ <input
435
+ type="radio"
436
+ name={radioName}
437
+ value={value}
438
+ checked={checked}
439
+ onChange={() => onSelect(value)}
440
+ disabled={disabled}
441
+ aria-labelledby={titleId}
442
+ aria-describedby={descriptionId}
443
+ className="stg:mt-0.5 stg:accent-primary stg:focus-visible:outline-none stg:focus-visible:ring-1 stg:focus-visible:ring-ring"
444
+ />
445
+ <span className="stg:min-w-0 stg:flex-1">
446
+ <span
447
+ id={titleId}
448
+ className="stg:block stg:text-xs stg:font-medium stg:text-foreground"
374
449
  >
375
- <input
376
- type="radio"
377
- name={name}
378
- value={option.value}
379
- checked={value === option.value}
380
- onChange={() => onChange(option.value)}
381
- disabled={disabled}
382
- className="stg:accent-primary stg:focus-visible:outline-none stg:focus-visible:ring-1 stg:focus-visible:ring-ring"
383
- />
384
- {option.label}
385
- </label>
386
- ))}
387
- </div>
388
- </fieldset>
450
+ {label}
451
+ </span>
452
+ <span
453
+ id={descriptionId}
454
+ className="stg:block stg:text-[0.65rem] stg:leading-snug stg:text-muted-foreground"
455
+ >
456
+ {description}
457
+ </span>
458
+ </span>
459
+ </label>
460
+ {children && (
461
+ <div
462
+ className={cn(
463
+ "stg:mt-2 stg:pl-6 stg:transition-opacity",
464
+ !checked && "stg:opacity-70",
465
+ )}
466
+ >
467
+ {children}
468
+ </div>
469
+ )}
470
+ </div>
389
471
  );
390
472
  }
391
473
 
@@ -426,6 +508,10 @@ function DefaultModelSelect({
426
508
  id={id}
427
509
  value={value}
428
510
  onChange={(e) => onChange(e.target.value)}
511
+ // Both harness rows show the same visible label; the accessible
512
+ // name stays unique per control by qualifying it with the harness
513
+ // (WCAG label-in-name: the visible text remains a prefix).
514
+ aria-label={`${label} — ${HARNESS_META[harness].label}`}
429
515
  disabled={disabled || isLoading}
430
516
  className={cn(
431
517
  "stg:w-full stg:rounded-md stg:border stg:border-input stg:bg-background stg:px-2.5 stg:py-1.5 stg:text-xs stg:text-foreground",
@@ -107,7 +107,10 @@ describe("AccountPreferencesPanel", () => {
107
107
  // Fields this form never renders must round-trip untouched.
108
108
  expect(input.idpId).toBe("auth0|abc");
109
109
  expect(input.email).toBe("ada@acme.example");
110
- // The update pipeline addresses the resource by org + slug.
110
+ // The update pipeline addresses id-first — the id is REQUIRED here:
111
+ // identity accounts are org-less, so the org+slug fallback can never
112
+ // match one (the save-path bug this pin guards against).
113
+ expect(input.id).toBe("ia-1");
111
114
  expect(input.org).toBe("acme");
112
115
  expect(input.slug).toBe("ada");
113
116
  });
@@ -149,7 +152,7 @@ describe("AccountPreferencesPanel", () => {
149
152
  },
150
153
  });
151
154
 
152
- it("renders the harness radio group and registry-fed model selects", async () => {
155
+ it("renders the harness option rows and registry-fed model selects", async () => {
153
156
  renderPanel(createMockStigmer());
154
157
  await findSyncedField("Keep answers terse.");
155
158
 
@@ -160,6 +163,37 @@ describe("AccountPreferencesPanel", () => {
160
163
  expect(screen.getByLabelText("Default model — Cursor")).toBeTruthy();
161
164
  });
162
165
 
166
+ it("pairs each harness row with its description (the row IS the context)", async () => {
167
+ renderPanel(createMockStigmer());
168
+ await findSyncedField("Keep answers terse.");
169
+
170
+ // Row subtitles come from HARNESS_META — the single source of harness
171
+ // display copy — plus the platform-default explainer.
172
+ expect(screen.getByText("Stigmer picks the harness for new sessions.")).toBeTruthy();
173
+ expect(screen.getByText("Stigmer's native agent runtime")).toBeTruthy();
174
+ expect(screen.getByText("Cursor IDE agent with codebase indexing")).toBeTruthy();
175
+ // The radios' accessible descriptions are wired via aria-describedby.
176
+ expect(
177
+ screen.getByRole("radio", { name: "Stigmer" }).getAttribute("aria-describedby"),
178
+ ).toBeTruthy();
179
+ });
180
+
181
+ it("keeps the non-selected harness's model select editable (per-harness models are independent of the default harness)", async () => {
182
+ renderPanel(createMockStigmer({ whoAmI: vi.fn(async () => ACCOUNT_WITH_DEFAULTS) }));
183
+ await findSyncedField("Keep answers terse.");
184
+
185
+ // Default harness is cursor, yet the Stigmer model stays settable:
186
+ // the launcher applies the model of the session's ACTIVE harness, so
187
+ // hiding or disabling it would remove a real capability.
188
+ await waitFor(() => {
189
+ expect(screen.getByRole("radio", { name: "Cursor" })).toHaveProperty("checked", true);
190
+ });
191
+ const nativeSelect = screen.getByLabelText("Default model — Stigmer") as HTMLSelectElement;
192
+ expect(nativeSelect.disabled).toBe(false);
193
+ fireEvent.change(nativeSelect, { target: { value: "claude-sonnet-4.6" } });
194
+ expect(nativeSelect.value).toBe("claude-sonnet-4.6");
195
+ });
196
+
163
197
  it("reflects the saved defaults in the form", async () => {
164
198
  renderPanel(createMockStigmer({ whoAmI: vi.fn(async () => ACCOUNT_WITH_DEFAULTS) }));
165
199
  await findSyncedField("Keep answers terse.");
@@ -3,7 +3,7 @@
3
3
  import { type FormEvent, useCallback, useId, useRef, useState } from "react";
4
4
  import { cn } from "@stigmer/theme";
5
5
  import { UNSTYLED_FIELDSET } from "../internal/element-resets.js";
6
- import { getUserMessage } from "@stigmer/sdk";
6
+ import { getUserMessage, toIdentityProviderUpdateInput } from "@stigmer/sdk";
7
7
  import type { IdentityProvider } from "@stigmer/protos/ai/stigmer/iam/identityprovider/v1/api_pb";
8
8
  import { IamRole } from "@stigmer/protos/ai/stigmer/iam/v1/enum_pb";
9
9
  import { timestampDate, type Timestamp } from "@bufbuild/protobuf/wkt";
@@ -132,10 +132,13 @@ export function IdentityProviderDetailPanel({
132
132
  e.preventDefault();
133
133
  clearError();
134
134
  try {
135
+ // Full-spec-replace safety: spread the complete mapped input and
136
+ // override only the edited fields, so unlisted spec fields (e.g.
137
+ // rate_limit_budget) survive the save. Fields the form clears
138
+ // (SSO providers have no JIT settings) are set to undefined
139
+ // explicitly — omitting them would carry the stale mapped value.
135
140
  const updated = await update({
136
- name: meta?.name ?? "",
137
- slug: meta?.slug,
138
- org: meta?.org ?? "",
141
+ ...toIdentityProviderUpdateInput(identityProvider),
139
142
  displayName: displayName.trim(),
140
143
  jwksUri: jwksUri.trim(),
141
144
  allowedIssuers: issuers
@@ -146,16 +149,16 @@ export function IdentityProviderDetailPanel({
146
149
  userinfoEndpoint: userinfoEndpoint.trim() || undefined,
147
150
  isSsoProvider: isSso,
148
151
  oidcClientId: isSso ? oidcClientId.trim() : undefined,
149
- ...(!isSso && {
150
- autoProvisionAccounts: autoProvision,
151
- autoGrantOnOrg: autoGrant,
152
- ...(autoGrant && autoGrantRole !== IamRole.iam_role_unspecified && {
153
- autoGrantRole,
154
- }),
155
- ...(autoGrant && tenantOrgClaim.trim() && {
156
- tenantOrgClaim: tenantOrgClaim.trim(),
157
- }),
158
- }),
152
+ autoProvisionAccounts: !isSso && autoProvision ? true : undefined,
153
+ autoGrantOnOrg: !isSso && autoGrant ? true : undefined,
154
+ autoGrantRole:
155
+ !isSso && autoGrant && autoGrantRole !== IamRole.iam_role_unspecified
156
+ ? autoGrantRole
157
+ : undefined,
158
+ tenantOrgClaim:
159
+ !isSso && autoGrant && tenantOrgClaim.trim()
160
+ ? tenantOrgClaim.trim()
161
+ : undefined,
159
162
  });
160
163
  setMode("view");
161
164
  onUpdated?.(updated);
@@ -164,7 +167,7 @@ export function IdentityProviderDetailPanel({
164
167
  }
165
168
  },
166
169
  [
167
- meta, displayName, jwksUri, issuers, audience,
170
+ identityProvider, displayName, jwksUri, issuers, audience,
168
171
  userinfoEndpoint, isSso, oidcClientId, autoProvision, autoGrant,
169
172
  autoGrantRole, tenantOrgClaim, update, clearError, onUpdated,
170
173
  ],
@@ -0,0 +1,80 @@
1
+ import { describe, it, expect, vi, afterEach } from "vitest";
2
+ import { cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react";
3
+ import { create } from "@bufbuild/protobuf";
4
+ import {
5
+ IdentityProviderSchema,
6
+ type IdentityProvider,
7
+ } from "@stigmer/protos/ai/stigmer/iam/identityprovider/v1/api_pb";
8
+ import { IamRole } from "@stigmer/protos/ai/stigmer/iam/v1/enum_pb";
9
+ import type { IdentityProviderInput } from "@stigmer/sdk";
10
+ import { StigmerContext } from "../../context";
11
+ import { IdentityProviderDetailPanel } from "../IdentityProviderDetailPanel";
12
+
13
+ /**
14
+ * Regression suite for the full-spec-replace wipe bug: before the
15
+ * generated-mapper migration, this panel hand-built its update input and
16
+ * NEVER sent `rate_limit_budget` — editing any IdP field zeroed the
17
+ * provider's rate limit. The panel must spread
18
+ * `toIdentityProviderUpdateInput` and override only what it edits.
19
+ */
20
+
21
+ const IDP: IdentityProvider = create(IdentityProviderSchema, {
22
+ metadata: {
23
+ id: "idp-1",
24
+ name: "Acme Okta",
25
+ slug: "acme-okta",
26
+ org: "acme",
27
+ },
28
+ spec: {
29
+ displayName: "Acme Okta",
30
+ jwksUri: "https://acme.okta.example/jwks",
31
+ allowedIssuers: ["https://acme.okta.example"],
32
+ expectedAudience: "stigmer",
33
+ rateLimitBudget: 120,
34
+ isSsoProvider: false,
35
+ autoProvisionAccounts: true,
36
+ autoGrantOnOrg: true,
37
+ autoGrantRole: IamRole.admin,
38
+ tenantOrgClaim: "org_slug",
39
+ },
40
+ });
41
+
42
+ function renderPanel(update: ReturnType<typeof vi.fn>) {
43
+ const client = {
44
+ identityProvider: { update },
45
+ } as never;
46
+ return render(
47
+ <StigmerContext.Provider value={client}>
48
+ <IdentityProviderDetailPanel identityProvider={IDP} />
49
+ </StigmerContext.Provider>,
50
+ );
51
+ }
52
+
53
+ afterEach(cleanup);
54
+
55
+ describe("IdentityProviderDetailPanel save payload", () => {
56
+ it("round-trips rate_limit_budget on a display-name edit (the wipe bug)", async () => {
57
+ const update = vi.fn(async (_input: IdentityProviderInput) => IDP);
58
+ renderPanel(update);
59
+
60
+ fireEvent.click(screen.getByRole("button", { name: "Edit" }));
61
+ const displayName = await screen.findByLabelText("Display name");
62
+ fireEvent.change(displayName, { target: { value: "Acme Okta (renamed)" } });
63
+ fireEvent.click(screen.getByRole("button", { name: "Save changes" }));
64
+
65
+ await waitFor(() => expect(update).toHaveBeenCalledTimes(1));
66
+ const input = update.mock.calls[0]![0];
67
+
68
+ // The wipe-bug guard: the form does not render the rate limit, yet it
69
+ // must survive the save.
70
+ expect(input.rateLimitBudget).toBe(120);
71
+ // The edited field.
72
+ expect(input.displayName).toBe("Acme Okta (renamed)");
73
+ // JIT settings round-trip from the edit state.
74
+ expect(input.autoGrantRole).toBe(IamRole.admin);
75
+ expect(input.tenantOrgClaim).toBe("org_slug");
76
+ // Addressing fields.
77
+ expect(input.org).toBe("acme");
78
+ expect(input.slug).toBe("acme-okta");
79
+ });
80
+ });
package/src/index.ts CHANGED
@@ -139,6 +139,8 @@ export {
139
139
  useSessionList,
140
140
  useSessionExecutions,
141
141
  useSessionConversation,
142
+ useExportTranscript,
143
+ TranscriptExportMenu,
142
144
  useSessionArtifacts,
143
145
  artifactKey,
144
146
  useSessionWriteBacks,
@@ -189,6 +191,9 @@ export type {
189
191
  UseSessionExecutionsReturn,
190
192
  SendFollowUpOptions,
191
193
  UseSessionConversationReturn,
194
+ UseExportTranscriptOptions,
195
+ UseExportTranscriptReturn,
196
+ TranscriptExportMenuProps,
192
197
  SessionArtifactEntry,
193
198
  UseSessionArtifactsReturn,
194
199
  SessionWriteBackEntry,
@@ -222,6 +227,11 @@ export type {
222
227
  RuntimeEnvProvider,
223
228
  SessionAudience,
224
229
  SessionPanelMode,
230
+ // The #664 pinning seam's contract type: SessionViewer/NewSessionViewer/
231
+ // useNewSessionFlow all accept it, and the docs name it — embedders must
232
+ // be able to import it from the root (oss#802; the exports map exposes no
233
+ // ./session subpath on purpose).
234
+ SessionRunConfig,
225
235
  SetupTabProps,
226
236
  SetupTabMutationCallbacks,
227
237
  } from "./session/index.js";
@@ -541,6 +551,7 @@ export type {
541
551
  SessionComposerHandle,
542
552
  SessionComposerProps,
543
553
  SessionComposerSubmitContext,
554
+ ComposerAutoApproveProps,
544
555
  InteractionModePickerProps,
545
556
  InteractionModeOption,
546
557
  } from "./composer/index.js";
@@ -567,7 +578,6 @@ export {
567
578
  McpToolSelector,
568
579
  useCreateMcpServer,
569
580
  useUpdateMcpServer,
570
- mcpServerToInput,
571
581
  toServerKey,
572
582
  IdentityTransportStep,
573
583
  EnvironmentAuthStep,
@@ -696,7 +706,6 @@ export {
696
706
  useDefaultAgent,
697
707
  useCreateAgent,
698
708
  useUpdateAgent,
699
- agentToInput,
700
709
  AgentCreationWizard,
701
710
  } from "./agent/index.js";
702
711
  export type {
@@ -853,6 +862,28 @@ export type {
853
862
  AccountPreferencesPanelProps,
854
863
  } from "./identity-account/index.js";
855
864
 
865
+ // Memory — agent-proposed, user-confirmed facts: data hook, decision
866
+ // hooks (confirm/reject/delete/edit), grouping helpers, and the list panel
867
+ export {
868
+ useMemories,
869
+ useConfirmMemory,
870
+ useRejectMemory,
871
+ useDeleteMemory,
872
+ useUpdateMemoryContent,
873
+ groupMemoriesByLifecycle,
874
+ formatMemoryProvenance,
875
+ MemoryListPanel,
876
+ } from "./memory/index.js";
877
+ export type {
878
+ UseMemoriesReturn,
879
+ UseConfirmMemoryReturn,
880
+ UseRejectMemoryReturn,
881
+ UseDeleteMemoryReturn,
882
+ UseUpdateMemoryContentReturn,
883
+ MemoryGroups,
884
+ MemoryListPanelProps,
885
+ } from "./memory/index.js";
886
+
856
887
  // IAM Policy — data hooks, behavior hooks, headless hook, and styled components for access management
857
888
  export {
858
889
  useGrantableRoles,
@@ -1080,6 +1111,7 @@ export { MembersSection } from "./settings/index.js";
1080
1111
  export { OrgProfileSection } from "./settings/index.js";
1081
1112
  export { OrgPreferencesSection } from "./settings/index.js";
1082
1113
  export { AccountPreferencesSection } from "./settings/index.js";
1114
+ export { MemorySection } from "./settings/index.js";
1083
1115
  export { EnvironmentsSection } from "./settings/index.js";
1084
1116
  export { InvitationsSection } from "./settings/index.js";
1085
1117
  export { IdentityProvidersSection } from "./settings/index.js";
@@ -1311,6 +1343,7 @@ export {
1311
1343
  ConnectSlackDialog,
1312
1344
  ConnectWhatsAppDialog,
1313
1345
  ChannelCredentialsDialog,
1346
+ ChannelRunConfigDialog,
1314
1347
  ChannelToolCredentials,
1315
1348
  ChannelConversationsDialog,
1316
1349
  ChannelTemplatesDialog,
@@ -1334,6 +1367,7 @@ export type {
1334
1367
  ConnectSlackDialogProps,
1335
1368
  ConnectWhatsAppDialogProps,
1336
1369
  ChannelCredentialsDialogProps,
1370
+ ChannelRunConfigDialogProps,
1337
1371
  ChannelToolCredentialsProps,
1338
1372
  ChannelConversationsDialogProps,
1339
1373
  ChannelTemplatesDialogProps,
@@ -0,0 +1,29 @@
1
+ // Client-side file download via Blob + object URL + anchor click.
2
+ //
3
+ // The one home of this routine — useExportResource, useExportCSV, and
4
+ // useExportTranscript all download through it. The pattern is proven inside
5
+ // the desktop (Tauri) webview as well as browsers, so SDK components can rely
6
+ // on it without a per-host seam.
7
+
8
+ /**
9
+ * Triggers a browser download of `content` as a UTF-8 text file.
10
+ *
11
+ * @param mimeType - Bare MIME type (e.g. `"text/markdown"`); the UTF-8
12
+ * charset is appended here so callers cannot drift on it.
13
+ */
14
+ export function downloadTextFile(
15
+ content: string,
16
+ filename: string,
17
+ mimeType: string,
18
+ ): void {
19
+ const blob = new Blob([content], { type: `${mimeType};charset=utf-8;` });
20
+ const url = URL.createObjectURL(blob);
21
+ const link = document.createElement("a");
22
+ link.href = url;
23
+ link.download = filename;
24
+ link.style.display = "none";
25
+ document.body.appendChild(link);
26
+ link.click();
27
+ document.body.removeChild(link);
28
+ URL.revokeObjectURL(url);
29
+ }
@@ -8,6 +8,7 @@ import type { Schedule } from "@stigmer/protos/ai/stigmer/agentic/schedule/v1/ap
8
8
  import type { Workflow } from "@stigmer/protos/ai/stigmer/agentic/workflow/v1/api_pb";
9
9
  import { serializeManifest } from "@stigmer/sdk";
10
10
  import { toast } from "../feedback/toast.js";
11
+ import { downloadTextFile } from "../internal/download.js";
11
12
  import { useCopyFeedback } from "../internal/useCopyFeedback.js";
12
13
  import { serializeWorkflowYaml } from "../workflow/serialize-workflow-yaml.js";
13
14
 
@@ -121,12 +122,12 @@ export function useExportResource({
121
122
 
122
123
  const downloadYaml = useCallback(() => {
123
124
  if (!yaml) return;
124
- downloadFile(yaml, `${slug}.yaml`, "text/yaml");
125
+ downloadTextFile(yaml, `${slug}.yaml`, "text/yaml");
125
126
  }, [yaml, slug]);
126
127
 
127
128
  const downloadJson = useCallback(() => {
128
129
  if (!json) return;
129
- downloadFile(json, `${slug}.json`, "application/json");
130
+ downloadTextFile(json, `${slug}.json`, "application/json");
130
131
  }, [json, slug]);
131
132
 
132
133
  return useMemo(
@@ -134,20 +135,3 @@ export function useExportResource({
134
135
  [copyYaml, copyJson, downloadYaml, downloadJson, yaml, json],
135
136
  );
136
137
  }
137
-
138
- // ---------------------------------------------------------------------------
139
- // File download
140
- // ---------------------------------------------------------------------------
141
-
142
- function downloadFile(content: string, filename: string, mimeType: string): void {
143
- const blob = new Blob([content], { type: `${mimeType};charset=utf-8;` });
144
- const url = URL.createObjectURL(blob);
145
- const link = document.createElement("a");
146
- link.href = url;
147
- link.download = filename;
148
- link.style.display = "none";
149
- document.body.appendChild(link);
150
- link.click();
151
- document.body.removeChild(link);
152
- URL.revokeObjectURL(url);
153
- }
@@ -16,7 +16,7 @@ import type { EnvVarDeclaration } from "@stigmer/protos/ai/stigmer/agentic/envir
16
16
  import { useMcpServer } from "./useMcpServer.js";
17
17
  import type { UseMcpServerReturn } from "./useMcpServer.js";
18
18
  import { useUpdateMcpServer } from "./useUpdateMcpServer.js";
19
- import { mcpServerToInput } from "./internal/mcpServerToInput.js";
19
+ import { toMcpServerUpdateInput } from "@stigmer/sdk";
20
20
  import { useMcpServerConnect } from "./useMcpServerConnect.js";
21
21
  import { useMcpServerCredentials } from "./useMcpServerCredentials.js";
22
22
  import {
@@ -214,7 +214,7 @@ export function McpServerDetailView({
214
214
  value: import("@stigmer/sdk").McpServerInput[K],
215
215
  ): Promise<boolean> => {
216
216
  if (!mcpServer) return false;
217
- const input = mcpServerToInput(mcpServer);
217
+ const input = toMcpServerUpdateInput(mcpServer);
218
218
  (input as unknown as Record<string, unknown>)[field] = value;
219
219
  try {
220
220
  const updated = await updateMcpServer(input);