@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
@@ -0,0 +1,178 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { render, screen, cleanup } from "@testing-library/react";
3
+
4
+ // ---------------------------------------------------------------------------
5
+ // Wiring-contract tests for the always-visible auto-approve toggle
6
+ // (stigmer/stigmer#816): SessionViewer hands the composer the flow's
7
+ // auto-approve state for every audience that may submit approvals
8
+ // (integrator AND endUser — the walk-away persona is an embedder's end
9
+ // user), never for guests, and the old armed-only indicator banner is gone
10
+ // (the toggle IS the indicator now).
11
+ // ---------------------------------------------------------------------------
12
+
13
+ type CapturedProps = Record<string, unknown>;
14
+
15
+ const composerProps: CapturedProps[] = [];
16
+ vi.mock("../../composer", async (importOriginal) => {
17
+ const actual = await importOriginal<typeof import("../../composer")>();
18
+ return {
19
+ ...actual,
20
+ SessionComposer: (props: CapturedProps) => {
21
+ composerProps.push(props);
22
+ return <div data-testid="composer-probe" />;
23
+ },
24
+ };
25
+ });
26
+
27
+ vi.mock("../../execution/MessageThread", () => ({
28
+ MessageThread: () => <div data-testid="thread-probe" />,
29
+ }));
30
+
31
+ vi.mock("../../execution/FileReviewDock", () => ({
32
+ FileReviewDock: () => <div data-testid="file-review-dock-probe" />,
33
+ }));
34
+
35
+ vi.mock("../facets/SetupTab", () => ({
36
+ SetupTab: () => <div data-testid="setup-probe" />,
37
+ }));
38
+
39
+ const stubWorkspace = {
40
+ entries: [],
41
+ hasEntries: false,
42
+ toInput: vi.fn().mockReturnValue([]),
43
+ addGitRepo: vi.fn(),
44
+ addLocalPath: vi.fn(),
45
+ removeEntry: vi.fn(),
46
+ clear: vi.fn(),
47
+ };
48
+
49
+ const stubConv = {
50
+ session: { spec: {} } as Record<string, unknown>,
51
+ isLoading: false,
52
+ loadError: null,
53
+ completedExecutions: [],
54
+ activeStreamExecution: null,
55
+ activePhase: null,
56
+ isStreaming: false,
57
+ isConnecting: false,
58
+ sendFollowUp: vi.fn(),
59
+ canSendFollowUp: true,
60
+ isSending: false,
61
+ sendError: null,
62
+ clearSendError: vi.fn(),
63
+ pendingUserMessage: null,
64
+ workspaceEntries: [],
65
+ mcpServerUsages: [],
66
+ skillRefs: [],
67
+ pendingApprovals: [],
68
+ submitApproval: vi.fn(),
69
+ submittingApprovalIds: new Set<string>(),
70
+ fileChangeSets: [],
71
+ submitFileDecision: vi.fn(),
72
+ submittingFileDecisionKeys: new Set<string>(),
73
+ fileDecisionErrors: new Map<string, Error>(),
74
+ streamError: null,
75
+ reconnectStream: vi.fn(),
76
+ approvalError: null,
77
+ retryLastSend: vi.fn(),
78
+ };
79
+
80
+ const setAutoApproveAll = vi.fn();
81
+ const stubSessionPageFlow = {
82
+ conv: stubConv,
83
+ harness: "native" as const,
84
+ executionTarget: undefined,
85
+ model: [undefined, vi.fn()] as const,
86
+ interactionMode: ["agent" as const, vi.fn()] as const,
87
+ agentRef: { org: "acme", slug: "support-bot" },
88
+ setAgentRef: vi.fn(),
89
+ resolution: null,
90
+ setResolution: vi.fn(),
91
+ isDefaultAgent: false,
92
+ mcpServerUsages: [],
93
+ setMcpServerUsages: vi.fn(),
94
+ skillRefs: [],
95
+ setSkillRefs: vi.fn(),
96
+ workspace: stubWorkspace,
97
+ sessionVariables: { entries: [], isEmpty: true, clear: vi.fn() },
98
+ autoApproveAll: false,
99
+ setAutoApproveAll,
100
+ submitApproval: vi.fn(),
101
+ handleSubmit: vi.fn(),
102
+ submitError: null as Error | null,
103
+ displayExecution: null,
104
+ allExecutions: [],
105
+ sandboxWorkspaceRoot: undefined,
106
+ };
107
+ vi.mock("../useSessionPageFlow", () => ({
108
+ useSessionPageFlow: () => stubSessionPageFlow,
109
+ }));
110
+
111
+ vi.mock("../../hooks", () => ({
112
+ useStigmer: () => ({
113
+ agentExecution: {
114
+ uploadAttachment: vi.fn(),
115
+ getArtifactContent: vi.fn(),
116
+ },
117
+ }),
118
+ }));
119
+
120
+ import { SessionViewer } from "../SessionViewer";
121
+
122
+ function lastComposerProps(): CapturedProps {
123
+ expect(composerProps.length).toBeGreaterThan(0);
124
+ return composerProps.at(-1)!;
125
+ }
126
+
127
+ beforeEach(() => {
128
+ composerProps.length = 0;
129
+ stubConv.session = { spec: {} };
130
+ stubSessionPageFlow.autoApproveAll = false;
131
+ });
132
+
133
+ afterEach(() => {
134
+ cleanup();
135
+ vi.clearAllMocks();
136
+ });
137
+
138
+ describe("SessionViewer — auto-approve toggle wiring (#816)", () => {
139
+ it("hands the composer the flow's auto-approve state (integrator)", () => {
140
+ render(<SessionViewer sessionId="ses_1" org="acme" />);
141
+
142
+ const autoApprove = lastComposerProps().autoApprove as
143
+ | { armed: boolean; onChange: (v: boolean) => void }
144
+ | undefined;
145
+ expect(autoApprove).toBeDefined();
146
+ expect(autoApprove!.armed).toBe(false);
147
+ expect(autoApprove!.onChange).toBe(setAutoApproveAll);
148
+ });
149
+
150
+ it("mirrors an armed flow state into the composer", () => {
151
+ stubSessionPageFlow.autoApproveAll = true;
152
+ render(<SessionViewer sessionId="ses_1" org="acme" />);
153
+
154
+ const autoApprove = lastComposerProps().autoApprove as { armed: boolean };
155
+ expect(autoApprove.armed).toBe(true);
156
+ });
157
+
158
+ it("endUser gets the toggle — the walk-away persona is an end user", () => {
159
+ render(<SessionViewer sessionId="ses_1" org="acme" audience="endUser" />);
160
+
161
+ expect(lastComposerProps().autoApprove).toBeDefined();
162
+ });
163
+
164
+ it("guests never get the toggle", () => {
165
+ render(<SessionViewer sessionId="ses_1" org="acme" audience="guest" />);
166
+
167
+ expect(lastComposerProps().autoApprove).toBeUndefined();
168
+ });
169
+
170
+ it("the armed-only indicator banner is gone — the toggle is the indicator", () => {
171
+ stubSessionPageFlow.autoApproveAll = true;
172
+ render(<SessionViewer sessionId="ses_1" org="acme" />);
173
+
174
+ expect(
175
+ screen.queryByText("Auto-approving tool calls for this session"),
176
+ ).toBeNull();
177
+ });
178
+ });
@@ -0,0 +1,151 @@
1
+ // SessionViewer's built-in transcript export (stigmer/stigmer#814): the
2
+ // control ships ON by default in the header corner — wherever a conversation
3
+ // is viewed, its transcript is one click away (the owner-ratified DD-011
4
+ // divergence recorded on SessionViewerProps.transcriptExport) — composes
5
+ // with host headerActions, and disappears on explicit opt-out.
6
+
7
+ import { describe, it, expect, vi, afterEach } from "vitest";
8
+ import { render, screen, cleanup } from "@testing-library/react";
9
+
10
+ vi.mock("../../composer", async (importOriginal) => {
11
+ const actual = await importOriginal<typeof import("../../composer")>();
12
+ return {
13
+ ...actual,
14
+ SessionComposer: () => <div data-testid="composer-probe" />,
15
+ };
16
+ });
17
+ vi.mock("../../execution/MessageThread", () => ({
18
+ MessageThread: () => <div data-testid="thread-probe" />,
19
+ }));
20
+ vi.mock("../../execution/FileReviewDock", () => ({
21
+ FileReviewDock: () => <div data-testid="file-review-dock-probe" />,
22
+ }));
23
+ vi.mock("../facets/SetupTab", () => ({
24
+ SetupTab: () => <div data-testid="setup-probe" />,
25
+ }));
26
+
27
+ const stubWorkspace = {
28
+ entries: [],
29
+ hasEntries: false,
30
+ toInput: vi.fn().mockReturnValue([]),
31
+ addGitRepo: vi.fn(),
32
+ addLocalPath: vi.fn(),
33
+ removeEntry: vi.fn(),
34
+ clear: vi.fn(),
35
+ };
36
+
37
+ const stubConv = {
38
+ session: { metadata: { id: "ses_1", org: "acme" }, spec: {} },
39
+ isLoading: false,
40
+ loadError: null,
41
+ completedExecutions: [],
42
+ activeStreamExecution: null,
43
+ activePhase: null,
44
+ isStreaming: false,
45
+ isConnecting: false,
46
+ sendFollowUp: vi.fn(),
47
+ canSendFollowUp: true,
48
+ isSending: false,
49
+ sendError: null,
50
+ clearSendError: vi.fn(),
51
+ pendingUserMessage: null,
52
+ workspaceEntries: [],
53
+ mcpServerUsages: [],
54
+ skillRefs: [],
55
+ pendingApprovals: [],
56
+ submitApproval: vi.fn(),
57
+ submittingApprovalIds: new Set<string>(),
58
+ fileChangeSets: [],
59
+ submitFileDecision: vi.fn(),
60
+ submittingFileDecisionKeys: new Set<string>(),
61
+ fileDecisionErrors: new Map<string, Error>(),
62
+ streamError: null,
63
+ reconnectStream: vi.fn(),
64
+ approvalError: null,
65
+ retryLastSend: vi.fn(),
66
+ };
67
+
68
+ vi.mock("../useSessionPageFlow", () => ({
69
+ useSessionPageFlow: () => ({
70
+ conv: stubConv,
71
+ harness: "native" as const,
72
+ executionTarget: undefined,
73
+ model: [undefined, vi.fn()] as const,
74
+ interactionMode: ["agent" as const, vi.fn()] as const,
75
+ agentRef: { org: "acme", slug: "support-bot" },
76
+ setAgentRef: vi.fn(),
77
+ resolution: null,
78
+ setResolution: vi.fn(),
79
+ isDefaultAgent: false,
80
+ mcpServerUsages: [],
81
+ setMcpServerUsages: vi.fn(),
82
+ skillRefs: [],
83
+ setSkillRefs: vi.fn(),
84
+ workspace: stubWorkspace,
85
+ sessionVariables: { entries: [], isEmpty: true, clear: vi.fn() },
86
+ autoApproveAll: false,
87
+ setAutoApproveAll: vi.fn(),
88
+ submitApproval: vi.fn(),
89
+ handleSubmit: vi.fn(),
90
+ submitError: null as Error | null,
91
+ displayExecution: null,
92
+ allExecutions: [],
93
+ sandboxWorkspaceRoot: undefined,
94
+ }),
95
+ }));
96
+
97
+ vi.mock("../../hooks", () => ({
98
+ useStigmer: () => ({
99
+ session: { get: vi.fn() },
100
+ agentExecution: {
101
+ uploadAttachment: vi.fn(),
102
+ getArtifactContent: vi.fn(),
103
+ listBySession: vi.fn(),
104
+ },
105
+ }),
106
+ }));
107
+
108
+ import { SessionViewer } from "../SessionViewer";
109
+
110
+ afterEach(cleanup);
111
+
112
+ function exportTrigger(): HTMLElement | null {
113
+ return screen.queryByRole("button", { name: "Export transcript" });
114
+ }
115
+
116
+ describe("SessionViewer transcript export wiring", () => {
117
+ it("renders the export control by default", () => {
118
+ render(<SessionViewer sessionId="ses_1" org="acme" />);
119
+ expect(exportTrigger()).not.toBeNull();
120
+ });
121
+
122
+ it("keeps the export control for the read-only observer audience — a bulk copy of what the observer already sees", () => {
123
+ render(<SessionViewer sessionId="ses_1" org="acme" audience="observer" />);
124
+ expect(exportTrigger()).not.toBeNull();
125
+ });
126
+
127
+ it("composes with host headerActions rather than replacing them", () => {
128
+ render(
129
+ <SessionViewer
130
+ sessionId="ses_1"
131
+ org="acme"
132
+ headerActions={<button>Share</button>}
133
+ />,
134
+ );
135
+ expect(screen.getByRole("button", { name: "Share" })).toBeTruthy();
136
+ expect(exportTrigger()).not.toBeNull();
137
+ });
138
+
139
+ it("disappears on explicit opt-out, leaving host actions intact", () => {
140
+ render(
141
+ <SessionViewer
142
+ sessionId="ses_1"
143
+ org="acme"
144
+ transcriptExport={false}
145
+ headerActions={<button>Share</button>}
146
+ />,
147
+ );
148
+ expect(exportTrigger()).toBeNull();
149
+ expect(screen.getByRole("button", { name: "Share" })).toBeTruthy();
150
+ });
151
+ });
@@ -0,0 +1,97 @@
1
+ // TranscriptExportMenu (stigmer/stigmer#814): the control's wiring — labels,
2
+ // action dispatch, busy state. The menu chrome itself (Base UI portal
3
+ // behavior) is the ActionMenu compound's concern; it is faked transparent
4
+ // here so the assertions target this component's own contract.
5
+
6
+ import { describe, it, expect, vi, beforeEach } from "vitest";
7
+ import { render, screen, cleanup, fireEvent } from "@testing-library/react";
8
+ import type { ReactNode } from "react";
9
+
10
+ vi.mock("../../action-menu/ActionMenu", () => {
11
+ function Root({ children }: { children: ReactNode }) {
12
+ return <div>{children}</div>;
13
+ }
14
+ return {
15
+ ActionMenu: Object.assign(Root, {
16
+ Trigger: ({
17
+ children,
18
+ "aria-label": ariaLabel,
19
+ }: {
20
+ children: ReactNode;
21
+ "aria-label"?: string;
22
+ }) => <button aria-label={ariaLabel}>{children}</button>,
23
+ Content: ({ children }: { children: ReactNode }) => <div>{children}</div>,
24
+ Item: ({
25
+ children,
26
+ onSelect,
27
+ disabled,
28
+ }: {
29
+ children: ReactNode;
30
+ onSelect?: () => void;
31
+ disabled?: boolean;
32
+ }) => (
33
+ <button onClick={onSelect} disabled={disabled}>
34
+ {children}
35
+ </button>
36
+ ),
37
+ }),
38
+ };
39
+ });
40
+
41
+ const exporter = {
42
+ copyMarkdown: vi.fn(() => Promise.resolve()),
43
+ downloadMarkdown: vi.fn(() => Promise.resolve()),
44
+ downloadJson: vi.fn(() => Promise.resolve()),
45
+ isExporting: false,
46
+ error: null as Error | null,
47
+ };
48
+ const useExportTranscript = vi.fn(
49
+ (_sessionId: string | null, _options?: unknown) => exporter,
50
+ );
51
+ vi.mock("../useExportTranscript", () => ({
52
+ useExportTranscript: (sessionId: string | null, options?: unknown) =>
53
+ useExportTranscript(sessionId, options),
54
+ }));
55
+
56
+ import { TranscriptExportMenu } from "../TranscriptExportMenu";
57
+
58
+ beforeEach(() => {
59
+ vi.clearAllMocks();
60
+ exporter.isExporting = false;
61
+ cleanup();
62
+ });
63
+
64
+ describe("TranscriptExportMenu", () => {
65
+ it("offers the three export actions and dispatches to the hook", () => {
66
+ render(<TranscriptExportMenu sessionId="ses_01" />);
67
+
68
+ fireEvent.click(screen.getByText("Copy transcript"));
69
+ fireEvent.click(screen.getByText("Download Markdown"));
70
+ fireEvent.click(screen.getByText("Download JSON"));
71
+
72
+ expect(exporter.copyMarkdown).toHaveBeenCalledTimes(1);
73
+ expect(exporter.downloadMarkdown).toHaveBeenCalledTimes(1);
74
+ expect(exporter.downloadJson).toHaveBeenCalledTimes(1);
75
+ });
76
+
77
+ it("labels the trigger for assistive tech", () => {
78
+ render(<TranscriptExportMenu sessionId="ses_01" />);
79
+ expect(
80
+ screen.getByRole("button", { name: "Export transcript" }),
81
+ ).toBeTruthy();
82
+ });
83
+
84
+ it("disables the actions while an export is in flight", () => {
85
+ exporter.isExporting = true;
86
+ render(<TranscriptExportMenu sessionId="ses_01" />);
87
+ const item = screen.getByText("Copy transcript").closest("button");
88
+ expect(item?.disabled).toBe(true);
89
+ });
90
+
91
+ it("forwards includeSuperseded to the hook", () => {
92
+ render(<TranscriptExportMenu sessionId="ses_01" includeSuperseded />);
93
+ expect(useExportTranscript).toHaveBeenCalledWith("ses_01", {
94
+ includeSuperseded: true,
95
+ });
96
+ });
97
+ });
@@ -0,0 +1,187 @@
1
+ // useExportTranscript (stigmer/stigmer#814): the lazy export actions run the
2
+ // REAL canonical assembly (fetchSessionTranscript from @stigmer/sdk) against
3
+ // a fake client, so these tests pin the hook's end-to-end content, filenames,
4
+ // clipboard strategy, and failure honesty — not a mock of the SDK.
5
+
6
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
7
+ import { renderHook, act } from "@testing-library/react";
8
+ import { create } from "@bufbuild/protobuf";
9
+ import {
10
+ AgentExecutionSchema,
11
+ } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
12
+ import {
13
+ ExecutionPhase,
14
+ MessageType,
15
+ } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
16
+ import { AgentExecutionListSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/io_pb";
17
+ import { SessionSchema } from "@stigmer/protos/ai/stigmer/agentic/session/v1/api_pb";
18
+
19
+ const toastSuccess = vi.fn();
20
+ const toastError = vi.fn();
21
+ vi.mock("../../feedback/toast", () => ({
22
+ toast: {
23
+ success: (...args: unknown[]) => toastSuccess(...args),
24
+ error: (...args: unknown[]) => toastError(...args),
25
+ },
26
+ }));
27
+
28
+ const fakeSession = create(SessionSchema, {
29
+ metadata: { id: "ses_01" },
30
+ spec: { subject: "Fix the flaky test" },
31
+ });
32
+
33
+ const fakeExecution = create(AgentExecutionSchema, {
34
+ metadata: { id: "aex_01" },
35
+ spec: { sessionId: "ses_01", message: "Why is CI red?" },
36
+ status: {
37
+ phase: ExecutionPhase.EXECUTION_COMPLETED,
38
+ messages: [
39
+ { type: MessageType.MESSAGE_THINKING, content: "Checking the loop." },
40
+ { type: MessageType.MESSAGE_AI, content: "Found it." },
41
+ ],
42
+ },
43
+ });
44
+
45
+ let listBySession: (input: unknown) => Promise<unknown>;
46
+ vi.mock("../../hooks", () => ({
47
+ useStigmer: () => ({
48
+ session: { get: () => Promise.resolve(fakeSession) },
49
+ agentExecution: {
50
+ listBySession: (input: unknown) => listBySession(input),
51
+ getArtifactContent: vi.fn(),
52
+ },
53
+ }),
54
+ }));
55
+
56
+ import { useExportTranscript } from "../useExportTranscript";
57
+
58
+ // ---------------------------------------------------------------------------
59
+ // DOM stubs: clipboard and blob downloads
60
+ // ---------------------------------------------------------------------------
61
+
62
+ let writeText: ReturnType<typeof vi.fn>;
63
+ let clipboardWrite: ReturnType<typeof vi.fn>;
64
+ let createdBlobs: Blob[];
65
+ let downloadedFilenames: string[];
66
+
67
+ beforeEach(() => {
68
+ vi.clearAllMocks();
69
+ listBySession = vi.fn(() =>
70
+ Promise.resolve(
71
+ create(AgentExecutionListSchema, {
72
+ totalPages: 1,
73
+ entries: [fakeExecution],
74
+ }),
75
+ ),
76
+ );
77
+
78
+ writeText = vi.fn(() => Promise.resolve());
79
+ clipboardWrite = vi.fn(() => Promise.resolve());
80
+ Object.defineProperty(navigator, "clipboard", {
81
+ value: { writeText },
82
+ configurable: true,
83
+ });
84
+ vi.stubGlobal("ClipboardItem", undefined);
85
+
86
+ createdBlobs = [];
87
+ downloadedFilenames = [];
88
+ URL.createObjectURL = vi.fn((blob: Blob) => {
89
+ createdBlobs.push(blob);
90
+ return "blob:mock";
91
+ });
92
+ URL.revokeObjectURL = vi.fn();
93
+ vi.spyOn(HTMLAnchorElement.prototype, "click").mockImplementation(function (
94
+ this: HTMLAnchorElement,
95
+ ) {
96
+ downloadedFilenames.push(this.download);
97
+ });
98
+ });
99
+
100
+ afterEach(() => {
101
+ vi.unstubAllGlobals();
102
+ vi.restoreAllMocks();
103
+ });
104
+
105
+ describe("useExportTranscript", () => {
106
+ it("is lazy — nothing is fetched until an action runs", () => {
107
+ renderHook(() => useExportTranscript("ses_01"));
108
+ expect(listBySession).not.toHaveBeenCalled();
109
+ });
110
+
111
+ it("copies the canonical Markdown via writeText when ClipboardItem is unavailable", async () => {
112
+ const { result } = renderHook(() => useExportTranscript("ses_01"));
113
+ await act(() => result.current.copyMarkdown());
114
+
115
+ expect(writeText).toHaveBeenCalledTimes(1);
116
+ const markdown = writeText.mock.calls[0][0] as string;
117
+ expect(markdown).toContain("# Fix the flaky test");
118
+ expect(markdown).toContain("Why is CI red?");
119
+ // The fidelity headline: thinking is in the export.
120
+ expect(markdown).toContain("**Thinking**");
121
+ expect(markdown).toContain("> Checking the loop.");
122
+ expect(toastSuccess).toHaveBeenCalledWith("Transcript copied to clipboard");
123
+ expect(result.current.error).toBeNull();
124
+ });
125
+
126
+ it("copies via a promise-carrying ClipboardItem when available (the WebKit gesture path)", async () => {
127
+ class FakeClipboardItem {
128
+ constructor(readonly items: Record<string, Promise<Blob>>) {}
129
+ }
130
+ vi.stubGlobal("ClipboardItem", FakeClipboardItem);
131
+ Object.defineProperty(navigator, "clipboard", {
132
+ value: { writeText, write: clipboardWrite },
133
+ configurable: true,
134
+ });
135
+
136
+ const { result } = renderHook(() => useExportTranscript("ses_01"));
137
+ await act(() => result.current.copyMarkdown());
138
+
139
+ // The write is handed the item synchronously; the content arrives via
140
+ // the promise — writeText (which would lose the gesture) is never used.
141
+ expect(clipboardWrite).toHaveBeenCalledTimes(1);
142
+ expect(writeText).not.toHaveBeenCalled();
143
+ const item = clipboardWrite.mock.calls[0][0][0] as InstanceType<
144
+ typeof FakeClipboardItem
145
+ >;
146
+ const blob = await item.items["text/plain"];
147
+ expect(await blob.text()).toContain("# Fix the flaky test");
148
+ });
149
+
150
+ it("downloads Markdown under the subject-slug filename", async () => {
151
+ const { result } = renderHook(() => useExportTranscript("ses_01"));
152
+ await act(() => result.current.downloadMarkdown());
153
+
154
+ expect(downloadedFilenames).toEqual(["fix-the-flaky-test-transcript.md"]);
155
+ expect(await createdBlobs[0].text()).toContain("# Fix the flaky test");
156
+ });
157
+
158
+ it("downloads JSON in the protojson-parity format", async () => {
159
+ const { result } = renderHook(() => useExportTranscript("ses_01"));
160
+ await act(() => result.current.downloadJson());
161
+
162
+ expect(downloadedFilenames).toEqual(["fix-the-flaky-test-transcript.json"]);
163
+ const parsed = JSON.parse(await createdBlobs[0].text());
164
+ expect(parsed.format).toBe("stigmer.ai/session-transcript/v1");
165
+ expect(parsed.turns[0].execution.status.messages[0].content).toBe(
166
+ "Checking the loop.",
167
+ );
168
+ });
169
+
170
+ it("surfaces fetch failures as a typed error and a toast — never a silent no-op", async () => {
171
+ listBySession = vi.fn(() => Promise.reject(new Error("network down")));
172
+ const { result } = renderHook(() => useExportTranscript("ses_01"));
173
+ await act(() => result.current.downloadMarkdown());
174
+
175
+ expect(result.current.error?.message).toBe("network down");
176
+ expect(toastError).toHaveBeenCalledWith(
177
+ "Couldn't export the transcript: network down",
178
+ );
179
+ expect(downloadedFilenames).toEqual([]);
180
+ });
181
+
182
+ it("explains itself when invoked without a session id", async () => {
183
+ const { result } = renderHook(() => useExportTranscript(null));
184
+ await act(() => result.current.downloadMarkdown());
185
+ expect(result.current.error?.message).toMatch(/without a session id/);
186
+ });
187
+ });
@@ -431,6 +431,39 @@ describe("useNewSessionFlow", () => {
431
431
  expect(execInput.autoApproveAll).toBe(true);
432
432
  });
433
433
 
434
+ it("carries the user's pre-arm toggle into the bootstrap create (#816)", async () => {
435
+ const opts = defaultOptions();
436
+ const { result } = renderHook(() => useNewSessionFlow(opts), { wrapper: createWrapper() });
437
+
438
+ expect(result.current.autoApproveAll).toBe(false);
439
+ act(() => result.current.setAutoApproveAll(true));
440
+ expect(result.current.autoApproveAll).toBe(true);
441
+
442
+ await act(async () => {
443
+ await result.current.submit("Hello");
444
+ });
445
+
446
+ const execInput = mockCreateExecution.mock.calls[0][0];
447
+ expect(execInput.autoApproveAll).toBe(true);
448
+ });
449
+
450
+ it("the user's toggle-off beats the host approval default (#816)", async () => {
451
+ const opts = defaultOptions();
452
+ const { result } = renderHook(() => useNewSessionFlow(opts), {
453
+ wrapper: createWrapper(undefined, null, { autoApproveAll: true }),
454
+ });
455
+
456
+ expect(result.current.autoApproveAll).toBe(true);
457
+ act(() => result.current.setAutoApproveAll(false));
458
+
459
+ await act(async () => {
460
+ await result.current.submit("Hello");
461
+ });
462
+
463
+ const execInput = mockCreateExecution.mock.calls[0][0];
464
+ expect(execInput.autoApproveAll).toBeUndefined();
465
+ });
466
+
434
467
  it("passes cursor harness in the sessionSpec after switching", async () => {
435
468
  const opts = defaultOptions();
436
469
  const { result } = renderHook(() => useNewSessionFlow(opts), { wrapper: createWrapper() });