@dxos/plugin-automation 0.7.4 → 0.7.5-labs.5f04cf6

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 (201) hide show
  1. package/dist/lib/browser/{AssistantPanel-N3QSALKY.mjs → AssistantPanel-KO6DZBTU.mjs} +10 -11
  2. package/dist/lib/browser/AssistantPanel-KO6DZBTU.mjs.map +7 -0
  3. package/dist/lib/browser/AutomationPanel-BMLM533Z.mjs +131 -0
  4. package/dist/lib/browser/AutomationPanel-BMLM533Z.mjs.map +7 -0
  5. package/dist/lib/browser/ChatContainer-SIAJFRFF.mjs +300 -0
  6. package/dist/lib/browser/ChatContainer-SIAJFRFF.mjs.map +7 -0
  7. package/dist/lib/browser/ai-client-6CRYUC6D.mjs +22 -0
  8. package/dist/lib/browser/ai-client-6CRYUC6D.mjs.map +7 -0
  9. package/dist/lib/browser/app-graph-builder-IJTTULDP.mjs +131 -0
  10. package/dist/lib/browser/app-graph-builder-IJTTULDP.mjs.map +7 -0
  11. package/dist/lib/browser/{chunk-7KB4UMXO.mjs → chunk-C3VRGDR6.mjs} +30 -4
  12. package/dist/lib/browser/chunk-C3VRGDR6.mjs.map +7 -0
  13. package/dist/lib/browser/chunk-EKJVAFT2.mjs +226 -0
  14. package/dist/lib/browser/chunk-EKJVAFT2.mjs.map +7 -0
  15. package/dist/lib/browser/{chunk-X5KMOH3I.mjs → chunk-HKX3D3ZP.mjs} +3 -3
  16. package/dist/lib/browser/chunk-HKX3D3ZP.mjs.map +7 -0
  17. package/dist/lib/browser/chunk-HZ4TA7HY.mjs +15 -0
  18. package/dist/lib/browser/chunk-HZ4TA7HY.mjs.map +7 -0
  19. package/dist/lib/browser/chunk-JSNPW6JF.mjs +14 -0
  20. package/dist/lib/browser/chunk-JSNPW6JF.mjs.map +7 -0
  21. package/dist/lib/browser/chunk-KPSDH6XZ.mjs +118 -0
  22. package/dist/lib/browser/chunk-KPSDH6XZ.mjs.map +7 -0
  23. package/dist/lib/browser/chunk-YZF3SDZL.mjs +139 -0
  24. package/dist/lib/browser/chunk-YZF3SDZL.mjs.map +7 -0
  25. package/dist/lib/browser/index.mjs +143 -249
  26. package/dist/lib/browser/index.mjs.map +4 -4
  27. package/dist/lib/browser/intent-resolver-AHDQ3KQP.mjs +24 -0
  28. package/dist/lib/browser/intent-resolver-AHDQ3KQP.mjs.map +7 -0
  29. package/dist/lib/browser/meta.json +1 -1
  30. package/dist/lib/browser/react-surface-IGMBLSV7.mjs +49 -0
  31. package/dist/lib/browser/react-surface-IGMBLSV7.mjs.map +7 -0
  32. package/dist/lib/browser/types/index.mjs +6 -3
  33. package/dist/lib/node/{AssistantPanel-RIA4TI3B.cjs → AssistantPanel-PPFS2HKK.cjs} +20 -21
  34. package/dist/lib/node/AssistantPanel-PPFS2HKK.cjs.map +7 -0
  35. package/dist/lib/node/{AutomationPanel-HZS5WKI5.cjs → AutomationPanel-RXJDVLCF.cjs} +52 -72
  36. package/dist/lib/node/AutomationPanel-RXJDVLCF.cjs.map +7 -0
  37. package/dist/lib/node/ChatContainer-GS2SC5OG.cjs +318 -0
  38. package/dist/lib/node/ChatContainer-GS2SC5OG.cjs.map +7 -0
  39. package/dist/lib/node/ai-client-A3RRU55B.cjs +38 -0
  40. package/dist/lib/node/ai-client-A3RRU55B.cjs.map +7 -0
  41. package/dist/lib/node/app-graph-builder-MF5M4QRS.cjs +147 -0
  42. package/dist/lib/node/app-graph-builder-MF5M4QRS.cjs.map +7 -0
  43. package/dist/lib/node/{chunk-DTJ7XVO2.cjs → chunk-5VF5JKUN.cjs} +7 -7
  44. package/dist/lib/node/chunk-5VF5JKUN.cjs.map +7 -0
  45. package/dist/lib/node/{chunk-CUCUWUAF.cjs → chunk-BUQOZA4N.cjs} +32 -8
  46. package/dist/lib/node/chunk-BUQOZA4N.cjs.map +7 -0
  47. package/dist/lib/node/chunk-DNLBVFR7.cjs +147 -0
  48. package/dist/lib/node/chunk-DNLBVFR7.cjs.map +7 -0
  49. package/dist/lib/node/chunk-TWDGP26W.cjs +172 -0
  50. package/dist/lib/node/chunk-TWDGP26W.cjs.map +7 -0
  51. package/dist/lib/node/chunk-VEGLN4YN.cjs +250 -0
  52. package/dist/lib/node/chunk-VEGLN4YN.cjs.map +7 -0
  53. package/dist/lib/node/chunk-Z2YFE5SJ.cjs +49 -0
  54. package/dist/lib/node/chunk-Z2YFE5SJ.cjs.map +7 -0
  55. package/dist/lib/node/{meta.cjs → chunk-ZS5RZ7RM.cjs} +12 -8
  56. package/dist/lib/node/chunk-ZS5RZ7RM.cjs.map +7 -0
  57. package/dist/lib/node/index.cjs +159 -279
  58. package/dist/lib/node/index.cjs.map +4 -4
  59. package/dist/lib/node/intent-resolver-TBHYXBDI.cjs +39 -0
  60. package/dist/lib/node/intent-resolver-TBHYXBDI.cjs.map +7 -0
  61. package/dist/lib/node/meta.json +1 -1
  62. package/dist/lib/node/react-surface-BVUZMKYS.cjs +69 -0
  63. package/dist/lib/node/react-surface-BVUZMKYS.cjs.map +7 -0
  64. package/dist/lib/node/types/index.cjs +10 -7
  65. package/dist/lib/node/types/index.cjs.map +2 -2
  66. package/dist/lib/node-esm/{AssistantPanel-72YH43CH.mjs → AssistantPanel-XSSKKCJY.mjs} +10 -11
  67. package/dist/lib/node-esm/AssistantPanel-XSSKKCJY.mjs.map +7 -0
  68. package/dist/lib/node-esm/AutomationPanel-GQZZ4UBI.mjs +132 -0
  69. package/dist/lib/node-esm/AutomationPanel-GQZZ4UBI.mjs.map +7 -0
  70. package/dist/lib/node-esm/ChatContainer-57ILGC3R.mjs +301 -0
  71. package/dist/lib/node-esm/ChatContainer-57ILGC3R.mjs.map +7 -0
  72. package/dist/lib/node-esm/ai-client-2GBZRHBA.mjs +23 -0
  73. package/dist/lib/node-esm/ai-client-2GBZRHBA.mjs.map +7 -0
  74. package/dist/lib/node-esm/app-graph-builder-5N7OK23B.mjs +132 -0
  75. package/dist/lib/node-esm/app-graph-builder-5N7OK23B.mjs.map +7 -0
  76. package/dist/lib/node-esm/chunk-5QUXIXNB.mjs +227 -0
  77. package/dist/lib/node-esm/chunk-5QUXIXNB.mjs.map +7 -0
  78. package/dist/lib/node-esm/chunk-ICHNUP5M.mjs +119 -0
  79. package/dist/lib/node-esm/chunk-ICHNUP5M.mjs.map +7 -0
  80. package/dist/lib/node-esm/chunk-ISYLEDVU.mjs +16 -0
  81. package/dist/lib/node-esm/chunk-ISYLEDVU.mjs.map +7 -0
  82. package/dist/lib/node-esm/{chunk-23LY7DYS.mjs → chunk-JCYVS5GG.mjs} +29 -4
  83. package/dist/lib/node-esm/chunk-JCYVS5GG.mjs.map +7 -0
  84. package/dist/lib/node-esm/chunk-L2B6VGMG.mjs +16 -0
  85. package/dist/lib/node-esm/chunk-L2B6VGMG.mjs.map +7 -0
  86. package/dist/lib/node-esm/chunk-U7HXYMPZ.mjs +141 -0
  87. package/dist/lib/node-esm/chunk-U7HXYMPZ.mjs.map +7 -0
  88. package/dist/lib/node-esm/{chunk-HNOBZHWK.mjs → chunk-X3LPRWIL.mjs} +3 -3
  89. package/dist/lib/node-esm/chunk-X3LPRWIL.mjs.map +7 -0
  90. package/dist/lib/node-esm/index.mjs +143 -249
  91. package/dist/lib/node-esm/index.mjs.map +4 -4
  92. package/dist/lib/node-esm/intent-resolver-DPJJHFGK.mjs +25 -0
  93. package/dist/lib/node-esm/intent-resolver-DPJJHFGK.mjs.map +7 -0
  94. package/dist/lib/node-esm/meta.json +1 -1
  95. package/dist/lib/node-esm/react-surface-77S6XJOM.mjs +50 -0
  96. package/dist/lib/node-esm/react-surface-77S6XJOM.mjs.map +7 -0
  97. package/dist/lib/node-esm/types/index.mjs +6 -3
  98. package/dist/types/src/AutomationPlugin.d.ts +1 -3
  99. package/dist/types/src/AutomationPlugin.d.ts.map +1 -1
  100. package/dist/types/src/artifacts.stories.d.ts +30 -0
  101. package/dist/types/src/artifacts.stories.d.ts.map +1 -0
  102. package/dist/types/src/capabilities/ai-client.d.ts +5 -0
  103. package/dist/types/src/capabilities/ai-client.d.ts.map +1 -0
  104. package/dist/types/src/capabilities/app-graph-builder.d.ts +180 -0
  105. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -0
  106. package/dist/types/src/capabilities/capabilities.d.ts +5 -0
  107. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -0
  108. package/dist/types/src/capabilities/index.d.ts +182 -0
  109. package/dist/types/src/capabilities/index.d.ts.map +1 -0
  110. package/dist/types/src/capabilities/intent-resolver.d.ts +4 -0
  111. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -0
  112. package/dist/types/src/capabilities/react-surface.d.ts +4 -0
  113. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
  114. package/dist/types/src/components/AssistantPanel/AssistantPanel.d.ts.map +1 -1
  115. package/dist/types/src/components/AutomationPanel/AutomationPanel.d.ts +1 -1
  116. package/dist/types/src/components/AutomationPanel/AutomationPanel.d.ts.map +1 -1
  117. package/dist/types/src/components/AutomationPanel/AutomationPanel.stories.d.ts.map +1 -1
  118. package/dist/types/src/components/ChatContainer.d.ts +8 -0
  119. package/dist/types/src/components/ChatContainer.d.ts.map +1 -0
  120. package/dist/types/src/components/Thread/Thread.d.ts +14 -0
  121. package/dist/types/src/components/Thread/Thread.d.ts.map +1 -0
  122. package/dist/types/src/components/Thread/Thread.stories.d.ts +12 -0
  123. package/dist/types/src/components/Thread/Thread.stories.d.ts.map +1 -0
  124. package/dist/types/src/components/Thread/index.d.ts +2 -0
  125. package/dist/types/src/components/Thread/index.d.ts.map +1 -0
  126. package/dist/types/src/components/TriggerEditor/TriggerEditor.d.ts +1 -2
  127. package/dist/types/src/components/TriggerEditor/TriggerEditor.d.ts.map +1 -1
  128. package/dist/types/src/components/TriggerEditor/TriggerEditor.stories.d.ts.map +1 -1
  129. package/dist/types/src/components/index.d.ts +8 -1
  130. package/dist/types/src/components/index.d.ts.map +1 -1
  131. package/dist/types/src/hooks/email.d.ts.map +1 -1
  132. package/dist/types/src/hooks/index.d.ts +1 -0
  133. package/dist/types/src/hooks/index.d.ts.map +1 -1
  134. package/dist/types/src/hooks/invocation-handler.d.ts.map +1 -1
  135. package/dist/types/src/hooks/processor.d.ts +46 -0
  136. package/dist/types/src/hooks/processor.d.ts.map +1 -0
  137. package/dist/types/src/hooks/processor.test.d.ts +2 -0
  138. package/dist/types/src/hooks/processor.test.d.ts.map +1 -0
  139. package/dist/types/src/index.d.ts +1 -2
  140. package/dist/types/src/index.d.ts.map +1 -1
  141. package/dist/types/src/meta.d.ts +1 -2
  142. package/dist/types/src/meta.d.ts.map +1 -1
  143. package/dist/types/src/testing/testing.d.ts +2 -0
  144. package/dist/types/src/testing/testing.d.ts.map +1 -1
  145. package/dist/types/src/translations.d.ts +76 -0
  146. package/dist/types/src/translations.d.ts.map +1 -1
  147. package/dist/types/src/types/schema.d.ts +40 -39
  148. package/dist/types/src/types/schema.d.ts.map +1 -1
  149. package/dist/types/src/types/types.d.ts +16 -5
  150. package/dist/types/src/types/types.d.ts.map +1 -1
  151. package/dist/types/tsconfig.tsbuildinfo +1 -0
  152. package/package.json +48 -47
  153. package/src/AutomationPlugin.tsx +85 -194
  154. package/src/artifacts.stories.tsx +180 -0
  155. package/src/capabilities/ai-client.ts +19 -0
  156. package/src/capabilities/app-graph-builder.ts +127 -0
  157. package/src/capabilities/capabilities.ts +12 -0
  158. package/src/capabilities/index.ts +12 -0
  159. package/src/capabilities/intent-resolver.ts +22 -0
  160. package/src/capabilities/react-surface.tsx +34 -0
  161. package/src/components/AssistantPanel/AssistantPanel.tsx +10 -8
  162. package/src/components/AutomationPanel/AutomationPanel.stories.tsx +1 -2
  163. package/src/components/AutomationPanel/AutomationPanel.tsx +61 -39
  164. package/src/components/ChatContainer.tsx +80 -0
  165. package/src/components/PromptEditor/PromptEditor.stories.tsx +3 -3
  166. package/src/components/Thread/Thread.stories.tsx +142 -0
  167. package/src/components/Thread/Thread.tsx +149 -0
  168. package/src/components/Thread/index.ts +5 -0
  169. package/src/components/TriggerEditor/TriggerEditor.stories.tsx +1 -2
  170. package/src/components/TriggerEditor/TriggerEditor.tsx +83 -16
  171. package/src/components/index.ts +5 -0
  172. package/src/hooks/email.ts +2 -2
  173. package/src/hooks/index.ts +1 -0
  174. package/src/hooks/invocation-handler.ts +2 -2
  175. package/src/hooks/processor.test.ts +15 -0
  176. package/src/hooks/processor.ts +161 -0
  177. package/src/index.ts +1 -4
  178. package/src/meta.ts +1 -1
  179. package/src/testing/testing.ts +9 -2
  180. package/src/translations.ts +13 -0
  181. package/src/types/schema.ts +9 -2
  182. package/src/types/types.ts +15 -21
  183. package/dist/lib/browser/AssistantPanel-N3QSALKY.mjs.map +0 -7
  184. package/dist/lib/browser/AutomationPanel-AQMN2CQR.mjs +0 -153
  185. package/dist/lib/browser/AutomationPanel-AQMN2CQR.mjs.map +0 -7
  186. package/dist/lib/browser/chunk-7KB4UMXO.mjs.map +0 -7
  187. package/dist/lib/browser/chunk-X5KMOH3I.mjs.map +0 -7
  188. package/dist/lib/browser/meta.mjs +0 -9
  189. package/dist/lib/browser/meta.mjs.map +0 -7
  190. package/dist/lib/node/AssistantPanel-RIA4TI3B.cjs.map +0 -7
  191. package/dist/lib/node/AutomationPanel-HZS5WKI5.cjs.map +0 -7
  192. package/dist/lib/node/chunk-CUCUWUAF.cjs.map +0 -7
  193. package/dist/lib/node/chunk-DTJ7XVO2.cjs.map +0 -7
  194. package/dist/lib/node/meta.cjs.map +0 -7
  195. package/dist/lib/node-esm/AssistantPanel-72YH43CH.mjs.map +0 -7
  196. package/dist/lib/node-esm/AutomationPanel-JUHOWQWW.mjs +0 -154
  197. package/dist/lib/node-esm/AutomationPanel-JUHOWQWW.mjs.map +0 -7
  198. package/dist/lib/node-esm/chunk-23LY7DYS.mjs.map +0 -7
  199. package/dist/lib/node-esm/chunk-HNOBZHWK.mjs.map +0 -7
  200. package/dist/lib/node-esm/meta.mjs +0 -10
  201. package/dist/lib/node-esm/meta.mjs.map +0 -7
@@ -0,0 +1,80 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import React, { useCallback, useMemo } from 'react';
6
+
7
+ import { Capabilities, useCapabilities, useCapability, useIntentDispatcher } from '@dxos/app-framework';
8
+ import { createSystemPrompt, type Message } from '@dxos/artifact';
9
+ import { DXN, QueueSubspaceTags } from '@dxos/keys';
10
+ import { getSpace } from '@dxos/react-client/echo';
11
+ import { useEdgeClient, useQueue } from '@dxos/react-edge-client';
12
+ import { StackItem } from '@dxos/react-ui-stack';
13
+
14
+ import { Thread } from './Thread';
15
+ import { AutomationCapabilities } from '../capabilities';
16
+ import { ChatProcessor } from '../hooks';
17
+ import { type GptChatType } from '../types';
18
+
19
+ export const ChatContainer = ({ chat, role }: { chat: GptChatType; role: string }) => {
20
+ const edgeClient = useEdgeClient();
21
+ const { dispatchPromise: dispatch } = useIntentDispatcher();
22
+ const space = getSpace(chat);
23
+ const aiClient = useCapability(AutomationCapabilities.AiClient);
24
+ const artifactDefinitions = useCapabilities(Capabilities.ArtifactDefinition);
25
+ const globalTools = useCapabilities(Capabilities.Tools);
26
+ const tools = useMemo(
27
+ () => [...globalTools.flat(), ...artifactDefinitions.flatMap((definition) => definition.tools)],
28
+ [globalTools, artifactDefinitions],
29
+ );
30
+ const systemPrompt = useMemo(
31
+ () => createSystemPrompt({ artifacts: artifactDefinitions.map((definition) => definition.instructions) }),
32
+ [artifactDefinitions],
33
+ );
34
+
35
+ // TODO(burdon): Create hook.
36
+ // TODO(wittjosiah): Should these be created in the component?
37
+ // TODO(zan): Combine with ai service client?
38
+ const processor = useMemo(
39
+ () =>
40
+ new ChatProcessor(
41
+ aiClient,
42
+ tools,
43
+ { space, dispatch },
44
+ { model: '@anthropic/claude-3-5-sonnet-20241022', systemPrompt },
45
+ ),
46
+ [aiClient, tools, space, systemPrompt],
47
+ );
48
+ // TODO(wittjosiah): Remove transformation.
49
+ const queueDxn = useMemo(
50
+ () => new DXN(DXN.kind.QUEUE, [QueueSubspaceTags.DATA, space!.id, chat.queue.dxn.parts.at(-1)!]),
51
+ [chat.queue.dxn],
52
+ );
53
+ const queue = useQueue<Message>(edgeClient, queueDxn);
54
+ const messages = useMemo(
55
+ () => [...queue.items, ...processor.messages.value],
56
+ [queue.items, processor.messages.value],
57
+ );
58
+
59
+ const handleSubmit = useCallback(
60
+ async (message: string) => {
61
+ // TODO(burdon): Button to cancel. Otherwise queue request.
62
+ if (processor.isStreaming) {
63
+ await processor.cancel();
64
+ }
65
+
66
+ const messages = await processor.request(message, queue.items);
67
+ // TODO(burdon): Append on success only? If approved by user? Clinet/server.
68
+ await queue.append(messages);
69
+ },
70
+ [processor, queue],
71
+ );
72
+
73
+ return (
74
+ <StackItem.Content toolbar={false} role={role}>
75
+ <Thread messages={messages} streaming={processor.isStreaming.value} onSubmit={handleSubmit} />
76
+ </StackItem.Content>
77
+ );
78
+ };
79
+
80
+ export default ChatContainer;
@@ -7,7 +7,7 @@ import '@dxos-theme';
7
7
  import { type Meta } from '@storybook/react';
8
8
  import React, { useState } from 'react';
9
9
 
10
- import { create } from '@dxos/live-object';
10
+ import { create, makeRef } from '@dxos/live-object';
11
11
  import { useClient } from '@dxos/react-client';
12
12
  import { withClientProvider } from '@dxos/react-client/testing';
13
13
  import { withLayout, withTheme } from '@dxos/storybook-utils';
@@ -34,14 +34,14 @@ const DefaultStory = () => {
34
34
  const space = client.spaces.default;
35
35
  return space.db.add(
36
36
  create(ChainType, {
37
- prompts: [create(ChainPromptType, { command: 'test', template, inputs: [] })],
37
+ prompts: [makeRef(create(ChainPromptType, { command: 'test', template, inputs: [] }))],
38
38
  }),
39
39
  );
40
40
  });
41
41
 
42
42
  return (
43
43
  <div role='none' className='flex w-[350px] border border-separator overflow-hidden'>
44
- <PromptEditor prompt={chain.prompts![0]!} />
44
+ <PromptEditor prompt={chain.prompts![0]!.target!} />
45
45
  </div>
46
46
  );
47
47
  };
@@ -0,0 +1,142 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import '@dxos-theme';
6
+
7
+ import { type StoryObj, type Meta } from '@storybook/react';
8
+ import React, { useEffect, useState } from 'react';
9
+
10
+ import { type Message } from '@dxos/artifact';
11
+ import { ObjectId } from '@dxos/echo-schema';
12
+ import { invariant } from '@dxos/invariant';
13
+ import { faker } from '@dxos/random';
14
+ import { IconButton, Input, Toolbar } from '@dxos/react-ui';
15
+ import { mx } from '@dxos/react-ui-theme';
16
+ import { withLayout, withSignals, withTheme } from '@dxos/storybook-utils';
17
+
18
+ import { Thread, type ThreadProps } from './Thread';
19
+
20
+ faker.seed(0);
21
+
22
+ const Render = ({ messages: _messages, ...props }: ThreadProps) => {
23
+ const [messages, setMessages] = useState(_messages);
24
+ useEffect(() => {
25
+ const message: Message = {
26
+ id: ObjectId.random(),
27
+ role: 'assistant',
28
+ content: [
29
+ {
30
+ type: 'text',
31
+ text: '',
32
+ },
33
+ ],
34
+ };
35
+
36
+ const i = setInterval(() => {
37
+ const block = message.content[0];
38
+ invariant(block.type === 'text');
39
+ block.text += ' ' + faker.lorem.word();
40
+ setMessages([..._messages, message]);
41
+ }, 200);
42
+
43
+ return () => clearInterval(i);
44
+ }, []);
45
+
46
+ return (
47
+ <div className='flex grow justify-center overflow-center bg-white dark:bg-black'>
48
+ <div className='flex w-[500px] bg-base'>
49
+ <Thread {...props} messages={messages} />
50
+ </div>
51
+ </div>
52
+ );
53
+ };
54
+
55
+ const meta: Meta<ThreadProps> = {
56
+ title: 'plugins/plugin-automation/Thread',
57
+ render: Render,
58
+ component: Thread,
59
+ decorators: [withSignals, withTheme, withLayout({ fullscreen: true, tooltips: true })],
60
+ };
61
+
62
+ export default meta;
63
+
64
+ type Story = StoryObj<ThreadProps>;
65
+
66
+ export const Default: Story = {
67
+ args: {
68
+ streaming: true,
69
+ messages: [
70
+ {
71
+ id: ObjectId.random(),
72
+ role: 'user',
73
+ content: [
74
+ {
75
+ type: 'text',
76
+ text: 'hello',
77
+ },
78
+ ],
79
+ },
80
+ // {
81
+ // id: ObjectId.random(),
82
+ // role: 'assistant',
83
+ // content: [
84
+ // {
85
+ // type: 'text',
86
+ // disposition: 'cot',
87
+ // text: ['1. Consider the question.', '2. Plan the route.', '3. Plot the course.'].join('\n'),
88
+ // },
89
+ // {
90
+ // type: 'text',
91
+ // text: 'Things',
92
+ // },
93
+ // {
94
+ // type: 'json',
95
+ // json: '{}',
96
+ // },
97
+ // ],
98
+ // },
99
+ ],
100
+ },
101
+ };
102
+
103
+ export const Line = {
104
+ render: () => {
105
+ const [lines, setLines] = useState<string[]>([]);
106
+ const [expanded, setExpanded] = useState(false);
107
+ return (
108
+ <div className='flex grow justify-center overflow-center bg-white dark:bg-black'>
109
+ <div className='flex flex-col w-[500px] bg-base'>
110
+ <Toolbar.Root>
111
+ <IconButton
112
+ icon='ph--plus--regular'
113
+ label='Add'
114
+ iconOnly
115
+ onClick={() => {
116
+ setLines((lines) => [...lines, faker.lorem.sentence(5)]);
117
+ // setExpanded((expanded) => !expanded);
118
+ }}
119
+ />
120
+ </Toolbar.Root>
121
+ <div>
122
+ {lines.map((line, i) => (
123
+ <div key={i} className='flex h-[32px] p-1 px-3 items-center'>
124
+ {line}
125
+ </div>
126
+ ))}
127
+ </div>
128
+ <div
129
+ className={mx(
130
+ 'flex flex-col justify-end p-1 items-center h-[40px] transition-h duration-[250ms]',
131
+ expanded && 'h-[72px]',
132
+ )}
133
+ >
134
+ <Input.Root>
135
+ <Input.TextInput placeholder='Enter message...' />
136
+ </Input.Root>
137
+ </div>
138
+ </div>
139
+ </div>
140
+ );
141
+ },
142
+ };
@@ -0,0 +1,149 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import React, { type KeyboardEventHandler, useCallback, useEffect, useRef, useState } from 'react';
6
+
7
+ import { type Message } from '@dxos/artifact';
8
+ import { Icon, Input, useThemeContext, type ThemedClassName } from '@dxos/react-ui';
9
+ import {
10
+ createBasicExtensions,
11
+ createMarkdownExtensions,
12
+ createThemeExtensions,
13
+ decorateMarkdown,
14
+ useTextEditor,
15
+ } from '@dxos/react-ui-editor';
16
+ import { Ball } from '@dxos/react-ui-sfx';
17
+ import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
18
+ import { mx } from '@dxos/react-ui-theme';
19
+
20
+ export type ThreadProps = {
21
+ messages: Message[];
22
+ streaming?: boolean;
23
+ onSubmit: (message: string) => void;
24
+ };
25
+
26
+ export const Thread = ({ messages, streaming, onSubmit }: ThreadProps) => {
27
+ const scrollRef = useRef<HTMLDivElement>(null);
28
+ const scroll = () => scrollRef.current?.scrollTo({ top: scrollRef.current.scrollHeight, behavior: 'smooth' });
29
+ const [text, setText] = useState('');
30
+ useEffect(() => scroll(), [messages]);
31
+
32
+ const handleKeyDown = useCallback<KeyboardEventHandler<HTMLInputElement>>(
33
+ (ev) => {
34
+ switch (ev.key) {
35
+ case 'Escape': {
36
+ setText('');
37
+ break;
38
+ }
39
+ case 'Enter': {
40
+ const value = text.trim();
41
+ if (value.length > 0) {
42
+ onSubmit(value);
43
+ setText('');
44
+ scroll();
45
+ }
46
+ break;
47
+ }
48
+ }
49
+ },
50
+ [text],
51
+ );
52
+
53
+ return (
54
+ <div className='flex flex-col grow overflow-hidden'>
55
+ <div
56
+ ref={scrollRef}
57
+ className='flex flex-col grow overflow-x-hidden overflow-y-scroll scrollbar-thin divide-y divide-separator'
58
+ >
59
+ {messages.map((message, i) => (
60
+ <ThreadMessage key={i} classNames='px-4 py-2' message={message} />
61
+ ))}
62
+ </div>
63
+
64
+ <div className='flex p-4 gap-3 items-center'>
65
+ <Ball active={streaming} />
66
+ <Input.Root>
67
+ <Input.TextInput
68
+ autoFocus
69
+ classNames='px-2 bg-base rounded'
70
+ placeholder='Ask a question...'
71
+ value={text}
72
+ onChange={(ev) => setText(ev.target.value)}
73
+ onKeyDown={handleKeyDown}
74
+ />
75
+ </Input.Root>
76
+ <Icon
77
+ icon={'ph--spinner-gap--regular'}
78
+ classNames={mx('animate-spin opacity-0 transition duration-500', streaming && 'opacity-100')}
79
+ size={6}
80
+ />
81
+ </div>
82
+ </div>
83
+ );
84
+ };
85
+
86
+ export type ThreadMessageProps = ThemedClassName<{
87
+ message: Message;
88
+ }>;
89
+
90
+ export const ThreadMessage = ({ classNames, message }: ThreadMessageProps) => {
91
+ if (typeof message !== 'object') {
92
+ return <div className={mx(classNames)}>{message}</div>;
93
+ }
94
+
95
+ const { role, content = [] } = message;
96
+ return (
97
+ <div className={mx('flex', classNames, role === 'user' && 'justify-end')}>
98
+ <div
99
+ className={mx(
100
+ 'block rounded-md p-1 px-2 bg-base overflow-hidden divide-y divid-separator',
101
+ role === 'user' ? 'dark:bg-blue-800' : 'whitespace-pre-wrap',
102
+ )}
103
+ >
104
+ {/* TODO(burdon): Use message ID for stable rendering. */}
105
+ {content.map((item, idx) => {
106
+ switch (item.type) {
107
+ case 'text':
108
+ return <Markdown key={idx} text={item.text.trim()} />;
109
+ case 'json':
110
+ return <Json key={idx} data={item.json} />;
111
+ default:
112
+ return <Json key={idx} data={item} />;
113
+ }
114
+ })}
115
+ </div>
116
+ </div>
117
+ );
118
+ };
119
+
120
+ const Markdown = ({ text, classNames }: ThemedClassName<{ text: string }>) => {
121
+ const { themeMode } = useThemeContext();
122
+ const { parentRef, view } = useTextEditor(() => ({
123
+ initialValue: text,
124
+ extensions: [
125
+ // TOOD(burdon): Optional line wrapping.
126
+ createBasicExtensions({ lineWrapping: true, readonly: true }),
127
+ createMarkdownExtensions(),
128
+ createThemeExtensions({ themeMode }),
129
+ decorateMarkdown(),
130
+ ],
131
+ }));
132
+
133
+ useEffect(() => {
134
+ view?.dispatch({
135
+ // TODO(burdon): Append?
136
+ changes: { from: 0, to: view.state.doc.length, insert: text },
137
+ });
138
+ }, [text]);
139
+
140
+ return <div ref={parentRef} className={mx('w-full overflow-hidden', classNames)} />;
141
+ };
142
+
143
+ const Json = ({ data, classNames }: ThemedClassName<{ data: any }>) => {
144
+ return (
145
+ <SyntaxHighlighter language='json' classNames='w-full overflow-hidden text-sm'>
146
+ {JSON.stringify(data, null, 2)}
147
+ </SyntaxHighlighter>
148
+ );
149
+ };
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ export * from './Thread';
@@ -7,9 +7,8 @@ import '@dxos-theme';
7
7
  import { type Meta } from '@storybook/react';
8
8
  import React, { useEffect, useState } from 'react';
9
9
 
10
- import { FunctionTrigger, TriggerKind } from '@dxos/functions';
10
+ import { FunctionType, FunctionTrigger, TriggerKind } from '@dxos/functions';
11
11
  import { create } from '@dxos/live-object';
12
- import { FunctionType } from '@dxos/plugin-script/types';
13
12
  import { useSpaces } from '@dxos/react-client/echo';
14
13
  import { withClientProvider } from '@dxos/react-client/testing';
15
14
  import { withLayout, withTheme } from '@dxos/storybook-utils';
@@ -2,27 +2,35 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import React from 'react';
5
+ import React, { useState } from 'react';
6
6
 
7
- import { FunctionTriggerSchema, type FunctionTriggerType, type FunctionTrigger, TriggerKind } from '@dxos/functions';
8
- import { FunctionType, ScriptType } from '@dxos/plugin-script/types';
7
+ import { ComputeGraph } from '@dxos/conductor';
8
+ import {
9
+ FunctionType,
10
+ FunctionTriggerSchema,
11
+ type FunctionTriggerType,
12
+ type FunctionTrigger,
13
+ ScriptType,
14
+ TriggerKind,
15
+ } from '@dxos/functions';
9
16
  import { Filter, useQuery, type Space } from '@dxos/react-client/echo';
10
- import { useTranslation } from '@dxos/react-ui';
11
- import { Form, SelectInput } from '@dxos/react-ui-form';
17
+ import { IconButton, Input, useTranslation } from '@dxos/react-ui';
18
+ import { Form, SelectInput, TextInput } from '@dxos/react-ui-form';
12
19
 
13
20
  import { AUTOMATION_PLUGIN } from '../../meta';
14
21
 
15
22
  export type TriggerEditorProps = {
16
23
  space: Space;
17
24
  trigger: FunctionTriggerType;
18
- storedTrigger?: FunctionTrigger;
19
25
  onSave?: (trigger: Omit<FunctionTrigger, 'id'>) => void;
20
26
  onCancel?: () => void;
21
27
  };
22
28
 
23
- export const TriggerEditor = ({ space, trigger, onSave, onCancel, storedTrigger }: TriggerEditorProps) => {
29
+ export const TriggerEditor = ({ space, trigger, onSave, onCancel }: TriggerEditorProps) => {
24
30
  const { t } = useTranslation(AUTOMATION_PLUGIN);
31
+
25
32
  const functions = useQuery(space, Filter.schema(FunctionType));
33
+ const workflows = useQuery(space, Filter.schema(ComputeGraph));
26
34
  const scripts = useQuery(space, Filter.schema(ScriptType));
27
35
 
28
36
  const handleSave = (values: FunctionTriggerType) => {
@@ -33,21 +41,17 @@ export const TriggerEditor = ({ space, trigger, onSave, onCancel, storedTrigger
33
41
  <Form<FunctionTriggerType>
34
42
  schema={FunctionTriggerSchema}
35
43
  values={trigger}
36
- filter={(props) => props.filter((p) => p.name !== 'meta')}
37
44
  onSave={handleSave}
38
45
  onCancel={onCancel}
39
46
  Custom={{
40
47
  ['function' satisfies keyof FunctionTriggerType]: (props) => (
41
- <SelectInput<FunctionTriggerType>
48
+ <SelectInput
42
49
  {...props}
43
- options={functions.map((fn) => ({
44
- value: fn.name,
45
- label: getFunctionName(scripts, fn),
46
- }))}
50
+ options={getWorkflowOptions(workflows).concat(getFunctionOptions(scripts, functions))}
47
51
  />
48
52
  ),
49
53
  ['spec.type' as const]: (props) => (
50
- <SelectInput<FunctionTriggerType>
54
+ <SelectInput
51
55
  {...props}
52
56
  options={Object.values(TriggerKind).map((kind) => ({
53
57
  value: kind,
@@ -55,11 +59,74 @@ export const TriggerEditor = ({ space, trigger, onSave, onCancel, storedTrigger
55
59
  }))}
56
60
  />
57
61
  ),
62
+ ['meta' as const]: (props) => {
63
+ const meta = props.getValue()!;
64
+
65
+ const [newMetaFieldName, setNewMetaFieldName] = useState('');
66
+
67
+ React.useEffect(() => props.onValueChange('object', { ...meta }), []);
68
+
69
+ return (
70
+ <>
71
+ <div>{props.label}</div>
72
+ {[...Object.keys(meta)].map((key) => {
73
+ const compositeKey: any = `meta.${key}`;
74
+ return (
75
+ <div key={compositeKey} role='none' className='flex items-center mt-2 gap-1'>
76
+ <div role='none' className='flex-1'>
77
+ <TextInput {...props} type={'string'} label={key} />
78
+ </div>
79
+ <IconButton
80
+ icon='ph--trash--regular'
81
+ iconOnly
82
+ classNames={'mt-6'}
83
+ label={t('trigger meta remove')}
84
+ onClick={() => {
85
+ const newValues: any = { ...props.getValue() };
86
+ delete newValues[key];
87
+ props.onValueChange('object', newValues);
88
+ }}
89
+ />
90
+ </div>
91
+ );
92
+ })}
93
+ <div role='none' className='flex items-center mt-2 gap-1 plb-1'>
94
+ <div role='none' className='flex-1'>
95
+ <Input.Root>
96
+ <Input.TextInput
97
+ placeholder={t('trigger meta prop name placeholder')}
98
+ value={newMetaFieldName}
99
+ onChange={(event) => setNewMetaFieldName(event.target.value)}
100
+ />
101
+ </Input.Root>
102
+ </div>
103
+ <IconButton
104
+ icon='ph--plus--regular'
105
+ iconOnly
106
+ label={t('trigger meta add')}
107
+ onClick={() => {
108
+ if (newMetaFieldName.length) {
109
+ const meta = props.getValue() ?? {};
110
+ const metaWithNewProp = { ...meta, [newMetaFieldName]: '' };
111
+ setNewMetaFieldName('');
112
+ props.onValueChange('object', metaWithNewProp);
113
+ }
114
+ }}
115
+ />
116
+ </div>
117
+ </>
118
+ );
119
+ },
58
120
  }}
59
121
  />
60
122
  );
61
123
  };
62
124
 
63
- const getFunctionName = (scripts: ScriptType[], fn: FunctionType) => {
64
- return scripts.find((s) => fn.source?.id === s.id)?.name ?? fn.name;
125
+ const getWorkflowOptions = (graphs: ComputeGraph[]) => {
126
+ return graphs.map((graph) => ({ label: `compute-${graph.id}`, value: `dxn:echo:@:${graph.id}` }));
127
+ };
128
+
129
+ const getFunctionOptions = (scripts: ScriptType[], functions: FunctionType[]) => {
130
+ const getLabel = (fn: FunctionType) => scripts.find((s) => fn.source?.target?.id === s.id)?.name ?? fn.name;
131
+ return functions.map((fn) => ({ label: getLabel(fn), value: `dxn:worker:${fn.name}` }));
65
132
  };
@@ -4,5 +4,10 @@
4
4
 
5
5
  import { lazy } from 'react';
6
6
 
7
+ export * from './PromptEditor';
8
+ export * from './Thread';
9
+ export * from './TriggerEditor';
10
+
7
11
  export const AssistantPanel = lazy(() => import('./AssistantPanel'));
8
12
  export const AutomationPanel = lazy(() => import('./AutomationPanel'));
13
+ export const ChatContainer = lazy(() => import('./ChatContainer'));
@@ -7,7 +7,7 @@ import { foreignKey } from '@dxos/echo-schema';
7
7
  import { log } from '@dxos/log';
8
8
  import { MailboxType } from '@dxos/plugin-inbox/types';
9
9
  import { MessageType } from '@dxos/plugin-space/types';
10
- import { type Space, Filter, create } from '@dxos/react-client/echo';
10
+ import { type Space, Filter, create, makeRef } from '@dxos/react-client/echo';
11
11
 
12
12
  export const SOURCE_ID = 'hub.dxos.network/api/mailbox';
13
13
 
@@ -41,7 +41,7 @@ export const handleEmail = async (space: Space, data: any) => {
41
41
  },
42
42
  ),
43
43
  );
44
- mailbox.messages?.push(object);
44
+ mailbox.messages?.push(makeRef(object));
45
45
  }
46
46
  }
47
47
 
@@ -2,4 +2,5 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
+ export * from './processor';
5
6
  export * from './useLocalTriggerManager';
@@ -4,12 +4,12 @@
4
4
 
5
5
  import { sleep } from '@dxos/async';
6
6
  import { getObjectCore, ResultFormat } from '@dxos/echo-db';
7
- import type { AnyObjectData } from '@dxos/echo-schema';
7
+ import { type AnyObjectData } from '@dxos/echo-schema';
8
+ import { FunctionType } from '@dxos/functions';
8
9
  import { type FunctionTrigger } from '@dxos/functions';
9
10
  import { invariant } from '@dxos/invariant';
10
11
  import { DXN, LOCAL_SPACE_TAG } from '@dxos/keys';
11
12
  import { log } from '@dxos/log';
12
- import { FunctionType } from '@dxos/plugin-script';
13
13
  import { type Client, type Config } from '@dxos/react-client';
14
14
  import { type Space } from '@dxos/react-client/echo';
15
15
 
@@ -0,0 +1,15 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { describe, it } from 'vitest';
6
+
7
+ import { ChatProcessor } from './processor';
8
+
9
+ describe('ChatProcessor', () => {
10
+ it('should be instantiable', ({ expect }) => {
11
+ const client = {} as any; // TODO(burdon): Create mock.
12
+ const processor = new ChatProcessor(client);
13
+ expect(processor).toBeDefined();
14
+ });
15
+ });