@stigmer/react 3.1.1 → 3.1.3

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 (483) hide show
  1. package/composer/SessionComposer.d.ts +25 -1
  2. package/composer/SessionComposer.d.ts.map +1 -1
  3. package/composer/SessionComposer.js +19 -8
  4. package/composer/SessionComposer.js.map +1 -1
  5. package/execution/ArtifactContentRenderer.d.ts +11 -1
  6. package/execution/ArtifactContentRenderer.d.ts.map +1 -1
  7. package/execution/ArtifactContentRenderer.js +39 -15
  8. package/execution/ArtifactContentRenderer.js.map +1 -1
  9. package/execution/ArtifactPreviewModal.d.ts +5 -5
  10. package/execution/ArtifactPreviewModal.js +4 -4
  11. package/execution/ArtifactPreviewModal.js.map +1 -1
  12. package/execution/FileChangeProgressBar.d.ts +47 -0
  13. package/execution/FileChangeProgressBar.d.ts.map +1 -0
  14. package/execution/FileChangeProgressBar.js +49 -0
  15. package/execution/FileChangeProgressBar.js.map +1 -0
  16. package/execution/FileChangesView.d.ts.map +1 -1
  17. package/execution/FileChangesView.js +19 -4
  18. package/execution/FileChangesView.js.map +1 -1
  19. package/execution/FilePathContext.d.ts +8 -1
  20. package/execution/FilePathContext.d.ts.map +1 -1
  21. package/execution/FilePathContext.js.map +1 -1
  22. package/execution/FilePathLink.d.ts.map +1 -1
  23. package/execution/FilePathLink.js +21 -6
  24. package/execution/FilePathLink.js.map +1 -1
  25. package/execution/FileReviewAtoms.d.ts +27 -0
  26. package/execution/FileReviewAtoms.d.ts.map +1 -0
  27. package/execution/FileReviewAtoms.js +64 -0
  28. package/execution/FileReviewAtoms.js.map +1 -0
  29. package/execution/FileReviewCard.d.ts +2 -2
  30. package/execution/FileReviewCard.d.ts.map +1 -1
  31. package/execution/FileReviewCard.js +32 -62
  32. package/execution/FileReviewCard.js.map +1 -1
  33. package/execution/MessageEntry.d.ts +20 -0
  34. package/execution/MessageEntry.d.ts.map +1 -1
  35. package/execution/MessageEntry.js +19 -5
  36. package/execution/MessageEntry.js.map +1 -1
  37. package/execution/MessageThread.d.ts +75 -13
  38. package/execution/MessageThread.d.ts.map +1 -1
  39. package/execution/MessageThread.js +195 -27
  40. package/execution/MessageThread.js.map +1 -1
  41. package/execution/PlanArtifactCard.d.ts +36 -21
  42. package/execution/PlanArtifactCard.d.ts.map +1 -1
  43. package/execution/PlanArtifactCard.js +39 -25
  44. package/execution/PlanArtifactCard.js.map +1 -1
  45. package/execution/PlanCompletionCard.d.ts +4 -4
  46. package/execution/PlanCompletionCard.d.ts.map +1 -1
  47. package/execution/PlanCompletionCard.js +4 -4
  48. package/execution/PlanCompletionCard.js.map +1 -1
  49. package/execution/PlanDocumentMessage.d.ts +32 -0
  50. package/execution/PlanDocumentMessage.d.ts.map +1 -0
  51. package/execution/PlanDocumentMessage.js +37 -0
  52. package/execution/PlanDocumentMessage.js.map +1 -0
  53. package/execution/PlanStreamingCard.d.ts +50 -0
  54. package/execution/PlanStreamingCard.d.ts.map +1 -0
  55. package/execution/PlanStreamingCard.js +40 -0
  56. package/execution/PlanStreamingCard.js.map +1 -0
  57. package/execution/index.d.ts +9 -1
  58. package/execution/index.d.ts.map +1 -1
  59. package/execution/index.js +4 -0
  60. package/execution/index.js.map +1 -1
  61. package/execution/use-build-from-plan-hotkey.d.ts +1 -1
  62. package/execution/use-build-from-plan-hotkey.js +1 -1
  63. package/execution/useArtifactCopy.d.ts +51 -0
  64. package/execution/useArtifactCopy.d.ts.map +1 -0
  65. package/execution/useArtifactCopy.js +75 -0
  66. package/execution/useArtifactCopy.js.map +1 -0
  67. package/execution/useArtifactDownload.d.ts.map +1 -1
  68. package/execution/useArtifactDownload.js +17 -5
  69. package/execution/useArtifactDownload.js.map +1 -1
  70. package/execution/useCreateAgentExecution.d.ts +12 -0
  71. package/execution/useCreateAgentExecution.d.ts.map +1 -1
  72. package/execution/useCreateAgentExecution.js +5 -1
  73. package/execution/useCreateAgentExecution.js.map +1 -1
  74. package/github/decodeGitHubContent.d.ts +29 -0
  75. package/github/decodeGitHubContent.d.ts.map +1 -0
  76. package/github/decodeGitHubContent.js +72 -0
  77. package/github/decodeGitHubContent.js.map +1 -0
  78. package/github/index.d.ts +1 -0
  79. package/github/index.d.ts.map +1 -1
  80. package/github/index.js +1 -0
  81. package/github/index.js.map +1 -1
  82. package/github/useGitHubFileReader.d.ts +26 -0
  83. package/github/useGitHubFileReader.d.ts.map +1 -0
  84. package/github/useGitHubFileReader.js +106 -0
  85. package/github/useGitHubFileReader.js.map +1 -0
  86. package/github/useGitHubTreeLister.d.ts.map +1 -1
  87. package/github/useGitHubTreeLister.js +6 -2
  88. package/github/useGitHubTreeLister.js.map +1 -1
  89. package/index.d.ts +9 -9
  90. package/index.d.ts.map +1 -1
  91. package/index.js +5 -5
  92. package/index.js.map +1 -1
  93. package/internal/ResizableSplit.d.ts +72 -22
  94. package/internal/ResizableSplit.d.ts.map +1 -1
  95. package/internal/ResizableSplit.js +96 -38
  96. package/internal/ResizableSplit.js.map +1 -1
  97. package/internal/VirtualizedThread.d.ts +5 -3
  98. package/internal/VirtualizedThread.d.ts.map +1 -1
  99. package/internal/VirtualizedThread.js +6 -4
  100. package/internal/VirtualizedThread.js.map +1 -1
  101. package/internal/file-icons/index.d.ts +17 -0
  102. package/internal/file-icons/index.d.ts.map +1 -0
  103. package/internal/file-icons/index.js +60 -0
  104. package/internal/file-icons/index.js.map +1 -0
  105. package/internal/file-tree/FileTreeNode.d.ts +18 -1
  106. package/internal/file-tree/FileTreeNode.d.ts.map +1 -1
  107. package/internal/file-tree/FileTreeNode.js +11 -2
  108. package/internal/file-tree/FileTreeNode.js.map +1 -1
  109. package/internal/markdown-components.d.ts +38 -1
  110. package/internal/markdown-components.d.ts.map +1 -1
  111. package/internal/markdown-components.js +86 -11
  112. package/internal/markdown-components.js.map +1 -1
  113. package/{workflow → internal}/motion-preference.d.ts +7 -4
  114. package/internal/motion-preference.d.ts.map +1 -0
  115. package/{workflow → internal}/motion-preference.js +7 -4
  116. package/internal/motion-preference.js.map +1 -0
  117. package/internal/store/index.d.ts +33 -0
  118. package/internal/store/index.d.ts.map +1 -1
  119. package/internal/store/index.js +59 -0
  120. package/internal/store/index.js.map +1 -1
  121. package/internal/store/workspace-editors-store.d.ts +145 -0
  122. package/internal/store/workspace-editors-store.d.ts.map +1 -0
  123. package/internal/store/workspace-editors-store.js +208 -0
  124. package/internal/store/workspace-editors-store.js.map +1 -0
  125. package/internal/store/workspace-file-selection-store.d.ts +38 -0
  126. package/internal/store/workspace-file-selection-store.d.ts.map +1 -0
  127. package/internal/store/workspace-file-selection-store.js +62 -0
  128. package/internal/store/workspace-file-selection-store.js.map +1 -0
  129. package/internal/useRevealLine.d.ts +49 -0
  130. package/internal/useRevealLine.d.ts.map +1 -0
  131. package/internal/useRevealLine.js +43 -0
  132. package/internal/useRevealLine.js.map +1 -0
  133. package/library/detect-plan-artifact.d.ts +43 -4
  134. package/library/detect-plan-artifact.d.ts.map +1 -1
  135. package/library/detect-plan-artifact.js +45 -5
  136. package/library/detect-plan-artifact.js.map +1 -1
  137. package/library/detect-streaming-plan.d.ts +45 -0
  138. package/library/detect-streaming-plan.d.ts.map +1 -0
  139. package/library/detect-streaming-plan.js +83 -0
  140. package/library/detect-streaming-plan.js.map +1 -0
  141. package/library/index.d.ts +4 -1
  142. package/library/index.d.ts.map +1 -1
  143. package/library/index.js +2 -1
  144. package/library/index.js.map +1 -1
  145. package/package.json +4 -4
  146. package/session/NewSessionViewer.d.ts +19 -4
  147. package/session/NewSessionViewer.d.ts.map +1 -1
  148. package/session/NewSessionViewer.js +52 -25
  149. package/session/NewSessionViewer.js.map +1 -1
  150. package/session/PlanEditor.d.ts +55 -0
  151. package/session/PlanEditor.d.ts.map +1 -0
  152. package/session/PlanEditor.js +88 -0
  153. package/session/PlanEditor.js.map +1 -0
  154. package/session/PlanStreamingDocument.d.ts +30 -0
  155. package/session/PlanStreamingDocument.d.ts.map +1 -0
  156. package/session/PlanStreamingDocument.js +37 -0
  157. package/session/PlanStreamingDocument.js.map +1 -0
  158. package/session/SessionPanelChip.d.ts +26 -0
  159. package/session/SessionPanelChip.d.ts.map +1 -0
  160. package/session/SessionPanelChip.js +40 -0
  161. package/session/SessionPanelChip.js.map +1 -0
  162. package/session/SessionViewer.d.ts +34 -7
  163. package/session/SessionViewer.d.ts.map +1 -1
  164. package/session/SessionViewer.js +334 -33
  165. package/session/SessionViewer.js.map +1 -1
  166. package/session/{inspector → facets}/ArtifactsTab.d.ts +10 -2
  167. package/session/facets/ArtifactsTab.d.ts.map +1 -0
  168. package/session/{inspector → facets}/ArtifactsTab.js +8 -3
  169. package/session/facets/ArtifactsTab.js.map +1 -0
  170. package/session/{inspector → facets}/ChangesTab.d.ts +3 -3
  171. package/session/facets/ChangesTab.d.ts.map +1 -0
  172. package/session/{inspector → facets}/ChangesTab.js +3 -3
  173. package/session/facets/ChangesTab.js.map +1 -0
  174. package/session/facets/InspectTab.d.ts.map +1 -0
  175. package/session/facets/InspectTab.js.map +1 -0
  176. package/session/{inspector → facets}/SetupTab.d.ts +11 -2
  177. package/session/facets/SetupTab.d.ts.map +1 -0
  178. package/session/{inspector → facets}/SetupTab.js +4 -3
  179. package/session/facets/SetupTab.js.map +1 -0
  180. package/session/{inspector → facets}/UsageTab.d.ts +1 -1
  181. package/session/facets/UsageTab.d.ts.map +1 -0
  182. package/session/{inspector → facets}/UsageTab.js +1 -1
  183. package/session/facets/UsageTab.js.map +1 -0
  184. package/session/{inspector → facets}/index.d.ts +0 -6
  185. package/session/facets/index.d.ts.map +1 -0
  186. package/session/{inspector → facets}/index.js +0 -3
  187. package/session/facets/index.js.map +1 -0
  188. package/session/index.d.ts +11 -2
  189. package/session/index.d.ts.map +1 -1
  190. package/session/index.js +15 -2
  191. package/session/index.js.map +1 -1
  192. package/session/plan-document.d.ts +21 -0
  193. package/session/plan-document.d.ts.map +1 -0
  194. package/session/plan-document.js +28 -0
  195. package/session/plan-document.js.map +1 -0
  196. package/session/useOpenFileChange.d.ts +20 -0
  197. package/session/useOpenFileChange.d.ts.map +1 -0
  198. package/session/useOpenFileChange.js +31 -0
  199. package/session/useOpenFileChange.js.map +1 -0
  200. package/session/usePlanDraft.d.ts +52 -0
  201. package/session/usePlanDraft.d.ts.map +1 -0
  202. package/session/usePlanDraft.js +63 -0
  203. package/session/usePlanDraft.js.map +1 -0
  204. package/session/useSessionConversation.d.ts +14 -1
  205. package/session/useSessionConversation.d.ts.map +1 -1
  206. package/session/useSessionConversation.js +20 -2
  207. package/session/useSessionConversation.js.map +1 -1
  208. package/session/useSessionPageFlow.d.ts.map +1 -1
  209. package/session/useSessionPageFlow.js +1 -0
  210. package/session/useSessionPageFlow.js.map +1 -1
  211. package/session/useSessionPanel.d.ts +120 -0
  212. package/session/useSessionPanel.d.ts.map +1 -0
  213. package/session/useSessionPanel.js +130 -0
  214. package/session/useSessionPanel.js.map +1 -0
  215. package/session/useSessionRailViews.d.ts +45 -0
  216. package/session/useSessionRailViews.d.ts.map +1 -0
  217. package/session/useSessionRailViews.js +105 -0
  218. package/session/useSessionRailViews.js.map +1 -0
  219. package/src/composer/SessionComposer.tsx +53 -10
  220. package/src/composer/__tests__/SessionComposer-contract.test.tsx +58 -4
  221. package/src/execution/ArtifactContentRenderer.tsx +101 -24
  222. package/src/execution/ArtifactPreviewModal.tsx +9 -9
  223. package/src/execution/FileChangeProgressBar.tsx +121 -0
  224. package/src/execution/FileChangesView.tsx +18 -4
  225. package/src/execution/FilePathContext.tsx +8 -1
  226. package/src/execution/FilePathLink.tsx +22 -6
  227. package/src/execution/FileReviewAtoms.tsx +89 -0
  228. package/src/execution/FileReviewCard.tsx +57 -90
  229. package/src/execution/MessageEntry.tsx +49 -1
  230. package/src/execution/MessageThread.tsx +308 -39
  231. package/src/execution/PlanArtifactCard.tsx +174 -64
  232. package/src/execution/PlanCompletionCard.tsx +5 -5
  233. package/src/execution/PlanDocumentMessage.tsx +96 -0
  234. package/src/execution/PlanStreamingCard.tsx +141 -0
  235. package/src/execution/__tests__/ArtifactContentRenderer.test.tsx +80 -1
  236. package/src/execution/__tests__/ArtifactPreviewModal.test.tsx +6 -6
  237. package/src/execution/__tests__/FileChangeProgressBar.test.tsx +125 -0
  238. package/src/execution/__tests__/FilePathLink.test.tsx +87 -0
  239. package/src/execution/__tests__/FileReviewCard.test.tsx +66 -7
  240. package/src/execution/__tests__/MessageThread.test.tsx +102 -1
  241. package/src/execution/__tests__/PlanArtifactCard.test.tsx +114 -26
  242. package/src/execution/__tests__/PlanCompletionCard.test.tsx +2 -2
  243. package/src/execution/__tests__/PlanDocumentMessage.test.tsx +55 -0
  244. package/src/execution/__tests__/PlanStreamingCard.test.tsx +56 -0
  245. package/src/execution/__tests__/buildThreadItems-plan-completion.test.ts +600 -22
  246. package/src/execution/__tests__/message-entry.test.tsx +17 -0
  247. package/src/execution/__tests__/useArtifactCopy.test.tsx +90 -0
  248. package/src/execution/__tests__/useArtifactDownload.test.tsx +2 -0
  249. package/src/execution/__tests__/useCreateAgentExecution.test.tsx +85 -0
  250. package/src/execution/index.ts +13 -1
  251. package/src/execution/use-build-from-plan-hotkey.ts +1 -1
  252. package/src/execution/useArtifactCopy.ts +109 -0
  253. package/src/execution/useArtifactDownload.ts +17 -5
  254. package/src/execution/useCreateAgentExecution.ts +18 -1
  255. package/src/github/__tests__/decodeGitHubContent.test.ts +102 -0
  256. package/src/github/__tests__/useGitHubFileReader.test.ts +183 -0
  257. package/src/github/decodeGitHubContent.ts +83 -0
  258. package/src/github/index.ts +1 -0
  259. package/src/github/useGitHubFileReader.ts +152 -0
  260. package/src/github/useGitHubTreeLister.ts +6 -2
  261. package/src/index.ts +69 -9
  262. package/src/internal/ResizableSplit.tsx +184 -51
  263. package/src/internal/VirtualizedThread.tsx +12 -4
  264. package/src/internal/__tests__/ResizableSplit.test.tsx +125 -3
  265. package/src/internal/__tests__/markdown-components.test.tsx +68 -0
  266. package/src/internal/__tests__/useRevealLine.test.tsx +97 -0
  267. package/src/internal/file-icons/__tests__/file-icons.test.tsx +35 -0
  268. package/src/internal/file-icons/index.tsx +137 -0
  269. package/src/internal/file-tree/FileTreeNode.tsx +56 -5
  270. package/src/internal/file-tree/__tests__/FileTreeNode-explorer.test.tsx +86 -0
  271. package/src/internal/markdown-components.tsx +116 -11
  272. package/src/{workflow → internal}/motion-preference.ts +7 -4
  273. package/src/internal/store/__tests__/workspace-editors-store.test.ts +216 -0
  274. package/src/internal/store/__tests__/workspace-file-selection-store.test.ts +118 -0
  275. package/src/internal/store/index.ts +94 -0
  276. package/src/internal/store/workspace-editors-store.ts +315 -0
  277. package/src/internal/store/workspace-file-selection-store.ts +88 -0
  278. package/src/internal/useRevealLine.ts +81 -0
  279. package/src/library/__tests__/detect-plan-artifact.test.ts +69 -0
  280. package/src/library/__tests__/detect-streaming-plan.test.ts +234 -0
  281. package/src/library/detect-plan-artifact.ts +62 -5
  282. package/src/library/detect-streaming-plan.ts +108 -0
  283. package/src/library/index.ts +7 -0
  284. package/src/session/NewSessionViewer.tsx +123 -55
  285. package/src/session/PlanEditor.tsx +361 -0
  286. package/src/session/PlanStreamingDocument.tsx +97 -0
  287. package/src/session/SessionPanelChip.tsx +103 -0
  288. package/src/session/SessionViewer.tsx +655 -96
  289. package/src/session/__tests__/NewSessionViewer.test.tsx +148 -0
  290. package/src/session/__tests__/PlanEditor.test.tsx +217 -0
  291. package/src/session/__tests__/PlanStreamingDocument.test.tsx +50 -0
  292. package/src/session/__tests__/SessionPanelChip.test.tsx +42 -0
  293. package/src/session/__tests__/SessionViewer-fileReviewDock.test.tsx +66 -4
  294. package/src/session/__tests__/SessionViewer-transcriptFileOpen.test.tsx +205 -0
  295. package/src/session/__tests__/audienceWiring.test.tsx +55 -19
  296. package/src/session/__tests__/useOpenFileChange.test.tsx +82 -0
  297. package/src/session/__tests__/usePlanDraft.test.ts +102 -0
  298. package/src/session/__tests__/useSessionConversation.test.tsx +44 -0
  299. package/src/session/__tests__/useSessionPanel.test.ts +298 -0
  300. package/src/session/__tests__/useSessionRailViews.test.tsx +124 -0
  301. package/src/session/{inspector → facets}/ArtifactsTab.tsx +27 -5
  302. package/src/session/{inspector → facets}/ChangesTab.tsx +3 -3
  303. package/src/session/{inspector → facets}/SetupTab.tsx +13 -1
  304. package/src/session/{inspector → facets}/UsageTab.tsx +1 -1
  305. package/src/session/{inspector → facets}/__tests__/ArtifactsTab.test.tsx +42 -8
  306. package/src/session/facets/__tests__/SetupTab.test.tsx +44 -0
  307. package/src/session/{inspector → facets}/index.ts +0 -16
  308. package/src/session/index.ts +30 -13
  309. package/src/session/plan-document.ts +30 -0
  310. package/src/session/useOpenFileChange.ts +53 -0
  311. package/src/session/usePlanDraft.ts +109 -0
  312. package/src/session/useSessionConversation.ts +38 -3
  313. package/src/session/useSessionPageFlow.ts +1 -0
  314. package/src/session/useSessionPanel.ts +288 -0
  315. package/src/session/useSessionRailViews.tsx +200 -0
  316. package/src/workflow/WorkflowExecutionGraph.tsx +1 -1
  317. package/src/workflow/index.ts +1 -1
  318. package/src/workflow/useFollowExecution.ts +1 -1
  319. package/src/workspace/EditorTabs.tsx +174 -0
  320. package/src/workspace/ExplorerTree.tsx +253 -0
  321. package/src/workspace/FileViewer.tsx +537 -0
  322. package/src/workspace/WorkspaceContentSearch.tsx +502 -0
  323. package/src/workspace/WorkspaceContentSearcher.ts +70 -0
  324. package/src/workspace/WorkspaceEditor.tsx +19 -16
  325. package/src/workspace/WorkspaceFileLister.ts +11 -0
  326. package/src/workspace/WorkspaceFileReader.ts +57 -0
  327. package/src/workspace/WorkspaceFileSearch.tsx +452 -0
  328. package/src/workspace/WorkspaceSurface.tsx +839 -0
  329. package/src/workspace/__tests__/EditorTabs.test.tsx +102 -0
  330. package/src/workspace/__tests__/ExplorerTree.test.tsx +204 -0
  331. package/src/workspace/__tests__/FileViewer.test.tsx +439 -0
  332. package/src/workspace/__tests__/WorkspaceContentSearch.test.tsx +229 -0
  333. package/src/workspace/__tests__/WorkspaceEditor-initialPanel.test.tsx +19 -0
  334. package/src/workspace/__tests__/WorkspaceFileSearch.test.tsx +227 -0
  335. package/src/workspace/__tests__/WorkspaceSurface.test.tsx +418 -0
  336. package/src/workspace/__tests__/a11y/harness.tsx +140 -0
  337. package/src/workspace/__tests__/a11y/mock-capabilities.ts +138 -0
  338. package/src/workspace/__tests__/a11y/workspace-editor.a11y.test.tsx +65 -0
  339. package/src/workspace/__tests__/a11y/workspace-explorer-search.a11y.test.tsx +112 -0
  340. package/src/workspace/__tests__/a11y/workspace-viewer-tabs.a11y.test.tsx +69 -0
  341. package/src/workspace/__tests__/findChangeForSelection.test.ts +132 -0
  342. package/src/workspace/__tests__/matchContentPreview.test.ts +55 -0
  343. package/src/workspace/__tests__/matchWorkspaceFiles.test.ts +104 -0
  344. package/src/workspace/__tests__/resolveWorkspaceFileSelection.test.ts +135 -0
  345. package/src/workspace/__tests__/useWorkspaceContentSearch.test.tsx +196 -0
  346. package/src/workspace/__tests__/useWorkspaceFileContent.test.tsx +190 -0
  347. package/src/workspace/__tests__/useWorkspaceFileSearch.test.tsx +162 -0
  348. package/src/workspace/findChangeForSelection.ts +61 -0
  349. package/src/workspace/index.ts +56 -2
  350. package/src/workspace/matchContentPreview.ts +51 -0
  351. package/src/workspace/matchWorkspaceFiles.ts +86 -0
  352. package/src/workspace/resolveWorkspaceFileSelection.ts +119 -0
  353. package/src/workspace/useWorkspaceContentSearch.ts +228 -0
  354. package/src/workspace/useWorkspaceFileContent.ts +129 -0
  355. package/src/workspace/useWorkspaceFileSearch.ts +184 -0
  356. package/src/workspace/useWorkspaceFiles.ts +27 -27
  357. package/src/workspace/workspaceListingCache.ts +93 -0
  358. package/styles.css +1 -1
  359. package/workflow/WorkflowExecutionGraph.js +1 -1
  360. package/workflow/WorkflowExecutionGraph.js.map +1 -1
  361. package/workflow/index.d.ts +1 -1
  362. package/workflow/index.d.ts.map +1 -1
  363. package/workflow/index.js +1 -1
  364. package/workflow/index.js.map +1 -1
  365. package/workflow/useFollowExecution.js +1 -1
  366. package/workflow/useFollowExecution.js.map +1 -1
  367. package/workspace/EditorTabs.d.ts +47 -0
  368. package/workspace/EditorTabs.d.ts.map +1 -0
  369. package/workspace/EditorTabs.js +78 -0
  370. package/workspace/EditorTabs.js.map +1 -0
  371. package/workspace/ExplorerTree.d.ts +32 -0
  372. package/workspace/ExplorerTree.d.ts.map +1 -0
  373. package/workspace/ExplorerTree.js +49 -0
  374. package/workspace/ExplorerTree.js.map +1 -0
  375. package/workspace/FileViewer.d.ts +93 -0
  376. package/workspace/FileViewer.d.ts.map +1 -0
  377. package/workspace/FileViewer.js +193 -0
  378. package/workspace/FileViewer.js.map +1 -0
  379. package/workspace/WorkspaceContentSearch.d.ts +41 -0
  380. package/workspace/WorkspaceContentSearch.d.ts.map +1 -0
  381. package/workspace/WorkspaceContentSearch.js +216 -0
  382. package/workspace/WorkspaceContentSearch.js.map +1 -0
  383. package/workspace/WorkspaceContentSearcher.d.ts +59 -0
  384. package/workspace/WorkspaceContentSearcher.d.ts.map +1 -0
  385. package/workspace/WorkspaceContentSearcher.js +7 -0
  386. package/workspace/WorkspaceContentSearcher.js.map +1 -0
  387. package/workspace/WorkspaceEditor.d.ts.map +1 -1
  388. package/workspace/WorkspaceEditor.js +8 -10
  389. package/workspace/WorkspaceEditor.js.map +1 -1
  390. package/workspace/WorkspaceFileLister.d.ts +11 -0
  391. package/workspace/WorkspaceFileLister.d.ts.map +1 -1
  392. package/workspace/WorkspaceFileReader.d.ts +48 -0
  393. package/workspace/WorkspaceFileReader.d.ts.map +1 -0
  394. package/workspace/WorkspaceFileReader.js +11 -0
  395. package/workspace/WorkspaceFileReader.js.map +1 -0
  396. package/workspace/WorkspaceFileSearch.d.ts +32 -0
  397. package/workspace/WorkspaceFileSearch.d.ts.map +1 -0
  398. package/workspace/WorkspaceFileSearch.js +175 -0
  399. package/workspace/WorkspaceFileSearch.js.map +1 -0
  400. package/workspace/WorkspaceSurface.d.ts +151 -0
  401. package/workspace/WorkspaceSurface.d.ts.map +1 -0
  402. package/workspace/WorkspaceSurface.js +220 -0
  403. package/workspace/WorkspaceSurface.js.map +1 -0
  404. package/workspace/findChangeForSelection.d.ts +19 -0
  405. package/workspace/findChangeForSelection.d.ts.map +1 -0
  406. package/workspace/findChangeForSelection.js +46 -0
  407. package/workspace/findChangeForSelection.js.map +1 -0
  408. package/workspace/index.d.ts +24 -2
  409. package/workspace/index.d.ts.map +1 -1
  410. package/workspace/index.js +13 -1
  411. package/workspace/index.js.map +1 -1
  412. package/workspace/matchContentPreview.d.ts +21 -0
  413. package/workspace/matchContentPreview.d.ts.map +1 -0
  414. package/workspace/matchContentPreview.js +36 -0
  415. package/workspace/matchContentPreview.js.map +1 -0
  416. package/workspace/matchWorkspaceFiles.d.ts +30 -0
  417. package/workspace/matchWorkspaceFiles.d.ts.map +1 -0
  418. package/workspace/matchWorkspaceFiles.js +58 -0
  419. package/workspace/matchWorkspaceFiles.js.map +1 -0
  420. package/workspace/resolveWorkspaceFileSelection.d.ts +19 -0
  421. package/workspace/resolveWorkspaceFileSelection.d.ts.map +1 -0
  422. package/workspace/resolveWorkspaceFileSelection.js +115 -0
  423. package/workspace/resolveWorkspaceFileSelection.js.map +1 -0
  424. package/workspace/useWorkspaceContentSearch.d.ts +69 -0
  425. package/workspace/useWorkspaceContentSearch.d.ts.map +1 -0
  426. package/workspace/useWorkspaceContentSearch.js +154 -0
  427. package/workspace/useWorkspaceContentSearch.js.map +1 -0
  428. package/workspace/useWorkspaceFileContent.d.ts +73 -0
  429. package/workspace/useWorkspaceFileContent.d.ts.map +1 -0
  430. package/workspace/useWorkspaceFileContent.js +57 -0
  431. package/workspace/useWorkspaceFileContent.js.map +1 -0
  432. package/workspace/useWorkspaceFileSearch.d.ts +61 -0
  433. package/workspace/useWorkspaceFileSearch.d.ts.map +1 -0
  434. package/workspace/useWorkspaceFileSearch.js +119 -0
  435. package/workspace/useWorkspaceFileSearch.js.map +1 -0
  436. package/workspace/useWorkspaceFiles.d.ts +13 -5
  437. package/workspace/useWorkspaceFiles.d.ts.map +1 -1
  438. package/workspace/useWorkspaceFiles.js +18 -19
  439. package/workspace/useWorkspaceFiles.js.map +1 -1
  440. package/workspace/workspaceListingCache.d.ts +47 -0
  441. package/workspace/workspaceListingCache.d.ts.map +1 -0
  442. package/workspace/workspaceListingCache.js +55 -0
  443. package/workspace/workspaceListingCache.js.map +1 -0
  444. package/session/inspector/ArtifactsTab.d.ts.map +0 -1
  445. package/session/inspector/ArtifactsTab.js.map +0 -1
  446. package/session/inspector/ChangesTab.d.ts.map +0 -1
  447. package/session/inspector/ChangesTab.js.map +0 -1
  448. package/session/inspector/InspectTab.d.ts.map +0 -1
  449. package/session/inspector/InspectTab.js.map +0 -1
  450. package/session/inspector/SessionInspector.d.ts +0 -64
  451. package/session/inspector/SessionInspector.d.ts.map +0 -1
  452. package/session/inspector/SessionInspector.js +0 -53
  453. package/session/inspector/SessionInspector.js.map +0 -1
  454. package/session/inspector/SetupTab.d.ts.map +0 -1
  455. package/session/inspector/SetupTab.js.map +0 -1
  456. package/session/inspector/UsageTab.d.ts.map +0 -1
  457. package/session/inspector/UsageTab.js.map +0 -1
  458. package/session/inspector/WorkspaceTab.d.ts +0 -35
  459. package/session/inspector/WorkspaceTab.d.ts.map +0 -1
  460. package/session/inspector/WorkspaceTab.js +0 -75
  461. package/session/inspector/WorkspaceTab.js.map +0 -1
  462. package/session/inspector/index.d.ts.map +0 -1
  463. package/session/inspector/index.js.map +0 -1
  464. package/session/inspector/useSessionInspector.d.ts +0 -49
  465. package/session/inspector/useSessionInspector.d.ts.map +0 -1
  466. package/session/inspector/useSessionInspector.js +0 -122
  467. package/session/inspector/useSessionInspector.js.map +0 -1
  468. package/src/session/inspector/SessionInspector.tsx +0 -162
  469. package/src/session/inspector/WorkspaceTab.tsx +0 -245
  470. package/src/session/inspector/__tests__/useSessionInspector.test.ts +0 -251
  471. package/src/session/inspector/useSessionInspector.ts +0 -178
  472. package/src/workspace/WorkspaceEntryFiles.tsx +0 -196
  473. package/workflow/motion-preference.d.ts.map +0 -1
  474. package/workflow/motion-preference.js.map +0 -1
  475. package/workspace/WorkspaceEntryFiles.d.ts +0 -22
  476. package/workspace/WorkspaceEntryFiles.d.ts.map +0 -1
  477. package/workspace/WorkspaceEntryFiles.js +0 -58
  478. package/workspace/WorkspaceEntryFiles.js.map +0 -1
  479. /package/session/{inspector → facets}/InspectTab.d.ts +0 -0
  480. /package/session/{inspector → facets}/InspectTab.js +0 -0
  481. /package/src/{workflow → internal}/__tests__/motion-preference.test.ts +0 -0
  482. /package/src/session/{inspector → facets}/InspectTab.tsx +0 -0
  483. /package/src/session/{inspector → facets}/__tests__/ChangesTab.test.tsx +0 -0
@@ -194,6 +194,23 @@ describe("MessageEntry — Human messages", () => {
194
194
  });
195
195
  });
196
196
 
197
+ // ---------------------------------------------------------------------------
198
+ // Build-from-plan turns — no special human-message treatment
199
+ // ---------------------------------------------------------------------------
200
+
201
+ describe("MessageEntry — Build-from-plan turns", () => {
202
+ it("renders any human message as the ordinary prompt bubble (the thread hides build turns before they reach MessageEntry)", () => {
203
+ // The retired chip treatment lived here; today buildThreadItems skips
204
+ // synthesizing the prompt item for a build turn entirely, so a human
205
+ // message that DOES reach MessageEntry is always ordinary prose.
206
+ const msg = makeMessage(MessageType.MESSAGE_HUMAN, "Build from plan");
207
+ const { container } = render(<MessageEntry message={msg} />);
208
+
209
+ expect(queryArticle(container, "User message")).not.toBeNull();
210
+ expect(queryArticle(container, "Build from plan")).toBeNull();
211
+ });
212
+ });
213
+
197
214
  // ---------------------------------------------------------------------------
198
215
  // System messages — unchanged
199
216
  // ---------------------------------------------------------------------------
@@ -0,0 +1,90 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { renderHook, act, 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 { useArtifactCopy } from "../useArtifactCopy";
7
+
8
+ function wrapperFor(stigmer: Stigmer) {
9
+ return ({ children }: { children: ReactNode }) => (
10
+ <StigmerContext.Provider value={stigmer}>{children}</StigmerContext.Provider>
11
+ );
12
+ }
13
+
14
+ function makeStigmer(getArtifactContent = vi.fn()) {
15
+ return { agentExecution: { getArtifactContent } } as unknown as Stigmer;
16
+ }
17
+
18
+ const writeText = vi.fn().mockResolvedValue(undefined);
19
+
20
+ beforeEach(() => {
21
+ writeText.mockClear();
22
+ vi.stubGlobal("navigator", { clipboard: { writeText } });
23
+ });
24
+
25
+ describe("useArtifactCopy", () => {
26
+ it("fetches content at click time and writes decoded text to the clipboard", async () => {
27
+ const bytes = new TextEncoder().encode("# Plan\n\nsteps");
28
+ const fn = vi.fn().mockResolvedValue({ content: bytes });
29
+ const { result } = renderHook(() => useArtifactCopy("aex_1"), {
30
+ wrapper: wrapperFor(makeStigmer(fn)),
31
+ });
32
+
33
+ expect(result.current.copied).toBe(false);
34
+ await act(async () => {
35
+ await result.current.copy("artifacts/aex_1/plan.md");
36
+ });
37
+
38
+ expect(fn).toHaveBeenCalledTimes(1);
39
+ const req = fn.mock.calls[0][0];
40
+ expect(req.executionId).toBe("aex_1");
41
+ expect(req.storageKey).toBe("artifacts/aex_1/plan.md");
42
+ expect(writeText).toHaveBeenCalledWith("# Plan\n\nsteps");
43
+ expect(result.current.copied).toBe(true);
44
+ expect(result.current.error).toBeNull();
45
+ });
46
+
47
+ it("resets the copied flag after the feedback window", async () => {
48
+ vi.useFakeTimers();
49
+ const bytes = new TextEncoder().encode("x");
50
+ const fn = vi.fn().mockResolvedValue({ content: bytes });
51
+ const { result } = renderHook(() => useArtifactCopy("aex_1"), {
52
+ wrapper: wrapperFor(makeStigmer(fn)),
53
+ });
54
+
55
+ await act(async () => {
56
+ await result.current.copy("artifacts/aex_1/plan.md");
57
+ });
58
+ expect(result.current.copied).toBe(true);
59
+
60
+ act(() => {
61
+ vi.advanceTimersByTime(2000);
62
+ });
63
+ expect(result.current.copied).toBe(false);
64
+ vi.useRealTimers();
65
+ });
66
+
67
+ it("is a no-op when the execution id is unknown", async () => {
68
+ const fn = vi.fn();
69
+ const { result } = renderHook(() => useArtifactCopy(null), {
70
+ wrapper: wrapperFor(makeStigmer(fn)),
71
+ });
72
+ await act(async () => {
73
+ await result.current.copy("artifacts/x/plan.md");
74
+ });
75
+ expect(fn).not.toHaveBeenCalled();
76
+ expect(writeText).not.toHaveBeenCalled();
77
+ });
78
+
79
+ it("surfaces an error when the content RPC fails", async () => {
80
+ const fn = vi.fn().mockRejectedValue(new Error("boom"));
81
+ const { result } = renderHook(() => useArtifactCopy("aex_1"), {
82
+ wrapper: wrapperFor(makeStigmer(fn)),
83
+ });
84
+ await act(async () => {
85
+ await result.current.copy("artifacts/aex_1/plan.md");
86
+ });
87
+ await waitFor(() => expect(result.current.error?.message).toBe("boom"));
88
+ expect(result.current.copied).toBe(false);
89
+ });
90
+ });
@@ -38,6 +38,8 @@ describe("useArtifactDownload", () => {
38
38
  const req = fn.mock.calls[0][0];
39
39
  expect(req.executionId).toBe("aex_1");
40
40
  expect(req.storageKey).toBe("artifacts/aex_1/file.bin");
41
+ // Save-to-disk actions force an attachment disposition on the minted URL.
42
+ expect(req.asAttachment).toBe(true);
41
43
  expect(result.current.isDownloading).toBe(false);
42
44
  expect(result.current.error).toBeNull();
43
45
  });
@@ -0,0 +1,85 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { renderHook, act } from "@testing-library/react";
3
+ import type { ReactNode } from "react";
4
+ import type { Stigmer } from "@stigmer/sdk";
5
+ import { InteractionMode } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
6
+ import { StigmerContext } from "../../context";
7
+ import { useCreateAgentExecution } from "../useCreateAgentExecution";
8
+
9
+ const mockCreate = vi.fn();
10
+
11
+ function makeMockClient(): Stigmer {
12
+ return {
13
+ agentExecution: { create: mockCreate },
14
+ } as unknown as Stigmer;
15
+ }
16
+
17
+ function createWrapper(client: Stigmer) {
18
+ return function Wrapper({ children }: { children: ReactNode }) {
19
+ return <StigmerContext.Provider value={client}>{children}</StigmerContext.Provider>;
20
+ };
21
+ }
22
+
23
+ beforeEach(() => {
24
+ vi.clearAllMocks();
25
+ mockCreate.mockResolvedValue({ metadata: { id: "aex-1" } });
26
+ });
27
+
28
+ describe("useCreateAgentExecution — executionConfig mapping", () => {
29
+ it("maps buildFromPlan into executionConfig (the Build-from-plan turn)", async () => {
30
+ const { result } = renderHook(() => useCreateAgentExecution(), {
31
+ wrapper: createWrapper(makeMockClient()),
32
+ });
33
+
34
+ await act(async () => {
35
+ await result.current.create({
36
+ org: "acme",
37
+ sessionId: "ses-1",
38
+ message: "Build from plan",
39
+ interactionMode: "agent",
40
+ buildFromPlan: true,
41
+ });
42
+ });
43
+
44
+ expect(mockCreate).toHaveBeenCalledTimes(1);
45
+ expect(mockCreate.mock.calls[0][0].executionConfig).toMatchObject({
46
+ interactionMode: InteractionMode.AGENT,
47
+ buildFromPlan: true,
48
+ });
49
+ });
50
+
51
+ it("builds an executionConfig when buildFromPlan is the only config input", async () => {
52
+ const { result } = renderHook(() => useCreateAgentExecution(), {
53
+ wrapper: createWrapper(makeMockClient()),
54
+ });
55
+
56
+ await act(async () => {
57
+ await result.current.create({
58
+ org: "acme",
59
+ sessionId: "ses-1",
60
+ message: "Build from plan",
61
+ buildFromPlan: true,
62
+ });
63
+ });
64
+
65
+ expect(mockCreate.mock.calls[0][0].executionConfig).toMatchObject({
66
+ buildFromPlan: true,
67
+ });
68
+ });
69
+
70
+ it("omits executionConfig entirely for an ordinary message", async () => {
71
+ const { result } = renderHook(() => useCreateAgentExecution(), {
72
+ wrapper: createWrapper(makeMockClient()),
73
+ });
74
+
75
+ await act(async () => {
76
+ await result.current.create({
77
+ org: "acme",
78
+ sessionId: "ses-1",
79
+ message: "Hello",
80
+ });
81
+ });
82
+
83
+ expect(mockCreate.mock.calls[0][0].executionConfig).toBeUndefined();
84
+ });
85
+ });
@@ -41,6 +41,12 @@ export type { PlanCompletionCardProps } from "./PlanCompletionCard.js";
41
41
  export { PlanArtifactCard } from "./PlanArtifactCard.js";
42
42
  export type { PlanArtifactCardProps } from "./PlanArtifactCard.js";
43
43
 
44
+ export { PlanStreamingCard } from "./PlanStreamingCard.js";
45
+ export type { PlanStreamingCardProps } from "./PlanStreamingCard.js";
46
+
47
+ export { PlanDocumentMessage } from "./PlanDocumentMessage.js";
48
+ export type { PlanDocumentMessageProps } from "./PlanDocumentMessage.js";
49
+
44
50
  export { useExecutionArtifacts } from "./useExecutionArtifacts.js";
45
51
  export type { UseExecutionArtifactsReturn } from "./useExecutionArtifacts.js";
46
52
 
@@ -56,6 +62,9 @@ export type {
56
62
  export { useArtifactDownload } from "./useArtifactDownload.js";
57
63
  export type { UseArtifactDownloadReturn } from "./useArtifactDownload.js";
58
64
 
65
+ export { useArtifactCopy } from "./useArtifactCopy.js";
66
+ export type { UseArtifactCopyReturn } from "./useArtifactCopy.js";
67
+
59
68
  export { useToolOutputContent } from "./useToolOutputContent.js";
60
69
  export type {
61
70
  UseToolOutputContentReturn,
@@ -160,7 +169,7 @@ export { MessageEntry } from "./MessageEntry.js";
160
169
  export type { MessageEntryProps } from "./MessageEntry.js";
161
170
 
162
171
  export { MessageThread } from "./MessageThread.js";
163
- export type { MessageThreadProps } from "./MessageThread.js";
172
+ export type { MessageThreadProps, ThreadContentColumn } from "./MessageThread.js";
164
173
 
165
174
  export { ThreadSkeleton } from "./ThreadSkeleton.js";
166
175
  export type { ThreadSkeletonProps } from "./ThreadSkeleton.js";
@@ -224,6 +233,9 @@ export type { FileReviewCardProps } from "./FileReviewCard.js";
224
233
  export { FileReviewDock } from "./FileReviewDock.js";
225
234
  export type { FileReviewDockProps } from "./FileReviewDock.js";
226
235
 
236
+ export { FileChangeProgressBar } from "./FileChangeProgressBar.js";
237
+ export type { FileChangeProgressBarProps } from "./FileChangeProgressBar.js";
238
+
227
239
  export { ApprovalContext, useApproval } from "./ApprovalContext.js";
228
240
  export type { ApprovalContextValue, UseApprovalResult } from "./ApprovalContext.js";
229
241
 
@@ -3,7 +3,7 @@
3
3
  import { useCallback, type KeyboardEvent } from "react";
4
4
 
5
5
  /**
6
- * Card-scoped `Cmd/Ctrl+Enter` accelerator for the "Build from plan" action.
6
+ * Card-scoped `Cmd/Ctrl+Enter` accelerator for a plan card's "Build" action.
7
7
  *
8
8
  * Returns an `onKeyDown` handler for a plan card's root element. Because it is
9
9
  * attached to the card (not `window`), it fires only when the keystroke
@@ -0,0 +1,109 @@
1
+ "use client";
2
+
3
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
+ import { create } from "@bufbuild/protobuf";
5
+ import { GetArtifactContentRequestSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/io_pb";
6
+ import { useStigmer } from "../hooks.js";
7
+ import { toError } from "../internal/toError.js";
8
+
9
+ /** How long the transient `copied` flag stays true after a successful copy. */
10
+ const COPIED_FEEDBACK_MS = 2000;
11
+
12
+ /** Return value of {@link useArtifactCopy}. */
13
+ export interface UseArtifactCopyReturn {
14
+ /**
15
+ * Fetch the artifact's text at click time and write it to the clipboard.
16
+ * No-op when the execution id is unknown or a copy is already in flight.
17
+ * Resolves once the copy has been attempted.
18
+ *
19
+ * @param storageKey - Storage key of the artifact to copy.
20
+ */
21
+ readonly copy: (storageKey: string) => Promise<void>;
22
+
23
+ /** `true` while the content is being fetched / written to the clipboard. */
24
+ readonly isCopying: boolean;
25
+
26
+ /**
27
+ * `true` for a brief window after a successful copy (drives "Copied"
28
+ * affordance feedback), then resets automatically.
29
+ */
30
+ readonly copied: boolean;
31
+
32
+ /** Error from the last failed copy attempt, or `null` when healthy. */
33
+ readonly error: Error | null;
34
+ }
35
+
36
+ /**
37
+ * Behavior hook that copies an execution artifact's text to the clipboard on
38
+ * demand.
39
+ *
40
+ * The imperative sibling of {@link useArtifactDownload}: it fetches content at
41
+ * click time via `getArtifactContent` (CORS-safe, through the Stigmer API)
42
+ * rather than requiring the caller to have loaded it. This suits content-free
43
+ * surfaces — a plan card that detects the artifact by convention and never
44
+ * fetches until the user acts — where a mount-time {@link useArtifactContent}
45
+ * would be wasteful.
46
+ *
47
+ * Returns an imperative `copy(storageKey)` action plus a transient `copied`
48
+ * flag for feedback, mirroring the modal's copy affordance so every surface
49
+ * shares one implementation.
50
+ *
51
+ * @param executionId - Execution that produced the artifacts, or `null` to
52
+ * disable (the action becomes a no-op).
53
+ *
54
+ * @example
55
+ * ```tsx
56
+ * const { copy, isCopying, copied } = useArtifactCopy(executionId);
57
+ * <button onClick={() => copy(artifact.storageKey)} disabled={isCopying}>
58
+ * {copied ? "Copied" : "Copy"}
59
+ * </button>
60
+ * ```
61
+ *
62
+ * @see useArtifactDownload — imperative save-to-disk counterpart
63
+ * @see useArtifactContent — declarative content reader (preview rendering)
64
+ */
65
+ export function useArtifactCopy(
66
+ executionId: string | null,
67
+ ): UseArtifactCopyReturn {
68
+ const stigmer = useStigmer();
69
+ const [isCopying, setIsCopying] = useState(false);
70
+ const [copied, setCopied] = useState(false);
71
+ const [error, setError] = useState<Error | null>(null);
72
+
73
+ // Clear the pending "copied" timer on unmount so a resolved copy never sets
74
+ // state on an unmounted component.
75
+ const copiedTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
76
+ useEffect(() => {
77
+ return () => {
78
+ if (copiedTimer.current !== null) clearTimeout(copiedTimer.current);
79
+ };
80
+ }, []);
81
+
82
+ const copy = useCallback(
83
+ async (storageKey: string) => {
84
+ if (!executionId || !storageKey || isCopying) return;
85
+ setIsCopying(true);
86
+ setError(null);
87
+ try {
88
+ const result = await stigmer.agentExecution.getArtifactContent(
89
+ create(GetArtifactContentRequestSchema, { executionId, storageKey }),
90
+ );
91
+ const text = new TextDecoder().decode(result.content);
92
+ await navigator.clipboard.writeText(text);
93
+ setCopied(true);
94
+ if (copiedTimer.current !== null) clearTimeout(copiedTimer.current);
95
+ copiedTimer.current = setTimeout(() => setCopied(false), COPIED_FEEDBACK_MS);
96
+ } catch (err) {
97
+ setError(toError(err));
98
+ } finally {
99
+ setIsCopying(false);
100
+ }
101
+ },
102
+ [executionId, stigmer, isCopying],
103
+ );
104
+
105
+ return useMemo(
106
+ () => ({ copy, isCopying, copied, error }),
107
+ [copy, isCopying, copied, error],
108
+ );
109
+ }
@@ -65,7 +65,16 @@ export function useArtifactDownload(
65
65
  setError(null);
66
66
  try {
67
67
  const result = await stigmer.agentExecution.getArtifactDownloadUrl(
68
- create(GetArtifactDownloadUrlRequestSchema, { executionId, storageKey }),
68
+ create(GetArtifactDownloadUrlRequestSchema, {
69
+ executionId,
70
+ storageKey,
71
+ // This is a save-to-disk action, so mint a URL that forces a
72
+ // browser download (Content-Disposition: attachment). Browsers
73
+ // ignore the anchor's `download` attribute cross-origin, so the
74
+ // disposition must ride on the URL itself. Contrast
75
+ // useArtifactDownloadUrl, which stays inline for `<img src>`.
76
+ asAttachment: true,
77
+ }),
69
78
  );
70
79
  if (result.downloadUrl) {
71
80
  triggerBrowserDownload(result.downloadUrl, fileName);
@@ -86,10 +95,13 @@ export function useArtifactDownload(
86
95
  }
87
96
 
88
97
  /**
89
- * Trigger a browser download for a URL via a transient anchor click. The
90
- * `download` hint is best-effort: browsers ignore it for cross-origin URLs
91
- * (e.g. R2), in which case the navigation still serves the file. Falls back to
92
- * a new tab in non-DOM environments.
98
+ * Trigger a browser download for a URL via a transient anchor click.
99
+ *
100
+ * The URL minted for this flow carries `Content-Disposition: attachment`
101
+ * (requested via `asAttachment`), so the browser saves the file even
102
+ * cross-origin — where it would ignore the anchor's `download` attribute. The
103
+ * `download` hint is still set as a same-origin nicety. Falls back to a new tab
104
+ * in non-DOM environments.
93
105
  */
94
106
  function triggerBrowserDownload(url: string, fileName?: string): void {
95
107
  if (typeof document === "undefined") {
@@ -54,6 +54,18 @@ export interface CreateAgentExecutionInput {
54
54
  * Maps to `ExecutionConfig.interaction_mode` in the proto.
55
55
  */
56
56
  readonly interactionMode?: "agent" | "plan";
57
+ /**
58
+ * Marks this execution as a Build-from-plan turn: the user approved a
59
+ * plan from a prior Plan-mode execution and asked the agent to implement
60
+ * it. The runner injects the implement-plan directive (pointing at the
61
+ * attached approved plan when present); the thread hides the turn's
62
+ * machine-written message entirely — the plan card above it is the
63
+ * visible cause. Surfaces without that treatment (the CLI, history)
64
+ * show the message text as-is.
65
+ *
66
+ * Maps to `ExecutionConfig.build_from_plan` in the proto.
67
+ */
68
+ readonly buildFromPlan?: boolean;
57
69
  /**
58
70
  * Auto-approve every tool call for this execution.
59
71
  *
@@ -158,7 +170,11 @@ export function useCreateAgentExecution(): UseCreateAgentExecutionReturn {
158
170
  setError(null);
159
171
 
160
172
  try {
161
- const hasConfig = input.modelName || input.interactionMode || input.structuredOutputSchema;
173
+ const hasConfig =
174
+ input.modelName ||
175
+ input.interactionMode ||
176
+ input.structuredOutputSchema ||
177
+ input.buildFromPlan;
162
178
  const executionConfig = hasConfig
163
179
  ? {
164
180
  ...(input.modelName ? { modelName: input.modelName } : {}),
@@ -168,6 +184,7 @@ export function useCreateAgentExecution(): UseCreateAgentExecutionReturn {
168
184
  ...(input.structuredOutputSchema
169
185
  ? { structuredOutputSchema: input.structuredOutputSchema }
170
186
  : {}),
187
+ ...(input.buildFromPlan ? { buildFromPlan: true } : {}),
171
188
  }
172
189
  : undefined;
173
190
 
@@ -0,0 +1,102 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import {
3
+ base64ToBytes,
4
+ detectBinary,
5
+ bytesToText,
6
+ normalizeGitHubContent,
7
+ } from "../decodeGitHubContent";
8
+ import { MAX_WORKSPACE_FILE_READ_BYTES } from "../../workspace/WorkspaceFileReader";
9
+
10
+ function utf8(s: string): Uint8Array {
11
+ return new TextEncoder().encode(s);
12
+ }
13
+
14
+ describe("base64ToBytes", () => {
15
+ it("decodes plain base64 to bytes", () => {
16
+ expect(bytesToText(base64ToBytes("SGVsbG8gV29ybGQh"))).toBe("Hello World!");
17
+ });
18
+
19
+ it("strips the newlines GitHub inserts every 60 chars", () => {
20
+ // GitHub's Contents API line-wraps its base64; atob would otherwise reject it.
21
+ const wrapped = "SGVsbG8g\nV29ybGQh\n";
22
+ expect(bytesToText(base64ToBytes(wrapped))).toBe("Hello World!");
23
+ });
24
+ });
25
+
26
+ describe("detectBinary", () => {
27
+ it("flags a buffer containing a NUL byte", () => {
28
+ expect(detectBinary(new Uint8Array([0x48, 0x00, 0x49]))).toBe(true);
29
+ });
30
+
31
+ it("treats NUL-free text as non-binary", () => {
32
+ expect(detectBinary(utf8("just some text"))).toBe(false);
33
+ });
34
+
35
+ it("only sniffs the head — a NUL past the sniff window is not detected", () => {
36
+ const bytes = new Uint8Array(9000).fill(0x61); // non-zero baseline
37
+ bytes[8500] = 0; // beyond the 8000-byte sniff window
38
+ expect(detectBinary(bytes)).toBe(false);
39
+ });
40
+ });
41
+
42
+ describe("bytesToText", () => {
43
+ it("decodes valid UTF-8", () => {
44
+ expect(bytesToText(utf8("héllo — 世界"))).toBe("héllo — 世界");
45
+ });
46
+
47
+ it("returns null for invalid UTF-8", () => {
48
+ // 0xFF is never a valid UTF-8 lead byte; the fatal decoder throws → null.
49
+ expect(bytesToText(new Uint8Array([0xff]))).toBeNull();
50
+ });
51
+ });
52
+
53
+ describe("normalizeGitHubContent", () => {
54
+ it("maps clean text to a utf-8 content record with the full size", () => {
55
+ const bytes = utf8("hello");
56
+ expect(normalizeGitHubContent(bytes, bytes.length)).toEqual({
57
+ text: "hello",
58
+ isBinary: false,
59
+ size: bytes.length,
60
+ encoding: "utf-8",
61
+ });
62
+ });
63
+
64
+ it("reports binary content with text null", () => {
65
+ const result = normalizeGitHubContent(new Uint8Array([0x00, 0x01, 0x02]), 3);
66
+ expect(result).toEqual({
67
+ text: null,
68
+ isBinary: true,
69
+ size: 3,
70
+ encoding: "base64",
71
+ });
72
+ });
73
+
74
+ it("marks undecodable (non-binary) bytes as unknown encoding", () => {
75
+ // 0xC0 is an invalid UTF-8 lead byte but not a NUL, so it is not "binary".
76
+ const result = normalizeGitHubContent(new Uint8Array([0xc0, 0x41]), 2);
77
+ expect(result).toEqual({
78
+ text: null,
79
+ isBinary: false,
80
+ size: 2,
81
+ encoding: "unknown",
82
+ });
83
+ });
84
+
85
+ it("truncates at the 1 MB cap and flags it, while size reports the full length", () => {
86
+ const total = MAX_WORKSPACE_FILE_READ_BYTES + 500;
87
+ const bytes = new Uint8Array(total).fill(0x61); // 'a'
88
+ const result = normalizeGitHubContent(bytes, total);
89
+
90
+ expect(result.truncated).toBe(true);
91
+ expect(result.size).toBe(total);
92
+ expect(result.text).toHaveLength(MAX_WORKSPACE_FILE_READ_BYTES);
93
+ expect(result.encoding).toBe("utf-8");
94
+ });
95
+
96
+ it("does not set truncated when exactly at the cap", () => {
97
+ const bytes = new Uint8Array(MAX_WORKSPACE_FILE_READ_BYTES).fill(0x61);
98
+ const result = normalizeGitHubContent(bytes, bytes.length);
99
+ expect(result.truncated).toBeUndefined();
100
+ expect(result.text).toHaveLength(MAX_WORKSPACE_FILE_READ_BYTES);
101
+ });
102
+ });