@dxos/plugin-assistant 0.8.4-staging.60fe92afc8 → 0.9.1-main.c7dcc2e112

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 (254) hide show
  1. package/dist/lib/neutral/{AgentArticle-K7XM46OQ.mjs → AgentArticle-2BEPWUMN.mjs} +9 -7
  2. package/dist/lib/neutral/AgentArticle-2BEPWUMN.mjs.map +7 -0
  3. package/dist/lib/neutral/AssistantPlugin.mjs +1 -1
  4. package/dist/lib/neutral/{AssistantSettings-GG52BLKS.mjs → AssistantSettings-7ALY4BMS.mjs} +3 -3
  5. package/dist/lib/neutral/AssistantSettings-7ALY4BMS.mjs.map +7 -0
  6. package/dist/lib/neutral/{ChatArticle-VNVZCDUR.mjs → ChatArticle-KT46SUFU.mjs} +2 -2
  7. package/dist/lib/neutral/{ChatCompanion-LBUHYWQG.mjs → ChatCompanion-YHC43RYQ.mjs} +4 -4
  8. package/dist/lib/neutral/ChatCompanion-YHC43RYQ.mjs.map +7 -0
  9. package/dist/lib/neutral/{ChatDialog-DCA6FLOV.mjs → ChatDialog-YZDZLIQA.mjs} +5 -4
  10. package/dist/lib/neutral/ChatDialog-YZDZLIQA.mjs.map +7 -0
  11. package/dist/lib/neutral/{PlanArticle-TS5ULWYS.mjs → PlanArticle-VPK2IYWU.mjs} +8 -2
  12. package/dist/lib/neutral/PlanArticle-VPK2IYWU.mjs.map +7 -0
  13. package/dist/lib/neutral/{RoutineArticle-5NYXHRG6.mjs → RoutineArticle-FK5BXQN5.mjs} +3 -3
  14. package/dist/lib/neutral/RoutineArticle-FK5BXQN5.mjs.map +7 -0
  15. package/dist/lib/neutral/SpaceHomePrompt-HTWQKLGI.mjs +113 -0
  16. package/dist/lib/neutral/SpaceHomePrompt-HTWQKLGI.mjs.map +7 -0
  17. package/dist/lib/neutral/SpaceHomeSuggestions-6G4E7GNJ.mjs +50 -0
  18. package/dist/lib/neutral/SpaceHomeSuggestions-6G4E7GNJ.mjs.map +7 -0
  19. package/dist/lib/neutral/TracePanel-T552TAOH.mjs +12 -0
  20. package/dist/lib/neutral/TracePanel-T552TAOH.mjs.map +7 -0
  21. package/dist/lib/neutral/{TriggerStatus-X7Y5JFZJ.mjs → TriggerStatus-RBOHHDOK.mjs} +3 -3
  22. package/dist/lib/neutral/TriggerStatus-RBOHHDOK.mjs.map +7 -0
  23. package/dist/lib/neutral/{agent-service-WCAP5MLI.mjs → agent-service-WV5CSHAG.mjs} +4 -2
  24. package/dist/lib/neutral/{agent-service-WCAP5MLI.mjs.map → agent-service-WV5CSHAG.mjs.map} +3 -3
  25. package/dist/lib/neutral/{app-graph-builder-WUFHQH3Y.mjs → app-graph-builder-PTSRZXRN.mjs} +13 -13
  26. package/dist/lib/neutral/app-graph-builder-PTSRZXRN.mjs.map +7 -0
  27. package/dist/lib/neutral/{blueprint-definition-MH2QLYQH.mjs → blueprint-definition-2XPYI35X.mjs} +4 -3
  28. package/dist/lib/neutral/blueprint-definition-2XPYI35X.mjs.map +7 -0
  29. package/dist/lib/neutral/capabilities/index.mjs +8 -8
  30. package/dist/lib/neutral/capabilities/index.mjs.map +2 -2
  31. package/dist/lib/neutral/{chunk-HLANPOBA.mjs → chunk-35EL65O4.mjs} +3 -3
  32. package/dist/lib/neutral/chunk-35EL65O4.mjs.map +7 -0
  33. package/dist/lib/neutral/chunk-54W7N6M6.mjs +445 -0
  34. package/dist/lib/neutral/chunk-54W7N6M6.mjs.map +7 -0
  35. package/dist/lib/neutral/{chunk-YXRGZYYH.mjs → chunk-5KB2WRI2.mjs} +6 -6
  36. package/dist/lib/neutral/chunk-5KB2WRI2.mjs.map +7 -0
  37. package/dist/lib/neutral/{TracePanel-KTZJ6JNR.mjs → chunk-5LKNH7FD.mjs} +5 -20
  38. package/dist/lib/neutral/chunk-5LKNH7FD.mjs.map +7 -0
  39. package/dist/lib/neutral/{chunk-SMFJD7BP.mjs → chunk-7FQT4XMI.mjs} +2 -2
  40. package/dist/lib/neutral/{chunk-RKVVK3R3.mjs → chunk-H77JTXAN.mjs} +17 -9
  41. package/dist/lib/neutral/chunk-H77JTXAN.mjs.map +7 -0
  42. package/dist/lib/neutral/{chunk-QAZMOFPI.mjs → chunk-IQZJ5TTM.mjs} +4 -4
  43. package/dist/lib/neutral/chunk-IQZJ5TTM.mjs.map +7 -0
  44. package/dist/lib/neutral/chunk-ZNDQVYUN.mjs +53 -0
  45. package/dist/lib/neutral/chunk-ZNDQVYUN.mjs.map +7 -0
  46. package/dist/lib/neutral/components/index.mjs +296 -109
  47. package/dist/lib/neutral/components/index.mjs.map +3 -3
  48. package/dist/lib/neutral/containers/index.mjs +12 -8
  49. package/dist/lib/neutral/containers/index.mjs.map +3 -3
  50. package/dist/lib/neutral/{create-chat-SGGQ6HVN.mjs → create-chat-Z3FNA6OJ.mjs} +2 -2
  51. package/dist/lib/neutral/{create-chat-SGGQ6HVN.mjs.map → create-chat-Z3FNA6OJ.mjs.map} +2 -2
  52. package/dist/lib/neutral/{create-object-U2SOFZLR.mjs → create-object-BJUDDKQQ.mjs} +2 -2
  53. package/dist/lib/neutral/execution-graph/index.mjs +156 -0
  54. package/dist/lib/neutral/execution-graph/index.mjs.map +4 -4
  55. package/dist/lib/neutral/{fork-chat-34KEF4ZJ.mjs → fork-chat-UYXYT6BT.mjs} +4 -4
  56. package/dist/lib/neutral/fork-chat-UYXYT6BT.mjs.map +7 -0
  57. package/dist/lib/neutral/hooks/index.mjs +233 -438
  58. package/dist/lib/neutral/hooks/index.mjs.map +4 -4
  59. package/dist/lib/neutral/index.mjs +3 -3
  60. package/dist/lib/neutral/markdown-extension-IJ5RQMQ4.mjs +126 -0
  61. package/dist/lib/neutral/markdown-extension-IJ5RQMQ4.mjs.map +7 -0
  62. package/dist/lib/neutral/meta.json +1 -1
  63. package/dist/lib/neutral/meta.mjs +1 -1
  64. package/dist/lib/neutral/operations/index.mjs +1 -1
  65. package/dist/lib/neutral/plugin.mjs +2 -2
  66. package/dist/lib/neutral/{react-surface-XRTBW5OJ.mjs → react-surface-ES7UHZ65.mjs} +29 -17
  67. package/dist/lib/neutral/react-surface-ES7UHZ65.mjs.map +7 -0
  68. package/dist/lib/neutral/{run-prompt-in-new-chat-D5IIE2C7.mjs → run-prompt-in-new-chat-NIAGCKQV.mjs} +11 -8
  69. package/dist/lib/neutral/run-prompt-in-new-chat-NIAGCKQV.mjs.map +7 -0
  70. package/dist/lib/neutral/{settings-W4BLWQ53.mjs → settings-7Q3RITIT.mjs} +3 -3
  71. package/dist/lib/neutral/{settings-W4BLWQ53.mjs.map → settings-7Q3RITIT.mjs.map} +3 -3
  72. package/dist/lib/neutral/{state-H3G7QCU6.mjs → state-XO2UYSSG.mjs} +2 -2
  73. package/dist/lib/neutral/{state-H3G7QCU6.mjs.map → state-XO2UYSSG.mjs.map} +3 -3
  74. package/dist/lib/neutral/testing.mjs +1 -1
  75. package/dist/lib/neutral/translations.mjs +18 -11
  76. package/dist/lib/neutral/translations.mjs.map +3 -3
  77. package/dist/lib/neutral/types/index.mjs +1 -1
  78. package/dist/types/dx.config.d.ts +28 -0
  79. package/dist/types/dx.config.d.ts.map +1 -0
  80. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  81. package/dist/types/src/capabilities/blueprint-definition.d.ts.map +1 -1
  82. package/dist/types/src/capabilities/index.d.ts +17 -57
  83. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  84. package/dist/types/src/capabilities/react-surface.d.ts +2 -2
  85. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  86. package/dist/types/src/components/Chat/Chat.d.ts +6 -3
  87. package/dist/types/src/components/Chat/Chat.d.ts.map +1 -1
  88. package/dist/types/src/components/Chat/Chat.stories.d.ts +32 -0
  89. package/dist/types/src/components/Chat/Chat.stories.d.ts.map +1 -0
  90. package/dist/types/src/components/ChatPrompt/ChatActions.d.ts.map +1 -1
  91. package/dist/types/src/components/ChatPrompt/ChatOptions.d.ts.map +1 -1
  92. package/dist/types/src/components/ProcessTree/ProcessTree.d.ts +6 -0
  93. package/dist/types/src/components/ProcessTree/ProcessTree.d.ts.map +1 -1
  94. package/dist/types/src/components/ProcessTree/ProcessTree.stories.d.ts +1 -0
  95. package/dist/types/src/components/ProcessTree/ProcessTree.stories.d.ts.map +1 -1
  96. package/dist/types/src/components/TaskList/TaskList.d.ts +8 -0
  97. package/dist/types/src/components/TaskList/TaskList.d.ts.map +1 -1
  98. package/dist/types/src/components/TaskList/TaskList.stories.d.ts +3 -0
  99. package/dist/types/src/components/TaskList/TaskList.stories.d.ts.map +1 -1
  100. package/dist/types/src/components/index.d.ts +1 -0
  101. package/dist/types/src/components/index.d.ts.map +1 -1
  102. package/dist/types/src/containers/ChatArticle/ChatArticle.d.ts +2 -2
  103. package/dist/types/src/containers/ChatArticle/ChatArticle.d.ts.map +1 -1
  104. package/dist/types/src/containers/ChatCompanion/ChatCompanion.d.ts +2 -2
  105. package/dist/types/src/containers/ChatDialog/ChatDialog.d.ts.map +1 -1
  106. package/dist/types/src/containers/PlanArticle/PlanArticle.d.ts.map +1 -1
  107. package/dist/types/src/containers/SpaceHomePrompt/SpaceHomePrompt.d.ts +15 -0
  108. package/dist/types/src/containers/SpaceHomePrompt/SpaceHomePrompt.d.ts.map +1 -0
  109. package/dist/types/src/containers/SpaceHomePrompt/index.d.ts +2 -0
  110. package/dist/types/src/containers/SpaceHomePrompt/index.d.ts.map +1 -0
  111. package/dist/types/src/containers/SpaceHomeSuggestions/SpaceHomeSuggestions.d.ts +13 -0
  112. package/dist/types/src/containers/SpaceHomeSuggestions/SpaceHomeSuggestions.d.ts.map +1 -0
  113. package/dist/types/src/containers/SpaceHomeSuggestions/index.d.ts +2 -0
  114. package/dist/types/src/containers/SpaceHomeSuggestions/index.d.ts.map +1 -0
  115. package/dist/types/src/containers/TracePanel/TracePanel.d.ts.map +1 -1
  116. package/dist/types/src/containers/index.d.ts +2 -0
  117. package/dist/types/src/containers/index.d.ts.map +1 -1
  118. package/dist/types/src/execution-graph/execution-graph.d.ts +18 -0
  119. package/dist/types/src/execution-graph/execution-graph.d.ts.map +1 -1
  120. package/dist/types/src/execution-graph/index.d.ts +2 -1
  121. package/dist/types/src/execution-graph/index.d.ts.map +1 -1
  122. package/dist/types/src/execution-graph/pending-block-status.d.ts +26 -0
  123. package/dist/types/src/execution-graph/pending-block-status.d.ts.map +1 -0
  124. package/dist/types/src/execution-graph/pending-block-status.test.d.ts +2 -0
  125. package/dist/types/src/execution-graph/pending-block-status.test.d.ts.map +1 -0
  126. package/dist/types/src/extensions/prompt-extension.d.ts +1 -1
  127. package/dist/types/src/extensions/prompt-extension.d.ts.map +1 -1
  128. package/dist/types/src/feed-logger.d.ts +3 -2
  129. package/dist/types/src/feed-logger.d.ts.map +1 -1
  130. package/dist/types/src/hooks/index.d.ts +2 -0
  131. package/dist/types/src/hooks/index.d.ts.map +1 -1
  132. package/dist/types/src/hooks/useProcessEphemeralStatus.d.ts +10 -0
  133. package/dist/types/src/hooks/useProcessEphemeralStatus.d.ts.map +1 -0
  134. package/dist/types/src/hooks/useTraceMessages.d.ts +12 -0
  135. package/dist/types/src/hooks/useTraceMessages.d.ts.map +1 -0
  136. package/dist/types/src/meta.d.ts +28 -2
  137. package/dist/types/src/meta.d.ts.map +1 -1
  138. package/dist/types/src/operations/run-prompt-in-new-chat.d.ts.map +1 -1
  139. package/dist/types/src/paths.d.ts.map +1 -1
  140. package/dist/types/src/processor/processor.d.ts +23 -0
  141. package/dist/types/src/processor/processor.d.ts.map +1 -1
  142. package/dist/types/src/translations.d.ts.map +1 -1
  143. package/dist/types/src/types/Assistant.d.ts +1 -1
  144. package/dist/types/src/types/AssistantCapabilities.d.ts.map +1 -1
  145. package/dist/types/src/types/AssistantEvents.d.ts.map +1 -1
  146. package/dist/types/src/types/AssistantOperation.d.ts +7 -7
  147. package/dist/types/src/util/error-cause.d.ts +3 -0
  148. package/dist/types/src/util/error-cause.d.ts.map +1 -0
  149. package/dist/types/tsconfig.tsbuildinfo +1 -1
  150. package/dx.config.ts +38 -0
  151. package/package.json +71 -70
  152. package/src/AssistantPlugin.conversations.json +1 -1
  153. package/src/AssistantPlugin.test.ts +1 -1
  154. package/src/AssistantPlugin.ts +2 -2
  155. package/src/capabilities/agent-service.ts +5 -1
  156. package/src/capabilities/app-graph-builder.ts +15 -20
  157. package/src/capabilities/blueprint-definition.ts +2 -0
  158. package/src/capabilities/index.ts +1 -7
  159. package/src/capabilities/markdown-extension.ts +5 -1
  160. package/src/capabilities/react-surface.tsx +38 -23
  161. package/src/capabilities/settings.ts +2 -2
  162. package/src/capabilities/state.ts +1 -1
  163. package/src/components/AgentProperties/AgentProperties.tsx +1 -1
  164. package/src/components/AssistantSettings/AssistantSettings.tsx +2 -2
  165. package/src/components/Chat/Chat.stories.tsx +94 -0
  166. package/src/components/Chat/Chat.tsx +39 -6
  167. package/src/components/ChatPrompt/ChatActions.tsx +4 -2
  168. package/src/components/ChatPrompt/ChatMcpErrors.tsx +1 -1
  169. package/src/components/ChatPrompt/ChatOptions.tsx +26 -29
  170. package/src/components/ChatPrompt/ChatPrompt.tsx +1 -1
  171. package/src/components/ChatPrompt/ChatReferences.tsx +1 -1
  172. package/src/components/ChatThread/Anchor.stories.tsx +1 -1
  173. package/src/components/ChatThread/ChatThread.stories.tsx +1 -1
  174. package/src/components/ChatThread/DEBUG.md +23 -0
  175. package/src/components/ChatThread/widgets/SummaryWidget.tsx +1 -1
  176. package/src/components/ChatThread/widgets/ToolWidget.tsx +1 -1
  177. package/src/components/ProcessTree/ProcessTree.stories.tsx +7 -0
  178. package/src/components/ProcessTree/ProcessTree.tsx +191 -88
  179. package/src/components/TaskList/TaskList.stories.tsx +23 -2
  180. package/src/components/TaskList/TaskList.tsx +122 -28
  181. package/src/components/TemplateEditor/TemplateEditor.tsx +1 -1
  182. package/src/components/TemplateEditor/TemplateForm.tsx +1 -1
  183. package/src/components/ToolBlock/ToolBlock.tsx +1 -1
  184. package/src/components/index.ts +1 -0
  185. package/src/containers/AgentArticle/AgentArticle.stories.tsx +4 -4
  186. package/src/containers/AgentArticle/AgentArticle.tsx +9 -9
  187. package/src/containers/ChatArticle/ChatArticle.tsx +18 -10
  188. package/src/containers/ChatCompanion/ChatCompanion.tsx +2 -2
  189. package/src/containers/ChatDialog/ChatDialog.tsx +5 -3
  190. package/src/containers/PlanArticle/PlanArticle.tsx +5 -1
  191. package/src/containers/RoutineArticle/RoutineArticle.tsx +2 -2
  192. package/src/containers/SpaceHomePrompt/SpaceHomePrompt.tsx +105 -0
  193. package/src/containers/SpaceHomePrompt/index.ts +5 -0
  194. package/src/containers/SpaceHomeSuggestions/SpaceHomeSuggestions.tsx +69 -0
  195. package/src/containers/SpaceHomeSuggestions/index.ts +5 -0
  196. package/src/containers/TracePanel/TracePanel.tsx +3 -30
  197. package/src/containers/TriggerStatus/TriggerStatus.tsx +2 -2
  198. package/src/containers/index.ts +2 -0
  199. package/src/execution-graph/execution-graph.ts +130 -0
  200. package/src/execution-graph/index.ts +10 -0
  201. package/src/execution-graph/pending-block-status.test.ts +114 -0
  202. package/src/execution-graph/pending-block-status.ts +88 -0
  203. package/src/execution-graph/sub-agent-delegation.test.ts +16 -1
  204. package/src/extensions/prompt-extension.ts +102 -73
  205. package/src/feed-logger.ts +21 -19
  206. package/src/hooks/index.ts +2 -0
  207. package/src/hooks/useChatProcessor.ts +1 -1
  208. package/src/hooks/useChatServices.ts +2 -2
  209. package/src/hooks/useChatToolbarActions.ts +5 -5
  210. package/src/hooks/useContextBinder.ts +1 -1
  211. package/src/hooks/useProcessEphemeralStatus.ts +192 -0
  212. package/src/hooks/useTraceMessages.ts +41 -0
  213. package/src/meta.ts +3 -31
  214. package/src/operations/create-chat.ts +1 -1
  215. package/src/operations/fork-chat.ts +3 -3
  216. package/src/operations/run-prompt-in-new-chat.ts +5 -3
  217. package/src/paths.ts +2 -2
  218. package/src/processor/processor.node.test.ts +71 -1
  219. package/src/processor/processor.ts +58 -2
  220. package/src/testing/data/trace-timeline-multiple.dx.json +1 -1
  221. package/src/testing/data/trace-timeline-remote.dx.json +1 -1
  222. package/src/testing/data/trace-timeline.dx.json +0 -1
  223. package/src/translations.ts +18 -10
  224. package/src/types/AssistantCapabilities.ts +3 -3
  225. package/src/types/AssistantEvents.ts +1 -1
  226. package/src/types/AssistantOperation.ts +1 -1
  227. package/src/util/error-cause.ts +21 -0
  228. package/dist/lib/neutral/AgentArticle-K7XM46OQ.mjs.map +0 -7
  229. package/dist/lib/neutral/AssistantSettings-GG52BLKS.mjs.map +0 -7
  230. package/dist/lib/neutral/ChatCompanion-LBUHYWQG.mjs.map +0 -7
  231. package/dist/lib/neutral/ChatDialog-DCA6FLOV.mjs.map +0 -7
  232. package/dist/lib/neutral/PlanArticle-TS5ULWYS.mjs.map +0 -7
  233. package/dist/lib/neutral/RoutineArticle-5NYXHRG6.mjs.map +0 -7
  234. package/dist/lib/neutral/TracePanel-KTZJ6JNR.mjs.map +0 -7
  235. package/dist/lib/neutral/TriggerStatus-X7Y5JFZJ.mjs.map +0 -7
  236. package/dist/lib/neutral/app-graph-builder-WUFHQH3Y.mjs.map +0 -7
  237. package/dist/lib/neutral/blueprint-definition-MH2QLYQH.mjs.map +0 -7
  238. package/dist/lib/neutral/chunk-HLANPOBA.mjs.map +0 -7
  239. package/dist/lib/neutral/chunk-QAZMOFPI.mjs.map +0 -7
  240. package/dist/lib/neutral/chunk-RKVVK3R3.mjs.map +0 -7
  241. package/dist/lib/neutral/chunk-VTK5R4H3.mjs +0 -9
  242. package/dist/lib/neutral/chunk-VTK5R4H3.mjs.map +0 -7
  243. package/dist/lib/neutral/chunk-XYHPOGTK.mjs +0 -43
  244. package/dist/lib/neutral/chunk-XYHPOGTK.mjs.map +0 -7
  245. package/dist/lib/neutral/chunk-YXRGZYYH.mjs.map +0 -7
  246. package/dist/lib/neutral/fork-chat-34KEF4ZJ.mjs.map +0 -7
  247. package/dist/lib/neutral/markdown-extension-YMIFDMYF.mjs +0 -110
  248. package/dist/lib/neutral/markdown-extension-YMIFDMYF.mjs.map +0 -7
  249. package/dist/lib/neutral/react-surface-XRTBW5OJ.mjs.map +0 -7
  250. package/dist/lib/neutral/run-prompt-in-new-chat-D5IIE2C7.mjs.map +0 -7
  251. package/src/testing/trace-timeline.node.conversations.json +0 -1
  252. /package/dist/lib/neutral/{ChatArticle-VNVZCDUR.mjs.map → ChatArticle-KT46SUFU.mjs.map} +0 -0
  253. /package/dist/lib/neutral/{chunk-SMFJD7BP.mjs.map → chunk-7FQT4XMI.mjs.map} +0 -0
  254. /package/dist/lib/neutral/{create-object-U2SOFZLR.mjs.map → create-object-BJUDDKQQ.mjs.map} +0 -0
@@ -2,44 +2,138 @@
2
2
  // Copyright 2026 DXOS.org
3
3
  //
4
4
 
5
- import React from 'react';
5
+ import React, { useMemo } from 'react';
6
6
 
7
7
  import { Plan } from '@dxos/assistant-toolkit';
8
+ import { type Process, type Trace } from '@dxos/compute';
8
9
  import { useObject } from '@dxos/react-client/echo';
10
+ import { type Space } from '@dxos/react-client/echo';
9
11
  import { Icon, Tag, type ThemedClassName } from '@dxos/react-ui';
10
12
  import { composable, composableProps } from '@dxos/react-ui';
11
- import { Row, RowList } from '@dxos/react-ui-list';
13
+ import { TextCrawl } from '@dxos/react-ui-components';
14
+ import { Listbox } from '@dxos/react-ui-list';
15
+
16
+ import { collectProcessActivityLines, deriveInFlightActivityLine } from '#execution-graph';
17
+ import { isTerminalActivityLine, useProcessEphemeralStatus } from '#hooks';
12
18
 
13
19
  export type TaskListProps = ThemedClassName<{
14
20
  plan: Plan.Plan;
21
+ space?: Space;
22
+ /**
23
+ * Supervisor conversation feed id; trace messages without a conversation id still match by pid.
24
+ */
25
+ conversationId?: string;
26
+ traceMessages?: readonly Trace.Message[];
15
27
  }>;
16
28
 
17
- export const TaskList = composable<HTMLDivElement, TaskListProps>(({ plan, ...props }, forwardedRef) => {
18
- const [snapshot] = useObject(plan);
19
- const tasks = snapshot?.tasks ?? [];
29
+ export const TaskList = composable<HTMLDivElement, TaskListProps>(
30
+ ({ plan, space, conversationId, traceMessages, ...props }, forwardedRef) => {
31
+ const [snapshot] = useObject(plan);
32
+ const tasks = snapshot?.tasks ?? [];
33
+
34
+ return (
35
+ <Listbox.Root>
36
+ <Listbox.Viewport {...composableProps(props, { classNames: 'dx-container' })} ref={forwardedRef}>
37
+ <Listbox.Content aria-label='Tasks'>
38
+ {tasks.map((task) => (
39
+ <TaskListItem
40
+ key={task.id}
41
+ task={task}
42
+ space={space}
43
+ traceMessages={traceMessages}
44
+ conversationId={conversationId}
45
+ />
46
+ ))}
47
+ </Listbox.Content>
48
+ </Listbox.Viewport>
49
+ </Listbox.Root>
50
+ );
51
+ },
52
+ );
53
+
54
+ type TaskListItemProps = {
55
+ task: Plan.Task;
56
+ space?: Space;
57
+ traceMessages?: readonly Trace.Message[];
58
+ conversationId?: string;
59
+ };
60
+
61
+ const TaskListItem = ({ task, space, traceMessages, conversationId }: TaskListItemProps) => {
62
+ const durableLines = useMemo(() => {
63
+ if (!task.agentPid || !traceMessages?.length) {
64
+ return [];
65
+ }
66
+ const lines = collectProcessActivityLines(traceMessages, String(task.agentPid), { conversationId });
67
+ if (task.status !== 'in-progress') {
68
+ return lines;
69
+ }
70
+ return lines.filter((line) => !isTerminalActivityLine(line));
71
+ }, [task.agentPid, task.status, traceMessages, conversationId]);
72
+
73
+ const ephemeralLine = useProcessEphemeralStatus(
74
+ task.delegated === true && task.status === 'in-progress' ? task.agentPid : undefined,
75
+ space,
76
+ );
77
+
78
+ const inFlightLine = useMemo(() => {
79
+ if (ephemeralLine || task.status !== 'in-progress' || !task.agentPid || !traceMessages?.length) {
80
+ return undefined;
81
+ }
82
+ return deriveInFlightActivityLine(traceMessages, String(task.agentPid), { conversationId });
83
+ }, [ephemeralLine, task.status, task.agentPid, traceMessages, conversationId]);
84
+
85
+ const activityLines = useMemo(() => {
86
+ if (ephemeralLine) {
87
+ return [ephemeralLine];
88
+ }
89
+ if (inFlightLine) {
90
+ return [inFlightLine];
91
+ }
92
+ return durableLines;
93
+ }, [durableLines, ephemeralLine, inFlightLine]);
94
+
95
+ const agentPid = task.agentPid;
96
+ const showActivity = task.delegated === true && agentPid != null && activityLines.length > 0;
97
+
98
+ return (
99
+ <Listbox.Item id={task.id} classNames='py-0'>
100
+ <div className='flex flex-col gap-0.5 min-w-0'>
101
+ <div className='flex items-center gap-2 min-w-0'>
102
+ <Icon
103
+ icon={task.status === 'done' ? 'ph--check--regular' : 'ph--circle--regular'}
104
+ classNames={task.status === 'done' ? 'text-success-text' : undefined}
105
+ size={4}
106
+ />
107
+ <span className='sr-only'>
108
+ {task.status === 'done' ? 'done' : task.status === 'in-progress' ? 'in progress' : 'to do'}
109
+ </span>
110
+ <span className='truncate flex-1'>{task.title}</span>
111
+ {task.status === 'in-progress' && <Tag palette='info'>pending</Tag>}
112
+ {task.agentPid && <Tag palette='info'>{Plan.formatAgentPidTag(task.agentPid)}</Tag>}
113
+ </div>
114
+ {showActivity && agentPid != null && <DelegatedTaskActivity agentPid={agentPid} lines={activityLines} />}
115
+ </div>
116
+ </Listbox.Item>
117
+ );
118
+ };
119
+
120
+ type DelegatedTaskActivityProps = {
121
+ agentPid: Process.ID;
122
+ lines: string[];
123
+ };
20
124
 
125
+ const DelegatedTaskActivity = ({ agentPid, lines }: DelegatedTaskActivityProps) => {
21
126
  return (
22
- <RowList.Root>
23
- <RowList.Viewport {...composableProps(props, { classNames: 'dx-container' })} ref={forwardedRef}>
24
- <RowList.Content aria-label='Tasks'>
25
- {tasks.map((task) => (
26
- <Row key={task.id} id={task.id} classNames='py-0'>
27
- <div className='flex items-center gap-2'>
28
- <Icon
29
- icon={task.status === 'done' ? 'ph--check--regular' : 'ph--circle--regular'}
30
- classNames={task.status === 'done' ? 'text-success-text' : undefined}
31
- size={4}
32
- />
33
- <span className='sr-only'>
34
- {task.status === 'done' ? 'done' : task.status === 'in-progress' ? 'in progress' : 'to do'}
35
- </span>
36
- <span className='truncate flex-1'>{task.title}</span>
37
- {task.status === 'in-progress' && <Tag palette='info'>pending</Tag>}
38
- </div>
39
- </Row>
40
- ))}
41
- </RowList.Content>
42
- </RowList.Viewport>
43
- </RowList.Root>
127
+ <div className='flex items-center gap-2 ps-6 min-w-0 text-placeholder'>
128
+ <Icon icon='ph--brain--regular' size={3} classNames='shrink-0 opacity-70' />
129
+ <TextCrawl
130
+ key={`${String(agentPid)}:${lines.length}:${lines[lines.length - 1]}`}
131
+ lines={lines}
132
+ autoAdvance
133
+ greedy
134
+ size='sm'
135
+ classNames='text-xs text-subdued min-w-0 flex-1'
136
+ />
137
+ </div>
44
138
  );
45
- });
139
+ };
@@ -34,7 +34,7 @@ export type TemplateEditorProps = {
34
34
 
35
35
  export const TemplateEditor = composable<HTMLDivElement, TemplateEditorProps>(
36
36
  ({ classNames, id, source, lineNumbers = true, ...props }, forwardedRef) => {
37
- const { t } = useTranslation(meta.id);
37
+ const { t } = useTranslation(meta.profile.key);
38
38
  const { themeMode } = useThemeContext();
39
39
  const { parentRef } = useTextEditor(() => {
40
40
  const target = source?.target;
@@ -31,7 +31,7 @@ export type TemplateFormProps = {
31
31
  };
32
32
 
33
33
  export const TemplateForm = ({ id, template, onChange }: TemplateFormProps) => {
34
- const { t } = useTranslation(meta.id);
34
+ const { t } = useTranslation(meta.profile.key);
35
35
  usePromptInputs(template, onChange);
36
36
 
37
37
  const handleInputKindChange = useCallback(
@@ -23,7 +23,7 @@ export type ToolBlockProps = XmlWidgetProps<{
23
23
  }>;
24
24
 
25
25
  export const ToolBlock = ({ view, blocks = [] }: ToolBlockProps) => {
26
- const { t } = useTranslation(meta.id);
26
+ const { t } = useTranslation(meta.profile.key);
27
27
 
28
28
  const items = useMemo<ToolPanelProps['items']>(() => {
29
29
  let lastToolCall: { tool: Tool.Any | undefined; block: ContentBlock.ToolCall } | undefined;
@@ -7,6 +7,7 @@ import { type ComponentType, lazy } from 'react';
7
7
 
8
8
  export * from './AgentProperties';
9
9
  export * from './Chat';
10
+ export { TracePanel } from '../containers/TracePanel/TracePanel';
10
11
  export * from './ProcessTree';
11
12
  export * from './TaskList';
12
13
  export * from './TemplateEditor';
@@ -8,7 +8,7 @@ import React from 'react';
8
8
 
9
9
  import { withPluginManager } from '@dxos/app-framework/testing';
10
10
  import { Plan, Agent } from '@dxos/assistant-toolkit';
11
- import { Filter, Obj, Ref } from '@dxos/echo';
11
+ import { Feed, Filter, Obj, Ref } from '@dxos/echo';
12
12
  import { AutomationPlugin } from '@dxos/plugin-automation/testing';
13
13
  import { ClientPlugin } from '@dxos/plugin-client/testing';
14
14
  import { initializeIdentity } from '@dxos/plugin-client/testing';
@@ -71,10 +71,10 @@ const meta = {
71
71
  const factory = createObjectFactory(space.db, random as any);
72
72
  const artifacts = yield* Effect.promise(() => factory(defaultSpec));
73
73
 
74
- const inputQueue = space.queues.create();
74
+ const inputFeed = space.db.add(Feed.make({}));
75
75
  if (inputs) {
76
76
  yield* Effect.promise(() =>
77
- inputQueue.append([
77
+ space.db.appendToFeed(inputFeed, [
78
78
  createMessage('user', [{ _tag: 'text', text: 'Summarize the current artifacts.' }]),
79
79
  createMessage('assistant', [
80
80
  { _tag: 'text', text: 'Here is a quick overview of the organizations and contacts in context.' },
@@ -92,7 +92,7 @@ const meta = {
92
92
  name: Obj.getLabel(obj) ?? 'Artifact',
93
93
  data: Ref.make(obj),
94
94
  })),
95
- feed: Ref.fromURI(inputQueue.uri),
95
+ feed: Ref.make(inputFeed),
96
96
  subscriptions: [],
97
97
  }),
98
98
  );
@@ -12,7 +12,7 @@ import { AppSurface, useObjectMenuItems } from '@dxos/app-toolkit/ui';
12
12
  import { Agent } from '@dxos/assistant-toolkit';
13
13
  import { Database, Feed, Filter, Obj, Query, Ref } from '@dxos/echo';
14
14
  import { useQuery } from '@dxos/react-client/echo';
15
- import { Card, Message, Panel, ScrollArea, Toolbar, useTranslation } from '@dxos/react-ui';
15
+ import { Card, Icon, IconButton, Message, Panel, ScrollArea, Toolbar, useTranslation } from '@dxos/react-ui';
16
16
  import { composable } from '@dxos/react-ui';
17
17
  import { Masonry } from '@dxos/react-ui-masonry';
18
18
  import { Menu } from '@dxos/react-ui-menu';
@@ -26,7 +26,7 @@ type Tab = 'artifacts' | 'inputs';
26
26
  export type AgentArticleProps = AppSurface.ObjectArticleProps<Agent.Agent>;
27
27
 
28
28
  export const AgentArticle = ({ role, subject: agent }: AgentArticleProps) => {
29
- const { t } = useTranslation(meta.id);
29
+ const { t } = useTranslation(meta.profile.key);
30
30
  const [tab, setTab] = useState<Tab>('artifacts');
31
31
  const [viewport, setViewport] = useState<HTMLElement | null>(null);
32
32
 
@@ -146,21 +146,21 @@ const ArtifactTileCard = composable<HTMLDivElement, { data: Obj.Unknown }>(({ da
146
146
  return (
147
147
  <Card.Root {...props} ref={forwardedRef} data-testid='board-item' fullWidth>
148
148
  <Card.Header>
149
- <Card.IconBlock>
150
- <Card.Icon icon={icon} />
151
- </Card.IconBlock>
149
+ <Card.Block>
150
+ <Icon icon={icon} />
151
+ </Card.Block>
152
152
  <Card.Title>{Obj.getLabel(data, { fallback: 'typename' })}</Card.Title>
153
153
  {/* TODO(wittjosiah): Reconcile with Card.Menu. */}
154
- <Card.IconBlock>
154
+ <Card.Block end>
155
155
  <Menu.Trigger asChild disabled={!objectMenuItems?.length}>
156
- <Toolbar.IconButton iconOnly variant='ghost' icon='ph--dots-three-vertical--regular' label='Actions' />
156
+ <IconButton iconOnly variant='ghost' icon='ph--dots-three-vertical--regular' label='Actions' />
157
157
  </Menu.Trigger>
158
158
  <Menu.Content items={objectMenuItems} />
159
- </Card.IconBlock>
159
+ </Card.Block>
160
160
  </Card.Header>
161
161
  <Card.Body>
162
162
  <Surface.Surface
163
- type={AppSurface.Card}
163
+ type={AppSurface.CardContent}
164
164
  limit={1}
165
165
  data={{ subject: data } satisfies AppSurface.ObjectCardData}
166
166
  />
@@ -2,14 +2,15 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import React, { forwardRef, useEffect, useRef } from 'react';
5
+ import React, { forwardRef, useCallback, useEffect, useRef } from 'react';
6
6
 
7
7
  import { Capabilities } from '@dxos/app-framework';
8
- import { useAtomCapability, useCapability } from '@dxos/app-framework/ui';
8
+ import { useAtomCapability, useCapability, useOperationInvoker } from '@dxos/app-framework/ui';
9
9
  import { type AppSurface } from '@dxos/app-toolkit/ui';
10
10
  import { getSpace } from '@dxos/client/echo';
11
11
  import { type Obj } from '@dxos/echo';
12
- import { useRegistry } from '@dxos/react-client/echo';
12
+ import { ClientOperation } from '@dxos/plugin-client';
13
+ import { useObject, useRegistry } from '@dxos/react-client/echo';
13
14
  import { Panel } from '@dxos/react-ui';
14
15
 
15
16
  import { Chat as ChatComponent, type ChatRootProps } from '#components';
@@ -33,10 +34,20 @@ export const ChatArticle = forwardRef<HTMLDivElement, ChatArticleProps>(
33
34
  const [online, setOnline] = useOnline();
34
35
  const { preset, ...chatProps } = usePresets(online);
35
36
  const processor = useChatProcessor({ space, chat, preset, runtime, registry, settings });
36
- const pendingSubmitted = useRef(false);
37
+
38
+ // Subscribe to the view type via `useObject` so the thread re-renders when ChatOptions changes it;
39
+ // a direct `chat.viewType` read in render does not establish a reactive dependency.
40
+ const [chatViewType] = useObject(chat, 'viewType');
41
+ const viewType = (chatViewType as Assistant.ChatView | undefined) ?? settings.chatView;
42
+
43
+ const { invokePromise } = useOperationInvoker();
44
+ const handleViewUsage = useCallback(() => {
45
+ void invokePromise(ClientOperation.OpenUsage, undefined);
46
+ }, [invokePromise]);
37
47
 
38
48
  // Reset the one-shot guard when the target conversation changes, so a pending prompt for a new
39
49
  // `attendableId` is still auto-submitted within the same mount.
50
+ const pendingSubmitted = useRef(false);
40
51
  useEffect(() => {
41
52
  pendingSubmitted.current = false;
42
53
  }, [attendableId]);
@@ -59,9 +70,6 @@ export const ChatArticle = forwardRef<HTMLDivElement, ChatArticleProps>(
59
70
  }
60
71
  }, [processor, attendableId, atomRegistry, stateAtom]);
61
72
 
62
- const feedTarget = chat?.feed.target;
63
- const view = (chat?.view as Assistant.ChatView | undefined) ?? settings.chatView;
64
-
65
73
  if (!processor) {
66
74
  return null;
67
75
  }
@@ -69,7 +77,7 @@ export const ChatArticle = forwardRef<HTMLDivElement, ChatArticleProps>(
69
77
  return (
70
78
  <ChatComponent.Root
71
79
  chat={chat}
72
- feed={feedTarget}
80
+ feed={chat?.feed.target}
73
81
  db={space?.db}
74
82
  processor={processor}
75
83
  onEvent={onEvent}
@@ -82,8 +90,8 @@ export const ChatArticle = forwardRef<HTMLDivElement, ChatArticleProps>(
82
90
  <Panel.Content>
83
91
  <ChatComponent.Content>
84
92
  <div className='dx-container relative'>
85
- <ChatComponent.Thread viewType={view} />
86
- {view !== 'summary' && (
93
+ <ChatComponent.Thread viewType={viewType} onViewUsage={handleViewUsage} />
94
+ {viewType !== 'summary' && (
87
95
  <div className='absolute bottom-2 left-0 right-0'>
88
96
  <div className='dx-document px-4'>
89
97
  <ChatComponent.Status classNames='px-3 rounded-sm bg-group-surface' />
@@ -6,7 +6,7 @@ import * as Option from 'effect/Option';
6
6
  import React, { forwardRef, useCallback, useMemo } from 'react';
7
7
 
8
8
  import { useOperationInvoker } from '@dxos/app-framework/ui';
9
- import { BlueprintsAnnotation } from '@dxos/app-toolkit';
9
+ import { AppAnnotation } from '@dxos/app-toolkit';
10
10
  import { type AppSurface } from '@dxos/app-toolkit/ui';
11
11
  import { Chat } from '@dxos/assistant-toolkit';
12
12
  import { getSpace } from '@dxos/client/echo';
@@ -83,7 +83,7 @@ const useBlueprints = ({ subject: chat, companionTo }: Pick<ChatCompanionProps,
83
83
  return [] as string[];
84
84
  }
85
85
 
86
- return Option.getOrElse(() => [] as string[])(BlueprintsAnnotation.get(Type.getSchema(schema)));
86
+ return Option.getOrElse(() => [] as string[])(AppAnnotation.BlueprintsAnnotation.get(Type.getSchema(schema)));
87
87
  }, [companionTo]);
88
88
 
89
89
  const existingBlueprints = useQuery(space?.db, Filter.type(Blueprint.Blueprint));
@@ -7,7 +7,7 @@ import React, { useCallback, useState } from 'react';
7
7
  import { useAtomCapability } from '@dxos/app-framework/ui';
8
8
  import { type Chat as ChatTypes } from '@dxos/assistant-toolkit';
9
9
  import { Obj } from '@dxos/echo';
10
- import { useRegistry } from '@dxos/react-client/echo';
10
+ import { useObject, useRegistry } from '@dxos/react-client/echo';
11
11
  import { useTranslation } from '@dxos/react-ui';
12
12
  import { ChatDialog as NaturalChatDialog } from '@dxos/react-ui-chat';
13
13
 
@@ -21,7 +21,7 @@ export type ChatDialogProps = {
21
21
  };
22
22
 
23
23
  export const ChatDialog = ({ chat }: ChatDialogProps) => {
24
- const { t } = useTranslation(meta.id);
24
+ const { t } = useTranslation(meta.profile.key);
25
25
 
26
26
  const db = chat && Obj.getDatabase(chat);
27
27
  const settings = useAtomCapability(AssistantCapabilities.Settings);
@@ -30,6 +30,8 @@ export const ChatDialog = ({ chat }: ChatDialogProps) => {
30
30
  const { preset, ...chatProps } = usePresets(online);
31
31
  const registry = useRegistry();
32
32
  const processor = useChatProcessor({ chat, preset, runtime, registry, settings });
33
+ // Subscribe via `useObject` so the thread re-renders when ChatOptions changes the view type.
34
+ const [chatViewType] = useObject(chat, 'viewType');
33
35
 
34
36
  // TODO(burdon): Refocus when open.
35
37
  const [open, setOpen] = useState(true);
@@ -56,7 +58,7 @@ export const ChatDialog = ({ chat }: ChatDialogProps) => {
56
58
  <NaturalChatDialog.Root open={open} expanded={expanded} onOpenChange={setOpen}>
57
59
  <NaturalChatDialog.Header title={t('assistant-dialog.title')} />
58
60
  <NaturalChatDialog.Content>
59
- <Chat.Thread viewType={(chat.view as Assistant.ChatView | undefined) ?? settings.chatView} />
61
+ <Chat.Thread viewType={(chatViewType as Assistant.ChatView | undefined) ?? settings.chatView} />
60
62
  </NaturalChatDialog.Content>
61
63
  <NaturalChatDialog.Footer classNames='p-1.5'>
62
64
  <Chat.Prompt {...chatProps} preset={preset?.id} online={online} onOnlineChange={setOnline} expandable />
@@ -6,15 +6,19 @@ import React from 'react';
6
6
 
7
7
  import { type AppSurface } from '@dxos/app-toolkit/ui';
8
8
  import { type Plan } from '@dxos/assistant-toolkit';
9
+ import { getSpace } from '@dxos/client/echo';
9
10
  import { Panel, ScrollArea, Toolbar } from '@dxos/react-ui';
10
11
  import { useAttention } from '@dxos/react-ui-attention';
11
12
 
12
13
  import { TaskList } from '#components';
14
+ import { useTraceMessages } from '#hooks';
13
15
 
14
16
  export type PlanArticleProps = AppSurface.ObjectArticleProps<Plan.Plan>;
15
17
 
16
18
  export const PlanArticle = ({ role, attendableId, subject }: PlanArticleProps) => {
17
19
  const { hasAttention } = useAttention(attendableId);
20
+ const space = getSpace(subject);
21
+ const traceMessages = useTraceMessages(space);
18
22
 
19
23
  return (
20
24
  <Panel.Root role={role} classNames='dx-document'>
@@ -24,7 +28,7 @@ export const PlanArticle = ({ role, attendableId, subject }: PlanArticleProps) =
24
28
  <Panel.Content asChild>
25
29
  <ScrollArea.Root orientation='vertical'>
26
30
  <ScrollArea.Viewport>
27
- <TaskList plan={subject} />
31
+ <TaskList plan={subject} space={space} traceMessages={traceMessages} />
28
32
  </ScrollArea.Viewport>
29
33
  </ScrollArea.Root>
30
34
  </Panel.Content>
@@ -27,7 +27,7 @@ type RunState =
27
27
 
28
28
  // TODO(burdon): Trigger editor.
29
29
  export const RoutineArticle = ({ role, attendableId, subject }: RoutineArticleProps) => {
30
- const { t } = useTranslation(meta.id);
30
+ const { t } = useTranslation(meta.profile.key);
31
31
  const { hasAttention } = useAttention(attendableId);
32
32
  const invoke = usePromptHandler(subject);
33
33
  const [state, setState] = useState<RunState>({ status: 'idle' });
@@ -73,7 +73,7 @@ export const RoutineArticle = ({ role, attendableId, subject }: RoutineArticlePr
73
73
  };
74
74
 
75
75
  const RoutineResult = ({ state }: { state: RunState }) => {
76
- const { t } = useTranslation(meta.id);
76
+ const { t } = useTranslation(meta.profile.key);
77
77
  switch (state.status) {
78
78
  case 'idle':
79
79
  return null;
@@ -0,0 +1,105 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import React, { useEffect, useMemo, useState } from 'react';
6
+
7
+ import { Capabilities } from '@dxos/app-framework';
8
+ import { useCapability, useOperationInvoker } from '@dxos/app-framework/ui';
9
+ import { LayoutOperation } from '@dxos/app-toolkit';
10
+ import { Event } from '@dxos/async';
11
+ import { type Space, useRegistry } from '@dxos/react-client/echo';
12
+ import { useTranslation } from '@dxos/react-ui';
13
+
14
+ import { ChatPrompt, type ChatEvent } from '#components';
15
+ import { useChatProcessor, useChatServices, useOnline, usePresets } from '#hooks';
16
+ import { meta } from '#meta';
17
+ import { AssistantCapabilities, AssistantOperation, type ChatType } from '#types';
18
+
19
+ import { getChatPath } from '../../paths';
20
+
21
+ type SpaceScopedProps = {
22
+ space?: Space;
23
+ };
24
+
25
+ /**
26
+ * Home article pinned-bottom contributor: the assistant prompt. Backed by an ephemeral in-memory
27
+ * chat whose sole responsibility is to collect the user's text, context bindings, and preset
28
+ * choice, then on submit: persist the chat to the space, queue the text as a pending prompt, and
29
+ * navigate to it. AI generation runs in the opened chat view — the processor here exists only to
30
+ * back the context-binder UI.
31
+ */
32
+ export const SpaceHomePrompt = ({ space }: SpaceScopedProps) => {
33
+ const { t } = useTranslation(meta.profile.key);
34
+ const { invokePromise } = useOperationInvoker();
35
+
36
+ const registry = useRegistry();
37
+ const atomRegistry = useCapability(Capabilities.AtomRegistry);
38
+ const stateAtom = useCapability(AssistantCapabilities.State);
39
+ const runtime = useChatServices({ id: space?.id });
40
+ const [online, setOnline] = useOnline();
41
+ const { preset, ...presetProps } = usePresets(online);
42
+
43
+ // In-memory backing chat (not yet added to the space). `nonce` forces a fresh chat after submit.
44
+ const [chat, setChat] = useState<ChatType.Chat>();
45
+ const [nonce, setNonce] = useState(0);
46
+ useEffect(() => {
47
+ if (!space) {
48
+ setChat(undefined);
49
+ return;
50
+ }
51
+ let cancelled = false;
52
+ void invokePromise(AssistantOperation.CreateChat, { db: space.db, addToSpace: false }).then((result) => {
53
+ if (!cancelled) {
54
+ setChat(result.data?.object);
55
+ }
56
+ });
57
+ return () => {
58
+ cancelled = true;
59
+ };
60
+ }, [space, nonce, invokePromise]);
61
+
62
+ const processor = useChatProcessor({ space, chat, preset, runtime, registry });
63
+
64
+ const event = useMemo(() => new Event<ChatEvent>(), []);
65
+ useEffect(() => {
66
+ return event.on((ev) => {
67
+ if (ev.type !== 'submit') {
68
+ return;
69
+ }
70
+ const text = ev.text.trim();
71
+ if (!space || !chat || text.length === 0) {
72
+ return;
73
+ }
74
+
75
+ // Persist the in-memory chat, queue the prompt, and open the chat (which submits it).
76
+ space.db.add(chat);
77
+ const chatPath = getChatPath(space.db.spaceId, chat.id);
78
+ atomRegistry.update(stateAtom, (current) => ({
79
+ ...current,
80
+ pendingPrompts: { ...current.pendingPrompts, [chatPath]: text },
81
+ }));
82
+ void invokePromise(LayoutOperation.Open, { subject: [chatPath] });
83
+ setNonce((current) => current + 1);
84
+ });
85
+ }, [event, space, chat, atomRegistry, stateAtom, invokePromise]);
86
+
87
+ if (!processor || !chat || !space) {
88
+ return null;
89
+ }
90
+
91
+ return (
92
+ <ChatPrompt
93
+ {...presetProps}
94
+ outline
95
+ chat={chat}
96
+ db={space.db}
97
+ processor={processor}
98
+ event={event}
99
+ preset={preset?.id}
100
+ online={online}
101
+ onOnlineChange={setOnline}
102
+ placeholder={t('space-home.prompt.placeholder')}
103
+ />
104
+ );
105
+ };
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ export { SpaceHomePrompt as default } from './SpaceHomePrompt';
@@ -0,0 +1,69 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import React, { useCallback } from 'react';
6
+
7
+ import { useOperationInvoker } from '@dxos/app-framework/ui';
8
+ import { type Space } from '@dxos/react-client/echo';
9
+ import { Card, IconButton, useTranslation } from '@dxos/react-ui';
10
+
11
+ import { meta } from '#meta';
12
+ import { AssistantOperation } from '#types';
13
+
14
+ /** Starter prompts shown on the Home page. Always rendered — they surface quick actions alongside
15
+ * the recent-objects masonry rather than only as an empty-state fallback. */
16
+ const SUGGESTION_KEYS = [
17
+ 'space-home.suggestion-draft-doc.label',
18
+ 'space-home.suggestion-data-type.label',
19
+ 'space-home.suggestion-ideas.label',
20
+ ] as const;
21
+
22
+ type SpaceScopedProps = {
23
+ space?: Space;
24
+ };
25
+
26
+ /**
27
+ * Home content contributor: starter-prompt cards. Each card runs its prompt in a new chat via the
28
+ * assistant operation. Always renders (below the recent-objects masonry) so the Home page offers
29
+ * quick entry points regardless of whether recent objects exist.
30
+ */
31
+ export const SpaceHomeSuggestions = ({ space }: SpaceScopedProps) => {
32
+ const { t } = useTranslation(meta.profile.key);
33
+ const { invokePromise } = useOperationInvoker();
34
+
35
+ const handleRunPrompt = useCallback(
36
+ (prompt: string) => {
37
+ if (!space) {
38
+ return;
39
+ }
40
+ void invokePromise(AssistantOperation.RunPromptInNewChat, { db: space.db, prompt });
41
+ },
42
+ [invokePromise, space],
43
+ );
44
+
45
+ return (
46
+ <>
47
+ <h2 className='text-sm font-medium text-description'>{t('space-home.suggestions.heading')}</h2>
48
+ {SUGGESTION_KEYS.map((key) => {
49
+ const prompt = t(key);
50
+ return (
51
+ <Card.Root
52
+ key={key}
53
+ fullWidth
54
+ role='button'
55
+ classNames='cursor-pointer'
56
+ onClick={() => handleRunPrompt(prompt)}
57
+ >
58
+ <Card.Header>
59
+ <Card.Block>
60
+ <IconButton variant='ghost' label={prompt} icon='ph--sparkle--regular' iconOnly />
61
+ </Card.Block>
62
+ <Card.Title>{prompt}</Card.Title>
63
+ </Card.Header>
64
+ </Card.Root>
65
+ );
66
+ })}
67
+ </>
68
+ );
69
+ };
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ export { SpaceHomeSuggestions as default } from './SpaceHomeSuggestions';