@stigmer/react 3.1.3 → 3.1.5

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 (248) hide show
  1. package/composer/SessionComposer.d.ts +31 -0
  2. package/composer/SessionComposer.d.ts.map +1 -1
  3. package/composer/SessionComposer.js +12 -4
  4. package/composer/SessionComposer.js.map +1 -1
  5. package/execution/ArtifactApplyButton.d.ts +25 -0
  6. package/execution/ArtifactApplyButton.d.ts.map +1 -0
  7. package/execution/ArtifactApplyButton.js +21 -0
  8. package/execution/ArtifactApplyButton.js.map +1 -0
  9. package/execution/ArtifactContentBody.d.ts +36 -0
  10. package/execution/ArtifactContentBody.d.ts.map +1 -0
  11. package/execution/ArtifactContentBody.js +62 -0
  12. package/execution/ArtifactContentBody.js.map +1 -0
  13. package/execution/ArtifactDocument.d.ts +48 -0
  14. package/execution/ArtifactDocument.d.ts.map +1 -0
  15. package/execution/ArtifactDocument.js +89 -0
  16. package/execution/ArtifactDocument.js.map +1 -0
  17. package/execution/ArtifactPreviewModal.d.ts +4 -4
  18. package/execution/ArtifactPreviewModal.d.ts.map +1 -1
  19. package/execution/ArtifactPreviewModal.js +15 -142
  20. package/execution/ArtifactPreviewModal.js.map +1 -1
  21. package/execution/ArtifactRow.d.ts +49 -0
  22. package/execution/ArtifactRow.d.ts.map +1 -0
  23. package/execution/ArtifactRow.js +56 -0
  24. package/execution/ArtifactRow.js.map +1 -0
  25. package/execution/ArtifactsWidget.d.ts +6 -9
  26. package/execution/ArtifactsWidget.d.ts.map +1 -1
  27. package/execution/ArtifactsWidget.js +15 -19
  28. package/execution/ArtifactsWidget.js.map +1 -1
  29. package/execution/FileReviewContext.d.ts +15 -1
  30. package/execution/FileReviewContext.d.ts.map +1 -1
  31. package/execution/FileReviewContext.js +20 -1
  32. package/execution/FileReviewContext.js.map +1 -1
  33. package/execution/MessageThread.d.ts +65 -2
  34. package/execution/MessageThread.d.ts.map +1 -1
  35. package/execution/MessageThread.js +42 -23
  36. package/execution/MessageThread.js.map +1 -1
  37. package/execution/TodoCard.d.ts +8 -0
  38. package/execution/TodoCard.d.ts.map +1 -1
  39. package/execution/TodoCard.js +6 -4
  40. package/execution/TodoCard.js.map +1 -1
  41. package/execution/TodoList.d.ts +20 -1
  42. package/execution/TodoList.d.ts.map +1 -1
  43. package/execution/TodoList.js +9 -6
  44. package/execution/TodoList.js.map +1 -1
  45. package/execution/ToolCallDetail.d.ts +3 -2
  46. package/execution/ToolCallDetail.d.ts.map +1 -1
  47. package/execution/ToolCallDetail.js +49 -16
  48. package/execution/ToolCallDetail.js.map +1 -1
  49. package/execution/file-review-status.d.ts +15 -0
  50. package/execution/file-review-status.d.ts.map +1 -1
  51. package/execution/file-review-status.js +22 -0
  52. package/execution/file-review-status.js.map +1 -1
  53. package/execution/index.d.ts +13 -7
  54. package/execution/index.d.ts.map +1 -1
  55. package/execution/index.js +7 -4
  56. package/execution/index.js.map +1 -1
  57. package/execution/useArtifactContent.d.ts +6 -1
  58. package/execution/useArtifactContent.d.ts.map +1 -1
  59. package/execution/useArtifactContent.js +7 -2
  60. package/execution/useArtifactContent.js.map +1 -1
  61. package/execution/useArtifactInspection.d.ts +85 -0
  62. package/execution/useArtifactInspection.d.ts.map +1 -0
  63. package/execution/useArtifactInspection.js +143 -0
  64. package/execution/useArtifactInspection.js.map +1 -0
  65. package/execution/useCreateAgentExecution.d.ts +11 -0
  66. package/execution/useCreateAgentExecution.d.ts.map +1 -1
  67. package/execution/useCreateAgentExecution.js +1 -0
  68. package/execution/useCreateAgentExecution.js.map +1 -1
  69. package/github/useGitHubFileReader.d.ts +4 -2
  70. package/github/useGitHubFileReader.d.ts.map +1 -1
  71. package/github/useGitHubFileReader.js +16 -3
  72. package/github/useGitHubFileReader.js.map +1 -1
  73. package/github/useGitHubTreeLister.d.ts +2 -1
  74. package/github/useGitHubTreeLister.d.ts.map +1 -1
  75. package/github/useGitHubTreeLister.js +7 -3
  76. package/github/useGitHubTreeLister.js.map +1 -1
  77. package/index.d.ts +4 -4
  78. package/index.d.ts.map +1 -1
  79. package/index.js +3 -3
  80. package/index.js.map +1 -1
  81. package/internal/MermaidDiagram.d.ts +47 -0
  82. package/internal/MermaidDiagram.d.ts.map +1 -0
  83. package/internal/MermaidDiagram.js +118 -0
  84. package/internal/MermaidDiagram.js.map +1 -0
  85. package/internal/VirtualizedThread.d.ts +3 -2
  86. package/internal/VirtualizedThread.d.ts.map +1 -1
  87. package/internal/VirtualizedThread.js +3 -2
  88. package/internal/VirtualizedThread.js.map +1 -1
  89. package/internal/markdown-components.d.ts.map +1 -1
  90. package/internal/markdown-components.js +34 -0
  91. package/internal/markdown-components.js.map +1 -1
  92. package/internal/mermaid-loader.d.ts +26 -0
  93. package/internal/mermaid-loader.d.ts.map +1 -0
  94. package/internal/mermaid-loader.js +34 -0
  95. package/internal/mermaid-loader.js.map +1 -0
  96. package/internal/mermaid-theme-css.d.ts +41 -0
  97. package/internal/mermaid-theme-css.d.ts.map +1 -0
  98. package/internal/mermaid-theme-css.js +179 -0
  99. package/internal/mermaid-theme-css.js.map +1 -0
  100. package/internal/slug.d.ts +9 -5
  101. package/internal/slug.d.ts.map +1 -1
  102. package/internal/slug.js +10 -6
  103. package/internal/slug.js.map +1 -1
  104. package/package.json +5 -4
  105. package/session/SessionViewer.d.ts +11 -1
  106. package/session/SessionViewer.d.ts.map +1 -1
  107. package/session/SessionViewer.js +101 -21
  108. package/session/SessionViewer.js.map +1 -1
  109. package/session/artifact-document.d.ts +13 -0
  110. package/session/artifact-document.d.ts.map +1 -0
  111. package/session/artifact-document.js +22 -0
  112. package/session/artifact-document.js.map +1 -0
  113. package/session/facets/ArtifactsTab.d.ts +30 -4
  114. package/session/facets/ArtifactsTab.d.ts.map +1 -1
  115. package/session/facets/ArtifactsTab.js +32 -10
  116. package/session/facets/ArtifactsTab.js.map +1 -1
  117. package/session/index.d.ts +3 -1
  118. package/session/index.d.ts.map +1 -1
  119. package/session/index.js +5 -1
  120. package/session/index.js.map +1 -1
  121. package/session/useSessionArtifacts.d.ts +17 -0
  122. package/session/useSessionArtifacts.d.ts.map +1 -1
  123. package/session/useSessionArtifacts.js +16 -5
  124. package/session/useSessionArtifacts.js.map +1 -1
  125. package/session/useSessionConversation.d.ts +17 -1
  126. package/session/useSessionConversation.d.ts.map +1 -1
  127. package/session/useSessionConversation.js +32 -4
  128. package/session/useSessionConversation.js.map +1 -1
  129. package/session/useSessionPageFlow.d.ts.map +1 -1
  130. package/session/useSessionPageFlow.js +1 -0
  131. package/session/useSessionPageFlow.js.map +1 -1
  132. package/session/useSessionPanel.d.ts +20 -0
  133. package/session/useSessionPanel.d.ts.map +1 -1
  134. package/session/useSessionPanel.js +13 -0
  135. package/session/useSessionPanel.js.map +1 -1
  136. package/session/useSessionRailViews.d.ts +13 -1
  137. package/session/useSessionRailViews.d.ts.map +1 -1
  138. package/session/useSessionRailViews.js +5 -3
  139. package/session/useSessionRailViews.js.map +1 -1
  140. package/session/useWorkspaceReadRefs.d.ts +45 -0
  141. package/session/useWorkspaceReadRefs.d.ts.map +1 -0
  142. package/session/useWorkspaceReadRefs.js +104 -0
  143. package/session/useWorkspaceReadRefs.js.map +1 -0
  144. package/src/composer/SessionComposer.tsx +69 -1
  145. package/src/composer/__tests__/SessionComposer-editing.test.tsx +128 -0
  146. package/src/execution/ArtifactApplyButton.tsx +79 -0
  147. package/src/execution/ArtifactContentBody.tsx +255 -0
  148. package/src/execution/ArtifactDocument.tsx +344 -0
  149. package/src/execution/ArtifactPreviewModal.tsx +39 -405
  150. package/src/execution/ArtifactRow.tsx +165 -0
  151. package/src/execution/ArtifactsWidget.tsx +24 -33
  152. package/src/execution/FileReviewContext.ts +27 -1
  153. package/src/execution/MessageThread.tsx +110 -22
  154. package/src/execution/TodoCard.tsx +20 -4
  155. package/src/execution/TodoList.tsx +26 -4
  156. package/src/execution/ToolCallDetail.tsx +69 -16
  157. package/src/execution/__tests__/ArtifactContentBody.test.tsx +131 -0
  158. package/src/execution/__tests__/ArtifactDocument.test.tsx +100 -0
  159. package/src/execution/__tests__/ArtifactRow.test.tsx +139 -0
  160. package/src/execution/__tests__/ArtifactsWidget.test.tsx +78 -0
  161. package/src/execution/__tests__/ToolCallDetail.test.tsx +188 -2
  162. package/src/execution/__tests__/file-review-status.test.ts +47 -0
  163. package/src/execution/__tests__/message-entry.test.tsx +50 -2
  164. package/src/execution/__tests__/message-thread-slots.test.tsx +463 -0
  165. package/src/execution/__tests__/useArtifactInspection.test.tsx +151 -0
  166. package/src/execution/__tests__/useCreateAgentExecution.test.tsx +35 -0
  167. package/src/execution/file-review-status.ts +24 -0
  168. package/src/execution/index.ts +27 -5
  169. package/src/execution/useArtifactContent.ts +7 -0
  170. package/src/execution/useArtifactInspection.ts +252 -0
  171. package/src/execution/useCreateAgentExecution.ts +12 -0
  172. package/src/github/__tests__/useGitHubFileReader.test.ts +35 -2
  173. package/src/github/__tests__/useGitHubTreeLister.test.ts +14 -0
  174. package/src/github/useGitHubFileReader.ts +19 -6
  175. package/src/github/useGitHubTreeLister.ts +7 -3
  176. package/src/index.ts +15 -2
  177. package/src/internal/MermaidDiagram.tsx +164 -0
  178. package/src/internal/VirtualizedThread.tsx +5 -1
  179. package/src/internal/__tests__/a11y/mermaid-theme.a11y.test.tsx +122 -0
  180. package/src/internal/__tests__/markdown-components.test.tsx +93 -1
  181. package/src/internal/__tests__/mermaid-diagram.test.tsx +282 -0
  182. package/src/internal/markdown-components.tsx +36 -1
  183. package/src/internal/mermaid-loader.ts +44 -0
  184. package/src/internal/mermaid-theme-css.ts +178 -0
  185. package/src/internal/slug.ts +10 -6
  186. package/src/session/SessionViewer.tsx +204 -35
  187. package/src/session/__tests__/SessionViewer-artifactTabs.test.tsx +210 -0
  188. package/src/session/__tests__/SessionViewer-editResubmit.test.tsx +233 -0
  189. package/src/session/__tests__/useSessionArtifacts.test.ts +57 -0
  190. package/src/session/__tests__/useSessionConversation.test.tsx +172 -1
  191. package/src/session/__tests__/useSessionPanel.test.ts +70 -1
  192. package/src/session/__tests__/useWorkspaceReadRefs.test.ts +245 -0
  193. package/src/session/artifact-document.ts +23 -0
  194. package/src/session/facets/ArtifactsTab.tsx +66 -18
  195. package/src/session/facets/__tests__/ArtifactsTab.test.tsx +111 -47
  196. package/src/session/index.ts +7 -1
  197. package/src/session/useSessionArtifacts.ts +16 -5
  198. package/src/session/useSessionConversation.ts +48 -6
  199. package/src/session/useSessionPageFlow.ts +1 -0
  200. package/src/session/useSessionPanel.ts +41 -0
  201. package/src/session/useSessionRailViews.tsx +21 -1
  202. package/src/session/useWorkspaceReadRefs.ts +118 -0
  203. package/src/workspace/FileViewer.tsx +144 -38
  204. package/src/workspace/WorkspaceFileReader.ts +21 -0
  205. package/src/workspace/__tests__/FileViewer.test.tsx +138 -3
  206. package/src/workspace/__tests__/useWorkspaceFileContent.test.tsx +20 -0
  207. package/src/workspace/__tests__/workspaceListingCache.test.ts +114 -0
  208. package/src/workspace/index.ts +4 -1
  209. package/src/workspace/useWorkspaceEntries.ts +11 -0
  210. package/src/workspace/useWorkspaceFileContent.ts +4 -1
  211. package/src/workspace/useWorkspaceFileSearch.ts +8 -3
  212. package/src/workspace/useWorkspaceFiles.ts +3 -2
  213. package/src/workspace/workspaceListingCache.ts +21 -7
  214. package/styles.css +1 -1
  215. package/workspace/FileViewer.d.ts +1 -1
  216. package/workspace/FileViewer.d.ts.map +1 -1
  217. package/workspace/FileViewer.js +55 -5
  218. package/workspace/FileViewer.js.map +1 -1
  219. package/workspace/WorkspaceFileReader.d.ts +17 -0
  220. package/workspace/WorkspaceFileReader.d.ts.map +1 -1
  221. package/workspace/WorkspaceFileReader.js +18 -0
  222. package/workspace/WorkspaceFileReader.js.map +1 -1
  223. package/workspace/index.d.ts +1 -1
  224. package/workspace/index.d.ts.map +1 -1
  225. package/workspace/index.js +1 -1
  226. package/workspace/index.js.map +1 -1
  227. package/workspace/useWorkspaceEntries.d.ts +11 -0
  228. package/workspace/useWorkspaceEntries.d.ts.map +1 -1
  229. package/workspace/useWorkspaceEntries.js.map +1 -1
  230. package/workspace/useWorkspaceFileContent.d.ts.map +1 -1
  231. package/workspace/useWorkspaceFileContent.js +4 -1
  232. package/workspace/useWorkspaceFileContent.js.map +1 -1
  233. package/workspace/useWorkspaceFileSearch.d.ts.map +1 -1
  234. package/workspace/useWorkspaceFileSearch.js +8 -3
  235. package/workspace/useWorkspaceFileSearch.js.map +1 -1
  236. package/workspace/useWorkspaceFiles.d.ts +2 -1
  237. package/workspace/useWorkspaceFiles.d.ts.map +1 -1
  238. package/workspace/useWorkspaceFiles.js +3 -2
  239. package/workspace/useWorkspaceFiles.js.map +1 -1
  240. package/workspace/workspaceListingCache.d.ts +1 -1
  241. package/workspace/workspaceListingCache.d.ts.map +1 -1
  242. package/workspace/workspaceListingCache.js +20 -7
  243. package/workspace/workspaceListingCache.js.map +1 -1
  244. package/execution/ArtifactCard.d.ts +0 -81
  245. package/execution/ArtifactCard.d.ts.map +0 -1
  246. package/execution/ArtifactCard.js +0 -130
  247. package/execution/ArtifactCard.js.map +0 -1
  248. package/src/execution/ArtifactCard.tsx +0 -320
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Token-driven `themeCSS` for Mermaid diagram interiors.
3
+ *
4
+ * Mermaid's built-in `default`/`dark` themes color a diagram's SVG interior
5
+ * with a fixed palette that ignores the host's `--stgm-*` preset (Corporate,
6
+ * Fintech, ...). The obvious fix — feeding resolved tokens into Mermaid's
7
+ * `themeVariables` — is blocked: our tokens are authored in `oklch()`, and
8
+ * Mermaid's color engine (`khroma`) parses only hex/rgb/hsl/keyword, so it
9
+ * throws or NaNs on `oklch(...)`. (Mermaid also gates every `themeVariables`
10
+ * value through the allowlist `^[\d "#%(),.;A-Za-z]+$`, which excludes `-` and
11
+ * would blank any `var(--...)`.)
12
+ *
13
+ * Instead we hand Mermaid this CSS via `initialize({ themeCSS })`. Mermaid
14
+ * injects it into the diagram's own `<style>` block, scoped under the render
15
+ * id (`#stgm-mermaid-N`), *after* the theme palette it computes. Because every
16
+ * selector in that block is id-prefixed, our rules and the palette's rules sit
17
+ * at equal specificity, so ours win by source order — and `!important` makes
18
+ * that bulletproof against the few palette rules that are themselves important
19
+ * or inlined. Crucially, `themeCSS` never passes through khroma, so the
20
+ * `oklch()`-backed `var(--stgm-*)` references flow straight to the browser,
21
+ * which resolves them against the `.stgm` scope the SVG lives in. That is why
22
+ * interiors track the active preset *and* color mode live, with no JS color
23
+ * reading and no re-render on theme change.
24
+ *
25
+ * `initialize({ theme })` still selects the built-in `default`/`dark` theme as
26
+ * the base, so any element or diagram type we do not target here degrades to
27
+ * Mermaid's stock palette — never a blank or mis-colored diagram.
28
+ *
29
+ * Palette intent (subtle surfaces + brand accents — legibility over brand
30
+ * saturation): node/actor bodies sit on a muted surface with quiet
31
+ * token-colored borders and edges; labels use the standard foreground; the
32
+ * brand `--stgm-primary` is reserved for structural headers (subgraph/cluster
33
+ * titles, sequence loop/section titles, the sequence-number badge).
34
+ *
35
+ * Coverage is the two families agents emit overwhelmingly — flowchart and
36
+ * sequence — plus the shared marker/edge-label chrome. Both label render paths
37
+ * are handled: SVG `text`/`tspan` (via `fill`) and HTML labels inside
38
+ * `foreignObject` (via `color`).
39
+ */
40
+ export const MERMAID_THEME_CSS = `
41
+ /* Shared: edge arrowheads / markers */
42
+ .marker {
43
+ fill: var(--stgm-border) !important;
44
+ stroke: var(--stgm-border) !important;
45
+ }
46
+ .marker.cross {
47
+ stroke: var(--stgm-border) !important;
48
+ }
49
+
50
+ /* Flowchart: node bodies */
51
+ .node rect,
52
+ .node circle,
53
+ .node ellipse,
54
+ .node polygon,
55
+ .node path {
56
+ fill: var(--stgm-muted) !important;
57
+ stroke: var(--stgm-border) !important;
58
+ }
59
+ .node .neo-node {
60
+ stroke: var(--stgm-border) !important;
61
+ }
62
+ .node .katex path {
63
+ fill: var(--stgm-foreground) !important;
64
+ stroke: var(--stgm-foreground) !important;
65
+ }
66
+
67
+ /* Flowchart: node labels (SVG text and HTML spans) */
68
+ .label {
69
+ color: var(--stgm-foreground) !important;
70
+ }
71
+ .label text,
72
+ .label span {
73
+ fill: var(--stgm-foreground) !important;
74
+ color: var(--stgm-foreground) !important;
75
+ }
76
+
77
+ /* Flowchart: edges */
78
+ .edgePath .path,
79
+ .flowchart-link {
80
+ stroke: var(--stgm-border) !important;
81
+ }
82
+ .arrowheadPath,
83
+ .root .anchor path {
84
+ fill: var(--stgm-border) !important;
85
+ stroke: var(--stgm-border) !important;
86
+ }
87
+
88
+ /* Flowchart: edge labels sit on the diagram surface for legibility */
89
+ .edgeLabel,
90
+ .edgeLabel p,
91
+ .edgeLabel rect,
92
+ .labelBkg {
93
+ background-color: var(--stgm-card) !important;
94
+ fill: var(--stgm-card) !important;
95
+ }
96
+
97
+ /* Flowchart: subgraph clusters — muted surface, brand-accent title */
98
+ .cluster rect {
99
+ fill: var(--stgm-muted-subtle) !important;
100
+ stroke: var(--stgm-border) !important;
101
+ }
102
+ .cluster-label text,
103
+ .cluster text {
104
+ fill: var(--stgm-primary) !important;
105
+ }
106
+ .cluster-label span,
107
+ .cluster span {
108
+ color: var(--stgm-primary) !important;
109
+ }
110
+
111
+ /* Sequence: actors and lifelines */
112
+ .actor {
113
+ fill: var(--stgm-muted) !important;
114
+ stroke: var(--stgm-border) !important;
115
+ }
116
+ text.actor > tspan {
117
+ fill: var(--stgm-foreground) !important;
118
+ }
119
+ .actor-line {
120
+ stroke: var(--stgm-border) !important;
121
+ }
122
+
123
+ /* Sequence: messages */
124
+ .messageLine0,
125
+ .messageLine1 {
126
+ stroke: var(--stgm-border) !important;
127
+ }
128
+ [id$="-arrowhead"] path,
129
+ [id$="-crosshead"] path {
130
+ fill: var(--stgm-border) !important;
131
+ stroke: var(--stgm-border) !important;
132
+ }
133
+ .messageText {
134
+ fill: var(--stgm-foreground) !important;
135
+ }
136
+
137
+ /* Sequence: number badge — brand accent with its paired foreground */
138
+ [id$="-sequencenumber"] {
139
+ fill: var(--stgm-primary) !important;
140
+ }
141
+ .sequenceNumber {
142
+ fill: var(--stgm-primary-foreground) !important;
143
+ }
144
+
145
+ /* Sequence: alt/opt/loop boxes and labels */
146
+ .labelBox {
147
+ fill: var(--stgm-muted-subtle) !important;
148
+ stroke: var(--stgm-border) !important;
149
+ }
150
+ .labelText,
151
+ .labelText > tspan {
152
+ fill: var(--stgm-foreground) !important;
153
+ }
154
+ .loopText,
155
+ .loopText > tspan,
156
+ .sectionTitle,
157
+ .sectionTitle > tspan {
158
+ fill: var(--stgm-primary) !important;
159
+ }
160
+ .loopLine {
161
+ stroke: var(--stgm-border) !important;
162
+ }
163
+
164
+ /* Sequence: notes and activations */
165
+ .note {
166
+ fill: var(--stgm-muted-subtle) !important;
167
+ stroke: var(--stgm-border) !important;
168
+ }
169
+ .noteText,
170
+ .noteText > tspan {
171
+ fill: var(--stgm-foreground) !important;
172
+ }
173
+ .activation0,
174
+ .activation1 {
175
+ fill: var(--stgm-muted) !important;
176
+ stroke: var(--stgm-border) !important;
177
+ }
178
+ `;
179
+ //# sourceMappingURL=mermaid-theme-css.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mermaid-theme-css.js","sourceRoot":"","sources":["../../src/internal/mermaid-theme-css.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0IhC,CAAC"}
@@ -7,17 +7,21 @@
7
7
  *
8
8
  * Rules (applied in order):
9
9
  * 1. Convert to lowercase
10
- * 2. Replace spaces with hyphens
10
+ * 2. Replace spaces and dots (namespace separators) with hyphens
11
11
  * 3. Strip everything except `a-z`, `0-9`, and `-`
12
12
  * 4. Collapse consecutive hyphens into one
13
13
  * 5. Trim leading / trailing hyphens
14
14
  *
15
+ * Dots are treated as namespace separators (e.g. `platform.sara` → `platform-sara`),
16
+ * matching the backend generators, so namespace-scoped names stay readable.
17
+ *
15
18
  * @example
16
19
  * ```ts
17
- * generateSlug("My Cool Agent") // "my-cool-agent"
18
- * generateSlug("Acme Corp!") // "acme-corp"
19
- * generateSlug(" --hello-- ") // "hello"
20
- * generateSlug("") // ""
20
+ * generateSlug("My Cool Agent") // "my-cool-agent"
21
+ * generateSlug("Acme Corp!") // "acme-corp"
22
+ * generateSlug(" --hello-- ") // "hello"
23
+ * generateSlug("platform.planton-arch") // "platform-planton-arch"
24
+ * generateSlug("") // ""
21
25
  * ```
22
26
  */
23
27
  export declare function generateSlug(name: string): string;
@@ -1 +1 @@
1
- {"version":3,"file":"slug.d.ts","sourceRoot":"","sources":["../../src/internal/slug.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOjD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C"}
1
+ {"version":3,"file":"slug.d.ts","sourceRoot":"","sources":["../../src/internal/slug.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOjD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C"}
package/internal/slug.js CHANGED
@@ -7,23 +7,27 @@
7
7
  *
8
8
  * Rules (applied in order):
9
9
  * 1. Convert to lowercase
10
- * 2. Replace spaces with hyphens
10
+ * 2. Replace spaces and dots (namespace separators) with hyphens
11
11
  * 3. Strip everything except `a-z`, `0-9`, and `-`
12
12
  * 4. Collapse consecutive hyphens into one
13
13
  * 5. Trim leading / trailing hyphens
14
14
  *
15
+ * Dots are treated as namespace separators (e.g. `platform.sara` → `platform-sara`),
16
+ * matching the backend generators, so namespace-scoped names stay readable.
17
+ *
15
18
  * @example
16
19
  * ```ts
17
- * generateSlug("My Cool Agent") // "my-cool-agent"
18
- * generateSlug("Acme Corp!") // "acme-corp"
19
- * generateSlug(" --hello-- ") // "hello"
20
- * generateSlug("") // ""
20
+ * generateSlug("My Cool Agent") // "my-cool-agent"
21
+ * generateSlug("Acme Corp!") // "acme-corp"
22
+ * generateSlug(" --hello-- ") // "hello"
23
+ * generateSlug("platform.planton-arch") // "platform-planton-arch"
24
+ * generateSlug("") // ""
21
25
  * ```
22
26
  */
23
27
  export function generateSlug(name) {
24
28
  return name
25
29
  .toLowerCase()
26
- .replace(/ /g, "-")
30
+ .replace(/[ .]/g, "-")
27
31
  .replace(/[^a-z0-9-]/g, "")
28
32
  .replace(/-{2,}/g, "-")
29
33
  .replace(/^-+|-+$/g, "");
@@ -1 +1 @@
1
- {"version":3,"file":"slug.js","sourceRoot":"","sources":["../../src/internal/slug.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,IAAI;SACR,WAAW,EAAE;SACb,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC;SAClB,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC;SAC1B,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC;SACtB,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;AAC7B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB;IAChC,OAAO,MAAM,CAAC,UAAU,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC"}
1
+ {"version":3,"file":"slug.js","sourceRoot":"","sources":["../../src/internal/slug.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,IAAI;SACR,WAAW,EAAE;SACb,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;SACrB,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC;SAC1B,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC;SACtB,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;AAC7B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB;IAChC,OAAO,MAAM,CAAC,UAAU,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stigmer/react",
3
- "version": "3.1.3",
3
+ "version": "3.1.5",
4
4
  "description": "React provider and client hook for the Stigmer platform SDK",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -37,7 +37,7 @@
37
37
  "dependencies": {
38
38
  "@bufbuild/protovalidate": "^1.1.1",
39
39
  "@dagrejs/dagre": "^1.1.4",
40
- "@stigmer/theme": "3.1.3",
40
+ "@stigmer/theme": "3.1.5",
41
41
  "diff": "^8.0.3",
42
42
  "fflate": "^0.8.2",
43
43
  "hast-util-to-jsx-runtime": "^2.3.6",
@@ -60,12 +60,13 @@
60
60
  "@codemirror/state": "^6.0.0",
61
61
  "@codemirror/view": "^6.0.0",
62
62
  "@lezer/highlight": "^1.0.0",
63
- "@stigmer/protos": "3.1.3",
64
- "@stigmer/sdk": "3.1.3",
63
+ "@stigmer/protos": "3.1.5",
64
+ "@stigmer/sdk": "3.1.5",
65
65
  "@tanstack/react-table": "^8.20.0",
66
66
  "@xyflow/react": "^12.0.0",
67
67
  "elkjs": "^0.9.0",
68
68
  "lucide-react": ">=0.400.0",
69
+ "mermaid": "^11.0.0",
69
70
  "react": "^19.0.0",
70
71
  "react-dom": "^19.0.0",
71
72
  "react-virtuoso": "^4.0.0",
@@ -4,6 +4,7 @@ import type { WorkspaceFileLister } from "../workspace/WorkspaceFileLister.js";
4
4
  import type { WorkspaceFileReader } from "../workspace/WorkspaceFileReader.js";
5
5
  import type { WorkspaceContentSearcher } from "../workspace/WorkspaceContentSearcher.js";
6
6
  import type { ApplyResourceResult } from "../library/useApplyResource.js";
7
+ import { type MessageThreadSlots } from "../execution/MessageThread.js";
7
8
  import type { RuntimeEnvProvider } from "./runtime-env.js";
8
9
  import type { SessionAudience } from "./audience.js";
9
10
  /** Props for {@link SessionViewer}. */
@@ -98,6 +99,15 @@ export interface SessionViewerProps {
98
99
  readonly accessSlot?: ReactNode;
99
100
  /** Called after a resource is applied from the Artifacts tab. */
100
101
  readonly onApplied?: (result: ApplyResourceResult) => void;
102
+ /**
103
+ * Component overrides for the conversation thread's chrome (message
104
+ * bubbles, approval card, to-dos card, plan cards, thinking indicator) —
105
+ * forwarded to the inner {@link MessageThread}. See
106
+ * {@link MessageThreadSlots}. Define the object and its components at
107
+ * module level (or memoize) so thread rows keep their streaming
108
+ * re-render guarantees.
109
+ */
110
+ readonly threadSlots?: MessageThreadSlots;
101
111
  /** Additional CSS classes for the root container. */
102
112
  readonly className?: string;
103
113
  }
@@ -143,5 +153,5 @@ export interface SessionViewerProps {
143
153
  * />
144
154
  * ```
145
155
  */
146
- export declare function SessionViewer({ sessionId, org, gitHubConnection, enableGitHub, enableLocal, onBrowseLocalFolder, workspaceFileLister, workspaceFileReader, workspaceContentSearcher, getRuntimeEnv, audience, headerActions, accessSlot, onApplied, className, }: SessionViewerProps): import("react/jsx-runtime").JSX.Element;
156
+ export declare function SessionViewer({ sessionId, org, gitHubConnection, enableGitHub, enableLocal, onBrowseLocalFolder, workspaceFileLister, workspaceFileReader, workspaceContentSearcher, getRuntimeEnv, audience, headerActions, accessSlot, onApplied, threadSlots, className, }: SessionViewerProps): import("react/jsx-runtime").JSX.Element;
147
157
  //# sourceMappingURL=SessionViewer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SessionViewer.d.ts","sourceRoot":"","sources":["../../src/session/SessionViewer.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA2D,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAQhG,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,kCAAkC,CAAC;AAClF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC/E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC/E,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;AAOzF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAoC1E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AA2ErD,uCAAuC;AACvC,MAAM,WAAW,kBAAkB;IACjC,sCAAsC;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,yBAAyB;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,yBAAyB,CAAC;IACtD;;;OAGG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAChC;;;;OAIG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5D;;;;OAIG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IACnD;;;;;OAKG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IACnD;;;;;;OAMG;IACH,QAAQ,CAAC,wBAAwB,CAAC,EAAE,wBAAwB,CAAC;IAC7D;;;;;;;OAOG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,kBAAkB,CAAC;IAC5C;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,eAAe,CAAC;IACpC;;;;OAIG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,CAAC;IACnC;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,CAAC;IAChC,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAC3D,qDAAqD;IACrD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,wBAAgB,aAAa,CAAC,EAC5B,SAAS,EACT,GAAG,EACH,gBAAgB,EAChB,YAAmB,EACnB,WAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,wBAAwB,EACxB,aAAa,EACb,QAAuB,EACvB,aAAa,EACb,UAAU,EACV,SAAS,EACT,SAAS,GACV,EAAE,kBAAkB,2CAkSpB"}
1
+ {"version":3,"file":"SessionViewer.d.ts","sourceRoot":"","sources":["../../src/session/SessionViewer.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA2D,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAQhG,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,kCAAkC,CAAC;AAClF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC/E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC/E,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;AAOzF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAM1E,OAAO,EAAiB,KAAK,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAqCvF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AA2ErD,uCAAuC;AACvC,MAAM,WAAW,kBAAkB;IACjC,sCAAsC;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,yBAAyB;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,yBAAyB,CAAC;IACtD;;;OAGG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAChC;;;;OAIG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5D;;;;OAIG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IACnD;;;;;OAKG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IACnD;;;;;;OAMG;IACH,QAAQ,CAAC,wBAAwB,CAAC,EAAE,wBAAwB,CAAC;IAC7D;;;;;;;OAOG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,kBAAkB,CAAC;IAC5C;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,eAAe,CAAC;IACpC;;;;OAIG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,CAAC;IACnC;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,CAAC;IAChC,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAC3D;;;;;;;OAOG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,kBAAkB,CAAC;IAC1C,qDAAqD;IACrD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,wBAAgB,aAAa,CAAC,EAC5B,SAAS,EACT,GAAG,EACH,gBAAgB,EAChB,YAAmB,EACnB,WAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,wBAAwB,EACxB,aAAa,EACb,QAAuB,EACvB,aAAa,EACb,UAAU,EACV,SAAS,EACT,WAAW,EACX,SAAS,GACV,EAAE,kBAAkB,2CA2TpB"}
@@ -28,11 +28,14 @@ import { usePlanDraft, planDraftKey } from "./usePlanDraft.js";
28
28
  import { PlanEditor } from "./PlanEditor.js";
29
29
  import { PlanStreamingDocument } from "./PlanStreamingDocument.js";
30
30
  import { PLAN_DOCUMENT_ENTRY_ID, PLAN_DOCUMENT_PATH } from "./plan-document.js";
31
+ import { ARTIFACT_DOCUMENT_ENTRY_ID } from "./artifact-document.js";
32
+ import { ArtifactDocument } from "../execution/ArtifactDocument.js";
31
33
  import { useSessionRailViews } from "./useSessionRailViews.js";
32
34
  import { useSessionPanel } from "./useSessionPanel.js";
33
35
  import { SessionPanelChip } from "./SessionPanelChip.js";
34
36
  import { useSessionWriteBacks } from "./useSessionWriteBacks.js";
35
- import { useSessionArtifacts } from "./useSessionArtifacts.js";
37
+ import { useWorkspaceReadRefs } from "./useWorkspaceReadRefs.js";
38
+ import { useSessionArtifacts, artifactKey, } from "./useSessionArtifacts.js";
36
39
  import { ExecutionPhase } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
37
40
  /**
38
41
  * Where the approved plan mounts in the implement execution's workspace —
@@ -126,7 +129,7 @@ const CONVERSATION_COLUMN_CLASS = "mx-auto w-full max-w-3xl";
126
129
  * />
127
130
  * ```
128
131
  */
129
- export function SessionViewer({ sessionId, org, gitHubConnection, enableGitHub = true, enableLocal = false, onBrowseLocalFolder, workspaceFileLister, workspaceFileReader, workspaceContentSearcher, getRuntimeEnv, audience = "integrator", headerActions, accessSlot, onApplied, className, }) {
132
+ export function SessionViewer({ sessionId, org, gitHubConnection, enableGitHub = true, enableLocal = false, onBrowseLocalFolder, workspaceFileLister, workspaceFileReader, workspaceContentSearcher, getRuntimeEnv, audience = "integrator", headerActions, accessSlot, onApplied, threadSlots, className, }) {
130
133
  const flow = useSessionPageFlow({ sessionId, org, getRuntimeEnv });
131
134
  const { conv } = flow;
132
135
  const isEndUser = audience === "endUser";
@@ -271,6 +274,20 @@ export function SessionViewer({ sessionId, org, gitHubConnection, enableGitHub =
271
274
  : executionId);
272
275
  panel.openPlanDocument();
273
276
  }, [sessionPlan, panel.openPlanDocument]);
277
+ // "Open artifact" (an Artifacts-facet row) → an editor-pane document tab.
278
+ // Writing the editors store re-renders only the panel subtree (which
279
+ // subscribes), never this streaming column. Plan artifacts are routed to the
280
+ // plan tab upstream (in ArtifactsTab), so they never reach here.
281
+ const handleOpenArtifact = useCallback((entry) => {
282
+ panel.openArtifact(entry.artifact);
283
+ }, [panel.openArtifact]);
284
+ // Double-click an artifact row → pin its tab (the leading single click already
285
+ // opened the preview via `handleOpenArtifact`). Mirrors the file tree's
286
+ // preview/pin split; plan artifacts are routed to the plan tab upstream (in
287
+ // ArtifactsTab) and never reach here.
288
+ const handleActivateArtifact = useCallback((entry) => {
289
+ panel.pinArtifact(entry.artifact);
290
+ }, [panel.pinArtifact]);
274
291
  // Open a transcript tool-call file path in the panel's read-only editor.
275
292
  // Resolves the (possibly absolute / subdir-prefixed) path to a
276
293
  // repo/root-relative selection the editor can fetch; on a definite hit it
@@ -294,9 +311,9 @@ export function SessionViewer({ sessionId, org, gitHubConnection, enableGitHub =
294
311
  if (!conv.session && !conv.isLoading) {
295
312
  return _jsx(SessionStarting, {});
296
313
  }
297
- return (_jsxs("div", { className: cn("relative flex h-full w-full flex-col", className), children: [_jsxs("div", { className: "absolute top-2 right-6 z-10 flex items-center gap-2", children: [headerActions, _jsx(SessionPanelChip, { isOpen: panel.isOpen, onToggle: panel.isOpen ? panel.closePanel : panel.openPanel, badgeCount: writeBackCount + artifactCount })] }), _jsx(ThreadSelectionContext.Provider, { value: selectionStore, children: _jsx(ResizableSplit, { resizablePane: "primary", collapsedPane: panel.isOpen ? "none" : "secondary", defaultSize: 420, minSize: 320, maxSize: 640, storageKey: "stgm-session-chat-width", responsiveCollapse: panel.isOpen ? "primary" : "none", ariaLabel: "Resize chat panel", className: "min-h-0 flex-1", primary: _jsx(ConversationColumn, { flow: flow, modelId: modelId, setModelId: setModelId, interactionMode: interactionMode, setInteractionMode: setInteractionMode, composerRef: composerRef, org: org, gitHubConnection: gitHubConnection, enableGitHub: enableGitHub, enableLocal: enableLocal, onBrowseLocalFolder: onBrowseLocalFolder, onBuildFromPlan: handleBuildFromPlan, onOpenPlan: handleOpenPlan, isBuildingFromPlan: isBuildingFromPlan, planAttachFailed: planAttachFailed, onDismissPlanAttachFailed: () => setPlanAttachFailed(false), onFilePathClick: handleTranscriptFilePathClick, isEndUser: isEndUser }), secondary: panel.isOpen ? (_jsx(SessionPanelRegion, { flow: flow, org: org, panel: panel, accessSlot: accessSlot, onApplied: onApplied, onImplementPlan: handleBuildFromPlan, implementPlanDisabled: !conv.canSendFollowUp || isBuildingFromPlan, sessionPlan: sessionPlan, streamingPlan: streamingPlan, planDraft: planDraft, openPlanExecutionId: openPlanExecutionId, onOpenPlan: handleOpenPlan, enableLocal: enableLocal, onBrowseLocalFolder: onBrowseLocalFolder, workspaceFileLister: workspaceFileLister, workspaceFileReader: workspaceFileReader, workspaceContentSearcher: workspaceContentSearcher, isEndUser: isEndUser })) : null }) })] }));
314
+ return (_jsxs("div", { className: cn("relative flex h-full w-full flex-col", className), children: [_jsxs("div", { className: "absolute top-2 right-6 z-10 flex items-center gap-2", children: [headerActions, _jsx(SessionPanelChip, { isOpen: panel.isOpen, onToggle: panel.isOpen ? panel.closePanel : panel.openPanel, badgeCount: writeBackCount + artifactCount })] }), _jsx(ThreadSelectionContext.Provider, { value: selectionStore, children: _jsx(ResizableSplit, { resizablePane: "primary", collapsedPane: panel.isOpen ? "none" : "secondary", defaultSize: 420, minSize: 320, maxSize: 640, storageKey: "stgm-session-chat-width", responsiveCollapse: panel.isOpen ? "primary" : "none", ariaLabel: "Resize chat panel", className: "min-h-0 flex-1", primary: _jsx(ConversationColumn, { flow: flow, modelId: modelId, setModelId: setModelId, interactionMode: interactionMode, setInteractionMode: setInteractionMode, composerRef: composerRef, org: org, gitHubConnection: gitHubConnection, enableGitHub: enableGitHub, enableLocal: enableLocal, onBrowseLocalFolder: onBrowseLocalFolder, onBuildFromPlan: handleBuildFromPlan, onOpenPlan: handleOpenPlan, isBuildingFromPlan: isBuildingFromPlan, planAttachFailed: planAttachFailed, onDismissPlanAttachFailed: () => setPlanAttachFailed(false), onFilePathClick: handleTranscriptFilePathClick, isEndUser: isEndUser, threadSlots: threadSlots }), secondary: panel.isOpen ? (_jsx(SessionPanelRegion, { flow: flow, org: org, panel: panel, accessSlot: accessSlot, onApplied: onApplied, onImplementPlan: handleBuildFromPlan, implementPlanDisabled: !conv.canSendFollowUp || isBuildingFromPlan, sessionPlan: sessionPlan, streamingPlan: streamingPlan, planDraft: planDraft, openPlanExecutionId: openPlanExecutionId, onOpenPlan: handleOpenPlan, onOpenArtifact: handleOpenArtifact, onActivateArtifact: handleActivateArtifact, enableLocal: enableLocal, onBrowseLocalFolder: onBrowseLocalFolder, workspaceFileLister: workspaceFileLister, workspaceFileReader: workspaceFileReader, workspaceContentSearcher: workspaceContentSearcher, isEndUser: isEndUser })) : null }) })] }));
298
315
  }
299
- const ConversationColumn = memo(function ConversationColumn({ flow, modelId, setModelId, interactionMode, setInteractionMode, composerRef, org, gitHubConnection, enableGitHub, enableLocal, onBrowseLocalFolder, onBuildFromPlan, onOpenPlan, isBuildingFromPlan, planAttachFailed, onDismissPlanAttachFailed, onFilePathClick, isEndUser, }) {
316
+ const ConversationColumn = memo(function ConversationColumn({ flow, modelId, setModelId, interactionMode, setInteractionMode, composerRef, org, gitHubConnection, enableGitHub, enableLocal, onBrowseLocalFolder, onBuildFromPlan, onOpenPlan, isBuildingFromPlan, planAttachFailed, onDismissPlanAttachFailed, onFilePathClick, isEndUser, threadSlots, }) {
300
317
  const { conv } = flow;
301
318
  // Approval failures are NOT folded into this banner: they now surface in-card,
302
319
  // beside the exact gate that failed (see MessageThread's approvalErrors →
@@ -314,16 +331,37 @@ const ConversationColumn = memo(function ConversationColumn({ flow, modelId, set
314
331
  const handleStop = useCallback(() => {
315
332
  void conv.stop();
316
333
  }, [conv.stop]);
317
- // Edit-and-resubmit: stop the in-flight turn and pre-fill the composer with
318
- // the original text. The append-only execution log can't be rewritten, so
319
- // the user reviews the prefilled message and resubmits it as a NEW execution
320
- // through the normal Send pipeline. The cancelled turn stays in history with
321
- // its phase badge an honest record rather than a silent edit.
334
+ // Edit-and-resubmit: stop the in-flight turn, pre-fill the composer with
335
+ // the original text, and remember which execution is being edited. The
336
+ // append-only execution log is never rewritten submitting while editing
337
+ // creates a NEW execution that carries `supersedesExecutionId`, and the
338
+ // conversation read model hides the superseded turn so the edited message
339
+ // replaces the original in place (Cursor-style, stigmer/stigmer#181).
340
+ //
341
+ // The editing state is explicit (banner + cancel in the composer) so the
342
+ // supersede link only attaches when the user actually resubmits the edit.
343
+ // Cancelling drops the link — a subsequent unrelated message appends
344
+ // normally without hiding any history.
345
+ const [editingExecutionId, setEditingExecutionId] = useState(null);
346
+ const activeExecutionId = conv.activeStreamExecution?.metadata?.id ?? null;
322
347
  const handleEditMessage = useCallback((text) => {
323
348
  void conv.stop();
349
+ setEditingExecutionId(activeExecutionId);
324
350
  composerRef.current?.setMessage(text);
325
351
  composerRef.current?.focus();
326
- }, [conv.stop, composerRef]);
352
+ }, [conv.stop, activeExecutionId, composerRef]);
353
+ const handleCancelEdit = useCallback(() => {
354
+ setEditingExecutionId(null);
355
+ composerRef.current?.setMessage("");
356
+ }, [composerRef]);
357
+ // Submit wrapper that attaches the supersede link while editing, then
358
+ // exits editing mode. Ordinary (non-editing) submits pass through as-is.
359
+ const handleComposerSubmit = useCallback((message, modelName, context) => {
360
+ void flow.handleSubmit(message, modelName, editingExecutionId
361
+ ? { ...context, supersedesExecutionId: editingExecutionId }
362
+ : context);
363
+ setEditingExecutionId(null);
364
+ }, [flow.handleSubmit, editingExecutionId]);
327
365
  // The composer-docked FileReviewDock sits OUTSIDE MessageThread, so the
328
366
  // thread's own FilePathContext provider does not reach it. This provider
329
367
  // gives the dock's file list the same path resolution and click routing
@@ -334,11 +372,14 @@ const ConversationColumn = memo(function ConversationColumn({ flow, modelId, set
334
372
  workspaceEntries: conv.workspaceEntries ?? [],
335
373
  onFilePathClick,
336
374
  }), [conv.workspaceEntries, onFilePathClick]);
337
- return (_jsxs("div", { className: "flex h-full min-w-0 flex-col", children: [_jsx(MessageThread, { executions: conv.completedExecutions, activeStreamExecution: conv.activeStreamExecution, pendingUserMessage: conv.pendingUserMessage, pendingMessageFailed: !!conv.sendError && !!conv.pendingUserMessage, onRetrySend: conv.retryLastSend, onRetryExecution: onRetryExecution, onApprovalSubmit: flow.submitApproval, submittingApprovalIds: conv.submittingApprovalIds, approvalErrors: conv.approvalErrors, showFileReviewRecords: true, onEditMessage: conv.isStoppable ? handleEditMessage : undefined, workspaceEntries: conv.workspaceEntries, onFilePathClick: onFilePathClick, sandboxWorkspaceRoot: flow.sandboxWorkspaceRoot, onBuildFromPlan: onBuildFromPlan, onOpenPlan: onOpenPlan, org: org, planActionsDisabled: !conv.canSendFollowUp || isBuildingFromPlan, planBuildPending: isBuildingFromPlan, contentColumn: "center", className: "flex-1" }), _jsxs("div", { className: CONVERSATION_COLUMN_CLASS, children: [planAttachFailed && (_jsx(PlanAttachFailedNotice, { onDismiss: onDismissPlanAttachFailed })), conv.isReconnecting && _jsx(ReconnectingIndicator, {}), conv.connectTimedOut && (_jsx(ConnectTimedOutBanner, { onRetry: conv.reconnectStream })), conv.isSlow && !conv.connectTimedOut && _jsx(SlowIndicator, {}), conv.streamError && (_jsx(StreamErrorBanner, { error: conv.streamError, onReconnect: conv.reconnectStream })), sendError && _jsx(SendErrorBanner, { error: sendError }), flow.autoApproveAll && (_jsx(AutoApproveIndicator, { onTurnOff: () => flow.setAutoApproveAll(false) })), _jsxs(FilePathContext.Provider, { value: dockFilePathCtx, children: [_jsx(FileChangeProgressBar, { progress: conv.fileChangeProgress }), _jsx(FileReviewDock, { changeSets: conv.fileChangeSets, onSubmit: conv.submitFileDecision, submittingDecisionKeys: conv.submittingFileDecisionKeys, decisionErrors: conv.fileDecisionErrors })] }), _jsx(SessionComposer, { ref: composerRef, onSubmit: flow.handleSubmit, isSubmitting: conv.isSending, disabled: !conv.canSendFollowUp, onStop: conv.isStoppable ? handleStop : undefined, isStopping: conv.isStopping, org: org, harness: flow.harness, defaultModelId: modelId, onModelChange: setModelId, interactionMode: interactionMode, onInteractionModeChange: setInteractionMode, showInteractionModePicker: true, workspace: flow.workspace, gitHubConnection: gitHubConnection, enableGitHub: enableGitHub, enableLocal: enableLocal, onBrowseLocalFolder: onBrowseLocalFolder, agentRef: flow.agentRef, onAgentRefChange: flow.setAgentRef, onAgentResolutionChange: flow.setResolution, isDefaultAgent: flow.isDefaultAgent, lockAgent: isEndUser, mcpServerUsages: isEndUser ? undefined : flow.mcpServerUsages, onMcpServerUsagesChange: isEndUser ? undefined : flow.setMcpServerUsages, skillRefs: isEndUser ? undefined : flow.skillRefs, onSkillRefsChange: isEndUser ? undefined : flow.setSkillRefs, sessionVariables: isEndUser ? undefined : flow.sessionVariables, className: "px-4 py-3" })] })] }));
375
+ return (_jsxs("div", { className: "flex h-full min-w-0 flex-col", children: [_jsx(MessageThread, { executions: conv.completedExecutions, activeStreamExecution: conv.activeStreamExecution, pendingUserMessage: conv.pendingUserMessage, pendingMessageFailed: !!conv.sendError && !!conv.pendingUserMessage, onRetrySend: conv.retryLastSend, onRetryExecution: onRetryExecution, onApprovalSubmit: flow.submitApproval, submittingApprovalIds: conv.submittingApprovalIds, approvalErrors: conv.approvalErrors, showFileReviewRecords: true, onEditMessage: conv.isStoppable ? handleEditMessage : undefined, workspaceEntries: conv.workspaceEntries, onFilePathClick: onFilePathClick, sandboxWorkspaceRoot: flow.sandboxWorkspaceRoot, onBuildFromPlan: onBuildFromPlan, onOpenPlan: onOpenPlan, org: org, planActionsDisabled: !conv.canSendFollowUp || isBuildingFromPlan, planBuildPending: isBuildingFromPlan, contentColumn: "center", slots: threadSlots, className: "flex-1" }), _jsxs("div", { className: CONVERSATION_COLUMN_CLASS, children: [planAttachFailed && (_jsx(PlanAttachFailedNotice, { onDismiss: onDismissPlanAttachFailed })), conv.isReconnecting && _jsx(ReconnectingIndicator, {}), conv.connectTimedOut && (_jsx(ConnectTimedOutBanner, { onRetry: conv.reconnectStream })), conv.isSlow && !conv.connectTimedOut && _jsx(SlowIndicator, {}), conv.streamError && (_jsx(StreamErrorBanner, { error: conv.streamError, onReconnect: conv.reconnectStream })), sendError && _jsx(SendErrorBanner, { error: sendError }), flow.autoApproveAll && (_jsx(AutoApproveIndicator, { onTurnOff: () => flow.setAutoApproveAll(false) })), _jsxs(FilePathContext.Provider, { value: dockFilePathCtx, children: [_jsx(FileChangeProgressBar, { progress: conv.fileChangeProgress }), _jsx(FileReviewDock, { changeSets: conv.fileChangeSets, onSubmit: conv.submitFileDecision, submittingDecisionKeys: conv.submittingFileDecisionKeys, decisionErrors: conv.fileDecisionErrors })] }), _jsx(SessionComposer, { ref: composerRef, onSubmit: handleComposerSubmit, isSubmitting: conv.isSending, disabled: !conv.canSendFollowUp, onStop: conv.isStoppable ? handleStop : undefined, isStopping: conv.isStopping, isEditing: editingExecutionId != null, onCancelEdit: handleCancelEdit, org: org, harness: flow.harness, defaultModelId: modelId, onModelChange: setModelId, interactionMode: interactionMode, onInteractionModeChange: setInteractionMode, showInteractionModePicker: true, workspace: flow.workspace, gitHubConnection: gitHubConnection, enableGitHub: enableGitHub, enableLocal: enableLocal, onBrowseLocalFolder: onBrowseLocalFolder, agentRef: flow.agentRef, onAgentRefChange: flow.setAgentRef, onAgentResolutionChange: flow.setResolution, isDefaultAgent: flow.isDefaultAgent, lockAgent: isEndUser, mcpServerUsages: isEndUser ? undefined : flow.mcpServerUsages, onMcpServerUsagesChange: isEndUser ? undefined : flow.setMcpServerUsages, skillRefs: isEndUser ? undefined : flow.skillRefs, onSkillRefsChange: isEndUser ? undefined : flow.setSkillRefs, sessionVariables: isEndUser ? undefined : flow.sessionVariables, className: "px-4 py-3" })] })] }));
338
376
  });
339
- function SessionPanelRegion({ flow, org, panel, accessSlot, onApplied, onImplementPlan, implementPlanDisabled, sessionPlan, streamingPlan, planDraft, openPlanExecutionId, onOpenPlan, enableLocal, onBrowseLocalFolder, workspaceFileLister, workspaceFileReader, workspaceContentSearcher, isEndUser, }) {
377
+ function SessionPanelRegion({ flow, org, panel, accessSlot, onApplied, onImplementPlan, implementPlanDisabled, sessionPlan, streamingPlan, planDraft, openPlanExecutionId, onOpenPlan, onOpenArtifact, onActivateArtifact, enableLocal, onBrowseLocalFolder, workspaceFileLister, workspaceFileReader, workspaceContentSearcher, isEndUser, }) {
340
378
  const selectedItem = useSelectedThreadItem();
341
379
  const { editors, activeKey, activeFile, reveal } = useWorkspaceEditors(panel.editorsStore);
380
+ // Session artifacts, deduped by identity — the source both the Artifacts
381
+ // rail-view badge and the open artifact document tabs resolve against.
382
+ const { artifacts: sessionArtifacts } = useSessionArtifacts(flow.allExecutions);
342
383
  // Honor a pending jump-to-line reveal only while it targets the active editor
343
384
  // (an EditorReveal is structurally a RevealTarget). Switching tabs changes
344
385
  // activeKey, so a stale reveal for another tab is naturally ignored.
@@ -379,14 +420,12 @@ function SessionPanelRegion({ flow, org, panel, accessSlot, onApplied, onImpleme
379
420
  // tab and its turn then ended without publishing, with no earlier plan to
380
421
  // fall back to.
381
422
  const openPlanIsLatest = openPlan?.executionId === sessionPlan?.executionId;
382
- const virtualDocuments = useMemo(() => {
383
- return [
384
- {
385
- entryId: PLAN_DOCUMENT_ENTRY_ID,
386
- path: PLAN_DOCUMENT_PATH,
387
- content: streamingPlan ? (_jsx(PlanStreamingDocument, { displayText: streamingPlan.displayText }, `streaming-${streamingPlan.executionId}`)) : openPlan ? (_jsx(PlanEditor, { plan: openPlan, draft: openPlanIsLatest ? planDraft : undefined, onBuildFromPlan: openPlanIsLatest ? onImplementPlan : undefined, buildDisabled: implementPlanDisabled, readOnly: !openPlanIsLatest }, planDraftKey(openPlan))) : (_jsx(PlanUnavailableNotice, {})),
388
- },
389
- ];
423
+ const planVirtualDocument = useMemo(() => {
424
+ return {
425
+ entryId: PLAN_DOCUMENT_ENTRY_ID,
426
+ path: PLAN_DOCUMENT_PATH,
427
+ content: streamingPlan ? (_jsx(PlanStreamingDocument, { displayText: streamingPlan.displayText }, `streaming-${streamingPlan.executionId}`)) : openPlan ? (_jsx(PlanEditor, { plan: openPlan, draft: openPlanIsLatest ? planDraft : undefined, onBuildFromPlan: openPlanIsLatest ? onImplementPlan : undefined, buildDisabled: implementPlanDisabled, readOnly: !openPlanIsLatest }, planDraftKey(openPlan))) : (_jsx(PlanUnavailableNotice, {})),
428
+ };
390
429
  }, [
391
430
  streamingPlan,
392
431
  openPlan,
@@ -395,6 +434,30 @@ function SessionPanelRegion({ flow, org, panel, accessSlot, onApplied, onImpleme
395
434
  onImplementPlan,
396
435
  implementPlanDisabled,
397
436
  ]);
437
+ // The open artifact document tabs (the artifact virtual-document FAMILY).
438
+ // Built in a SEPARATE memo from the plan doc so artifact-tab churn never
439
+ // recreates the plan doc (and vice versa). Bounded to what is actually open:
440
+ // one document per editor whose entry id is the artifact family's, resolved
441
+ // back to its `SessionArtifactEntry` by the same `artifactKey` used to open
442
+ // it (single source of truth for artifact identity). Only the ACTIVE doc's
443
+ // `content` is mounted by the surface, so building these elements is cheap;
444
+ // keying on `contentHash` remounts a tab whose artifact was overwritten by a
445
+ // later execution. A tab whose artifact no longer exists degrades to an
446
+ // honest notice rather than vanishing.
447
+ const artifactByKey = useMemo(() => new Map(sessionArtifacts.map((entry) => [artifactKey(entry.artifact), entry])), [sessionArtifacts]);
448
+ const artifactVirtualDocuments = useMemo(() => {
449
+ return editors
450
+ .filter((editor) => editor.entryId === ARTIFACT_DOCUMENT_ENTRY_ID)
451
+ .map((editor) => {
452
+ const entry = artifactByKey.get(editor.path);
453
+ return {
454
+ entryId: ARTIFACT_DOCUMENT_ENTRY_ID,
455
+ path: editor.path,
456
+ content: entry ? (_jsx(ArtifactDocument, { artifact: entry.artifact, executionId: entry.executionId, org: org, isTerminal: entry.isTerminal, onApplied: onApplied }, entry.artifact.contentHash || editor.path)) : (_jsx(ArtifactUnavailableNotice, {})),
457
+ };
458
+ });
459
+ }, [editors, artifactByKey, org, onApplied]);
460
+ const virtualDocuments = useMemo(() => [planVirtualDocument, ...artifactVirtualDocuments], [planVirtualDocument, artifactVirtualDocuments]);
398
461
  const handleRemoveAgent = useCallback(() => {
399
462
  flow.setAgentRef(null);
400
463
  flow.setResolution(null);
@@ -440,6 +503,8 @@ function SessionPanelRegion({ flow, org, panel, accessSlot, onApplied, onImpleme
440
503
  onApplied,
441
504
  onImplementPlan,
442
505
  onOpenPlan,
506
+ onOpenArtifact,
507
+ onActivateArtifact,
443
508
  });
444
509
  // Explorer-footer folder attach (desktop only — needs the native picker).
445
510
  // Same wiring the retired Workspace tab used.
@@ -449,7 +514,13 @@ function SessionPanelRegion({ flow, org, panel, accessSlot, onApplied, onImpleme
449
514
  if (path)
450
515
  flow.workspace.addLocalPath(path);
451
516
  }, [onBrowseLocalFolder, flow.workspace.addLocalPath]);
452
- return (_jsx(WorkspaceSurface, { entries: flow.workspace.entries, lister: workspaceFileLister, reader: workspaceFileReader, searcher: workspaceContentSearcher, view: panel.view, onViewChange: panel.setView, extraViews: railViews, virtualDocuments: virtualDocuments, onRemoveEntry: flow.workspace.remove, onAddLocalFolder: canAddLocalFolder ? handleAddLocalFolder : undefined, editors: editors, selectedFile: activeFile, onOpenFile: panel.openFile, onActivateEditor: panel.activateEditor, onPinEditor: panel.pinEditor, onCloseEditor: panel.closeEditor, onCollapse: panel.closePanel, change: openFileChange ?? undefined, reveal: activeReveal, className: "h-full" }));
517
+ // Entries decorated with per-entry read refs (the latest write-back commit
518
+ // SHA), so the surface's read-side capabilities — file viewer, tree listing,
519
+ // name search — resolve at the ref the agent's changes actually live on. A
520
+ // read-side projection only: `flow.workspace.entries` stays undecorated for
521
+ // setup and session input.
522
+ const surfaceEntries = useWorkspaceReadRefs(flow.allExecutions, flow.workspace.entries);
523
+ return (_jsx(WorkspaceSurface, { entries: surfaceEntries, lister: workspaceFileLister, reader: workspaceFileReader, searcher: workspaceContentSearcher, view: panel.view, onViewChange: panel.setView, extraViews: railViews, virtualDocuments: virtualDocuments, onRemoveEntry: flow.workspace.remove, onAddLocalFolder: canAddLocalFolder ? handleAddLocalFolder : undefined, editors: editors, selectedFile: activeFile, onOpenFile: panel.openFile, onActivateEditor: panel.activateEditor, onPinEditor: panel.pinEditor, onCloseEditor: panel.closeEditor, onCollapse: panel.closePanel, change: openFileChange ?? undefined, reveal: activeReveal, className: "h-full" }));
453
524
  }
454
525
  // ---------------------------------------------------------------------------
455
526
  // Local sub-components (error states)
@@ -479,6 +550,15 @@ function AutoApproveIndicator({ onTurnOff }) {
479
550
  function PlanUnavailableNotice() {
480
551
  return (_jsxs("div", { role: "status", className: "mx-auto flex w-full max-w-3xl flex-col items-center gap-1 px-4 py-8 text-center", children: [_jsx("p", { className: "text-xs font-medium text-foreground", children: "This turn ended before a plan was completed." }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Any partial plan text remains in the conversation." })] }));
481
552
  }
553
+ /**
554
+ * Shown in an artifact document tab whose artifact is no longer among the
555
+ * session's outputs (an edge only reachable if the executions backing the tab
556
+ * changed underneath it). The tab stays closable; the content is honest rather
557
+ * than blank.
558
+ */
559
+ function ArtifactUnavailableNotice() {
560
+ return (_jsxs("div", { role: "status", className: "mx-auto flex w-full max-w-3xl flex-col items-center gap-1 px-4 py-8 text-center", children: [_jsx("p", { className: "text-xs font-medium text-foreground", children: "This artifact is no longer available." }), _jsx("p", { className: "text-xs text-muted-foreground", children: "It may have been replaced by a newer version of this session." })] }));
561
+ }
482
562
  /**
483
563
  * Non-blocking notice for a build that fell back to message-only implement
484
564
  * (the approved plan.md could not be uploaded/attached). The build itself