@dxos/plugin-assistant 0.8.4-main.abd8ff62ef → 0.8.4-main.bc2380dfbc

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 (414) hide show
  1. package/LICENSE +102 -5
  2. package/README.md +1 -1
  3. package/dist/lib/neutral/AgentArticle-5XDDEXMP.mjs +169 -0
  4. package/dist/lib/neutral/AgentArticle-5XDDEXMP.mjs.map +7 -0
  5. package/dist/lib/neutral/AgentProperties-CNH4JMDZ.mjs +100 -0
  6. package/dist/lib/neutral/AgentProperties-CNH4JMDZ.mjs.map +7 -0
  7. package/dist/lib/neutral/AssistantPlugin.mjs +10 -0
  8. package/dist/lib/neutral/AssistantPlugin.node.mjs +46 -0
  9. package/dist/lib/neutral/AssistantPlugin.node.mjs.map +7 -0
  10. package/dist/lib/neutral/AssistantPlugin.workerd.mjs +40 -0
  11. package/dist/lib/neutral/AssistantPlugin.workerd.mjs.map +7 -0
  12. package/dist/lib/neutral/BlueprintArticle-U7LUBFOS.mjs +27 -0
  13. package/dist/lib/neutral/BlueprintArticle-U7LUBFOS.mjs.map +7 -0
  14. package/dist/lib/neutral/ChatArticle-WVAFZPVI.mjs +8 -0
  15. package/dist/lib/neutral/ChatCompanion-DFRMRUK3.mjs +133 -0
  16. package/dist/lib/neutral/ChatCompanion-DFRMRUK3.mjs.map +7 -0
  17. package/dist/lib/neutral/ChatDialog-QKZLG7SQ.mjs +72 -0
  18. package/dist/lib/neutral/ChatDialog-QKZLG7SQ.mjs.map +7 -0
  19. package/dist/lib/neutral/PlanArticle-TS5ULWYS.mjs +28 -0
  20. package/dist/lib/neutral/PlanArticle-TS5ULWYS.mjs.map +7 -0
  21. package/dist/lib/neutral/RoutineArticle-H7ECM7AH.mjs +116 -0
  22. package/dist/lib/neutral/RoutineArticle-H7ECM7AH.mjs.map +7 -0
  23. package/dist/lib/neutral/RoutineList-CLDXGQUD.mjs +48 -0
  24. package/dist/lib/neutral/RoutineList-CLDXGQUD.mjs.map +7 -0
  25. package/dist/lib/neutral/TracePanel-C77SPEIS.mjs +672 -0
  26. package/dist/lib/neutral/TracePanel-C77SPEIS.mjs.map +7 -0
  27. package/dist/lib/neutral/TriggerStatus-WTFYUIG5.mjs +92 -0
  28. package/dist/lib/neutral/TriggerStatus-WTFYUIG5.mjs.map +7 -0
  29. package/dist/lib/neutral/ai-service-GJXMI5OI.mjs +22 -0
  30. package/dist/lib/neutral/ai-service-GJXMI5OI.mjs.map +7 -0
  31. package/dist/lib/neutral/app-graph-builder-5QBICPYP.mjs +200 -0
  32. package/dist/lib/neutral/app-graph-builder-5QBICPYP.mjs.map +7 -0
  33. package/dist/lib/neutral/blueprint-definition-VXJYQ5AE.mjs +32 -0
  34. package/dist/lib/neutral/blueprint-definition-VXJYQ5AE.mjs.map +7 -0
  35. package/dist/lib/neutral/blueprints/index.mjs +8 -0
  36. package/dist/lib/neutral/capabilities/index.mjs +35 -0
  37. package/dist/lib/neutral/capabilities/index.mjs.map +7 -0
  38. package/dist/lib/neutral/chunk-3FIV362Z.mjs +8 -0
  39. package/dist/lib/neutral/chunk-3FIV362Z.mjs.map +7 -0
  40. package/dist/lib/neutral/chunk-4CQZCMRP.mjs +25 -0
  41. package/dist/lib/neutral/chunk-4CQZCMRP.mjs.map +7 -0
  42. package/dist/lib/{browser/blueprints/index.mjs → neutral/chunk-5H6UJHLF.mjs} +4 -5
  43. package/dist/lib/neutral/chunk-5H6UJHLF.mjs.map +7 -0
  44. package/dist/lib/neutral/chunk-ITJX2YZZ.mjs +98 -0
  45. package/dist/lib/neutral/chunk-ITJX2YZZ.mjs.map +7 -0
  46. package/dist/lib/neutral/chunk-IZQWGJNG.mjs +380 -0
  47. package/dist/lib/neutral/chunk-IZQWGJNG.mjs.map +7 -0
  48. package/dist/lib/neutral/chunk-SLIPV6NN.mjs +106 -0
  49. package/dist/lib/neutral/chunk-SLIPV6NN.mjs.map +7 -0
  50. package/dist/lib/neutral/companion-chat-provisioner-2Y6PRSDK.mjs +116 -0
  51. package/dist/lib/neutral/companion-chat-provisioner-2Y6PRSDK.mjs.map +7 -0
  52. package/dist/lib/{browser/chunk-VK53MITK.mjs → neutral/components/index.mjs} +139 -141
  53. package/dist/lib/neutral/components/index.mjs.map +7 -0
  54. package/dist/lib/neutral/containers/index.mjs +29 -0
  55. package/dist/lib/neutral/containers/index.mjs.map +7 -0
  56. package/dist/lib/{browser/create-chat-E2ZLVTLP.mjs → neutral/create-chat-QQZURUBW.mjs} +10 -12
  57. package/dist/lib/neutral/create-chat-QQZURUBW.mjs.map +7 -0
  58. package/dist/lib/neutral/create-object-UZCJCPWM.mjs +93 -0
  59. package/dist/lib/neutral/create-object-UZCJCPWM.mjs.map +7 -0
  60. package/dist/lib/neutral/edge-model-resolver-XDNBZ3US.mjs +21 -0
  61. package/dist/lib/neutral/edge-model-resolver-XDNBZ3US.mjs.map +7 -0
  62. package/dist/lib/{browser/ensure-companion-chat-7GDMXSQW.mjs → neutral/ensure-companion-chat-5S4LZM2H.mjs} +9 -16
  63. package/dist/lib/neutral/ensure-companion-chat-5S4LZM2H.mjs.map +7 -0
  64. package/dist/lib/{browser/chunk-M55MBYG7.mjs → neutral/hooks/index.mjs} +106 -111
  65. package/dist/lib/neutral/hooks/index.mjs.map +7 -0
  66. package/dist/lib/neutral/index.mjs +32 -0
  67. package/dist/lib/neutral/local-model-resolver-I2BENFFS.mjs +24 -0
  68. package/dist/lib/neutral/local-model-resolver-I2BENFFS.mjs.map +7 -0
  69. package/dist/lib/neutral/markdown-KTBFOMFM.mjs +110 -0
  70. package/dist/lib/neutral/markdown-KTBFOMFM.mjs.map +7 -0
  71. package/dist/lib/neutral/meta.json +1 -0
  72. package/dist/lib/neutral/meta.mjs +12 -0
  73. package/dist/lib/neutral/migrations-7DEMVBOG.mjs +28 -0
  74. package/dist/lib/neutral/migrations-7DEMVBOG.mjs.map +7 -0
  75. package/dist/lib/{browser/on-create-space-4J3KTNAJ.mjs → neutral/on-create-space-Q2R7OSP6.mjs} +4 -7
  76. package/dist/lib/neutral/on-create-space-Q2R7OSP6.mjs.map +7 -0
  77. package/dist/lib/neutral/operation-handler-SF36MOB5.mjs +13 -0
  78. package/dist/lib/neutral/operation-handler-SF36MOB5.mjs.map +7 -0
  79. package/dist/lib/neutral/operations/index.mjs +8 -0
  80. package/dist/lib/neutral/plugin.mjs +16 -0
  81. package/dist/lib/neutral/plugin.mjs.map +7 -0
  82. package/dist/lib/neutral/react-surface-T4R65UJU.mjs +174 -0
  83. package/dist/lib/neutral/react-surface-T4R65UJU.mjs.map +7 -0
  84. package/dist/lib/{browser/resolve-navigation-targets-3ZPQE6SZ.mjs → neutral/resolve-navigation-targets-VXBSWV5L.mjs} +3 -5
  85. package/dist/lib/neutral/resolve-navigation-targets-VXBSWV5L.mjs.map +7 -0
  86. package/dist/lib/{browser/run-prompt-in-new-chat-AA3KPBDN.mjs → neutral/run-prompt-in-new-chat-VJXXZ6SI.mjs} +16 -19
  87. package/dist/lib/neutral/run-prompt-in-new-chat-VJXXZ6SI.mjs.map +7 -0
  88. package/dist/lib/{browser/set-current-chat-P4VHI3YF.mjs → neutral/set-current-chat-ZFDCYK5I.mjs} +4 -10
  89. package/dist/lib/neutral/set-current-chat-ZFDCYK5I.mjs.map +7 -0
  90. package/dist/lib/neutral/settings-W4BLWQ53.mjs +28 -0
  91. package/dist/lib/neutral/settings-W4BLWQ53.mjs.map +7 -0
  92. package/dist/lib/neutral/state-H3G7QCU6.mjs +28 -0
  93. package/dist/lib/neutral/state-H3G7QCU6.mjs.map +7 -0
  94. package/dist/lib/neutral/testing.mjs +8 -0
  95. package/dist/lib/neutral/toggle-trace-panel-debug-JGTB6KVJ.mjs +21 -0
  96. package/dist/lib/neutral/toggle-trace-panel-debug-JGTB6KVJ.mjs.map +7 -0
  97. package/dist/lib/neutral/toolkit-GVWXPDLJ.mjs +14 -0
  98. package/dist/lib/neutral/toolkit-GVWXPDLJ.mjs.map +7 -0
  99. package/dist/lib/{browser → neutral}/translations.mjs +5 -2
  100. package/dist/lib/neutral/translations.mjs.map +7 -0
  101. package/dist/lib/neutral/types/index.mjs +20 -0
  102. package/dist/lib/neutral/types/index.mjs.map +7 -0
  103. package/dist/lib/{browser/update-chat-name-VWKNUON7.mjs → neutral/update-chat-name-ENXRUMDJ.mjs} +3 -5
  104. package/dist/lib/neutral/update-chat-name-ENXRUMDJ.mjs.map +7 -0
  105. package/dist/types/src/AssistantPlugin.d.ts.map +1 -1
  106. package/dist/types/src/AssistantPlugin.node.d.ts.map +1 -1
  107. package/dist/types/src/AssistantPlugin.workerd.d.ts +4 -0
  108. package/dist/types/src/AssistantPlugin.workerd.d.ts.map +1 -0
  109. package/dist/types/src/capabilities/companion-chat-provisioner.d.ts.map +1 -1
  110. package/dist/types/src/capabilities/create-object.d.ts +31 -0
  111. package/dist/types/src/capabilities/create-object.d.ts.map +1 -0
  112. package/dist/types/src/capabilities/index.d.ts +27 -1
  113. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  114. package/dist/types/src/capabilities/markdown.d.ts +1 -1
  115. package/dist/types/src/capabilities/migrations.d.ts +2 -1
  116. package/dist/types/src/capabilities/migrations.d.ts.map +1 -1
  117. package/dist/types/src/components/Chat/Chat.d.ts +11 -5
  118. package/dist/types/src/components/Chat/Chat.d.ts.map +1 -1
  119. package/dist/types/src/components/ChatPrompt/ChatOptions.d.ts +2 -2
  120. package/dist/types/src/components/ChatPrompt/ChatOptions.d.ts.map +1 -1
  121. package/dist/types/src/components/ChatPrompt/ChatReferences.d.ts +2 -2
  122. package/dist/types/src/components/ChatPrompt/ChatReferences.d.ts.map +1 -1
  123. package/dist/types/src/components/ChatThread/ChatThread.stories.d.ts +2 -3
  124. package/dist/types/src/components/ChatThread/ChatThread.stories.d.ts.map +1 -1
  125. package/dist/types/src/components/ChatThread/registry.d.ts.map +1 -1
  126. package/dist/types/src/components/ChatThread/widgets/StatusWidget.d.ts +2 -15
  127. package/dist/types/src/components/ChatThread/widgets/StatusWidget.d.ts.map +1 -1
  128. package/dist/types/src/components/ChatThread/widgets/SummaryWidget.d.ts.map +1 -1
  129. package/dist/types/src/components/ProcessTree/ProcessTree.d.ts.map +1 -1
  130. package/dist/types/src/components/TaskList/TaskList.d.ts +10 -0
  131. package/dist/types/src/components/TaskList/TaskList.d.ts.map +1 -0
  132. package/dist/types/src/components/TaskList/TaskList.stories.d.ts +16 -0
  133. package/dist/types/src/components/TaskList/TaskList.stories.d.ts.map +1 -0
  134. package/dist/types/src/components/TaskList/index.d.ts +2 -0
  135. package/dist/types/src/components/TaskList/index.d.ts.map +1 -0
  136. package/dist/types/src/components/TemplateEditor/TemplateForm.d.ts +1 -2
  137. package/dist/types/src/components/TemplateEditor/TemplateForm.d.ts.map +1 -1
  138. package/dist/types/src/components/TemplateEditor/TemplateForm.stories.d.ts +1 -1
  139. package/dist/types/src/components/TemplateEditor/TemplateForm.stories.d.ts.map +1 -1
  140. package/dist/types/src/components/Toolbox/Toolbox.d.ts.map +1 -1
  141. package/dist/types/src/components/index.d.ts +1 -0
  142. package/dist/types/src/components/index.d.ts.map +1 -1
  143. package/dist/types/src/containers/AgentArticle/AgentArticle.d.ts.map +1 -1
  144. package/dist/types/src/containers/{ChatContainer/ChatContainer.d.ts → ChatArticle/ChatArticle.d.ts} +3 -3
  145. package/dist/types/src/containers/ChatArticle/ChatArticle.d.ts.map +1 -0
  146. package/dist/types/src/containers/ChatArticle/index.d.ts +2 -0
  147. package/dist/types/src/containers/ChatArticle/index.d.ts.map +1 -0
  148. package/dist/types/src/containers/ChatCompanion/ChatCompanion.d.ts.map +1 -1
  149. package/dist/types/src/containers/PlanArticle/PlanArticle.d.ts +6 -0
  150. package/dist/types/src/containers/PlanArticle/PlanArticle.d.ts.map +1 -0
  151. package/dist/types/src/containers/PlanArticle/index.d.ts +2 -0
  152. package/dist/types/src/containers/PlanArticle/index.d.ts.map +1 -0
  153. package/dist/types/src/containers/RoutineArticle/RoutineArticle.d.ts.map +1 -1
  154. package/dist/types/src/containers/RoutineArticle/RoutineArticle.stories.d.ts +15 -0
  155. package/dist/types/src/containers/RoutineArticle/RoutineArticle.stories.d.ts.map +1 -0
  156. package/dist/types/src/containers/TracePanel/TracePanel.d.ts.map +1 -1
  157. package/dist/types/src/containers/TracePanel/TracePanel.stories.d.ts.map +1 -1
  158. package/dist/types/src/containers/TracePanel/execution-graph.d.ts +11 -2
  159. package/dist/types/src/containers/TracePanel/execution-graph.d.ts.map +1 -1
  160. package/dist/types/src/containers/TracePanel/span-tree.d.ts +73 -0
  161. package/dist/types/src/containers/TracePanel/span-tree.d.ts.map +1 -0
  162. package/dist/types/src/containers/TracePanel/span-tree.test.d.ts +2 -0
  163. package/dist/types/src/containers/TracePanel/span-tree.test.d.ts.map +1 -0
  164. package/dist/types/src/containers/index.d.ts +4 -3
  165. package/dist/types/src/containers/index.d.ts.map +1 -1
  166. package/dist/types/src/feed-logger.d.ts +13 -0
  167. package/dist/types/src/feed-logger.d.ts.map +1 -0
  168. package/dist/types/src/hooks/useBlueprintRegistry.d.ts +3 -3
  169. package/dist/types/src/hooks/useBlueprintRegistry.d.ts.map +1 -1
  170. package/dist/types/src/hooks/useChatProcessor.d.ts +1 -1
  171. package/dist/types/src/hooks/useChatProcessor.d.ts.map +1 -1
  172. package/dist/types/src/hooks/useChatServices.d.ts +1 -1
  173. package/dist/types/src/hooks/useChatServices.d.ts.map +1 -1
  174. package/dist/types/src/hooks/useChatToolbarActions.d.ts.map +1 -1
  175. package/dist/types/src/hooks/useContextBinder.d.ts +2 -2
  176. package/dist/types/src/hooks/useContextBinder.d.ts.map +1 -1
  177. package/dist/types/src/hooks/useContextObjects.d.ts +2 -2
  178. package/dist/types/src/hooks/useContextObjects.d.ts.map +1 -1
  179. package/dist/types/src/index.d.ts +2 -4
  180. package/dist/types/src/index.d.ts.map +1 -1
  181. package/dist/types/src/operations/create-chat.d.ts +2 -2
  182. package/dist/types/src/operations/create-chat.d.ts.map +1 -1
  183. package/dist/types/src/operations/ensure-companion-chat.d.ts +2 -2
  184. package/dist/types/src/operations/ensure-companion-chat.d.ts.map +1 -1
  185. package/dist/types/src/operations/index.d.ts +0 -1
  186. package/dist/types/src/operations/index.d.ts.map +1 -1
  187. package/dist/types/src/operations/on-create-space.d.ts +2 -2
  188. package/dist/types/src/operations/on-create-space.d.ts.map +1 -1
  189. package/dist/types/src/operations/run-prompt-in-new-chat.d.ts +2 -2
  190. package/dist/types/src/operations/run-prompt-in-new-chat.d.ts.map +1 -1
  191. package/dist/types/src/operations/set-current-chat.d.ts +2 -2
  192. package/dist/types/src/operations/set-current-chat.d.ts.map +1 -1
  193. package/dist/types/src/operations/toggle-trace-panel-debug.d.ts +5 -0
  194. package/dist/types/src/operations/toggle-trace-panel-debug.d.ts.map +1 -0
  195. package/dist/types/src/operations/update-chat-name.d.ts +2 -2
  196. package/dist/types/src/operations/update-chat-name.d.ts.map +1 -1
  197. package/dist/types/src/plugin.d.ts +4 -0
  198. package/dist/types/src/plugin.d.ts.map +1 -0
  199. package/dist/types/src/processor/processor.d.ts +11 -11
  200. package/dist/types/src/processor/processor.d.ts.map +1 -1
  201. package/dist/types/src/testing/test-generator.d.ts +2 -3
  202. package/dist/types/src/testing/test-generator.d.ts.map +1 -1
  203. package/dist/types/src/testing/test-sequence.d.ts +3 -3
  204. package/dist/types/src/testing/test-sequence.d.ts.map +1 -1
  205. package/dist/types/src/testing.d.ts +2 -0
  206. package/dist/types/src/testing.d.ts.map +1 -0
  207. package/dist/types/src/translations.d.ts.map +1 -1
  208. package/dist/types/src/types/Assistant.d.ts +1 -0
  209. package/dist/types/src/types/Assistant.d.ts.map +1 -1
  210. package/dist/types/src/types/AssistantCapabilities.d.ts +30 -0
  211. package/dist/types/src/types/AssistantCapabilities.d.ts.map +1 -0
  212. package/dist/types/src/types/AssistantEvents.d.ts +3 -0
  213. package/dist/types/src/types/AssistantEvents.d.ts.map +1 -0
  214. package/dist/types/src/{operations/definitions.d.ts → types/AssistantOperation.d.ts} +10 -6
  215. package/dist/types/src/types/AssistantOperation.d.ts.map +1 -0
  216. package/dist/types/src/types/Settings.d.ts +1 -0
  217. package/dist/types/src/types/Settings.d.ts.map +1 -1
  218. package/dist/types/src/types/index.d.ts +3 -2
  219. package/dist/types/src/types/index.d.ts.map +1 -1
  220. package/dist/types/src/util/suggestions.d.ts +9 -0
  221. package/dist/types/src/util/suggestions.d.ts.map +1 -0
  222. package/dist/types/src/util/suggestions.test.d.ts +2 -0
  223. package/dist/types/src/util/suggestions.test.d.ts.map +1 -0
  224. package/dist/types/tsconfig.tsbuildinfo +1 -1
  225. package/package.json +152 -128
  226. package/src/AssistantPlugin.node.ts +7 -88
  227. package/src/AssistantPlugin.test.ts +3 -3
  228. package/src/AssistantPlugin.ts +126 -0
  229. package/src/AssistantPlugin.workerd.ts +41 -0
  230. package/src/blueprints/assistant/blueprint.ts +1 -1
  231. package/src/capabilities/app-graph-builder.ts +48 -9
  232. package/src/capabilities/companion-chat-provisioner.ts +4 -5
  233. package/src/capabilities/create-object.ts +107 -0
  234. package/src/capabilities/index.ts +1 -0
  235. package/src/capabilities/markdown.ts +2 -2
  236. package/src/capabilities/migrations.ts +3 -3
  237. package/src/capabilities/react-surface.tsx +15 -7
  238. package/src/components/Chat/Chat.tsx +35 -5
  239. package/src/components/ChatPrompt/ChatMcpErrors.tsx +1 -1
  240. package/src/components/ChatPrompt/ChatOptions.stories.tsx +4 -4
  241. package/src/components/ChatPrompt/ChatOptions.tsx +12 -11
  242. package/src/components/ChatPrompt/ChatPrompt.tsx +3 -3
  243. package/src/components/ChatPrompt/ChatReferences.tsx +3 -3
  244. package/src/components/ChatPrompt/ChatStatus.tsx +1 -1
  245. package/src/components/ChatPrompt/ChatStatusIndicator.tsx +1 -1
  246. package/src/components/ChatThread/Anchor.stories.tsx +3 -3
  247. package/src/components/ChatThread/ChatThread.stories.tsx +25 -29
  248. package/src/components/ChatThread/ChatThread.tsx +1 -1
  249. package/src/components/ChatThread/registry.tsx +8 -4
  250. package/src/components/ChatThread/sync.test.ts +4 -6
  251. package/src/components/ChatThread/widgets/ReasoningWidget.ts +2 -2
  252. package/src/components/ChatThread/widgets/StatusWidget.ts +3 -83
  253. package/src/components/ChatThread/widgets/SummaryWidget.tsx +1 -3
  254. package/src/components/ProcessTree/ProcessTree.tsx +2 -1
  255. package/src/components/TaskList/TaskList.stories.tsx +44 -0
  256. package/src/components/TaskList/TaskList.tsx +45 -0
  257. package/src/{cli → components/TaskList}/index.ts +1 -1
  258. package/src/components/TemplateEditor/TemplateEditor.stories.tsx +1 -1
  259. package/src/components/TemplateEditor/TemplateForm.stories.tsx +2 -6
  260. package/src/components/TemplateEditor/TemplateForm.tsx +48 -69
  261. package/src/components/Toolbox/Toolbox.stories.tsx +4 -4
  262. package/src/components/Toolbox/Toolbox.tsx +2 -2
  263. package/src/components/index.ts +1 -0
  264. package/src/containers/AgentArticle/AgentArticle.stories.tsx +7 -7
  265. package/src/containers/AgentArticle/AgentArticle.tsx +11 -7
  266. package/src/containers/AgentProperties/AgentProperties.stories.tsx +4 -4
  267. package/src/containers/AgentProperties/AgentProperties.tsx +4 -4
  268. package/src/containers/{ChatContainer/ChatContainer.tsx → ChatArticle/ChatArticle.tsx} +11 -9
  269. package/src/containers/ChatArticle/index.ts +5 -0
  270. package/src/containers/ChatCompanion/ChatCompanion.tsx +20 -29
  271. package/src/containers/PlanArticle/PlanArticle.tsx +33 -0
  272. package/src/containers/PlanArticle/index.ts +5 -0
  273. package/src/containers/RoutineArticle/RoutineArticle.stories.tsx +75 -0
  274. package/src/containers/RoutineArticle/RoutineArticle.tsx +74 -24
  275. package/src/containers/RoutineList/RoutineList.stories.tsx +3 -3
  276. package/src/containers/RoutineList/RoutineList.tsx +3 -3
  277. package/src/containers/TracePanel/TracePanel.stories.tsx +33 -26
  278. package/src/containers/TracePanel/TracePanel.tsx +72 -33
  279. package/src/containers/TracePanel/dxn-extractor.ts +7 -7
  280. package/src/containers/TracePanel/execution-graph.test.ts +218 -213
  281. package/src/containers/TracePanel/execution-graph.ts +316 -271
  282. package/src/containers/TracePanel/span-tree.test.ts +166 -0
  283. package/src/containers/TracePanel/span-tree.ts +214 -0
  284. package/src/containers/index.ts +4 -3
  285. package/src/{queue-logger.ts → feed-logger.ts} +37 -27
  286. package/src/hooks/useBlueprintRegistry.ts +12 -6
  287. package/src/hooks/useChatProcessor.ts +3 -3
  288. package/src/hooks/useChatServices.ts +1 -1
  289. package/src/hooks/useChatToolbarActions.ts +86 -88
  290. package/src/hooks/useContextBinder.ts +4 -4
  291. package/src/hooks/useContextObjects.ts +2 -2
  292. package/src/index.ts +2 -9
  293. package/src/operations/create-chat.ts +16 -11
  294. package/src/operations/ensure-companion-chat.ts +47 -45
  295. package/src/operations/index.ts +1 -2
  296. package/src/operations/on-create-space.ts +3 -3
  297. package/src/operations/prompt.test.ts +5 -5
  298. package/src/operations/resolve-navigation-targets.ts +2 -2
  299. package/src/operations/run-prompt-in-new-chat.ts +85 -84
  300. package/src/operations/set-current-chat.ts +3 -4
  301. package/src/operations/toggle-trace-panel-debug.ts +27 -0
  302. package/src/operations/update-chat-name.ts +2 -2
  303. package/src/plugin.ts +11 -0
  304. package/src/processor/processor.test.ts +1 -1
  305. package/src/processor/processor.ts +15 -15
  306. package/src/testing/test-generator.test.ts +41 -52
  307. package/src/testing/test-generator.ts +174 -189
  308. package/src/testing/test-sequence.ts +3 -3
  309. package/src/testing/trace-timeline.conversations.json +1 -1
  310. package/src/testing/trace-timeline.test.ts +3 -5
  311. package/src/testing.ts +7 -0
  312. package/src/translations.ts +5 -2
  313. package/src/types/Assistant.ts +2 -1
  314. package/src/types/AssistantCapabilities.ts +35 -0
  315. package/src/types/AssistantEvents.ts +11 -0
  316. package/src/{operations/definitions.ts → types/AssistantOperation.ts} +15 -0
  317. package/src/types/Settings.ts +6 -0
  318. package/src/types/index.ts +5 -2
  319. package/src/util/suggestions.test.ts +52 -0
  320. package/src/util/suggestions.ts +42 -0
  321. package/dist/lib/browser/blueprints/index.mjs.map +0 -7
  322. package/dist/lib/browser/chunk-DVOOFAWU.mjs +0 -175
  323. package/dist/lib/browser/chunk-DVOOFAWU.mjs.map +0 -7
  324. package/dist/lib/browser/chunk-M55MBYG7.mjs.map +0 -7
  325. package/dist/lib/browser/chunk-SEMCG4ZK.mjs +0 -195
  326. package/dist/lib/browser/chunk-SEMCG4ZK.mjs.map +0 -7
  327. package/dist/lib/browser/chunk-VK53MITK.mjs.map +0 -7
  328. package/dist/lib/browser/cli/index.mjs +0 -73
  329. package/dist/lib/browser/cli/index.mjs.map +0 -7
  330. package/dist/lib/browser/components/index.mjs +0 -20
  331. package/dist/lib/browser/create-chat-E2ZLVTLP.mjs.map +0 -7
  332. package/dist/lib/browser/ensure-companion-chat-7GDMXSQW.mjs.map +0 -7
  333. package/dist/lib/browser/hooks/index.mjs +0 -39
  334. package/dist/lib/browser/index.mjs +0 -84
  335. package/dist/lib/browser/index.mjs.map +0 -7
  336. package/dist/lib/browser/meta.json +0 -1
  337. package/dist/lib/browser/on-create-space-4J3KTNAJ.mjs.map +0 -7
  338. package/dist/lib/browser/operations/index.mjs +0 -13
  339. package/dist/lib/browser/operations/index.mjs.map +0 -7
  340. package/dist/lib/browser/resolve-navigation-targets-3ZPQE6SZ.mjs.map +0 -7
  341. package/dist/lib/browser/run-prompt-in-new-chat-AA3KPBDN.mjs.map +0 -7
  342. package/dist/lib/browser/set-current-chat-P4VHI3YF.mjs.map +0 -7
  343. package/dist/lib/browser/translations.mjs.map +0 -7
  344. package/dist/lib/browser/types/index.mjs +0 -18
  345. package/dist/lib/browser/update-chat-name-VWKNUON7.mjs.map +0 -7
  346. package/dist/lib/node-esm/AssistantSettings-23A5IMHS.mjs +0 -41
  347. package/dist/lib/node-esm/AssistantSettings-23A5IMHS.mjs.map +0 -7
  348. package/dist/lib/node-esm/blueprints/index.mjs +0 -32
  349. package/dist/lib/node-esm/blueprints/index.mjs.map +0 -7
  350. package/dist/lib/node-esm/chunk-FWC3ZGPH.mjs +0 -933
  351. package/dist/lib/node-esm/chunk-FWC3ZGPH.mjs.map +0 -7
  352. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
  353. package/dist/lib/node-esm/chunk-JKEB3NFZ.mjs +0 -2302
  354. package/dist/lib/node-esm/chunk-JKEB3NFZ.mjs.map +0 -7
  355. package/dist/lib/node-esm/chunk-MBDVPB4V.mjs +0 -176
  356. package/dist/lib/node-esm/chunk-MBDVPB4V.mjs.map +0 -7
  357. package/dist/lib/node-esm/chunk-XAFVSEUJ.mjs +0 -196
  358. package/dist/lib/node-esm/chunk-XAFVSEUJ.mjs.map +0 -7
  359. package/dist/lib/node-esm/cli/index.mjs +0 -74
  360. package/dist/lib/node-esm/cli/index.mjs.map +0 -7
  361. package/dist/lib/node-esm/components/index.mjs +0 -21
  362. package/dist/lib/node-esm/create-chat-UQQ3542N.mjs +0 -74
  363. package/dist/lib/node-esm/create-chat-UQQ3542N.mjs.map +0 -7
  364. package/dist/lib/node-esm/ensure-companion-chat-LVCPD4DJ.mjs +0 -66
  365. package/dist/lib/node-esm/ensure-companion-chat-LVCPD4DJ.mjs.map +0 -7
  366. package/dist/lib/node-esm/hooks/index.mjs +0 -40
  367. package/dist/lib/node-esm/index.mjs +0 -85
  368. package/dist/lib/node-esm/index.mjs.map +0 -7
  369. package/dist/lib/node-esm/meta.json +0 -1
  370. package/dist/lib/node-esm/on-create-space-M63UBTTT.mjs +0 -23
  371. package/dist/lib/node-esm/on-create-space-M63UBTTT.mjs.map +0 -7
  372. package/dist/lib/node-esm/operations/index.mjs +0 -14
  373. package/dist/lib/node-esm/operations/index.mjs.map +0 -7
  374. package/dist/lib/node-esm/resolve-navigation-targets-HO77CAFT.mjs +0 -23
  375. package/dist/lib/node-esm/resolve-navigation-targets-HO77CAFT.mjs.map +0 -7
  376. package/dist/lib/node-esm/run-prompt-in-new-chat-RIRYYYPK.mjs +0 -107
  377. package/dist/lib/node-esm/run-prompt-in-new-chat-RIRYYYPK.mjs.map +0 -7
  378. package/dist/lib/node-esm/set-current-chat-Z7GJ52VX.mjs +0 -55
  379. package/dist/lib/node-esm/set-current-chat-Z7GJ52VX.mjs.map +0 -7
  380. package/dist/lib/node-esm/translations.mjs +0 -173
  381. package/dist/lib/node-esm/translations.mjs.map +0 -7
  382. package/dist/lib/node-esm/types/index.mjs +0 -19
  383. package/dist/lib/node-esm/update-chat-name-GV4HX32Z.mjs +0 -57
  384. package/dist/lib/node-esm/update-chat-name-GV4HX32Z.mjs.map +0 -7
  385. package/dist/types/src/cli/index.d.ts +0 -2
  386. package/dist/types/src/cli/index.d.ts.map +0 -1
  387. package/dist/types/src/cli/plugin.d.ts +0 -10
  388. package/dist/types/src/cli/plugin.d.ts.map +0 -1
  389. package/dist/types/src/containers/ChatContainer/ChatContainer.d.ts.map +0 -1
  390. package/dist/types/src/containers/ChatContainer/index.d.ts +0 -2
  391. package/dist/types/src/containers/ChatContainer/index.d.ts.map +0 -1
  392. package/dist/types/src/operations/definitions.d.ts.map +0 -1
  393. package/dist/types/src/queue-logger.d.ts +0 -11
  394. package/dist/types/src/queue-logger.d.ts.map +0 -1
  395. package/dist/types/src/types/capabilities.d.ts +0 -32
  396. package/dist/types/src/types/capabilities.d.ts.map +0 -1
  397. package/dist/types/src/types/events.d.ts +0 -5
  398. package/dist/types/src/types/events.d.ts.map +0 -1
  399. package/src/AssistantPlugin.tsx +0 -238
  400. package/src/cli/plugin.ts +0 -83
  401. package/src/containers/ChatContainer/index.ts +0 -5
  402. package/src/types/capabilities.ts +0 -35
  403. package/src/types/events.ts +0 -11
  404. /package/dist/lib/{browser/chunk-J5LGTIGS.mjs.map → neutral/AssistantPlugin.mjs.map} +0 -0
  405. /package/dist/lib/{browser → neutral}/AssistantSettings-GG52BLKS.mjs +0 -0
  406. /package/dist/lib/{browser → neutral}/AssistantSettings-GG52BLKS.mjs.map +0 -0
  407. /package/dist/lib/{browser/components/index.mjs.map → neutral/ChatArticle-WVAFZPVI.mjs.map} +0 -0
  408. /package/dist/lib/{browser/hooks → neutral/blueprints}/index.mjs.map +0 -0
  409. /package/dist/lib/{browser → neutral}/chunk-J5LGTIGS.mjs +0 -0
  410. /package/dist/lib/{browser/types/index.mjs.map → neutral/chunk-J5LGTIGS.mjs.map} +0 -0
  411. /package/dist/lib/{node-esm/components → neutral}/index.mjs.map +0 -0
  412. /package/dist/lib/{node-esm/chunk-HSLMI22Q.mjs.map → neutral/meta.mjs.map} +0 -0
  413. /package/dist/lib/{node-esm/hooks → neutral/operations}/index.mjs.map +0 -0
  414. /package/dist/lib/{node-esm/types/index.mjs.map → neutral/testing.mjs.map} +0 -0
@@ -3,10 +3,10 @@
3
3
  //
4
4
 
5
5
  import * as Array from 'effect/Array';
6
+ import * as Either from 'effect/Either';
6
7
  import { pipe } from 'effect/Function';
7
- import * as Order from 'effect/Order';
8
8
  import * as Pipeable from 'effect/Pipeable';
9
- import * as Predicate from 'effect/Predicate';
9
+ import * as Schema from 'effect/Schema';
10
10
  import * as Struct from 'effect/Struct';
11
11
 
12
12
  import { AgentRequestBegin, AgentRequestEnd, CompleteBlock } from '@dxos/assistant';
@@ -15,6 +15,8 @@ import { AGENT_PROCESS_KEY, Process } from '@dxos/functions-runtime';
15
15
  import { LogLevel, log } from '@dxos/log';
16
16
  import { type Commit } from '@dxos/react-ui-components';
17
17
 
18
+ import { ROOT_SPAN_ID, type Span, buildSpanTree, isSpanBeginEvent, isSpanEndEvent, walkSpanTree } from './span-tree';
19
+
18
20
  /**
19
21
  * Branch name for top-level operation invocations.
20
22
  */
@@ -33,39 +35,15 @@ const ICONS = {
33
35
  icon: 'ph--user--regular',
34
36
  level: LogLevel.VERBOSE,
35
37
  },
36
- assistantMessage: {
37
- icon: 'ph--drone--regular',
38
- level: LogLevel.VERBOSE,
39
- },
40
38
  statusMessage: {
41
39
  icon: 'ph--info--regular',
42
40
  level: LogLevel.VERBOSE,
43
41
  },
44
- toolCall: {
45
- icon: 'ph--wrench--regular',
46
- level: LogLevel.VERBOSE,
47
- },
48
- toolResult: {
49
- icon: 'ph--wrench--regular',
50
- level: LogLevel.INFO,
51
- },
52
- toolResultError: {
53
- icon: 'ph--wrench--regular',
54
- level: LogLevel.ERROR,
55
- },
56
- toolResultSuccess: {
57
- icon: 'ph--wrench--regular',
58
- level: LogLevel.INFO,
59
- },
60
- runningAgent: {
61
- icon: 'ph--spinner-gap--regular',
62
- level: LogLevel.INFO,
63
- },
64
42
  operationStart: {
65
43
  icon: 'ph--function--regular',
66
44
  level: LogLevel.VERBOSE,
67
45
  },
68
- operationEnd: {
46
+ operationEndSuccess: {
69
47
  icon: 'ph--function--regular',
70
48
  level: LogLevel.INFO,
71
49
  },
@@ -73,10 +51,6 @@ const ICONS = {
73
51
  icon: 'ph--function--regular',
74
52
  level: LogLevel.ERROR,
75
53
  },
76
- operationEndSuccess: {
77
- icon: 'ph--function--regular',
78
- level: LogLevel.INFO,
79
- },
80
54
  agentRequestRunning: {
81
55
  icon: 'ph--spinner-gap--regular',
82
56
  level: LogLevel.VERBOSE,
@@ -87,26 +61,6 @@ const ICONS = {
87
61
  },
88
62
  } as const;
89
63
 
90
- const tagPid = (pid: string) => `pid:${pid}`;
91
- const tagParentPid = (parentPid: string) => `parent-pid:${parentPid}`;
92
- const tagConversation = (conversationId: string) => `conversation:${conversationId}`;
93
- const tagOperationBegin = (pid: string) => `operation-begin:${pid}`;
94
- const tagStartMarker = (pid: string) => `start-marker:${pid}`;
95
-
96
- const getTags = (meta: Trace.Meta) => {
97
- const tags: string[] = [];
98
- if (meta.pid) {
99
- tags.push(tagPid(meta.pid));
100
- }
101
- if (meta.parentPid) {
102
- tags.push(tagParentPid(meta.parentPid));
103
- }
104
- if (meta.conversationId) {
105
- tags.push(tagConversation(meta.conversationId));
106
- }
107
- return tags;
108
- };
109
-
110
64
  export interface BuildExecutionGraphParams {
111
65
  traceMessages: Trace.Message[];
112
66
  activeProcesses?: readonly Process.Info[];
@@ -115,190 +69,326 @@ export interface BuildExecutionGraphParams {
115
69
 
116
70
  /**
117
71
  * Builds a Timeline-compatible execution graph from trace messages and active processes.
72
+ *
73
+ * The conversion runs in two stages:
74
+ * 1. `buildSpanTree` converts the flat list of trace messages into a hierarchical
75
+ * tree of spans grouped by process id (pid). See `./span-tree.ts`.
76
+ * 2. `spanTreeToCommits` walks the tree and emits one commit per event, using the
77
+ * tree structure to decide which branch each commit belongs to and how to wire parents.
78
+ *
118
79
  * Pure function — no signals or atoms.
119
80
  */
81
+ export type ExecutionGraph = {
82
+ branches: string[];
83
+ commits: Commit[];
84
+ spanTree: Span;
85
+ };
86
+
120
87
  export const buildExecutionGraph = ({
121
88
  traceMessages,
122
89
  activeProcesses = [],
123
90
  eventLimit = 500,
124
- }: BuildExecutionGraphParams): { branches: string[]; commits: Commit[] } => {
125
- const builder = new GraphBuilder();
126
-
127
- const events = traceMessages
128
- .flatMap((message) =>
129
- message.events.map((event: Trace.Event, index) => ({
130
- ...event,
131
- meta: message.meta,
132
- id: 'id' in event ? (event as any).id : `${message.id}:${index}`,
133
- })),
134
- )
135
- .slice(-eventLimit);
91
+ }: BuildExecutionGraphParams): ExecutionGraph => {
92
+ const spanTree = buildSpanTree(traceMessages, { eventLimit });
93
+ const built = spanTreeToCommits(spanTree, activeProcesses);
94
+ log('trace execution graph', {
95
+ traceMessages: traceMessages.length,
96
+ commits: built.commits.length,
97
+ branches: built.branches.length,
98
+ activeProcesses: activeProcesses.length,
99
+ });
100
+ return { ...built, spanTree };
101
+ };
136
102
 
137
- builder.addBranch(MAIN_BRANCH);
103
+ /**
104
+ * Visual representation of a single trace event — icon, log level, message string.
105
+ * `undefined` indicates that the event should not produce a commit.
106
+ */
107
+ interface EventPresentation {
108
+ icon: string;
109
+ level: LogLevel;
110
+ message: string;
111
+ idSuffix?: string;
112
+ }
138
113
 
139
- for (const event of events) {
140
- if (Trace.isOfType(AgentRequestBegin, event)) {
141
- builder.addCommit({
142
- id: event.id,
143
- branch: event.meta.parentPid ?? MAIN_BRANCH,
144
- parents: builder.computeParents(
145
- CommitSelector.branch(event.meta.parentPid ?? MAIN_BRANCH).pipe(
146
- CommitSelector.compose(CommitSelector.last()),
147
- ),
148
- ),
149
- tags: [...getTags(event.meta), event.meta.pid && tagStartMarker(event.meta.pid)].filter(
150
- Predicate.isNotNullable,
151
- ),
152
- timestamp: new Date(event.timestamp),
153
- icon: ICONS.agentRequestBegin.icon,
154
- level: ICONS.agentRequestBegin.level,
155
- message: 'Agent processing request...',
156
- });
157
- } else if (Trace.isOfType(AgentRequestEnd, event)) {
158
- builder.addCommit({
159
- id: event.id,
160
- branch: event.meta.parentPid ?? MAIN_BRANCH,
161
- parents: builder.computeParents(
162
- CommitSelector.unionAll(
163
- CommitSelector.branch(event.meta.parentPid ?? MAIN_BRANCH).pipe(
164
- CommitSelector.orElse(CommitSelector.tag(event.meta.pid && tagPid(event.meta.pid))),
165
- CommitSelector.compose(CommitSelector.last()),
166
- ),
167
- CommitSelector.tag(event.meta.pid && tagPid(event.meta.pid)).pipe(
168
- CommitSelector.compose(CommitSelector.last()),
169
- ),
170
- ),
171
- ),
172
- tags: getTags(event.meta),
173
- timestamp: new Date(event.timestamp),
174
- icon: ICONS.agentRequestEnd.icon,
175
- level: ICONS.agentRequestEnd.level,
176
- message: 'Agent completed request',
177
- });
178
- } else if (Trace.isOfType(CompleteBlock, event)) {
179
- switch (event.data.block._tag) {
180
- case 'text': {
181
- if (event.data.role === 'user') {
182
- builder.addCommit({
183
- id: event.id,
184
- branch: event.meta.pid ?? MAIN_BRANCH,
185
- parents: builder.computeParents(
186
- CommitSelector.branch(event.meta.pid ?? MAIN_BRANCH).pipe(
187
- CommitSelector.compose(CommitSelector.last()),
188
- CommitSelector.orElse(
189
- CommitSelector.tag(event.meta.pid && tagStartMarker(event.meta.pid)).pipe(
190
- CommitSelector.compose(CommitSelector.last()),
191
- ),
192
- ),
193
- ),
194
- ),
195
- tags: getTags(event.meta),
196
- timestamp: new Date(event.timestamp),
114
+ const presentEvent = (event: Trace.FlatEvent): EventPresentation | undefined => {
115
+ if (Trace.isOfType(AgentRequestBegin, event)) {
116
+ if (Either.isLeft(Schema.validateEither(AgentRequestBegin.schema)(event.data))) {
117
+ log('invalid trace event', { type: event.type });
118
+ return undefined;
119
+ }
120
+ return {
121
+ icon: ICONS.agentRequestBegin.icon,
122
+ level: ICONS.agentRequestBegin.level,
123
+ message: 'Agent processing request...',
124
+ };
125
+ }
126
+ if (Trace.isOfType(AgentRequestEnd, event)) {
127
+ if (Either.isLeft(Schema.validateEither(AgentRequestEnd.schema)(event.data))) {
128
+ log('invalid trace event', { type: event.type });
129
+ return undefined;
130
+ }
131
+ return {
132
+ icon: ICONS.agentRequestEnd.icon,
133
+ level: ICONS.agentRequestEnd.level,
134
+ message: 'Agent completed request',
135
+ };
136
+ }
137
+ if (Trace.isOfType(CompleteBlock, event)) {
138
+ if (Either.isLeft(Schema.validateEither(CompleteBlock.schema)(event.data))) {
139
+ log('invalid trace event', { type: event.type });
140
+ return undefined;
141
+ }
142
+ switch (event.data.block._tag) {
143
+ case 'text':
144
+ return event.data.role === 'user'
145
+ ? {
197
146
  icon: ICONS.userMessage.icon,
198
147
  level: ICONS.userMessage.level,
199
148
  message: trimText(event.data.block.text),
200
- });
201
- }
202
- break;
203
- }
204
- case 'status': {
205
- builder.addCommit({
206
- id: event.id,
207
- branch: event.meta.pid ?? MAIN_BRANCH,
208
- parents: builder.computeParents(
209
- CommitSelector.branch(event.meta.pid ?? MAIN_BRANCH).pipe(
210
- CommitSelector.compose(CommitSelector.last()),
211
- CommitSelector.orElse(
212
- CommitSelector.tag(event.meta.pid && tagPid(event.meta.pid)).pipe(
213
- CommitSelector.compose(CommitSelector.last()),
214
- ),
215
- ),
216
- ),
217
- ),
218
- tags: getTags(event.meta),
219
- timestamp: new Date(event.timestamp),
220
- icon: ICONS.statusMessage.icon,
221
- level: ICONS.statusMessage.level,
222
- message: trimText(event.data.block.statusText),
223
- });
224
- break;
225
- }
149
+ }
150
+ : undefined;
151
+ case 'status':
152
+ return {
153
+ icon: ICONS.statusMessage.icon,
154
+ level: ICONS.statusMessage.level,
155
+ message: trimText(event.data.block.statusText),
156
+ };
157
+ default:
158
+ return undefined;
159
+ }
160
+ }
161
+ if (Trace.isOfType(Trace.OperationStart, event)) {
162
+ if (Either.isLeft(Schema.validateEither(Trace.OperationStart.schema)(event.data))) {
163
+ log('invalid trace event', { type: event.type });
164
+ return undefined;
165
+ }
166
+ return {
167
+ icon: ICONS.operationStart.icon,
168
+ level: ICONS.operationStart.level,
169
+ message: event.data.name ?? event.data.key,
170
+ idSuffix: `${event.data.key}:start`,
171
+ };
172
+ }
173
+ if (Trace.isOfType(Trace.OperationEnd, event)) {
174
+ if (Either.isLeft(Schema.validateEither(Trace.OperationEnd.schema)(event.data))) {
175
+ log('invalid trace event', { type: event.type });
176
+ return undefined;
177
+ }
178
+ const success = event.data.outcome === 'success';
179
+ return {
180
+ icon: success ? ICONS.operationEndSuccess.icon : ICONS.operationEndError.icon,
181
+ level: success ? ICONS.operationEndSuccess.level : ICONS.operationEndError.level,
182
+ message: `${event.data.name ?? event.data.key} - ${success ? 'Success' : 'Error'}`,
183
+ idSuffix: `${event.data.key}:end`,
184
+ };
185
+ }
186
+ return undefined;
187
+ };
188
+
189
+ /**
190
+ * A span is "collapsible" when it would otherwise render as an empty fork-and-merge:
191
+ * exactly two events that form a begin/end pair and no child sub-spans. In that case
192
+ * we emit a single commit for the end event on the parent branch.
193
+ *
194
+ * A *pending* span (one whose end event has not yet been recorded) is never collapsed:
195
+ * its trailing events should keep rendering as middle commits on the span's own branch
196
+ * — never as a fake "end" commit on the parent branch.
197
+ *
198
+ * Both nested spans and top-level spans (whose parent is the synthetic root) are eligible
199
+ * once they have completed: a span with no inner activity is collapsed regardless of where
200
+ * it sits in the tree.
201
+ */
202
+ const isCollapsibleSpan = (span: Span, parent: Span | null): boolean => {
203
+ if (span.id === ROOT_SPAN_ID) {
204
+ return false;
205
+ }
206
+ if (span.events.length !== 2) {
207
+ return false;
208
+ }
209
+ if (span.children.length > 0) {
210
+ return false;
211
+ }
212
+ const firstEvent = span.events[0];
213
+ const lastEvent = span.events[span.events.length - 1];
214
+ if (!isSpanBeginEvent(firstEvent) || !isSpanEndEvent(lastEvent)) {
215
+ return false;
216
+ }
217
+ return parent !== null;
218
+ };
219
+
220
+ /**
221
+ * Converts a span tree into a flat list of commits + branches suitable for `Timeline` rendering.
222
+ *
223
+ * For every span the algorithm:
224
+ * - emits the **first** event as a "begin" commit on the parent span's branch (fork);
225
+ * - emits **middle** events as commits on the span's own branch;
226
+ * - emits the **last** event as an "end" commit on the parent span's branch with two parents
227
+ * (the begin commit and the last commit on the span's own branch — a merge).
228
+ *
229
+ * Collapsible spans (`isCollapsibleSpan`) emit only the end commit on the parent branch with
230
+ * a single parent — a tidier rendering for sub-operations that have no inner detail.
231
+ *
232
+ * Events are processed in global chronological order so that sub-operations of a span interleave
233
+ * naturally with the span's own middle events.
234
+ */
235
+ const spanTreeToCommits = (
236
+ root: Span,
237
+ activeProcesses: readonly Process.Info[],
238
+ ): { branches: string[]; commits: Commit[] } => {
239
+ const builder = new GraphBuilder();
240
+ builder.addBranch(MAIN_BRANCH);
241
+
242
+ // Build a child → parent lookup using the tree's own structure (already correctly parented
243
+ // when the tree was constructed, including sequential spans of the same process).
244
+ const parentBySpan = new Map<Span, Span | null>();
245
+ parentBySpan.set(root, null);
246
+ const indexParents = (span: Span): void => {
247
+ for (const child of span.children) {
248
+ parentBySpan.set(child, span);
249
+ indexParents(child);
250
+ }
251
+ };
252
+ indexParents(root);
253
+
254
+ const findParentSpan = (span: Span): Span | null => parentBySpan.get(span) ?? null;
255
+
256
+ // The branch name for a span is its pid — multiple sequential spans of the same process
257
+ // (e.g. successive agent requests in one session) share a branch so they reuse a single lane.
258
+ const branchOf = (span: Span | null): string => {
259
+ if (!span || span.id === ROOT_SPAN_ID) {
260
+ return MAIN_BRANCH;
261
+ }
262
+ return span.meta.pid ?? span.id;
263
+ };
264
+
265
+ // Selector that returns the most recent commit walking up the ancestor chain of `span`,
266
+ // starting from `span`'s own branch and falling back through parent branches up to main.
267
+ // Used when a commit emitted on a parent/ancestor branch needs to attach to something
268
+ // that hasn't been written to yet (e.g. the first child sub-span begins before any
269
+ // middle event has landed on the parent branch).
270
+ const lastInAncestorChain = (span: Span | null): CommitSelector => {
271
+ const branches: string[] = [];
272
+ const seen = new Set<string>();
273
+ let current = span;
274
+ while (current) {
275
+ const branch = branchOf(current);
276
+ if (!seen.has(branch)) {
277
+ seen.add(branch);
278
+ branches.push(branch);
226
279
  }
227
- } else if (Trace.isOfType(Trace.OperationStart, event)) {
228
- builder.addCommit({
229
- id: `${event.id}:${event.data.key}:start`,
230
- branch: event.meta.parentPid ?? MAIN_BRANCH,
231
- parents: builder.computeParents(
232
- CommitSelector.branch(event.meta.parentPid ?? MAIN_BRANCH).pipe(
233
- CommitSelector.andAlso(CommitSelector.tag(event.meta.parentPid && tagStartMarker(event.meta.parentPid))),
234
- CommitSelector.compose(CommitSelector.orderByTimestamp()),
235
- CommitSelector.compose(CommitSelector.last()),
236
- CommitSelector.orElse(CommitSelector.branch(event.meta.parentPid)),
237
- CommitSelector.compose(CommitSelector.last()),
238
- ),
280
+ current = findParentSpan(current);
281
+ }
282
+ if (!seen.has(MAIN_BRANCH)) {
283
+ branches.push(MAIN_BRANCH);
284
+ }
285
+ return CommitSelector.firstOf(
286
+ ...branches.map((branch) => CommitSelector.branch(branch).pipe(CommitSelector.compose(CommitSelector.last()))),
287
+ );
288
+ };
289
+
290
+ // Flatten the tree into a chronological stream, recording each event's position within its span
291
+ // so we can detect begin/end events.
292
+ interface EventCursor {
293
+ span: Span;
294
+ event: Trace.FlatEvent;
295
+ eventIndex: number;
296
+ globalIndex: number;
297
+ }
298
+ const stream: EventCursor[] = [];
299
+ walkSpanTree(root, (span) => {
300
+ span.events.forEach((event, eventIndex) => {
301
+ stream.push({ span, event, eventIndex, globalIndex: stream.length });
302
+ });
303
+ });
304
+ stream.sort((a, b) => a.event.timestamp - b.event.timestamp || a.globalIndex - b.globalIndex);
305
+
306
+ // Track the id of the begin commit for each non-collapsible span so the end commit can merge into it.
307
+ const beginCommitIdBySpan = new Map<string, string>();
308
+
309
+ for (const { span, event, globalIndex } of stream) {
310
+ const presentation = presentEvent(event);
311
+ if (!presentation) {
312
+ continue;
313
+ }
314
+
315
+ // Classify based on event TYPE, not array index. A pending span's trailing
316
+ // middle event (e.g. a user message awaiting an agent response) must not be
317
+ // mistaken for the span's end event — otherwise it would render on the
318
+ // parent branch instead of the span's own branch.
319
+ const isBeginEvent = isSpanBeginEvent(event);
320
+ const isEndEvent = isSpanEndEvent(event);
321
+ const parentSpan = findParentSpan(span);
322
+ const collapsible = isCollapsibleSpan(span, parentSpan);
323
+
324
+ // Collapsed spans: skip the begin event entirely; only the end event renders.
325
+ if (collapsible && isBeginEvent) {
326
+ continue;
327
+ }
328
+
329
+ const parentBranch = branchOf(parentSpan);
330
+ const ownBranch = branchOf(span);
331
+
332
+ let branch: string;
333
+ let parents: string[];
334
+
335
+ if (span.id === ROOT_SPAN_ID) {
336
+ // Root-level events (no pid) attach sequentially to main.
337
+ branch = MAIN_BRANCH;
338
+ parents = builder.computeParents(
339
+ CommitSelector.branch(MAIN_BRANCH).pipe(CommitSelector.compose(CommitSelector.last())),
340
+ );
341
+ } else if (collapsible) {
342
+ // Only the end event reaches here for collapsible spans.
343
+ // Walk the ancestor chain so the commit attaches to the most recent ancestor
344
+ // even when the immediate parent branch has not received a commit yet.
345
+ branch = parentBranch;
346
+ parents = builder.computeParents(lastInAncestorChain(parentSpan));
347
+ } else if (isBeginEvent) {
348
+ // Begin commit: fork off the parent branch.
349
+ // Walk the ancestor chain to find the most recent ancestor commit to fork from.
350
+ branch = parentBranch;
351
+ parents = builder.computeParents(lastInAncestorChain(parentSpan));
352
+ } else if (isEndEvent) {
353
+ // End commit: merge from the span's own branch back into the parent branch.
354
+ branch = parentBranch;
355
+ const beginId = beginCommitIdBySpan.get(span.id);
356
+ parents = builder.computeParents(
357
+ CommitSelector.unionAll(
358
+ beginId ? CommitSelector.id(beginId) : lastInAncestorChain(parentSpan),
359
+ CommitSelector.branch(ownBranch).pipe(CommitSelector.compose(CommitSelector.last())),
239
360
  ),
240
- tags: [
241
- ...getTags(event.meta),
242
- tagOperationBegin(`${event.meta.pid ?? 'unknown'}:${event.data.key}`),
243
- event.meta.pid && tagStartMarker(event.meta.pid),
244
- ].filter(Predicate.isNotNullable),
245
- timestamp: new Date(event.timestamp),
246
- icon: ICONS.operationStart.icon,
247
- level: ICONS.operationStart.level,
248
- message: event.data.name ?? event.data.key,
249
- });
250
- } else if (Trace.isOfType(Trace.OperationEnd, event)) {
251
- const children = builder.findCommits(
252
- CommitSelector.anyTags([
253
- event.meta.pid && tagPid(event.meta.pid),
254
- event.meta.pid && tagParentPid(event.meta.pid),
255
- ]),
256
361
  );
257
- builder.addCommit(
258
- {
259
- id: `${event.id}:${event.data.key}:end`,
260
- branch: event.meta.parentPid ?? MAIN_BRANCH,
261
- parents: builder.computeParents(
262
- CommitSelector.branch(event.meta.parentPid ?? MAIN_BRANCH).pipe(
263
- CommitSelector.compose(
264
- CommitSelector.not(
265
- CommitSelector.tag(tagOperationBegin(`${event.meta.pid ?? 'unknown'}:${event.data.key}`)),
266
- ),
267
- ),
268
- CommitSelector.orElse(CommitSelector.tag(event.meta.parentPid && tagStartMarker(event.meta.parentPid))),
269
- CommitSelector.compose(CommitSelector.last()),
270
- CommitSelector.andAlso(
271
- CommitSelector.anyTags([
272
- event.meta.pid && tagPid(event.meta.pid),
273
- event.meta.pid && tagParentPid(event.meta.pid),
274
- ]).pipe(
275
- CommitSelector.compose(
276
- CommitSelector.not(
277
- CommitSelector.tag(tagOperationBegin(`${event.meta.pid ?? 'unknown'}:${event.data.key}`)),
278
- ),
279
- ),
280
- CommitSelector.compose(CommitSelector.last()),
281
- ),
282
- ),
283
- ),
284
- ),
285
- tags: getTags(event.meta),
286
- timestamp: new Date(event.timestamp),
287
- icon: event.data.outcome === 'success' ? ICONS.operationEndSuccess.icon : ICONS.operationEndError.icon,
288
- level: event.data.outcome === 'success' ? ICONS.operationEndSuccess.level : ICONS.operationEndError.level,
289
- message: `${event.data.name ?? event.data.key} - ${event.data.outcome === 'success' ? 'Success' : 'Error'}`,
290
- },
291
- {
292
- replace:
293
- // TODO(dmaretskyi): Deduping events in subbranches brekas graph.
294
- !event.meta.parentPid || children.length > 1 // 1 is the operation begin commit.
295
- ? undefined
296
- : CommitSelector.tag(tagOperationBegin(`${event.meta.pid ?? 'unknown'}:${event.data.key}`)),
297
- },
362
+ } else {
363
+ // Middle event: continue the span's own branch.
364
+ branch = ownBranch;
365
+ parents = builder.computeParents(
366
+ CommitSelector.branch(ownBranch).pipe(
367
+ CommitSelector.compose(CommitSelector.last()),
368
+ CommitSelector.orElse(lastInAncestorChain(parentSpan)),
369
+ ),
298
370
  );
299
371
  }
372
+
373
+ const commitId = formatCommitId(span, globalIndex, presentation.idSuffix);
374
+ const commit: Commit = {
375
+ id: commitId,
376
+ branch,
377
+ parents,
378
+ timestamp: new Date(event.timestamp),
379
+ icon: presentation.icon,
380
+ level: presentation.level,
381
+ message: presentation.message,
382
+ };
383
+ builder.addCommit(commit);
384
+
385
+ // Record begin commit so a later end event can merge into it.
386
+ if (isBeginEvent && !collapsible && span.id !== ROOT_SPAN_ID) {
387
+ beginCommitIdBySpan.set(span.id, commitId);
388
+ }
300
389
  }
301
390
 
391
+ // Append "running" indicators for processes that are still active.
302
392
  for (const process of activeProcesses) {
303
393
  if (
304
394
  process.key === AGENT_PROCESS_KEY &&
@@ -331,17 +421,16 @@ export const buildExecutionGraph = ({
331
421
  }
332
422
  }
333
423
 
334
- const built = builder.build();
335
- log('trace execution graph', {
336
- traceMessages: traceMessages.length,
337
- flatEvents: events.length,
338
- commits: built.commits.length,
339
- branches: built.branches.length,
340
- activeProcesses: activeProcesses.length,
341
- });
342
- return built;
424
+ return builder.build();
343
425
  };
344
426
 
427
+ const formatCommitId = (span: Span, globalIndex: number, suffix?: string): string => {
428
+ const base = `${span.id}:${globalIndex}`;
429
+ return suffix ? `${base}:${suffix}` : base;
430
+ };
431
+
432
+ const trimText = (text: string) => text.slice(0, 100).trim().split('\n')[0];
433
+
345
434
  type Falsy = false | null | undefined;
346
435
 
347
436
  export interface CommitSelector extends Pipeable.Pipeable {
@@ -479,9 +568,6 @@ export const CommitSelector = {
479
568
  }
480
569
  return [];
481
570
  }),
482
-
483
- orderByTimestamp: (): CommitSelector =>
484
- CommitSelector.make(Array.sortWith((a) => a.timestamp?.getTime() ?? 0, Order.number)),
485
571
  };
486
572
 
487
573
  class GraphBuilder {
@@ -496,50 +582,11 @@ class GraphBuilder {
496
582
  return this.#branches.has(branch);
497
583
  }
498
584
 
499
- addCommit(
500
- commit: Commit,
501
- opts?: {
502
- ifMissing?: CommitSelector;
503
- /**
504
- * Replace in-place the last commit that matches the selector.
505
- */
506
- replace?: CommitSelector;
507
- },
508
- ) {
509
- if (opts?.ifMissing) {
510
- if (this.findCommits(opts.ifMissing).length > 0) {
511
- return;
512
- }
513
- }
585
+ addCommit(commit: Commit) {
514
586
  this.addBranch(commit.branch);
515
- if (opts?.replace) {
516
- const matches = this.findCommits(opts.replace);
517
- if (matches.length > 0) {
518
- const replaced = matches.at(-1)!;
519
- this.#commits.splice(this.#commits.indexOf(replaced), 1);
520
- // Update parents to point to the new commit.
521
- for (const existingCommit of this.#commits) {
522
- if (existingCommit.parents) {
523
- for (let i = 0; i < existingCommit.parents.length; i++) {
524
- if (existingCommit.parents[i] === replaced.id) {
525
- existingCommit.parents[i] = commit.id;
526
- }
527
- }
528
- }
529
- }
530
- this.#commits.push(commit);
531
- return;
532
- }
533
- }
534
587
  this.#commits.push(commit);
535
588
  }
536
589
 
537
- removeCommit(selector: CommitSelector) {
538
- for (const commit of this.findCommits(selector)) {
539
- this.#commits.splice(this.#commits.indexOf(commit), 1);
540
- }
541
- }
542
-
543
590
  /**
544
591
  * Computes parents — picks first matching commit's id.
545
592
  */
@@ -575,5 +622,3 @@ class GraphBuilder {
575
622
  };
576
623
  }
577
624
  }
578
-
579
- const trimText = (text: string) => text.slice(0, 100).trim().split('\n')[0];