@stigmer/react 3.1.1 → 3.1.2

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 (426) hide show
  1. package/composer/SessionComposer.d.ts +24 -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/FilePathContext.d.ts +8 -1
  10. package/execution/FilePathContext.d.ts.map +1 -1
  11. package/execution/FilePathContext.js.map +1 -1
  12. package/execution/FilePathLink.d.ts.map +1 -1
  13. package/execution/FilePathLink.js +21 -6
  14. package/execution/FilePathLink.js.map +1 -1
  15. package/execution/MessageEntry.d.ts +29 -0
  16. package/execution/MessageEntry.d.ts.map +1 -1
  17. package/execution/MessageEntry.js +29 -5
  18. package/execution/MessageEntry.js.map +1 -1
  19. package/execution/MessageThread.d.ts +76 -13
  20. package/execution/MessageThread.d.ts.map +1 -1
  21. package/execution/MessageThread.js +178 -24
  22. package/execution/MessageThread.js.map +1 -1
  23. package/execution/PlanArtifactCard.d.ts +27 -18
  24. package/execution/PlanArtifactCard.d.ts.map +1 -1
  25. package/execution/PlanArtifactCard.js +13 -23
  26. package/execution/PlanArtifactCard.js.map +1 -1
  27. package/execution/PlanDocumentMessage.d.ts +32 -0
  28. package/execution/PlanDocumentMessage.d.ts.map +1 -0
  29. package/execution/PlanDocumentMessage.js +37 -0
  30. package/execution/PlanDocumentMessage.js.map +1 -0
  31. package/execution/PlanStreamingCard.d.ts +45 -0
  32. package/execution/PlanStreamingCard.d.ts.map +1 -0
  33. package/execution/PlanStreamingCard.js +35 -0
  34. package/execution/PlanStreamingCard.js.map +1 -0
  35. package/execution/index.d.ts +5 -1
  36. package/execution/index.d.ts.map +1 -1
  37. package/execution/index.js +2 -0
  38. package/execution/index.js.map +1 -1
  39. package/execution/useCreateAgentExecution.d.ts +10 -0
  40. package/execution/useCreateAgentExecution.d.ts.map +1 -1
  41. package/execution/useCreateAgentExecution.js +5 -1
  42. package/execution/useCreateAgentExecution.js.map +1 -1
  43. package/github/decodeGitHubContent.d.ts +29 -0
  44. package/github/decodeGitHubContent.d.ts.map +1 -0
  45. package/github/decodeGitHubContent.js +72 -0
  46. package/github/decodeGitHubContent.js.map +1 -0
  47. package/github/index.d.ts +1 -0
  48. package/github/index.d.ts.map +1 -1
  49. package/github/index.js +1 -0
  50. package/github/index.js.map +1 -1
  51. package/github/useGitHubFileReader.d.ts +26 -0
  52. package/github/useGitHubFileReader.d.ts.map +1 -0
  53. package/github/useGitHubFileReader.js +106 -0
  54. package/github/useGitHubFileReader.js.map +1 -0
  55. package/github/useGitHubTreeLister.d.ts.map +1 -1
  56. package/github/useGitHubTreeLister.js +6 -2
  57. package/github/useGitHubTreeLister.js.map +1 -1
  58. package/index.d.ts +9 -9
  59. package/index.d.ts.map +1 -1
  60. package/index.js +5 -5
  61. package/index.js.map +1 -1
  62. package/internal/ResizableSplit.d.ts +72 -22
  63. package/internal/ResizableSplit.d.ts.map +1 -1
  64. package/internal/ResizableSplit.js +96 -38
  65. package/internal/ResizableSplit.js.map +1 -1
  66. package/internal/VirtualizedThread.d.ts +5 -3
  67. package/internal/VirtualizedThread.d.ts.map +1 -1
  68. package/internal/VirtualizedThread.js +6 -4
  69. package/internal/VirtualizedThread.js.map +1 -1
  70. package/internal/file-icons/index.d.ts +17 -0
  71. package/internal/file-icons/index.d.ts.map +1 -0
  72. package/internal/file-icons/index.js +60 -0
  73. package/internal/file-icons/index.js.map +1 -0
  74. package/internal/file-tree/FileTreeNode.d.ts +18 -1
  75. package/internal/file-tree/FileTreeNode.d.ts.map +1 -1
  76. package/internal/file-tree/FileTreeNode.js +11 -2
  77. package/internal/file-tree/FileTreeNode.js.map +1 -1
  78. package/internal/markdown-components.d.ts +38 -1
  79. package/internal/markdown-components.d.ts.map +1 -1
  80. package/internal/markdown-components.js +86 -11
  81. package/internal/markdown-components.js.map +1 -1
  82. package/{workflow → internal}/motion-preference.d.ts +7 -4
  83. package/internal/motion-preference.d.ts.map +1 -0
  84. package/{workflow → internal}/motion-preference.js +7 -4
  85. package/internal/motion-preference.js.map +1 -0
  86. package/internal/store/index.d.ts +33 -0
  87. package/internal/store/index.d.ts.map +1 -1
  88. package/internal/store/index.js +59 -0
  89. package/internal/store/index.js.map +1 -1
  90. package/internal/store/workspace-editors-store.d.ts +145 -0
  91. package/internal/store/workspace-editors-store.d.ts.map +1 -0
  92. package/internal/store/workspace-editors-store.js +208 -0
  93. package/internal/store/workspace-editors-store.js.map +1 -0
  94. package/internal/store/workspace-file-selection-store.d.ts +38 -0
  95. package/internal/store/workspace-file-selection-store.d.ts.map +1 -0
  96. package/internal/store/workspace-file-selection-store.js +62 -0
  97. package/internal/store/workspace-file-selection-store.js.map +1 -0
  98. package/internal/useRevealLine.d.ts +49 -0
  99. package/internal/useRevealLine.d.ts.map +1 -0
  100. package/internal/useRevealLine.js +43 -0
  101. package/internal/useRevealLine.js.map +1 -0
  102. package/library/detect-plan-artifact.d.ts +21 -0
  103. package/library/detect-plan-artifact.d.ts.map +1 -1
  104. package/library/detect-plan-artifact.js +20 -0
  105. package/library/detect-plan-artifact.js.map +1 -1
  106. package/library/detect-streaming-plan.d.ts +45 -0
  107. package/library/detect-streaming-plan.d.ts.map +1 -0
  108. package/library/detect-streaming-plan.js +83 -0
  109. package/library/detect-streaming-plan.js.map +1 -0
  110. package/library/index.d.ts +4 -1
  111. package/library/index.d.ts.map +1 -1
  112. package/library/index.js +2 -1
  113. package/library/index.js.map +1 -1
  114. package/package.json +4 -4
  115. package/session/NewSessionViewer.d.ts +19 -4
  116. package/session/NewSessionViewer.d.ts.map +1 -1
  117. package/session/NewSessionViewer.js +52 -25
  118. package/session/NewSessionViewer.js.map +1 -1
  119. package/session/PlanEditor.d.ts +55 -0
  120. package/session/PlanEditor.d.ts.map +1 -0
  121. package/session/PlanEditor.js +88 -0
  122. package/session/PlanEditor.js.map +1 -0
  123. package/session/PlanStreamingDocument.d.ts +30 -0
  124. package/session/PlanStreamingDocument.d.ts.map +1 -0
  125. package/session/PlanStreamingDocument.js +37 -0
  126. package/session/PlanStreamingDocument.js.map +1 -0
  127. package/session/SessionPanelChip.d.ts +26 -0
  128. package/session/SessionPanelChip.d.ts.map +1 -0
  129. package/session/SessionPanelChip.js +40 -0
  130. package/session/SessionPanelChip.js.map +1 -0
  131. package/session/SessionViewer.d.ts +34 -7
  132. package/session/SessionViewer.d.ts.map +1 -1
  133. package/session/SessionViewer.js +315 -33
  134. package/session/SessionViewer.js.map +1 -1
  135. package/session/{inspector → facets}/ArtifactsTab.d.ts +10 -2
  136. package/session/facets/ArtifactsTab.d.ts.map +1 -0
  137. package/session/{inspector → facets}/ArtifactsTab.js +8 -3
  138. package/session/facets/ArtifactsTab.js.map +1 -0
  139. package/session/{inspector → facets}/ChangesTab.d.ts +3 -3
  140. package/session/facets/ChangesTab.d.ts.map +1 -0
  141. package/session/{inspector → facets}/ChangesTab.js +3 -3
  142. package/session/facets/ChangesTab.js.map +1 -0
  143. package/session/facets/InspectTab.d.ts.map +1 -0
  144. package/session/facets/InspectTab.js.map +1 -0
  145. package/session/{inspector → facets}/SetupTab.d.ts +11 -2
  146. package/session/facets/SetupTab.d.ts.map +1 -0
  147. package/session/{inspector → facets}/SetupTab.js +4 -3
  148. package/session/facets/SetupTab.js.map +1 -0
  149. package/session/{inspector → facets}/UsageTab.d.ts +1 -1
  150. package/session/facets/UsageTab.d.ts.map +1 -0
  151. package/session/{inspector → facets}/UsageTab.js +1 -1
  152. package/session/facets/UsageTab.js.map +1 -0
  153. package/session/{inspector → facets}/index.d.ts +0 -6
  154. package/session/facets/index.d.ts.map +1 -0
  155. package/session/{inspector → facets}/index.js +0 -3
  156. package/session/facets/index.js.map +1 -0
  157. package/session/index.d.ts +11 -2
  158. package/session/index.d.ts.map +1 -1
  159. package/session/index.js +15 -2
  160. package/session/index.js.map +1 -1
  161. package/session/plan-document.d.ts +9 -0
  162. package/session/plan-document.d.ts.map +1 -0
  163. package/session/plan-document.js +17 -0
  164. package/session/plan-document.js.map +1 -0
  165. package/session/useOpenFileChange.d.ts +20 -0
  166. package/session/useOpenFileChange.d.ts.map +1 -0
  167. package/session/useOpenFileChange.js +31 -0
  168. package/session/useOpenFileChange.js.map +1 -0
  169. package/session/usePlanDraft.d.ts +52 -0
  170. package/session/usePlanDraft.d.ts.map +1 -0
  171. package/session/usePlanDraft.js +63 -0
  172. package/session/usePlanDraft.js.map +1 -0
  173. package/session/useSessionConversation.d.ts +6 -0
  174. package/session/useSessionConversation.d.ts.map +1 -1
  175. package/session/useSessionConversation.js +1 -0
  176. package/session/useSessionConversation.js.map +1 -1
  177. package/session/useSessionPageFlow.d.ts.map +1 -1
  178. package/session/useSessionPageFlow.js +1 -0
  179. package/session/useSessionPageFlow.js.map +1 -1
  180. package/session/useSessionPanel.d.ts +120 -0
  181. package/session/useSessionPanel.d.ts.map +1 -0
  182. package/session/useSessionPanel.js +130 -0
  183. package/session/useSessionPanel.js.map +1 -0
  184. package/session/useSessionRailViews.d.ts +45 -0
  185. package/session/useSessionRailViews.d.ts.map +1 -0
  186. package/session/useSessionRailViews.js +105 -0
  187. package/session/useSessionRailViews.js.map +1 -0
  188. package/src/composer/SessionComposer.tsx +52 -10
  189. package/src/composer/__tests__/SessionComposer-contract.test.tsx +58 -4
  190. package/src/execution/ArtifactContentRenderer.tsx +101 -24
  191. package/src/execution/FilePathContext.tsx +8 -1
  192. package/src/execution/FilePathLink.tsx +22 -6
  193. package/src/execution/MessageEntry.tsx +111 -1
  194. package/src/execution/MessageThread.tsx +291 -36
  195. package/src/execution/PlanArtifactCard.tsx +63 -31
  196. package/src/execution/PlanDocumentMessage.tsx +96 -0
  197. package/src/execution/PlanStreamingCard.tsx +137 -0
  198. package/src/execution/__tests__/ArtifactContentRenderer.test.tsx +80 -1
  199. package/src/execution/__tests__/FilePathLink.test.tsx +87 -0
  200. package/src/execution/__tests__/MessageThread.test.tsx +101 -0
  201. package/src/execution/__tests__/PlanArtifactCard.test.tsx +66 -12
  202. package/src/execution/__tests__/PlanDocumentMessage.test.tsx +55 -0
  203. package/src/execution/__tests__/PlanStreamingCard.test.tsx +49 -0
  204. package/src/execution/__tests__/buildThreadItems-plan-completion.test.ts +504 -22
  205. package/src/execution/__tests__/message-entry.test.tsx +48 -0
  206. package/src/execution/__tests__/useCreateAgentExecution.test.tsx +85 -0
  207. package/src/execution/index.ts +7 -1
  208. package/src/execution/useCreateAgentExecution.ts +16 -1
  209. package/src/github/__tests__/decodeGitHubContent.test.ts +102 -0
  210. package/src/github/__tests__/useGitHubFileReader.test.ts +183 -0
  211. package/src/github/decodeGitHubContent.ts +83 -0
  212. package/src/github/index.ts +1 -0
  213. package/src/github/useGitHubFileReader.ts +152 -0
  214. package/src/github/useGitHubTreeLister.ts +6 -2
  215. package/src/index.ts +63 -9
  216. package/src/internal/ResizableSplit.tsx +184 -51
  217. package/src/internal/VirtualizedThread.tsx +12 -4
  218. package/src/internal/__tests__/ResizableSplit.test.tsx +125 -3
  219. package/src/internal/__tests__/markdown-components.test.tsx +68 -0
  220. package/src/internal/__tests__/useRevealLine.test.tsx +97 -0
  221. package/src/internal/file-icons/__tests__/file-icons.test.tsx +35 -0
  222. package/src/internal/file-icons/index.tsx +137 -0
  223. package/src/internal/file-tree/FileTreeNode.tsx +43 -3
  224. package/src/internal/file-tree/__tests__/FileTreeNode-explorer.test.tsx +86 -0
  225. package/src/internal/markdown-components.tsx +116 -11
  226. package/src/{workflow → internal}/motion-preference.ts +7 -4
  227. package/src/internal/store/__tests__/workspace-editors-store.test.ts +216 -0
  228. package/src/internal/store/__tests__/workspace-file-selection-store.test.ts +118 -0
  229. package/src/internal/store/index.ts +94 -0
  230. package/src/internal/store/workspace-editors-store.ts +315 -0
  231. package/src/internal/store/workspace-file-selection-store.ts +88 -0
  232. package/src/internal/useRevealLine.ts +81 -0
  233. package/src/library/__tests__/detect-plan-artifact.test.ts +48 -0
  234. package/src/library/__tests__/detect-streaming-plan.test.ts +234 -0
  235. package/src/library/detect-plan-artifact.ts +35 -0
  236. package/src/library/detect-streaming-plan.ts +108 -0
  237. package/src/library/index.ts +5 -0
  238. package/src/session/NewSessionViewer.tsx +123 -55
  239. package/src/session/PlanEditor.tsx +361 -0
  240. package/src/session/PlanStreamingDocument.tsx +97 -0
  241. package/src/session/SessionPanelChip.tsx +103 -0
  242. package/src/session/SessionViewer.tsx +617 -90
  243. package/src/session/__tests__/NewSessionViewer.test.tsx +148 -0
  244. package/src/session/__tests__/PlanEditor.test.tsx +217 -0
  245. package/src/session/__tests__/PlanStreamingDocument.test.tsx +50 -0
  246. package/src/session/__tests__/SessionPanelChip.test.tsx +42 -0
  247. package/src/session/__tests__/SessionViewer-fileReviewDock.test.tsx +9 -4
  248. package/src/session/__tests__/SessionViewer-transcriptFileOpen.test.tsx +205 -0
  249. package/src/session/__tests__/audienceWiring.test.tsx +55 -19
  250. package/src/session/__tests__/useOpenFileChange.test.tsx +82 -0
  251. package/src/session/__tests__/usePlanDraft.test.ts +102 -0
  252. package/src/session/__tests__/useSessionPanel.test.ts +298 -0
  253. package/src/session/__tests__/useSessionRailViews.test.tsx +124 -0
  254. package/src/session/{inspector → facets}/ArtifactsTab.tsx +27 -5
  255. package/src/session/{inspector → facets}/ChangesTab.tsx +3 -3
  256. package/src/session/{inspector → facets}/SetupTab.tsx +13 -1
  257. package/src/session/{inspector → facets}/UsageTab.tsx +1 -1
  258. package/src/session/{inspector → facets}/__tests__/ArtifactsTab.test.tsx +35 -1
  259. package/src/session/facets/__tests__/SetupTab.test.tsx +44 -0
  260. package/src/session/{inspector → facets}/index.ts +0 -16
  261. package/src/session/index.ts +30 -13
  262. package/src/session/plan-document.ts +19 -0
  263. package/src/session/useOpenFileChange.ts +53 -0
  264. package/src/session/usePlanDraft.ts +109 -0
  265. package/src/session/useSessionConversation.ts +7 -0
  266. package/src/session/useSessionPageFlow.ts +1 -0
  267. package/src/session/useSessionPanel.ts +288 -0
  268. package/src/session/useSessionRailViews.tsx +200 -0
  269. package/src/workflow/WorkflowExecutionGraph.tsx +1 -1
  270. package/src/workflow/index.ts +1 -1
  271. package/src/workflow/useFollowExecution.ts +1 -1
  272. package/src/workspace/EditorTabs.tsx +145 -0
  273. package/src/workspace/ExplorerTree.tsx +247 -0
  274. package/src/workspace/FileViewer.tsx +516 -0
  275. package/src/workspace/WorkspaceContentSearch.tsx +478 -0
  276. package/src/workspace/WorkspaceContentSearcher.ts +70 -0
  277. package/src/workspace/WorkspaceFileLister.ts +11 -0
  278. package/src/workspace/WorkspaceFileReader.ts +57 -0
  279. package/src/workspace/WorkspaceFileSearch.tsx +429 -0
  280. package/src/workspace/WorkspaceSurface.tsx +781 -0
  281. package/src/workspace/__tests__/EditorTabs.test.tsx +100 -0
  282. package/src/workspace/__tests__/ExplorerTree.test.tsx +152 -0
  283. package/src/workspace/__tests__/FileViewer.test.tsx +421 -0
  284. package/src/workspace/__tests__/WorkspaceContentSearch.test.tsx +196 -0
  285. package/src/workspace/__tests__/WorkspaceFileSearch.test.tsx +206 -0
  286. package/src/workspace/__tests__/WorkspaceSurface.test.tsx +348 -0
  287. package/src/workspace/__tests__/findChangeForSelection.test.ts +132 -0
  288. package/src/workspace/__tests__/matchContentPreview.test.ts +55 -0
  289. package/src/workspace/__tests__/matchWorkspaceFiles.test.ts +104 -0
  290. package/src/workspace/__tests__/resolveWorkspaceFileSelection.test.ts +135 -0
  291. package/src/workspace/__tests__/useWorkspaceContentSearch.test.tsx +196 -0
  292. package/src/workspace/__tests__/useWorkspaceFileContent.test.tsx +190 -0
  293. package/src/workspace/__tests__/useWorkspaceFileSearch.test.tsx +162 -0
  294. package/src/workspace/findChangeForSelection.ts +61 -0
  295. package/src/workspace/index.ts +56 -2
  296. package/src/workspace/matchContentPreview.ts +51 -0
  297. package/src/workspace/matchWorkspaceFiles.ts +86 -0
  298. package/src/workspace/resolveWorkspaceFileSelection.ts +119 -0
  299. package/src/workspace/useWorkspaceContentSearch.ts +228 -0
  300. package/src/workspace/useWorkspaceFileContent.ts +129 -0
  301. package/src/workspace/useWorkspaceFileSearch.ts +184 -0
  302. package/src/workspace/useWorkspaceFiles.ts +27 -27
  303. package/src/workspace/workspaceListingCache.ts +93 -0
  304. package/styles.css +1 -1
  305. package/workflow/WorkflowExecutionGraph.js +1 -1
  306. package/workflow/WorkflowExecutionGraph.js.map +1 -1
  307. package/workflow/index.d.ts +1 -1
  308. package/workflow/index.d.ts.map +1 -1
  309. package/workflow/index.js +1 -1
  310. package/workflow/index.js.map +1 -1
  311. package/workflow/useFollowExecution.js +1 -1
  312. package/workflow/useFollowExecution.js.map +1 -1
  313. package/workspace/EditorTabs.d.ts +29 -0
  314. package/workspace/EditorTabs.d.ts.map +1 -0
  315. package/workspace/EditorTabs.js +66 -0
  316. package/workspace/EditorTabs.js.map +1 -0
  317. package/workspace/ExplorerTree.d.ts +32 -0
  318. package/workspace/ExplorerTree.d.ts.map +1 -0
  319. package/workspace/ExplorerTree.js +45 -0
  320. package/workspace/ExplorerTree.js.map +1 -0
  321. package/workspace/FileViewer.d.ts +93 -0
  322. package/workspace/FileViewer.d.ts.map +1 -0
  323. package/workspace/FileViewer.js +182 -0
  324. package/workspace/FileViewer.js.map +1 -0
  325. package/workspace/WorkspaceContentSearch.d.ts +41 -0
  326. package/workspace/WorkspaceContentSearch.d.ts.map +1 -0
  327. package/workspace/WorkspaceContentSearch.js +195 -0
  328. package/workspace/WorkspaceContentSearch.js.map +1 -0
  329. package/workspace/WorkspaceContentSearcher.d.ts +59 -0
  330. package/workspace/WorkspaceContentSearcher.d.ts.map +1 -0
  331. package/workspace/WorkspaceContentSearcher.js +7 -0
  332. package/workspace/WorkspaceContentSearcher.js.map +1 -0
  333. package/workspace/WorkspaceFileLister.d.ts +11 -0
  334. package/workspace/WorkspaceFileLister.d.ts.map +1 -1
  335. package/workspace/WorkspaceFileReader.d.ts +48 -0
  336. package/workspace/WorkspaceFileReader.d.ts.map +1 -0
  337. package/workspace/WorkspaceFileReader.js +11 -0
  338. package/workspace/WorkspaceFileReader.js.map +1 -0
  339. package/workspace/WorkspaceFileSearch.d.ts +32 -0
  340. package/workspace/WorkspaceFileSearch.d.ts.map +1 -0
  341. package/workspace/WorkspaceFileSearch.js +155 -0
  342. package/workspace/WorkspaceFileSearch.js.map +1 -0
  343. package/workspace/WorkspaceSurface.d.ts +151 -0
  344. package/workspace/WorkspaceSurface.d.ts.map +1 -0
  345. package/workspace/WorkspaceSurface.js +184 -0
  346. package/workspace/WorkspaceSurface.js.map +1 -0
  347. package/workspace/findChangeForSelection.d.ts +19 -0
  348. package/workspace/findChangeForSelection.d.ts.map +1 -0
  349. package/workspace/findChangeForSelection.js +46 -0
  350. package/workspace/findChangeForSelection.js.map +1 -0
  351. package/workspace/index.d.ts +24 -2
  352. package/workspace/index.d.ts.map +1 -1
  353. package/workspace/index.js +13 -1
  354. package/workspace/index.js.map +1 -1
  355. package/workspace/matchContentPreview.d.ts +21 -0
  356. package/workspace/matchContentPreview.d.ts.map +1 -0
  357. package/workspace/matchContentPreview.js +36 -0
  358. package/workspace/matchContentPreview.js.map +1 -0
  359. package/workspace/matchWorkspaceFiles.d.ts +30 -0
  360. package/workspace/matchWorkspaceFiles.d.ts.map +1 -0
  361. package/workspace/matchWorkspaceFiles.js +58 -0
  362. package/workspace/matchWorkspaceFiles.js.map +1 -0
  363. package/workspace/resolveWorkspaceFileSelection.d.ts +19 -0
  364. package/workspace/resolveWorkspaceFileSelection.d.ts.map +1 -0
  365. package/workspace/resolveWorkspaceFileSelection.js +115 -0
  366. package/workspace/resolveWorkspaceFileSelection.js.map +1 -0
  367. package/workspace/useWorkspaceContentSearch.d.ts +69 -0
  368. package/workspace/useWorkspaceContentSearch.d.ts.map +1 -0
  369. package/workspace/useWorkspaceContentSearch.js +154 -0
  370. package/workspace/useWorkspaceContentSearch.js.map +1 -0
  371. package/workspace/useWorkspaceFileContent.d.ts +73 -0
  372. package/workspace/useWorkspaceFileContent.d.ts.map +1 -0
  373. package/workspace/useWorkspaceFileContent.js +57 -0
  374. package/workspace/useWorkspaceFileContent.js.map +1 -0
  375. package/workspace/useWorkspaceFileSearch.d.ts +61 -0
  376. package/workspace/useWorkspaceFileSearch.d.ts.map +1 -0
  377. package/workspace/useWorkspaceFileSearch.js +119 -0
  378. package/workspace/useWorkspaceFileSearch.js.map +1 -0
  379. package/workspace/useWorkspaceFiles.d.ts +13 -5
  380. package/workspace/useWorkspaceFiles.d.ts.map +1 -1
  381. package/workspace/useWorkspaceFiles.js +18 -19
  382. package/workspace/useWorkspaceFiles.js.map +1 -1
  383. package/workspace/workspaceListingCache.d.ts +47 -0
  384. package/workspace/workspaceListingCache.d.ts.map +1 -0
  385. package/workspace/workspaceListingCache.js +55 -0
  386. package/workspace/workspaceListingCache.js.map +1 -0
  387. package/session/inspector/ArtifactsTab.d.ts.map +0 -1
  388. package/session/inspector/ArtifactsTab.js.map +0 -1
  389. package/session/inspector/ChangesTab.d.ts.map +0 -1
  390. package/session/inspector/ChangesTab.js.map +0 -1
  391. package/session/inspector/InspectTab.d.ts.map +0 -1
  392. package/session/inspector/InspectTab.js.map +0 -1
  393. package/session/inspector/SessionInspector.d.ts +0 -64
  394. package/session/inspector/SessionInspector.d.ts.map +0 -1
  395. package/session/inspector/SessionInspector.js +0 -53
  396. package/session/inspector/SessionInspector.js.map +0 -1
  397. package/session/inspector/SetupTab.d.ts.map +0 -1
  398. package/session/inspector/SetupTab.js.map +0 -1
  399. package/session/inspector/UsageTab.d.ts.map +0 -1
  400. package/session/inspector/UsageTab.js.map +0 -1
  401. package/session/inspector/WorkspaceTab.d.ts +0 -35
  402. package/session/inspector/WorkspaceTab.d.ts.map +0 -1
  403. package/session/inspector/WorkspaceTab.js +0 -75
  404. package/session/inspector/WorkspaceTab.js.map +0 -1
  405. package/session/inspector/index.d.ts.map +0 -1
  406. package/session/inspector/index.js.map +0 -1
  407. package/session/inspector/useSessionInspector.d.ts +0 -49
  408. package/session/inspector/useSessionInspector.d.ts.map +0 -1
  409. package/session/inspector/useSessionInspector.js +0 -122
  410. package/session/inspector/useSessionInspector.js.map +0 -1
  411. package/src/session/inspector/SessionInspector.tsx +0 -162
  412. package/src/session/inspector/WorkspaceTab.tsx +0 -245
  413. package/src/session/inspector/__tests__/useSessionInspector.test.ts +0 -251
  414. package/src/session/inspector/useSessionInspector.ts +0 -178
  415. package/src/workspace/WorkspaceEntryFiles.tsx +0 -196
  416. package/workflow/motion-preference.d.ts.map +0 -1
  417. package/workflow/motion-preference.js.map +0 -1
  418. package/workspace/WorkspaceEntryFiles.d.ts +0 -22
  419. package/workspace/WorkspaceEntryFiles.d.ts.map +0 -1
  420. package/workspace/WorkspaceEntryFiles.js +0 -58
  421. package/workspace/WorkspaceEntryFiles.js.map +0 -1
  422. /package/session/{inspector → facets}/InspectTab.d.ts +0 -0
  423. /package/session/{inspector → facets}/InspectTab.js +0 -0
  424. /package/src/{workflow → internal}/__tests__/motion-preference.test.ts +0 -0
  425. /package/src/session/{inspector → facets}/InspectTab.tsx +0 -0
  426. /package/src/session/{inspector → facets}/__tests__/ChangesTab.test.tsx +0 -0
@@ -9,14 +9,16 @@ import {
9
9
  AgentExecutionSpecSchema,
10
10
  ExecutionConfigSchema,
11
11
  } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/spec_pb";
12
+ import { ExecutionArtifactSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/artifact_pb";
12
13
  import { ApiResourceMetadataSchema } from "@stigmer/protos/ai/stigmer/commons/apiresource/metadata_pb";
13
14
  import { AgentMessageSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
14
15
  import {
16
+ ExecutionArtifactKind,
15
17
  ExecutionPhase,
16
18
  InteractionMode,
17
19
  MessageType,
18
20
  } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
19
- import { buildThreadItems } from "../MessageThread";
21
+ import { buildThreadItems, type ThreadItem } from "../MessageThread";
20
22
 
21
23
  function makeMessage(type: MessageType, content: string) {
22
24
  const msg = create(AgentMessageSchema);
@@ -25,12 +27,22 @@ function makeMessage(type: MessageType, content: string) {
25
27
  return msg;
26
28
  }
27
29
 
30
+ function makePlanArtifact(executionId: string) {
31
+ const artifact = create(ExecutionArtifactSchema);
32
+ artifact.name = "plan.md";
33
+ artifact.kind = ExecutionArtifactKind.FILE;
34
+ artifact.storageKey = `artifacts/${executionId}/plan.md`;
35
+ return artifact;
36
+ }
37
+
28
38
  function makeExecution(opts: {
29
39
  id: string;
30
40
  specMessage?: string;
31
41
  phase?: ExecutionPhase;
32
42
  interactionMode?: InteractionMode;
43
+ buildFromPlan?: boolean;
33
44
  messages?: ReturnType<typeof makeMessage>[];
45
+ withPlanArtifact?: boolean;
34
46
  }): AgentExecution {
35
47
  const exec = create(AgentExecutionSchema);
36
48
 
@@ -40,9 +52,14 @@ function makeExecution(opts: {
40
52
 
41
53
  const spec = create(AgentExecutionSpecSchema);
42
54
  spec.message = opts.specMessage ?? "test message";
43
- if (opts.interactionMode !== undefined) {
55
+ if (opts.interactionMode !== undefined || opts.buildFromPlan !== undefined) {
44
56
  const config = create(ExecutionConfigSchema);
45
- config.interactionMode = opts.interactionMode;
57
+ if (opts.interactionMode !== undefined) {
58
+ config.interactionMode = opts.interactionMode;
59
+ }
60
+ if (opts.buildFromPlan !== undefined) {
61
+ config.buildFromPlan = opts.buildFromPlan;
62
+ }
46
63
  spec.executionConfig = config;
47
64
  }
48
65
  exec.spec = spec;
@@ -52,11 +69,21 @@ function makeExecution(opts: {
52
69
  if (opts.messages) {
53
70
  status.messages = opts.messages;
54
71
  }
72
+ if (opts.withPlanArtifact) {
73
+ status.artifacts = [makePlanArtifact(opts.id)];
74
+ }
55
75
  exec.status = status;
56
76
 
57
77
  return exec;
58
78
  }
59
79
 
80
+ function planItems(items: readonly ThreadItem[]) {
81
+ return items.filter(
82
+ (i): i is Extract<ThreadItem, { kind: "plan-completion" }> =>
83
+ i.kind === "plan-completion",
84
+ );
85
+ }
86
+
60
87
  describe("buildThreadItems plan-completion variant", () => {
61
88
  it("emits plan-completion for COMPLETED Plan-mode execution", () => {
62
89
  const exec = makeExecution({
@@ -67,10 +94,11 @@ describe("buildThreadItems plan-completion variant", () => {
67
94
  });
68
95
 
69
96
  const items = buildThreadItems([exec], null, null, false, undefined);
70
- const planItem = items.find((i) => i.kind === "plan-completion");
97
+ const cards = planItems(items);
71
98
 
72
- expect(planItem).toBeDefined();
73
- expect(planItem!.key).toBe("plan-completion");
99
+ expect(cards).toHaveLength(1);
100
+ expect(cards[0].key).toBe("exec-plan-plan-completion");
101
+ expect(cards[0].isLatestPlan).toBe(true);
74
102
  });
75
103
 
76
104
  it("does NOT emit plan-completion for COMPLETED Agent-mode execution", () => {
@@ -82,9 +110,8 @@ describe("buildThreadItems plan-completion variant", () => {
82
110
  });
83
111
 
84
112
  const items = buildThreadItems([exec], null, null, false, undefined);
85
- const planItem = items.find((i) => i.kind === "plan-completion");
86
113
 
87
- expect(planItem).toBeUndefined();
114
+ expect(planItems(items)).toHaveLength(0);
88
115
  });
89
116
 
90
117
  it("does NOT emit plan-completion for COMPLETED execution with no interactionMode", () => {
@@ -95,9 +122,8 @@ describe("buildThreadItems plan-completion variant", () => {
95
122
  });
96
123
 
97
124
  const items = buildThreadItems([exec], null, null, false, undefined);
98
- const planItem = items.find((i) => i.kind === "plan-completion");
99
125
 
100
- expect(planItem).toBeUndefined();
126
+ expect(planItems(items)).toHaveLength(0);
101
127
  });
102
128
 
103
129
  it("does NOT emit plan-completion for FAILED Plan-mode execution", () => {
@@ -109,9 +135,8 @@ describe("buildThreadItems plan-completion variant", () => {
109
135
  });
110
136
 
111
137
  const items = buildThreadItems([exec], null, null, false, undefined);
112
- const planItem = items.find((i) => i.kind === "plan-completion");
113
138
 
114
- expect(planItem).toBeUndefined();
139
+ expect(planItems(items)).toHaveLength(0);
115
140
  });
116
141
 
117
142
  it("does NOT emit plan-completion for TERMINATED Plan-mode execution", () => {
@@ -122,9 +147,8 @@ describe("buildThreadItems plan-completion variant", () => {
122
147
  });
123
148
 
124
149
  const items = buildThreadItems([exec], null, null, false, undefined);
125
- const planItem = items.find((i) => i.kind === "plan-completion");
126
150
 
127
- expect(planItem).toBeUndefined();
151
+ expect(planItems(items)).toHaveLength(0);
128
152
  });
129
153
 
130
154
  it("does NOT emit plan-completion when a Plan execution is actively streaming", () => {
@@ -136,12 +160,11 @@ describe("buildThreadItems plan-completion variant", () => {
136
160
  });
137
161
 
138
162
  const items = buildThreadItems([], exec, null, false, undefined);
139
- const planItem = items.find((i) => i.kind === "plan-completion");
140
163
 
141
- expect(planItem).toBeUndefined();
164
+ expect(planItems(items)).toHaveLength(0);
142
165
  });
143
166
 
144
- it("emits plan-completion only for the last execution in a multi-execution thread", () => {
167
+ it("emits one card, attached to the plan segment, for [agent, plan]", () => {
145
168
  const agentExec = makeExecution({
146
169
  id: "exec-1",
147
170
  phase: ExecutionPhase.EXECUTION_COMPLETED,
@@ -163,17 +186,21 @@ describe("buildThreadItems plan-completion variant", () => {
163
186
  false,
164
187
  undefined,
165
188
  );
166
- const planItems = items.filter((i) => i.kind === "plan-completion");
189
+ const cards = planItems(items);
167
190
 
168
- expect(planItems).toHaveLength(1);
191
+ expect(cards).toHaveLength(1);
192
+ expect(cards[0].isLatestPlan).toBe(true);
169
193
  });
170
194
 
171
- it("does NOT emit plan-completion when last execution is Agent after a Plan", () => {
195
+ it("keeps the plan card (with build authority) after a later Agent turn", () => {
196
+ // The plan is still the thread's latest plan — an intervening Agent turn
197
+ // must not strip the review card or its Build action.
172
198
  const planExec = makeExecution({
173
199
  id: "exec-plan",
174
200
  phase: ExecutionPhase.EXECUTION_COMPLETED,
175
201
  interactionMode: InteractionMode.PLAN,
176
202
  messages: [makeMessage(MessageType.MESSAGE_AI, "Here is the plan")],
203
+ withPlanArtifact: true,
177
204
  });
178
205
 
179
206
  const agentExec = makeExecution({
@@ -190,8 +217,463 @@ describe("buildThreadItems plan-completion variant", () => {
190
217
  false,
191
218
  undefined,
192
219
  );
193
- const planItem = items.find((i) => i.kind === "plan-completion");
220
+ const cards = planItems(items);
221
+
222
+ expect(cards).toHaveLength(1);
223
+ expect(cards[0].key).toBe("exec-plan-plan-completion");
224
+ expect(cards[0].isLatestPlan).toBe(true);
225
+ // The card closes the plan's own segment — it precedes the agent turn.
226
+ const cardIndex = items.indexOf(cards[0]);
227
+ const agentSpecIndex = items.findIndex((i) => i.key === "exec-impl-spec");
228
+ expect(cardIndex).toBeLessThan(agentSpecIndex);
229
+ });
230
+
231
+ it("emits a card per plan; only the newest carries isLatestPlan", () => {
232
+ const planA = makeExecution({
233
+ id: "exec-plan-a",
234
+ phase: ExecutionPhase.EXECUTION_COMPLETED,
235
+ interactionMode: InteractionMode.PLAN,
236
+ messages: [makeMessage(MessageType.MESSAGE_AI, "Plan A")],
237
+ withPlanArtifact: true,
238
+ });
239
+ const planB = makeExecution({
240
+ id: "exec-plan-b",
241
+ phase: ExecutionPhase.EXECUTION_COMPLETED,
242
+ interactionMode: InteractionMode.PLAN,
243
+ messages: [makeMessage(MessageType.MESSAGE_AI, "Plan B")],
244
+ withPlanArtifact: true,
245
+ });
246
+
247
+ const items = buildThreadItems([planA, planB], null, null, false, undefined);
248
+ const cards = planItems(items);
249
+
250
+ expect(cards).toHaveLength(2);
251
+ expect(cards[0].key).toBe("exec-plan-a-plan-completion");
252
+ expect(cards[0].isLatestPlan).toBe(false);
253
+ expect(cards[1].key).toBe("exec-plan-b-plan-completion");
254
+ expect(cards[1].isLatestPlan).toBe(true);
255
+ });
256
+
257
+ it("skips a superseded plan that never published an artifact", () => {
258
+ // No artifact and no build authority → the card would be an empty shell.
259
+ const planA = makeExecution({
260
+ id: "exec-plan-a",
261
+ phase: ExecutionPhase.EXECUTION_COMPLETED,
262
+ interactionMode: InteractionMode.PLAN,
263
+ messages: [makeMessage(MessageType.MESSAGE_AI, "Plan A")],
264
+ withPlanArtifact: false,
265
+ });
266
+ const planB = makeExecution({
267
+ id: "exec-plan-b",
268
+ phase: ExecutionPhase.EXECUTION_COMPLETED,
269
+ interactionMode: InteractionMode.PLAN,
270
+ messages: [makeMessage(MessageType.MESSAGE_AI, "Plan B")],
271
+ withPlanArtifact: false,
272
+ });
273
+
274
+ const items = buildThreadItems([planA, planB], null, null, false, undefined);
275
+ const cards = planItems(items);
276
+
277
+ // Only the latest (artifact-less) plan renders — as the fallback CTA.
278
+ expect(cards).toHaveLength(1);
279
+ expect(cards[0].key).toBe("exec-plan-b-plan-completion");
280
+ expect(cards[0].isLatestPlan).toBe(true);
281
+ expect(cards[0].planArtifact).toBeUndefined();
282
+ });
283
+
284
+ it("carries the plan artifact on the item when published", () => {
285
+ const exec = makeExecution({
286
+ id: "exec-plan",
287
+ phase: ExecutionPhase.EXECUTION_COMPLETED,
288
+ interactionMode: InteractionMode.PLAN,
289
+ messages: [makeMessage(MessageType.MESSAGE_AI, "Here is the plan")],
290
+ withPlanArtifact: true,
291
+ });
292
+
293
+ const items = buildThreadItems([exec], null, null, false, undefined);
294
+ const card = planItems(items)[0];
295
+
296
+ expect(card.planArtifact?.name).toBe("plan.md");
297
+ });
298
+ });
299
+
300
+ describe("buildThreadItems plan-message collapse (artifact published)", () => {
301
+ function messageItems(items: readonly ThreadItem[]) {
302
+ return items.filter(
303
+ (i): i is Extract<ThreadItem, { kind: "message" }> =>
304
+ i.kind === "message",
305
+ );
306
+ }
307
+
308
+ it("removes the plan message from the thread — the card is the turn's sole plan representation", () => {
309
+ const exec = makeExecution({
310
+ id: "exec-plan",
311
+ phase: ExecutionPhase.EXECUTION_COMPLETED,
312
+ interactionMode: InteractionMode.PLAN,
313
+ messages: [
314
+ makeMessage(MessageType.MESSAGE_AI, "Let me look around first."),
315
+ makeMessage(MessageType.MESSAGE_AI, "# The Plan\n\n1. Do the thing"),
316
+ ],
317
+ withPlanArtifact: true,
318
+ });
319
+
320
+ const items = buildThreadItems([exec], null, null, false, undefined);
321
+
322
+ // The opening narration stays; the plan message (m1) is collapsed.
323
+ const keys = messageItems(items).map((i) => i.key);
324
+ expect(keys).toContain("exec-plan-m0");
325
+ expect(keys).not.toContain("exec-plan-m1");
326
+ expect(planItems(items)).toHaveLength(1);
327
+ });
328
+
329
+ it("lifts the plan's leading H1 onto the card as its title", () => {
330
+ const exec = makeExecution({
331
+ id: "exec-plan",
332
+ phase: ExecutionPhase.EXECUTION_COMPLETED,
333
+ interactionMode: InteractionMode.PLAN,
334
+ messages: [
335
+ makeMessage(MessageType.MESSAGE_AI, "# Refactor auth\n\nSteps…"),
336
+ ],
337
+ withPlanArtifact: true,
338
+ });
339
+
340
+ const items = buildThreadItems([exec], null, null, false, undefined);
341
+ expect(planItems(items)[0].planTitle).toBe("Refactor auth");
342
+ });
343
+
344
+ it("extracts the title through a plan-scoped enclosing fence (bare fence included)", () => {
345
+ // Same unwrap the document renderers apply, so card and plan tab agree.
346
+ const exec = makeExecution({
347
+ id: "exec-plan",
348
+ phase: ExecutionPhase.EXECUTION_COMPLETED,
349
+ interactionMode: InteractionMode.PLAN,
350
+ messages: [
351
+ makeMessage(MessageType.MESSAGE_AI, "```\n# Fenced Plan\n\nBody\n```"),
352
+ ],
353
+ withPlanArtifact: true,
354
+ });
355
+
356
+ const items = buildThreadItems([exec], null, null, false, undefined);
357
+ expect(planItems(items)[0].planTitle).toBe("Fenced Plan");
358
+ });
359
+
360
+ it("leaves the title undefined when the plan has no leading H1", () => {
361
+ const exec = makeExecution({
362
+ id: "exec-plan",
363
+ phase: ExecutionPhase.EXECUTION_COMPLETED,
364
+ interactionMode: InteractionMode.PLAN,
365
+ messages: [makeMessage(MessageType.MESSAGE_AI, "Just prose, no title")],
366
+ withPlanArtifact: true,
367
+ });
368
+
369
+ const items = buildThreadItems([exec], null, null, false, undefined);
370
+ expect(planItems(items)[0].planTitle).toBeUndefined();
371
+ });
372
+
373
+ it("never stamps isPlanDocument when the message collapsed into the card", () => {
374
+ const exec = makeExecution({
375
+ id: "exec-plan",
376
+ phase: ExecutionPhase.EXECUTION_COMPLETED,
377
+ interactionMode: InteractionMode.PLAN,
378
+ messages: [makeMessage(MessageType.MESSAGE_AI, "# The Plan")],
379
+ withPlanArtifact: true,
380
+ });
381
+
382
+ const items = buildThreadItems([exec], null, null, false, undefined);
383
+ expect(
384
+ messageItems(items).filter((i) => i.isPlanDocument === true),
385
+ ).toHaveLength(0);
386
+ });
387
+
388
+ it("keeps the plan message inline while streaming (no artifact yet)", () => {
389
+ const exec = makeExecution({
390
+ id: "exec-live",
391
+ phase: ExecutionPhase.EXECUTION_IN_PROGRESS,
392
+ interactionMode: InteractionMode.PLAN,
393
+ messages: [makeMessage(MessageType.MESSAGE_AI, "# Draft so far")],
394
+ });
395
+
396
+ const items = buildThreadItems([], exec, null, false, undefined);
397
+ expect(messageItems(items).map((i) => i.key)).toContain("exec-live-m0");
398
+ });
399
+ });
400
+
401
+ describe("buildThreadItems plan-document stamping (no-artifact fallback)", () => {
402
+ function planDocumentItems(items: readonly ThreadItem[]) {
403
+ return items.filter(
404
+ (i): i is Extract<ThreadItem, { kind: "message" }> =>
405
+ i.kind === "message" && i.isPlanDocument === true,
406
+ );
407
+ }
408
+
409
+ it("stamps the last AI message with content of a completed Plan execution", () => {
410
+ const exec = makeExecution({
411
+ id: "exec-plan",
412
+ phase: ExecutionPhase.EXECUTION_COMPLETED,
413
+ interactionMode: InteractionMode.PLAN,
414
+ messages: [
415
+ makeMessage(MessageType.MESSAGE_AI, "Let me look around first."),
416
+ makeMessage(MessageType.MESSAGE_AI, "# The Plan\n\n1. Do the thing"),
417
+ ],
418
+ });
419
+
420
+ const items = buildThreadItems([exec], null, null, false, undefined);
421
+ const docs = planDocumentItems(items);
422
+
423
+ expect(docs).toHaveLength(1);
424
+ expect(docs[0].key).toBe("exec-plan-m1");
425
+ });
426
+
427
+ it("does not stamp any message on an Agent-mode execution", () => {
428
+ const exec = makeExecution({
429
+ id: "exec-agent",
430
+ phase: ExecutionPhase.EXECUTION_COMPLETED,
431
+ interactionMode: InteractionMode.AGENT,
432
+ messages: [makeMessage(MessageType.MESSAGE_AI, "# Not a plan")],
433
+ });
434
+
435
+ const items = buildThreadItems([exec], null, null, false, undefined);
436
+
437
+ expect(planDocumentItems(items)).toHaveLength(0);
438
+ });
439
+
440
+ it("does not stamp while the Plan execution is still streaming", () => {
441
+ const exec = makeExecution({
442
+ id: "exec-live",
443
+ phase: ExecutionPhase.EXECUTION_IN_PROGRESS,
444
+ interactionMode: InteractionMode.PLAN,
445
+ messages: [makeMessage(MessageType.MESSAGE_AI, "Draft so far...")],
446
+ });
447
+
448
+ const items = buildThreadItems([], exec, null, false, undefined);
449
+
450
+ expect(planDocumentItems(items)).toHaveLength(0);
451
+ });
452
+
453
+ it("skips trailing empty AI messages when selecting the plan message", () => {
454
+ // Mirrors the runner's extractFinalPlanText: the plan is the last AI
455
+ // message WITH content, not merely the last AI message.
456
+ const exec = makeExecution({
457
+ id: "exec-plan",
458
+ phase: ExecutionPhase.EXECUTION_COMPLETED,
459
+ interactionMode: InteractionMode.PLAN,
460
+ messages: [
461
+ makeMessage(MessageType.MESSAGE_AI, "# The Plan"),
462
+ makeMessage(MessageType.MESSAGE_AI, " "),
463
+ ],
464
+ });
465
+
466
+ const items = buildThreadItems([exec], null, null, false, undefined);
467
+ const docs = planDocumentItems(items);
468
+
469
+ expect(docs).toHaveLength(1);
470
+ expect(docs[0].key).toBe("exec-plan-m0");
471
+ });
472
+ });
473
+
474
+ describe("buildThreadItems plan-writing (live streaming collapse)", () => {
475
+ function messageItems(items: readonly ThreadItem[]) {
476
+ return items.filter(
477
+ (i): i is Extract<ThreadItem, { kind: "message" }> =>
478
+ i.kind === "message",
479
+ );
480
+ }
481
+
482
+ function planWritingItems(items: readonly ThreadItem[]) {
483
+ return items.filter(
484
+ (i): i is Extract<ThreadItem, { kind: "plan-writing" }> =>
485
+ i.kind === "plan-writing",
486
+ );
487
+ }
488
+
489
+ /** buildThreadItems with the collapseStreamingPlan opt-in enabled. */
490
+ function buildWithLiveCollapse(
491
+ executions: AgentExecution[],
492
+ activeStreamExecution: AgentExecution | null,
493
+ ) {
494
+ return buildThreadItems(
495
+ executions,
496
+ activeStreamExecution,
497
+ null,
498
+ false,
499
+ undefined,
500
+ undefined,
501
+ false,
502
+ false,
503
+ false,
504
+ true,
505
+ );
506
+ }
507
+
508
+ const streamingPlanExec = () =>
509
+ makeExecution({
510
+ id: "exec-live",
511
+ phase: ExecutionPhase.EXECUTION_IN_PROGRESS,
512
+ interactionMode: InteractionMode.PLAN,
513
+ messages: [
514
+ makeMessage(MessageType.MESSAGE_AI, "Let me look around first."),
515
+ makeMessage(MessageType.MESSAGE_AI, "# Live Plan\n\nDraft body"),
516
+ ],
517
+ });
518
+
519
+ it("suppresses the streaming plan message and emits a plan-writing card", () => {
520
+ const items = buildWithLiveCollapse([], streamingPlanExec());
521
+
522
+ const keys = messageItems(items).map((i) => i.key);
523
+ expect(keys).toContain("exec-live-m0"); // narration stays
524
+ expect(keys).not.toContain("exec-live-m1"); // the live plan is collapsed
525
+
526
+ const cards = planWritingItems(items);
527
+ expect(cards).toHaveLength(1);
528
+ expect(cards[0].executionId).toBe("exec-live");
529
+ expect(cards[0].planTitle).toBe("Live Plan");
530
+ expect(cards[0].planSize).toBe("# Live Plan\n\nDraft body".length);
531
+ });
532
+
533
+ it("shares the completion item's key, so the card holds one thread position across the handoff", () => {
534
+ const liveItems = buildWithLiveCollapse([], streamingPlanExec());
535
+
536
+ const completedExec = makeExecution({
537
+ id: "exec-live",
538
+ phase: ExecutionPhase.EXECUTION_COMPLETED,
539
+ interactionMode: InteractionMode.PLAN,
540
+ messages: [makeMessage(MessageType.MESSAGE_AI, "# Live Plan\n\nDraft body")],
541
+ withPlanArtifact: true,
542
+ });
543
+ const settledItems = buildWithLiveCollapse([completedExec], null);
544
+
545
+ const liveCard = planWritingItems(liveItems)[0];
546
+ const settledCard = planItems(settledItems)[0];
547
+ expect(liveCard.key).toBe(settledCard.key);
548
+ });
549
+
550
+ it("keeps the plan streaming inline when the host has no plan surface (collapseStreamingPlan=false)", () => {
551
+ const items = buildThreadItems([], streamingPlanExec(), null, false, undefined);
552
+
553
+ expect(planWritingItems(items)).toHaveLength(0);
554
+ expect(messageItems(items).map((i) => i.key)).toContain("exec-live-m1");
555
+ });
556
+
557
+ it("never collapses a non-terminal Plan execution that is NOT the active stream", () => {
558
+ // The transient completed-list/active-stream skew must not sprout a live
559
+ // card for a stale copy of the execution.
560
+ const items = buildWithLiveCollapse([streamingPlanExec()], null);
561
+
562
+ expect(planWritingItems(items)).toHaveLength(0);
563
+ expect(messageItems(items).map((i) => i.key)).toContain("exec-live-m1");
564
+ });
565
+
566
+ it("does not collapse streaming narration (no leading H1)", () => {
567
+ const exec = makeExecution({
568
+ id: "exec-live",
569
+ phase: ExecutionPhase.EXECUTION_IN_PROGRESS,
570
+ interactionMode: InteractionMode.PLAN,
571
+ messages: [makeMessage(MessageType.MESSAGE_AI, "Exploring the repo…")],
572
+ });
573
+
574
+ const items = buildWithLiveCollapse([], exec);
575
+
576
+ expect(planWritingItems(items)).toHaveLength(0);
577
+ expect(messageItems(items).map((i) => i.key)).toContain("exec-live-m0");
578
+ });
579
+
580
+ it("does not collapse an Agent-mode streaming turn, even with a plan-shaped message", () => {
581
+ const exec = makeExecution({
582
+ id: "exec-agent",
583
+ phase: ExecutionPhase.EXECUTION_IN_PROGRESS,
584
+ interactionMode: InteractionMode.AGENT,
585
+ messages: [makeMessage(MessageType.MESSAGE_AI, "# Looks like a plan")],
586
+ });
587
+
588
+ const items = buildWithLiveCollapse([], exec);
589
+
590
+ expect(planWritingItems(items)).toHaveLength(0);
591
+ });
592
+
593
+ it("reverts to inline text when the turn stops without completing", () => {
594
+ // Cancelled mid-plan: no live card (turn is terminal), no completion card
595
+ // (not COMPLETED) — the partial plan renders inline, an honest record.
596
+ const exec = makeExecution({
597
+ id: "exec-live",
598
+ phase: ExecutionPhase.EXECUTION_CANCELLED,
599
+ interactionMode: InteractionMode.PLAN,
600
+ messages: [makeMessage(MessageType.MESSAGE_AI, "# Live Plan\n\nPartial")],
601
+ });
602
+
603
+ const items = buildWithLiveCollapse([exec], null);
604
+
605
+ expect(planWritingItems(items)).toHaveLength(0);
606
+ expect(planItems(items)).toHaveLength(0);
607
+ expect(messageItems(items).map((i) => i.key)).toContain("exec-live-m0");
608
+ });
609
+
610
+ it("extracts the live title through an unterminated plan fence", () => {
611
+ const exec = makeExecution({
612
+ id: "exec-live",
613
+ phase: ExecutionPhase.EXECUTION_IN_PROGRESS,
614
+ interactionMode: InteractionMode.PLAN,
615
+ messages: [
616
+ makeMessage(MessageType.MESSAGE_AI, "```markdown\n# Fenced Live Plan\n\nBody"),
617
+ ],
618
+ });
619
+
620
+ const items = buildWithLiveCollapse([], exec);
621
+ expect(planWritingItems(items)[0]?.planTitle).toBe("Fenced Live Plan");
622
+ });
623
+
624
+ it("leaves the title undefined while only the H1's first characters have streamed", () => {
625
+ const exec = makeExecution({
626
+ id: "exec-live",
627
+ phase: ExecutionPhase.EXECUTION_IN_PROGRESS,
628
+ interactionMode: InteractionMode.PLAN,
629
+ messages: [makeMessage(MessageType.MESSAGE_AI, "# Re")],
630
+ });
631
+
632
+ const items = buildWithLiveCollapse([], exec);
633
+ const cards = planWritingItems(items);
634
+ expect(cards).toHaveLength(1);
635
+ // extractLeadingH1 matches at end-of-input, so even a partial title
636
+ // resolves; "Re" is the growing title, not undefined.
637
+ expect(cards[0].planTitle).toBe("Re");
638
+ });
639
+ });
640
+
641
+ describe("buildThreadItems build-from-plan prompt", () => {
642
+ function promptItem(items: readonly ThreadItem[], execId: string) {
643
+ return items.find(
644
+ (i): i is Extract<ThreadItem, { kind: "message" }> =>
645
+ i.kind === "message" && i.key === `${execId}-spec`,
646
+ );
647
+ }
648
+
649
+ it("stamps isBuildFromPlan on the prompt of a build turn", () => {
650
+ const exec = makeExecution({
651
+ id: "exec-build",
652
+ specMessage: "Build from plan",
653
+ interactionMode: InteractionMode.AGENT,
654
+ buildFromPlan: true,
655
+ messages: [makeMessage(MessageType.MESSAGE_AI, "Implementing…")],
656
+ });
657
+
658
+ const items = buildThreadItems([exec], null, null, false, undefined);
659
+ const prompt = promptItem(items, "exec-build");
660
+
661
+ expect(prompt).toBeDefined();
662
+ expect(prompt!.isBuildFromPlan).toBe(true);
663
+ expect(prompt!.message.content).toBe("Build from plan");
664
+ });
665
+
666
+ it("leaves the flag unset on an ordinary prompt (renders as a normal bubble)", () => {
667
+ const exec = makeExecution({
668
+ id: "exec-normal",
669
+ interactionMode: InteractionMode.AGENT,
670
+ messages: [makeMessage(MessageType.MESSAGE_AI, "Done")],
671
+ });
672
+
673
+ const items = buildThreadItems([exec], null, null, false, undefined);
674
+ const prompt = promptItem(items, "exec-normal");
194
675
 
195
- expect(planItem).toBeUndefined();
676
+ expect(prompt).toBeDefined();
677
+ expect(prompt!.isBuildFromPlan).toBeUndefined();
196
678
  });
197
679
  });
@@ -194,6 +194,54 @@ describe("MessageEntry — Human messages", () => {
194
194
  });
195
195
  });
196
196
 
197
+ // ---------------------------------------------------------------------------
198
+ // Build-from-plan chip — the flagged human turn renders as a compact pill
199
+ // ---------------------------------------------------------------------------
200
+
201
+ describe("MessageEntry — Build-from-plan chip", () => {
202
+ it("renders a compact chip instead of a prompt bubble when flagged", () => {
203
+ const msg = makeMessage(MessageType.MESSAGE_HUMAN, "Build from plan");
204
+ const { container } = render(
205
+ <MessageEntry message={msg} isBuildFromPlan />,
206
+ );
207
+
208
+ const chip = queryArticle(container, "Build from plan");
209
+ expect(chip).not.toBeNull();
210
+ expect(chip!.textContent).toContain("Build from plan");
211
+ // The ordinary prompt bubble must not render alongside the chip.
212
+ expect(queryArticle(container, "User message")).toBeNull();
213
+ });
214
+
215
+ it("keeps the ordinary bubble when the flag is absent", () => {
216
+ const msg = makeMessage(MessageType.MESSAGE_HUMAN, "Build from plan");
217
+ const { container } = render(<MessageEntry message={msg} />);
218
+
219
+ expect(queryArticle(container, "User message")).not.toBeNull();
220
+ expect(queryArticle(container, "Build from plan")).toBeNull();
221
+ });
222
+
223
+ it("never offers Edit on the chip (the label is not user prose)", () => {
224
+ const msg = makeMessage(MessageType.MESSAGE_HUMAN, "Build from plan");
225
+ const { container } = render(
226
+ <MessageEntry message={msg} isBuildFromPlan onEdit={() => {}} />,
227
+ );
228
+
229
+ expect(
230
+ container.querySelector('[aria-label="Edit message"]'),
231
+ ).toBeNull();
232
+ });
233
+
234
+ it("ignores the flag on non-human messages", () => {
235
+ const msg = makeMessage(MessageType.MESSAGE_AI, "Implementing…");
236
+ const { container } = render(
237
+ <MessageEntry message={msg} isBuildFromPlan />,
238
+ );
239
+
240
+ expect(queryArticle(container, "AI response")).not.toBeNull();
241
+ expect(queryArticle(container, "Build from plan")).toBeNull();
242
+ });
243
+ });
244
+
197
245
  // ---------------------------------------------------------------------------
198
246
  // System messages — unchanged
199
247
  // ---------------------------------------------------------------------------