@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
@@ -1,14 +1,27 @@
1
1
  "use client";
2
2
 
3
- import { useCallback, useMemo, useRef, type ReactNode } from "react";
3
+ import { memo, useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from "react";
4
+ import { create } from "@bufbuild/protobuf";
4
5
  import { cn } from "@stigmer/theme";
5
- import { getUserMessage, type ResourceRef } from "@stigmer/sdk";
6
+ import { getUserMessage, type AttachmentInput, type ResourceRef } from "@stigmer/sdk";
7
+ import {
8
+ GetArtifactContentRequestSchema,
9
+ UploadAttachmentRequestSchema,
10
+ } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/io_pb";
6
11
  import type { UseGitHubConnectionReturn } from "../github/useGitHubConnection.js";
7
12
  import type { WorkspaceFileLister } from "../workspace/WorkspaceFileLister.js";
13
+ import type { WorkspaceFileReader } from "../workspace/WorkspaceFileReader.js";
14
+ import type { WorkspaceContentSearcher } from "../workspace/WorkspaceContentSearcher.js";
15
+ import { resolveWorkspaceFileSelection } from "../workspace/resolveWorkspaceFileSelection.js";
16
+ import {
17
+ WorkspaceSurface,
18
+ type SurfaceVirtualDocument,
19
+ } from "../workspace/WorkspaceSurface.js";
8
20
  import type { InteractionModeOption, SessionComposerHandle } from "../composer/index.js";
9
21
  import type { ApplyResourceResult } from "../library/useApplyResource.js";
10
22
  import { ResizableSplit } from "../internal/ResizableSplit.js";
11
23
  import { SelectionStore } from "../internal/store/selection-store.js";
24
+ import { useWorkspaceEditors, isVirtualEntryId } from "../internal/store/index.js";
12
25
  import { ThreadSelectionContext } from "../execution/ThreadSelectionContext.js";
13
26
  import { useSelectedThreadItem } from "../execution/useThreadSelection.js";
14
27
  import { MessageThread } from "../execution/MessageThread.js";
@@ -16,18 +29,98 @@ import { FileReviewDock } from "../execution/FileReviewDock.js";
16
29
  import { ThreadSkeleton } from "../execution/ThreadSkeleton.js";
17
30
  import { SessionComposer } from "../composer/index.js";
18
31
  import { SecretFlowErrorGuide, isSecretFlowError } from "../error/index.js";
32
+ import { useStigmer } from "../hooks.js";
33
+ import {
34
+ PLAN_ARTIFACT_NAME,
35
+ findLatestSessionPlan,
36
+ findPlanArtifact,
37
+ type SessionPlan,
38
+ } from "../library/detect-plan-artifact.js";
39
+ import { findStreamingPlan } from "../library/detect-streaming-plan.js";
19
40
  import { useSessionPageFlow } from "./useSessionPageFlow.js";
20
- import { SessionInspector } from "./inspector/SessionInspector.js";
41
+ import { useOpenFileChange } from "./useOpenFileChange.js";
42
+ import { usePlanDraft, planDraftKey, type PlanDraftController } from "./usePlanDraft.js";
43
+ import { PlanEditor } from "./PlanEditor.js";
44
+ import { PlanStreamingDocument } from "./PlanStreamingDocument.js";
45
+ import { PLAN_DOCUMENT_ENTRY_ID, PLAN_DOCUMENT_PATH } from "./plan-document.js";
46
+ import { useSessionRailViews } from "./useSessionRailViews.js";
47
+ import { useSessionPanel, type SessionPanelController } from "./useSessionPanel.js";
48
+ import { SessionPanelChip } from "./SessionPanelChip.js";
49
+ import { useSessionWriteBacks } from "./useSessionWriteBacks.js";
50
+ import { useSessionArtifacts } from "./useSessionArtifacts.js";
51
+ import type { SetupTabProps } from "./facets/SetupTab.js";
52
+ import { ExecutionPhase } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
21
53
  import type { RuntimeEnvProvider } from "./runtime-env.js";
22
54
  import type { SessionAudience } from "./audience.js";
23
55
 
24
56
  /**
25
- * Message submitted when the user implements a plan. References the published
26
- * `plan.md` explicitly so the agent acts on the durable artifact rather than
27
- * relying on re-reading its own prior chat message.
57
+ * Where the approved plan mounts in the implement execution's workspace
58
+ * the harnesses' standard attachment inputs directory, where the runner's
59
+ * implement-plan directive points the agent (shared/implement-plan-prompt.ts).
60
+ * Attaching at this exact path is what makes the build implement the document
61
+ * the user approved (including in-place edits), not a paraphrase from memory.
28
62
  */
29
- const IMPLEMENT_PLAN_MESSAGE =
30
- "Implement the plan above (saved as plan.md). Follow it step by step and make the changes it describes.";
63
+ const APPROVED_PLAN_MOUNT_PATH = `.stigmer/inputs/${PLAN_ARTIFACT_NAME}`;
64
+
65
+ /**
66
+ * The build turn's user message — a short human-readable label, NOT the
67
+ * implement instruction. The agent-facing instruction is runner-injected
68
+ * (keyed off `executionConfig.buildFromPlan`), so this text exists for
69
+ * humans: the thread renders the turn as a compact chip from the same flag,
70
+ * and surfaces without the chip treatment (the CLI, older clients) show
71
+ * this label as-is.
72
+ */
73
+ const BUILD_FROM_PLAN_MESSAGE = "Build from plan";
74
+
75
+ /**
76
+ * A plan the active execution is writing right now, resolved at the viewer
77
+ * level (from `findStreamingPlan`) so the panel's plan tab can render it
78
+ * live. The streaming sibling of {@link SessionPlan}: `executionId` keys the
79
+ * document (a new turn's stream resets the tab's view state) and forms the
80
+ * `<executionId>:streaming` identity for the plan-tab auto-open trigger.
81
+ */
82
+ interface SessionStreamingPlan {
83
+ /** ID of the execution writing the plan. */
84
+ readonly executionId: string;
85
+ /** The live plan text (fence-stripped display projection). */
86
+ readonly displayText: string;
87
+ }
88
+
89
+ /**
90
+ * Reads the published plan's full text for the build handoff. Refuses a
91
+ * truncated read (content RPC caps at 512 KB): attaching a partial plan would
92
+ * silently implement half a document — the caller falls back to
93
+ * conversation-history implement instead.
94
+ */
95
+ async function fetchPlanText(
96
+ stigmer: ReturnType<typeof useStigmer>,
97
+ plan: SessionPlan,
98
+ ): Promise<string> {
99
+ const result = await stigmer.agentExecution.getArtifactContent(
100
+ create(GetArtifactContentRequestSchema, {
101
+ executionId: plan.executionId,
102
+ storageKey: plan.artifact.storageKey,
103
+ }),
104
+ );
105
+ if (result.truncated) {
106
+ throw new Error("plan content truncated — not attaching a partial plan");
107
+ }
108
+ return new TextDecoder().decode(result.content);
109
+ }
110
+
111
+ /**
112
+ * Width and anchoring of the conversation reading column, shared by the
113
+ * thread and the composer/banners block so they can never drift apart.
114
+ *
115
+ * Centered (the classic chat reading view, matching Cursor's agent panel):
116
+ * with the session panel collapsed the column sits in the middle of the
117
+ * row; opening the panel narrows the chat pane below the column max, so
118
+ * the column simply fills it. The thread applies the same geometry via
119
+ * `contentColumn="center"`, which adds the item gutter; the composer/banner
120
+ * block's children carry their own edge padding, so only width and
121
+ * anchoring live here.
122
+ */
123
+ const CONVERSATION_COLUMN_CLASS = "mx-auto w-full max-w-3xl";
31
124
 
32
125
  /** Props for {@link SessionViewer}. */
33
126
  export interface SessionViewerProps {
@@ -70,6 +163,21 @@ export interface SessionViewerProps {
70
163
  * expandable file tree. (DD-004 capability injection, DD-011 opt-in.)
71
164
  */
72
165
  readonly workspaceFileLister?: WorkspaceFileLister;
166
+ /**
167
+ * Platform-injected content reader for the read-only file viewer. When
168
+ * provided, clicking a file in the Workspace tree opens it in a contextual
169
+ * "Viewer" tab. GitHub Contents/blob on web, Tauri fs on desktop; `undefined`
170
+ * degrades the viewer to an "unavailable here" state (DD-004, DD-011 opt-in).
171
+ */
172
+ readonly workspaceFileReader?: WorkspaceFileReader;
173
+ /**
174
+ * Platform-injected content (text) searcher. When provided, the session
175
+ * panel's Search pane gains a `Name | Text` toggle for full-text search
176
+ * across the workspace. Desktop injects a native ripgrep-backed searcher;
177
+ * web leaves it undefined (git content search needs a branch-accurate
178
+ * backend — DD-09), keeping Search filename-only there (DD-004, DD-011 opt-in).
179
+ */
180
+ readonly workspaceContentSearcher?: WorkspaceContentSearcher;
73
181
  /**
74
182
  * Supplies host-app environment variables for every follow-up
75
183
  * execution (e.g. short-lived credentials for MCP tools, minted as
@@ -92,11 +200,18 @@ export interface SessionViewerProps {
92
200
  */
93
201
  readonly audience?: SessionAudience;
94
202
  /**
95
- * Slot for host-injected header actions (e.g., Share button with
96
- * PermissionGate). Rendered in the top-right corner of the viewer.
97
- * Keeps the SDK organism unopinionated about Console auth (DD-004).
203
+ * Slot for host-injected header actions. Rendered in the top-right corner
204
+ * of the viewer, beside the panel chip. Keeps the SDK organism
205
+ * unopinionated about Console auth (DD-004).
98
206
  */
99
207
  readonly headerActions?: ReactNode;
208
+ /**
209
+ * Host-injected access management control (e.g. the Console's
210
+ * `ManageAccessButton` with its own permission gating). Rendered inside
211
+ * the panel's Config facet rather than the header — access is session
212
+ * configuration, not a moment-to-moment action (DD-004 slot injection).
213
+ */
214
+ readonly accessSlot?: ReactNode;
100
215
  /** Called after a resource is applied from the Artifacts tab. */
101
216
  readonly onApplied?: (result: ApplyResourceResult) => void;
102
217
  /** Additional CSS classes for the root container. */
@@ -110,10 +225,13 @@ export interface SessionViewerProps {
110
225
  * Owns `useSessionPageFlow` internally and composes:
111
226
  * - **Conversation column** (primary): `MessageThread` + error
112
227
  * banners + `SessionComposer`
113
- * - **SessionInspector** (secondary): tabbed panel with Workspace,
114
- * Config, Changes, Artifacts, Usage, and Inspect facets
228
+ * - **Session panel** (secondary): the unified `WorkspaceSurface` — explorer,
229
+ * search, read-only editor, and the session facets (Config, Changes,
230
+ * Artifacts, Usage, Inspect) as injected rail views. Collapsed by default
231
+ * to a top-right chip; opening a file (tree, search, or a transcript path)
232
+ * expands it.
115
233
  *
116
- * Connected via `ResizableSplit` with persisted width.
234
+ * Connected via `ResizableSplit` with persisted chat width.
117
235
  *
118
236
  * Thread selection is provided via `ThreadSelectionContext` (Phase 2
119
237
  * opt-in) for render-isolated per-item selection.
@@ -150,9 +268,12 @@ export function SessionViewer({
150
268
  enableLocal = false,
151
269
  onBrowseLocalFolder,
152
270
  workspaceFileLister,
271
+ workspaceFileReader,
272
+ workspaceContentSearcher,
153
273
  getRuntimeEnv,
154
274
  audience = "integrator",
155
275
  headerActions,
276
+ accessSlot,
156
277
  onApplied,
157
278
  className,
158
279
  }: SessionViewerProps) {
@@ -170,16 +291,179 @@ export function SessionViewer({
170
291
  }
171
292
  const selectionStore = selectionStoreRef.current;
172
293
 
294
+ const phase =
295
+ flow.displayExecution?.status?.phase ??
296
+ ExecutionPhase.EXECUTION_PHASE_UNSPECIFIED;
297
+ const hasPhase = phase !== ExecutionPhase.EXECUTION_PHASE_UNSPECIFIED;
298
+
299
+ // Facet arrival counts: rail badges inside the panel, the chip's dot-count
300
+ // while collapsed. Arrivals never auto-open the panel (badge-only signal).
301
+ const { hasWriteBacks, writeBackCount } = useSessionWriteBacks(flow.allExecutions);
302
+ const { artifactCount } = useSessionArtifacts(flow.allExecutions);
303
+
304
+ // The session's current plan (latest published plan.md across executions)
305
+ // and its viewer-owned draft. Ownership at this level is deliberate: the
306
+ // panel unmounts facet content on every view switch, so a facet-local
307
+ // draft would silently lose the user's edits (see usePlanDraft).
308
+ const stigmer = useStigmer();
309
+ const sessionPlan = useMemo(
310
+ () => findLatestSessionPlan(flow.allExecutions),
311
+ [flow.allExecutions],
312
+ );
313
+ const planDraft = usePlanDraft(sessionPlan);
314
+
315
+ // The plan the ACTIVE turn is writing right now (the H1 convention — see
316
+ // findStreamingPlan), resolved at this level for the same reason
317
+ // sessionPlan is: the panel's plan tab renders it. Recomputed per stream
318
+ // commit by design — this component already re-renders on every commit
319
+ // (it reads `flow`), and the scan touches only the last AI message.
320
+ const streamingPlan = useMemo<SessionStreamingPlan | undefined>(() => {
321
+ const activeExec = conv.activeStreamExecution;
322
+ const live = findStreamingPlan(activeExec);
323
+ const executionId = activeExec?.metadata?.id;
324
+ return live && executionId
325
+ ? { executionId, displayText: live.displayText }
326
+ : undefined;
327
+ }, [conv.activeStreamExecution]);
328
+ const [isBuildingFromPlan, setIsBuildingFromPlan] = useState(false);
329
+ const [planAttachFailed, setPlanAttachFailed] = useState(false);
330
+
331
+ // WHICH plan the panel's plan document tab shows. `null` means the session's
332
+ // current (latest) plan — the editable, buildable one; an execution id
333
+ // selects that turn's historical plan, rendered read-only. One tab, host-
334
+ // controlled content (Decision 3 in DD-16).
335
+ const [openPlanExecutionId, setOpenPlanExecutionId] = useState<string | null>(null);
336
+
337
+ // The plan tab's identity across both plan lifecycles: while a plan
338
+ // streams, the in-flight turn owns the tab (`<executionId>:streaming`);
339
+ // once published, the artifact identity (planDraftKey) takes over. Each
340
+ // transition is a NEW identity, so useSessionPanel's planKey trigger
341
+ // auto-opens the tab the moment a plan starts streaming AND (idempotently)
342
+ // when it settles into the published plan.
343
+ const currentPlanKey = sessionPlan ? planDraftKey(sessionPlan) : null;
344
+ const panelPlanKey = streamingPlan
345
+ ? `${streamingPlan.executionId}:streaming`
346
+ : currentPlanKey;
347
+
348
+ // A new plan identity (a plan starting to stream, the first published
349
+ // plan, or a refinement) supersedes any historical plan the user had
350
+ // opened: the tab snaps back to "latest" so the auto-open always surfaces
351
+ // the incoming plan. Adjust-state-during-render — own state only, the
352
+ // established idiom.
353
+ const [prevPlanKey, setPrevPlanKey] = useState(panelPlanKey);
354
+ if (panelPlanKey !== prevPlanKey) {
355
+ setPrevPlanKey(panelPlanKey);
356
+ setOpenPlanExecutionId(null);
357
+ }
358
+
359
+ // The unified-panel controller: owns the open-editor group store, the
360
+ // open/collapsed state, and the rail-view FSM. Shared with the launcher
361
+ // (DD-016). The editor store is owned here (never subscribed at this level)
362
+ // so opening/switching files re-renders only the panel subtree — the
363
+ // SessionPanelRegion subscribes, not the conversation column — preserving
364
+ // streaming render isolation (DD-009/DD-010, invariant 2).
365
+ const panel = useSessionPanel({
366
+ phase: hasPhase ? phase : null,
367
+ hasChanges: hasWriteBacks,
368
+ planKey: panelPlanKey,
369
+ });
370
+
173
371
  const handleBuildFromPlan = useCallback(() => {
174
372
  // Switch the picker to Agent (so subsequent turns stay in Agent) and submit
175
- // the implement message immediately through the composer's full pipeline.
373
+ // the implement message through the composer's full pipeline.
176
374
  // `interactionMode: "agent"` is passed explicitly to win the same-tick race
177
375
  // where the composer prop has not yet re-rendered from "plan".
178
376
  setInteractionMode("agent");
179
- composerRef.current?.submit(IMPLEMENT_PLAN_MESSAGE, {
180
- interactionMode: "agent",
181
- });
182
- }, [setInteractionMode]);
377
+ setPlanAttachFailed(false);
378
+
379
+ // `buildFromPlan` (not message text) is what tells the runner to inject
380
+ // the implement directive and the thread to render the compact chip —
381
+ // with or without the attachment (the runner picks the directive variant).
382
+ const submitImplementTurn = (attachments?: AttachmentInput[]) => {
383
+ composerRef.current?.submit(BUILD_FROM_PLAN_MESSAGE, {
384
+ interactionMode: "agent",
385
+ buildFromPlan: true,
386
+ attachments,
387
+ });
388
+ };
389
+
390
+ // No published artifact (the bare-CTA fallback card) — nothing to attach.
391
+ if (!sessionPlan) {
392
+ submitImplementTurn();
393
+ return;
394
+ }
395
+
396
+ // Deterministic handoff: upload the APPROVED plan (draft if edited, else
397
+ // the artifact text — one uniform path) and attach it to the implement
398
+ // execution. The published artifact itself stays immutable; the approved
399
+ // copy is a new input on the build turn (edit-as-input provenance).
400
+ setIsBuildingFromPlan(true);
401
+ void (async () => {
402
+ try {
403
+ const approvedText =
404
+ planDraft.readDraft() ?? (await fetchPlanText(stigmer, sessionPlan));
405
+ const response = await stigmer.agentExecution.uploadAttachment(
406
+ create(UploadAttachmentRequestSchema, {
407
+ filename: PLAN_ARTIFACT_NAME,
408
+ content: new TextEncoder().encode(approvedText),
409
+ contentType: "text/markdown",
410
+ }),
411
+ );
412
+ submitImplementTurn([
413
+ {
414
+ filename: PLAN_ARTIFACT_NAME,
415
+ storageKey: response.storageKey,
416
+ mountPath: APPROVED_PLAN_MOUNT_PATH,
417
+ contentType: "text/markdown",
418
+ },
419
+ ]);
420
+ } catch {
421
+ // Attachment plumbing must never block the build: implement from
422
+ // conversation history alone, and say so (non-blocking notice).
423
+ setPlanAttachFailed(true);
424
+ submitImplementTurn();
425
+ } finally {
426
+ setIsBuildingFromPlan(false);
427
+ }
428
+ })();
429
+ }, [setInteractionMode, sessionPlan, planDraft.readDraft, stigmer]);
430
+
431
+ // "Open plan" (a thread plan card, or the Artifacts facet's plan.md) → the
432
+ // panel's plan document tab. Opening the LATEST plan clears the historical
433
+ // selection (the tab shows the editable current plan); any other execution
434
+ // id opens that turn's plan read-only in the same tab.
435
+ const handleOpenPlan = useCallback(
436
+ (executionId: string) => {
437
+ setOpenPlanExecutionId(
438
+ sessionPlan && executionId === sessionPlan.executionId
439
+ ? null
440
+ : executionId,
441
+ );
442
+ panel.openPlanDocument();
443
+ },
444
+ [sessionPlan, panel.openPlanDocument],
445
+ );
446
+
447
+ // Open a transcript tool-call file path in the panel's read-only editor.
448
+ // Resolves the (possibly absolute / subdir-prefixed) path to a
449
+ // repo/root-relative selection the editor can fetch; on a definite hit it
450
+ // opens the file (expanding the panel) and returns `true` (handled),
451
+ // otherwise returns `false` so the path keeps its default copy / GitHub-link
452
+ // behavior. Writing the editor store re-renders only the panel subtree
453
+ // (which subscribes), never this streaming column.
454
+ const handleTranscriptFilePathClick = useCallback(
455
+ (path: string): boolean => {
456
+ const selection = resolveWorkspaceFileSelection(
457
+ path,
458
+ flow.workspace.entries,
459
+ flow.sandboxWorkspaceRoot,
460
+ );
461
+ if (!selection) return false;
462
+ panel.openFile(selection.entryId, selection.path);
463
+ return true;
464
+ },
465
+ [flow.workspace.entries, flow.sandboxWorkspaceRoot, panel.openFile],
466
+ );
183
467
 
184
468
  if (conv.isLoading) {
185
469
  return (
@@ -199,22 +483,36 @@ export function SessionViewer({
199
483
 
200
484
  return (
201
485
  <div className={cn("relative flex h-full w-full flex-col", className)}>
202
- {headerActions && (
203
- <div className="absolute top-2 right-6 z-10">
204
- {headerActions}
205
- </div>
206
- )}
486
+ {/* Top-right controls: host actions + the panel chip. The chip is the
487
+ panel's always-mounted toggle; while collapsed it carries only the
488
+ pending-item count. Execution status is never surfaced as header
489
+ chrome — the thread itself communicates run state. */}
490
+ <div className="absolute top-2 right-6 z-10 flex items-center gap-2">
491
+ {headerActions}
492
+ <SessionPanelChip
493
+ isOpen={panel.isOpen}
494
+ onToggle={panel.isOpen ? panel.closePanel : panel.openPanel}
495
+ badgeCount={writeBackCount + artifactCount}
496
+ />
497
+ </div>
207
498
 
208
499
  <ThreadSelectionContext.Provider value={selectionStore}>
500
+ {/* One layout, collapsed by default: chat fills the row until the panel
501
+ opens, then becomes the fixed narrow pane on the left while the
502
+ panel takes the flexible region. Collapsing goes through the
503
+ split's `collapsedPane` (CSS, not conditional structure), so the
504
+ conversation is always the same first child and an open/close never
505
+ remounts it (invariant 1). */}
209
506
  <ResizableSplit
210
- defaultSize={384}
211
- minSize={280}
212
- maxSize={600}
213
- storageKey="stgm-session-inspector-width"
214
- className={cn(
215
- "min-h-0 flex-1",
216
- "[&>*:nth-child(2)]:max-lg:hidden [&>*:nth-child(3)]:max-lg:hidden",
217
- )}
507
+ resizablePane="primary"
508
+ collapsedPane={panel.isOpen ? "none" : "secondary"}
509
+ defaultSize={420}
510
+ minSize={320}
511
+ maxSize={640}
512
+ storageKey="stgm-session-chat-width"
513
+ responsiveCollapse={panel.isOpen ? "primary" : "none"}
514
+ ariaLabel="Resize chat panel"
515
+ className="min-h-0 flex-1"
218
516
  primary={
219
517
  <ConversationColumn
220
518
  flow={flow}
@@ -229,23 +527,37 @@ export function SessionViewer({
229
527
  enableLocal={enableLocal}
230
528
  onBrowseLocalFolder={onBrowseLocalFolder}
231
529
  onBuildFromPlan={handleBuildFromPlan}
530
+ onOpenPlan={handleOpenPlan}
531
+ isBuildingFromPlan={isBuildingFromPlan}
532
+ planAttachFailed={planAttachFailed}
533
+ onDismissPlanAttachFailed={() => setPlanAttachFailed(false)}
534
+ onFilePathClick={handleTranscriptFilePathClick}
232
535
  isEndUser={isEndUser}
233
536
  />
234
537
  }
235
538
  secondary={
236
- <InspectorPanel
237
- flow={flow}
238
- org={org}
239
- selectionStore={selectionStore}
240
- onApplied={onApplied}
241
- onImplementPlan={handleBuildFromPlan}
242
- enableGitHub={enableGitHub}
243
- enableLocal={enableLocal}
244
- gitHubConnection={gitHubConnection}
245
- onBrowseLocalFolder={onBrowseLocalFolder}
246
- workspaceFileLister={workspaceFileLister}
247
- isEndUser={isEndUser}
248
- />
539
+ panel.isOpen ? (
540
+ <SessionPanelRegion
541
+ flow={flow}
542
+ org={org}
543
+ panel={panel}
544
+ accessSlot={accessSlot}
545
+ onApplied={onApplied}
546
+ onImplementPlan={handleBuildFromPlan}
547
+ implementPlanDisabled={!conv.canSendFollowUp || isBuildingFromPlan}
548
+ sessionPlan={sessionPlan}
549
+ streamingPlan={streamingPlan}
550
+ planDraft={planDraft}
551
+ openPlanExecutionId={openPlanExecutionId}
552
+ onOpenPlan={handleOpenPlan}
553
+ enableLocal={enableLocal}
554
+ onBrowseLocalFolder={onBrowseLocalFolder}
555
+ workspaceFileLister={workspaceFileLister}
556
+ workspaceFileReader={workspaceFileReader}
557
+ workspaceContentSearcher={workspaceContentSearcher}
558
+ isEndUser={isEndUser}
559
+ />
560
+ ) : null
249
561
  }
250
562
  />
251
563
  </ThreadSelectionContext.Provider>
@@ -270,10 +582,23 @@ interface ConversationColumnProps {
270
582
  readonly enableLocal: boolean;
271
583
  readonly onBrowseLocalFolder?: () => Promise<string | null>;
272
584
  readonly onBuildFromPlan: () => void;
585
+ /** Opens a plan in the panel's plan document tab (thread card "Open plan"). */
586
+ readonly onOpenPlan: (executionId: string) => void;
587
+ /** True while the approved plan is being uploaded ahead of the build turn. */
588
+ readonly isBuildingFromPlan: boolean;
589
+ /** True when the last build fell back to a message-only implement. */
590
+ readonly planAttachFailed: boolean;
591
+ readonly onDismissPlanAttachFailed: () => void;
592
+ /**
593
+ * Opens a transcript tool-call file path in the Viewer. Returns `true` when it
594
+ * resolved and opened the file (suppressing the link's default), `false` to
595
+ * let the path keep its copy / GitHub-link behavior.
596
+ */
597
+ readonly onFilePathClick: (path: string) => boolean;
273
598
  readonly isEndUser: boolean;
274
599
  }
275
600
 
276
- function ConversationColumn({
601
+ const ConversationColumn = memo(function ConversationColumn({
277
602
  flow,
278
603
  modelId,
279
604
  setModelId,
@@ -286,6 +611,11 @@ function ConversationColumn({
286
611
  enableLocal,
287
612
  onBrowseLocalFolder,
288
613
  onBuildFromPlan,
614
+ onOpenPlan,
615
+ isBuildingFromPlan,
616
+ planAttachFailed,
617
+ onDismissPlanAttachFailed,
618
+ onFilePathClick,
289
619
  isEndUser,
290
620
  }: ConversationColumnProps) {
291
621
  const { conv } = flow;
@@ -340,14 +670,20 @@ function ConversationColumn({
340
670
  showFileReviewRecords
341
671
  onEditMessage={conv.isStoppable ? handleEditMessage : undefined}
342
672
  workspaceEntries={conv.workspaceEntries}
673
+ onFilePathClick={onFilePathClick}
343
674
  sandboxWorkspaceRoot={flow.sandboxWorkspaceRoot}
344
675
  onBuildFromPlan={onBuildFromPlan}
676
+ onOpenPlan={onOpenPlan}
345
677
  org={org}
346
- planActionsDisabled={!conv.canSendFollowUp}
347
- centerContent
678
+ planActionsDisabled={!conv.canSendFollowUp || isBuildingFromPlan}
679
+ planBuildPending={isBuildingFromPlan}
680
+ contentColumn="center"
348
681
  className="flex-1"
349
682
  />
350
- <div className="mx-auto w-full max-w-3xl">
683
+ <div className={CONVERSATION_COLUMN_CLASS}>
684
+ {planAttachFailed && (
685
+ <PlanAttachFailedNotice onDismiss={onDismissPlanAttachFailed} />
686
+ )}
351
687
  {conv.isReconnecting && <ReconnectingIndicator />}
352
688
  {conv.connectTimedOut && (
353
689
  <ConnectTimedOutBanner onRetry={conv.reconnectStream} />
@@ -407,41 +743,165 @@ function ConversationColumn({
407
743
  </div>
408
744
  </div>
409
745
  );
410
- }
746
+ });
411
747
 
412
748
  // ---------------------------------------------------------------------------
413
- // Inspector panel (right/secondary) — reads selection from store
749
+ // Session panel region (right/secondary) — the unified panel's subtree
414
750
  // ---------------------------------------------------------------------------
415
751
 
416
- interface InspectorPanelProps {
752
+ interface SessionPanelRegionProps {
417
753
  readonly flow: ReturnType<typeof useSessionPageFlow>;
418
754
  readonly org: string;
419
- readonly selectionStore: SelectionStore;
755
+ /**
756
+ * The panel controller owned by `SessionViewer`. Its editor store is
757
+ * subscribed here (not at `SessionViewer`) so opening/switching files
758
+ * re-renders only this region, never the conversation column.
759
+ */
760
+ readonly panel: SessionPanelController;
761
+ /** Host access management control, surfaced in the Config facet. */
762
+ readonly accessSlot?: ReactNode;
420
763
  readonly onApplied?: (result: ApplyResourceResult) => void;
421
- /** Implement a plan from the Artifacts tab (same action as the thread card). */
764
+ /** Implement a plan (plan tab primary + Artifacts preview action). */
422
765
  readonly onImplementPlan?: () => void;
423
- readonly enableGitHub: boolean;
766
+ readonly implementPlanDisabled?: boolean;
767
+ /** The session's latest plan — the plan tab's editable content. */
768
+ readonly sessionPlan?: SessionPlan;
769
+ /**
770
+ * The plan the active turn is writing right now. While present it OWNS the
771
+ * plan tab: the tab renders the live document (`PlanStreamingDocument`)
772
+ * instead of a published plan — the in-flight plan is the newest
773
+ * deliverable, and the snap-back in `SessionViewer` has already cleared any
774
+ * historical selection when this identity appeared.
775
+ */
776
+ readonly streamingPlan?: SessionStreamingPlan;
777
+ /** Viewer-owned plan draft (survives panel collapse and view switches). */
778
+ readonly planDraft: PlanDraftController;
779
+ /**
780
+ * Which plan the plan document tab shows: `null` for the latest (editable),
781
+ * or an execution id whose historical plan renders read-only.
782
+ */
783
+ readonly openPlanExecutionId: string | null;
784
+ /** Opens a plan in the plan tab (routed to the Artifacts facet's plan.md). */
785
+ readonly onOpenPlan: (executionId: string) => void;
424
786
  readonly enableLocal: boolean;
425
- readonly gitHubConnection?: UseGitHubConnectionReturn;
426
787
  readonly onBrowseLocalFolder?: () => Promise<string | null>;
427
788
  readonly workspaceFileLister?: WorkspaceFileLister;
789
+ readonly workspaceFileReader?: WorkspaceFileReader;
790
+ readonly workspaceContentSearcher?: WorkspaceContentSearcher;
428
791
  readonly isEndUser: boolean;
429
792
  }
430
793
 
431
- function InspectorPanel({
794
+ function SessionPanelRegion({
432
795
  flow,
433
796
  org,
434
- selectionStore,
797
+ panel,
798
+ accessSlot,
435
799
  onApplied,
436
800
  onImplementPlan,
437
- enableGitHub,
801
+ implementPlanDisabled,
802
+ sessionPlan,
803
+ streamingPlan,
804
+ planDraft,
805
+ openPlanExecutionId,
806
+ onOpenPlan,
438
807
  enableLocal,
439
- gitHubConnection,
440
808
  onBrowseLocalFolder,
441
809
  workspaceFileLister,
810
+ workspaceFileReader,
811
+ workspaceContentSearcher,
442
812
  isEndUser,
443
- }: InspectorPanelProps) {
813
+ }: SessionPanelRegionProps) {
444
814
  const selectedItem = useSelectedThreadItem();
815
+ const { editors, activeKey, activeFile, reveal } = useWorkspaceEditors(
816
+ panel.editorsStore,
817
+ );
818
+
819
+ // Honor a pending jump-to-line reveal only while it targets the active editor
820
+ // (an EditorReveal is structurally a RevealTarget). Switching tabs changes
821
+ // activeKey, so a stale reveal for another tab is naturally ignored.
822
+ const activeReveal =
823
+ reveal && reveal.key === activeKey ? reveal : undefined;
824
+
825
+ // This region is the level that subscribes to thread selection; report it to
826
+ // the controller so an open panel can auto-surface the Inspect view. (A
827
+ // collapsed panel never sees — and deliberately never reacts to — selection.)
828
+ useEffect(() => {
829
+ panel.notifySelection(selectedItem);
830
+ }, [selectedItem, panel.notifySelection]);
831
+
832
+ // Correlate the active file with its session change for diff-as-default
833
+ // (DD-06 parity with the transcript's rendering of the same change). A
834
+ // virtual document (the plan tab) is not a workspace file — pass `null` so
835
+ // the hook's no-file guard skips the net-change fold entirely, rather than
836
+ // relying on the correlation harmlessly missing the sentinel id.
837
+ const openFileChange = useOpenFileChange(
838
+ activeFile && !isVirtualEntryId(activeFile.entryId) ? activeFile : null,
839
+ flow.allExecutions,
840
+ flow.workspace.entries,
841
+ flow.sandboxWorkspaceRoot,
842
+ );
843
+
844
+ // Resolve WHICH plan the plan document tab shows: the latest (editable,
845
+ // buildable, draft-backed) unless a historical execution id was opened —
846
+ // that plan renders read-only. A stale id (execution gone, or its plan
847
+ // artifact missing) degrades to the latest rather than an empty tab.
848
+ const openPlan = useMemo<SessionPlan | undefined>(() => {
849
+ if (
850
+ openPlanExecutionId === null ||
851
+ openPlanExecutionId === sessionPlan?.executionId
852
+ ) {
853
+ return sessionPlan;
854
+ }
855
+ const execution = flow.allExecutions.find(
856
+ (e) => e.metadata?.id === openPlanExecutionId,
857
+ );
858
+ const artifact = findPlanArtifact(execution);
859
+ return artifact
860
+ ? { executionId: openPlanExecutionId, artifact }
861
+ : sessionPlan;
862
+ }, [openPlanExecutionId, sessionPlan, flow.allExecutions]);
863
+
864
+ // The plan document tab (SurfaceVirtualDocument): the panel's editor-area
865
+ // rendering of the session's plan. Three states, in precedence order:
866
+ // a STREAMING plan owns the tab (live document, no actions); otherwise the
867
+ // resolved `openPlan` renders in the editor (keyed by plan identity so
868
+ // switching plans resets view state cleanly); otherwise an honest empty
869
+ // notice (DD-006) — reachable only when a streaming plan auto-opened the
870
+ // tab and its turn then ended without publishing, with no earlier plan to
871
+ // fall back to.
872
+ const openPlanIsLatest = openPlan?.executionId === sessionPlan?.executionId;
873
+ const virtualDocuments = useMemo<readonly SurfaceVirtualDocument[]>(() => {
874
+ return [
875
+ {
876
+ entryId: PLAN_DOCUMENT_ENTRY_ID,
877
+ path: PLAN_DOCUMENT_PATH,
878
+ content: streamingPlan ? (
879
+ <PlanStreamingDocument
880
+ key={`streaming-${streamingPlan.executionId}`}
881
+ displayText={streamingPlan.displayText}
882
+ />
883
+ ) : openPlan ? (
884
+ <PlanEditor
885
+ key={planDraftKey(openPlan)}
886
+ plan={openPlan}
887
+ draft={openPlanIsLatest ? planDraft : undefined}
888
+ onBuildFromPlan={openPlanIsLatest ? onImplementPlan : undefined}
889
+ buildDisabled={implementPlanDisabled}
890
+ readOnly={!openPlanIsLatest}
891
+ />
892
+ ) : (
893
+ <PlanUnavailableNotice />
894
+ ),
895
+ },
896
+ ];
897
+ }, [
898
+ streamingPlan,
899
+ openPlan,
900
+ openPlanIsLatest,
901
+ planDraft,
902
+ onImplementPlan,
903
+ implementPlanDisabled,
904
+ ]);
445
905
 
446
906
  const handleRemoveAgent = useCallback(() => {
447
907
  flow.setAgentRef(null);
@@ -470,7 +930,7 @@ function InspectorPanel({
470
930
  [flow.skillRefs, flow.setSkillRefs],
471
931
  );
472
932
 
473
- const sessionConfig = useMemo(
933
+ const sessionConfig = useMemo<SetupTabProps>(
474
934
  () => ({
475
935
  agentRef: flow.agentRef,
476
936
  isDefaultAgent: flow.isDefaultAgent,
@@ -480,8 +940,8 @@ function InspectorPanel({
480
940
  harness: flow.harness,
481
941
  executionTarget: flow.executionTarget,
482
942
  modelId: flow.model[0],
483
- // End users see the configuration but cannot strip it — the Setup
484
- // tab renders read-only without mutation callbacks (DD-011).
943
+ // End users see the configuration but cannot strip it — the Config
944
+ // facet renders read-only without mutation callbacks (DD-011).
485
945
  mutations: isEndUser
486
946
  ? undefined
487
947
  : {
@@ -489,42 +949,59 @@ function InspectorPanel({
489
949
  onRemoveMcp: handleRemoveMcp,
490
950
  onRemoveSkill: handleRemoveSkill,
491
951
  },
952
+ accessSlot,
492
953
  }),
493
954
  [
494
955
  flow.agentRef, flow.isDefaultAgent, flow.mcpServerUsages, flow.skillRefs,
495
956
  flow.sessionVariables, flow.harness, flow.executionTarget, flow.model,
496
957
  isEndUser, handleRemoveAgent, handleRemoveMcp, handleRemoveSkill,
958
+ accessSlot,
497
959
  ],
498
960
  );
499
961
 
500
- const workspaceConfig = useMemo(
501
- () => ({
502
- actions: {
503
- workspace: flow.workspace,
504
- enableGitHub,
505
- enableLocal,
506
- gitHubConnection,
507
- onBrowseLocalFolder,
508
- workspaceFileLister,
509
- },
510
- }),
511
- [flow.workspace, enableGitHub, enableLocal, gitHubConnection, onBrowseLocalFolder, workspaceFileLister],
512
- );
962
+ // The session facets (Config / Changes / Artifacts / Usage / Inspect) as
963
+ // injected rail views — the full inspector feature set inside one panel.
964
+ const railViews = useSessionRailViews({
965
+ allExecutions: flow.allExecutions,
966
+ org,
967
+ sessionConfig,
968
+ selectedItem,
969
+ onApplied,
970
+ onImplementPlan,
971
+ onOpenPlan,
972
+ });
973
+
974
+ // Explorer-footer folder attach (desktop only — needs the native picker).
975
+ // Same wiring the retired Workspace tab used.
976
+ const canAddLocalFolder = enableLocal && !!onBrowseLocalFolder;
977
+ const handleAddLocalFolder = useCallback(async () => {
978
+ const path = await onBrowseLocalFolder?.();
979
+ if (path) flow.workspace.addLocalPath(path);
980
+ }, [onBrowseLocalFolder, flow.workspace.addLocalPath]);
513
981
 
514
982
  return (
515
- <aside className="flex h-full flex-col overflow-hidden">
516
- <SessionInspector
517
- displayExecution={flow.displayExecution}
518
- allExecutions={flow.allExecutions}
519
- org={org}
520
- selectedItem={selectedItem}
521
- onApplied={onApplied}
522
- onImplementPlan={onImplementPlan}
523
- sessionConfig={sessionConfig}
524
- workspaceConfig={workspaceConfig}
525
- className="min-h-0 flex-1"
526
- />
527
- </aside>
983
+ <WorkspaceSurface
984
+ entries={flow.workspace.entries}
985
+ lister={workspaceFileLister}
986
+ reader={workspaceFileReader}
987
+ searcher={workspaceContentSearcher}
988
+ view={panel.view}
989
+ onViewChange={panel.setView}
990
+ extraViews={railViews}
991
+ virtualDocuments={virtualDocuments}
992
+ onRemoveEntry={flow.workspace.remove}
993
+ onAddLocalFolder={canAddLocalFolder ? handleAddLocalFolder : undefined}
994
+ editors={editors}
995
+ selectedFile={activeFile}
996
+ onOpenFile={panel.openFile}
997
+ onActivateEditor={panel.activateEditor}
998
+ onPinEditor={panel.pinEditor}
999
+ onCloseEditor={panel.closeEditor}
1000
+ onCollapse={panel.closePanel}
1001
+ change={openFileChange ?? undefined}
1002
+ reveal={activeReveal}
1003
+ className="h-full"
1004
+ />
528
1005
  );
529
1006
  }
530
1007
 
@@ -593,6 +1070,56 @@ function AutoApproveIndicator({ onTurnOff }: { onTurnOff: () => void }) {
593
1070
  );
594
1071
  }
595
1072
 
1073
+ /**
1074
+ * The plan document tab's empty state — reachable only when a streaming plan
1075
+ * auto-opened the tab and its turn then ended without publishing (stopped or
1076
+ * failed) while the session has no earlier published plan to fall back to.
1077
+ * The partial plan reverts to the conversation (the thread un-collapses it),
1078
+ * so the notice points there. Never a blank pane (DD-006).
1079
+ */
1080
+ function PlanUnavailableNotice() {
1081
+ return (
1082
+ <div
1083
+ role="status"
1084
+ className="mx-auto flex w-full max-w-3xl flex-col items-center gap-1 px-4 py-8 text-center"
1085
+ >
1086
+ <p className="text-xs font-medium text-foreground">
1087
+ This turn ended before a plan was completed.
1088
+ </p>
1089
+ <p className="text-xs text-muted-foreground">
1090
+ Any partial plan text remains in the conversation.
1091
+ </p>
1092
+ </div>
1093
+ );
1094
+ }
1095
+
1096
+ /**
1097
+ * Non-blocking notice for a build that fell back to message-only implement
1098
+ * (the approved plan.md could not be uploaded/attached). The build itself
1099
+ * proceeded — the agent follows the plan from conversation history — so this
1100
+ * is a status, not an alert, and it is dismissible.
1101
+ */
1102
+ function PlanAttachFailedNotice({ onDismiss }: { onDismiss: () => void }) {
1103
+ return (
1104
+ <div
1105
+ role="status"
1106
+ className="flex items-center gap-2 border-t border-border-muted px-4 py-1.5 text-xs text-muted-foreground"
1107
+ >
1108
+ <span className="min-w-0 flex-1 truncate">
1109
+ Couldn&rsquo;t attach plan.md — the agent will follow the plan from the
1110
+ conversation instead.
1111
+ </span>
1112
+ <button
1113
+ type="button"
1114
+ onClick={onDismiss}
1115
+ className="shrink-0 rounded font-medium text-foreground underline-offset-2 hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
1116
+ >
1117
+ Dismiss
1118
+ </button>
1119
+ </div>
1120
+ );
1121
+ }
1122
+
596
1123
  function SendErrorBanner({ error }: { error: Error }) {
597
1124
  if (isSecretFlowError(error)) {
598
1125
  return <SecretFlowErrorGuide error={error} className="mx-4 my-2" />;