@dxos/plugin-automation 0.7.4 → 0.7.5-labs.a279d8c

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,180 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import '@dxos-theme';
6
+
7
+ import { type Meta } from '@storybook/react';
8
+ import React, { useMemo, useState } from 'react';
9
+
10
+ import { Capabilities, Surface, useCapabilities } from '@dxos/app-framework';
11
+ import { withPluginManager } from '@dxos/app-framework/testing';
12
+ import { type Tool, type Message } from '@dxos/artifact';
13
+ import { type ArtifactsContext, capabilities, genericTools, localServiceEndpoints } from '@dxos/artifact-testing';
14
+ import { AIServiceClientImpl } from '@dxos/assistant';
15
+ import { create } from '@dxos/client/echo';
16
+ import { createStatic, ObjectId } from '@dxos/echo-schema';
17
+ import { EdgeHttpClient } from '@dxos/edge-client';
18
+ import { DXN, QueueSubspaceTags, SpaceId } from '@dxos/keys';
19
+ import { ChessPlugin } from '@dxos/plugin-chess';
20
+ import { ChessType } from '@dxos/plugin-chess/types';
21
+ import { MapPlugin } from '@dxos/plugin-map';
22
+ import { useQueue } from '@dxos/react-edge-client';
23
+ import { IconButton, Input, Toolbar } from '@dxos/react-ui';
24
+ import { mx } from '@dxos/react-ui-theme';
25
+ import { withLayout, withSignals, withTheme } from '@dxos/storybook-utils';
26
+
27
+ import { Thread } from './components';
28
+ import { ChatProcessor } from './hooks';
29
+ import { createProcessorOptions } from './testing';
30
+
31
+ const endpoints = localServiceEndpoints;
32
+
33
+ type RenderProps = {
34
+ items?: ArtifactsContext['items']; // TODO(burdon): Typedef.
35
+ };
36
+
37
+ const Render = ({ items: _items }: RenderProps) => {
38
+ const artifactDefinitions = useCapabilities(Capabilities.ArtifactDefinition);
39
+
40
+ // Configuration.
41
+ const tools = useMemo<Tool[]>(
42
+ () => [
43
+ // prettier-ignore
44
+ ...genericTools,
45
+ ...artifactDefinitions.flatMap((definition) => definition.tools),
46
+ ],
47
+ [genericTools, artifactDefinitions],
48
+ );
49
+
50
+ // TODO(burdon): Common naming/packaging.
51
+ const [edgeClient] = useState(() => new EdgeHttpClient(endpoints.edge));
52
+ const [aiClient] = useState(() => new AIServiceClientImpl({ endpoint: endpoints.ai }));
53
+
54
+ // Queue.
55
+ const [queueDxn, setQueueDxn] = useState(() => randomQueueDxn());
56
+ const queue = useQueue<Message>(edgeClient, DXN.parse(queueDxn, true));
57
+
58
+ // Artifacts.
59
+ // TODO(burdon): Factor out class.
60
+ const [artifactsContext] = useState(() =>
61
+ create<ArtifactsContext>({
62
+ items: _items ?? [],
63
+ getArtifacts() {
64
+ return this.items;
65
+ },
66
+ addArtifact(artifact) {
67
+ this.items.push(artifact);
68
+ },
69
+ }),
70
+ );
71
+
72
+ // TODO(burdon): Create hook.
73
+ const processor = useMemo(
74
+ () =>
75
+ new ChatProcessor(
76
+ aiClient,
77
+ tools,
78
+ { artifacts: artifactsContext },
79
+ createProcessorOptions(artifactDefinitions.map((definition) => definition.instructions)),
80
+ ),
81
+ [aiClient, tools, artifactsContext, artifactDefinitions],
82
+ );
83
+
84
+ // State.
85
+ const artifactItems = artifactsContext.items.toReversed();
86
+ const messages = [...queue.items, ...processor.messages.value];
87
+
88
+ const handleSubmit = async (message: string) => {
89
+ // TODO(burdon): Button to cancel. Otherwise queue request.
90
+ if (processor.isStreaming) {
91
+ await processor.cancel();
92
+ }
93
+
94
+ const messages = await processor.request(message, queue.items);
95
+ // TODO(burdon): Append on success only? If approved by user? Clinet/server.
96
+ queue.append(messages);
97
+ };
98
+
99
+ return (
100
+ <div className='grid grid-cols-2 w-full h-full divide-x divide-separator overflow-hidden'>
101
+ {/* Thread */}
102
+ <div className='flex flex-col gap-4 overflow-hidden'>
103
+ <Toolbar.Root classNames='p-2'>
104
+ <Input.Root>
105
+ <Input.TextInput
106
+ spellCheck={false}
107
+ placeholder='Queue DXN'
108
+ value={queueDxn}
109
+ onClick={() => setQueueDxn('')}
110
+ onChange={(ev) => setQueueDxn(ev.target.value)}
111
+ />
112
+ <IconButton
113
+ iconOnly
114
+ label='Copy DXN'
115
+ icon='ph--copy--regular'
116
+ onClick={() => navigator.clipboard.writeText(queueDxn)}
117
+ />
118
+ <IconButton
119
+ iconOnly
120
+ label='Clear history'
121
+ icon='ph--trash--regular'
122
+ onClick={() => setQueueDxn(randomQueueDxn())}
123
+ />
124
+ <IconButton iconOnly label='Stop' icon='ph--stop--regular' onClick={() => processor.cancel()} />
125
+ </Input.Root>
126
+ </Toolbar.Root>
127
+
128
+ <Thread messages={messages} streaming={processor.isStreaming.value} onSubmit={handleSubmit} />
129
+ </div>
130
+
131
+ {/* Artifacts Deck/Mosaic */}
132
+ <div className='overflow-hidden grid grid-rows-[2fr_1fr] divide-y divide-separator'>
133
+ {artifactItems.length > 0 && (
134
+ <div className={mx('flex grow overflow-hidden', artifactItems.length === 1 && 'row-span-2')}>
135
+ <Surface role='canvas-node' limit={1} data={artifactItems[0]} />
136
+ </div>
137
+ )}
138
+
139
+ {artifactItems.length > 1 && (
140
+ <div className='flex shrink-0 overflow-hidden divide-x divide-separator'>
141
+ <div className='flex flex-1 h-full'>
142
+ {artifactItems.slice(1, 3).map((item, idx) => (
143
+ <Surface key={idx} role='canvas-node' limit={1} data={item} />
144
+ ))}
145
+ </div>
146
+ </div>
147
+ )}
148
+ </div>
149
+ </div>
150
+ );
151
+ };
152
+
153
+ const randomQueueDxn = () =>
154
+ new DXN(DXN.kind.QUEUE, [QueueSubspaceTags.DATA, SpaceId.random(), ObjectId.random()]).toString();
155
+
156
+ const meta: Meta<typeof Render> = {
157
+ title: 'plugins/plugin-automation/artifacts',
158
+ render: Render,
159
+ decorators: [
160
+ //
161
+ withSignals,
162
+ withTheme,
163
+ withLayout({ fullscreen: true, tooltips: true }),
164
+ withPluginManager({ plugins: [ChessPlugin(), MapPlugin()], capabilities }),
165
+ ],
166
+ };
167
+
168
+ export default meta;
169
+
170
+ export const Default = {};
171
+
172
+ export const WithInitialItems = {
173
+ args: {
174
+ items: [
175
+ createStatic(ChessType, {
176
+ fen: 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1',
177
+ }),
178
+ ],
179
+ },
180
+ };
@@ -0,0 +1,19 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { contributes, type PluginsContext } from '@dxos/app-framework';
6
+ import { AIServiceClientImpl } from '@dxos/assistant';
7
+ import { ClientCapabilities } from '@dxos/plugin-client';
8
+
9
+ import { AutomationCapabilities } from './capabilities';
10
+
11
+ // TODO(wittjosiah): Factor out.
12
+ const DEFAULT_AI_SERVICE_URL = 'http://localhost:8788';
13
+
14
+ export default (context: PluginsContext) => {
15
+ const client = context.requestCapability(ClientCapabilities.Client);
16
+ const endpoint = client.config.values.runtime?.app?.env?.DX_AI_SERVICE_URL ?? DEFAULT_AI_SERVICE_URL;
17
+ const aiClient = new AIServiceClientImpl({ endpoint });
18
+ return contributes(AutomationCapabilities.AiClient, aiClient);
19
+ };
@@ -0,0 +1,127 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Capabilities, contributes, type PluginsContext } from '@dxos/app-framework';
6
+ import { ClientCapabilities } from '@dxos/plugin-client';
7
+ import { createExtension, toSignal } from '@dxos/plugin-graph';
8
+ import { memoizeQuery } from '@dxos/plugin-space';
9
+ import { getTypename, parseId, SpaceState } from '@dxos/react-client/echo';
10
+
11
+ import { AUTOMATION_PLUGIN } from '../meta';
12
+
13
+ export default (context: PluginsContext) => {
14
+ const resolve = (typename: string) =>
15
+ context.requestCapabilities(Capabilities.Metadata).find(({ id }) => id === typename)?.metadata ?? {};
16
+
17
+ return contributes(Capabilities.AppGraphBuilder, [
18
+ createExtension({
19
+ id: `${AUTOMATION_PLUGIN}/automation-for-subject`,
20
+ resolver: ({ id }) => {
21
+ if (!id.endsWith('~automation')) {
22
+ return;
23
+ }
24
+
25
+ const type = 'orphan-settings-for-subject';
26
+ const icon = 'ph--magic-wand--regular';
27
+
28
+ const client = context.requestCapability(ClientCapabilities.Client);
29
+ const [subjectId] = id.split('~');
30
+ const { spaceId, objectId } = parseId(subjectId);
31
+ const spaces = toSignal(
32
+ (onChange) => client.spaces.subscribe(() => onChange()).unsubscribe,
33
+ () => client.spaces.get(),
34
+ );
35
+ const space = spaces?.find((space) => space.id === spaceId && space.state.get() === SpaceState.SPACE_READY);
36
+ if (!objectId) {
37
+ // TODO(burdon): Ref SPACE_PLUGIN ns.
38
+ const label = space
39
+ ? space.properties.name || ['unnamed space label', { ns: AUTOMATION_PLUGIN }]
40
+ : ['unnamed object settings label', { ns: AUTOMATION_PLUGIN }];
41
+
42
+ // TODO(wittjosiah): Support comments for arbitrary subjects.
43
+ // This is to ensure that the comments panel is not stuck on an old object.
44
+ return {
45
+ id,
46
+ type,
47
+ data: null,
48
+ properties: {
49
+ icon,
50
+ label,
51
+ showResolvedThreads: false,
52
+ object: null,
53
+ space,
54
+ },
55
+ };
56
+ }
57
+
58
+ const [object] = memoizeQuery(space, { id: objectId });
59
+ if (!object || !subjectId) {
60
+ return;
61
+ }
62
+
63
+ const meta = resolve(getTypename(object) ?? '');
64
+ const label = meta.label?.(object) ||
65
+ object.name ||
66
+ meta.placeholder || ['unnamed object settings label', { ns: AUTOMATION_PLUGIN }];
67
+
68
+ return {
69
+ id,
70
+ type,
71
+ data: null,
72
+ properties: {
73
+ icon,
74
+ label,
75
+ object,
76
+ },
77
+ };
78
+ },
79
+ }),
80
+ createExtension({
81
+ id: `${AUTOMATION_PLUGIN}/assistant-for-subject`,
82
+ resolver: ({ id }) => {
83
+ // TODO(Zan): Find util (or make one). Effect schema!!
84
+ if (!id.endsWith('~assistant')) {
85
+ return;
86
+ }
87
+
88
+ const client = context.requestCapability(ClientCapabilities.Client);
89
+ const [subjectId] = id.split('~');
90
+ const { spaceId, objectId } = parseId(subjectId);
91
+ const spaces = toSignal(
92
+ (onChange) => client.spaces.subscribe(() => onChange()).unsubscribe,
93
+ () => client.spaces.get(),
94
+ );
95
+ const space = spaces?.find((space) => space.id === spaceId && space.state.get() === SpaceState.SPACE_READY);
96
+ if (!objectId) {
97
+ // TODO(wittjosiah): Support assistant for arbitrary subjects.
98
+ // This is to ensure that the assistant panel is not stuck on an old object.
99
+ return {
100
+ id,
101
+ type: 'orphan-automation-for-subject',
102
+ data: null,
103
+ properties: {
104
+ icon: 'ph--atom--regular',
105
+ label: ['assistant panel label', { ns: AUTOMATION_PLUGIN }],
106
+ object: null,
107
+ space,
108
+ },
109
+ };
110
+ }
111
+
112
+ const [object] = memoizeQuery(space, { id: objectId });
113
+
114
+ return {
115
+ id,
116
+ type: 'orphan-automation-for-subject',
117
+ data: null,
118
+ properties: {
119
+ icon: 'ph--atom--regular',
120
+ label: ['assistant panel label', { ns: AUTOMATION_PLUGIN }],
121
+ object,
122
+ },
123
+ };
124
+ },
125
+ }),
126
+ ]);
127
+ };
@@ -0,0 +1,12 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { defineCapability } from '@dxos/app-framework';
6
+ import { type AIServiceClientImpl } from '@dxos/assistant';
7
+
8
+ import { AUTOMATION_PLUGIN } from '../meta';
9
+
10
+ export namespace AutomationCapabilities {
11
+ export const AiClient = defineCapability<AIServiceClientImpl>(`${AUTOMATION_PLUGIN}/capability/ai-client`);
12
+ }
@@ -0,0 +1,12 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { lazy } from '@dxos/app-framework';
6
+
7
+ export const AiClient = lazy(() => import('./ai-client'));
8
+ export const AppGraphBuilder = lazy(() => import('./app-graph-builder'));
9
+ export const IntentResolver = lazy(() => import('./intent-resolver'));
10
+ export const ReactSurface = lazy(() => import('./react-surface'));
11
+
12
+ export * from './capabilities';
@@ -0,0 +1,22 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Capabilities, contributes, createResolver } from '@dxos/app-framework';
6
+ import { ObjectId } from '@dxos/echo-schema';
7
+ import { create, makeRef } from '@dxos/live-object';
8
+
9
+ import { AutomationAction, GptChatType } from '../types';
10
+
11
+ export default () =>
12
+ contributes(
13
+ Capabilities.IntentResolver,
14
+ createResolver(AutomationAction.Create, ({ name }) => ({
15
+ data: {
16
+ object: create(GptChatType, {
17
+ name,
18
+ queue: makeRef({ id: ObjectId.random() }), // new DXN(DXN.kind.QUEUE, [QueueSubspaceTags.DATA, SpaceId.random(), ObjectId.random()]).toString(),
19
+ }),
20
+ },
21
+ })),
22
+ );
@@ -0,0 +1,34 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import React from 'react';
6
+
7
+ import { Capabilities, contributes, createSurface } from '@dxos/app-framework';
8
+ import { getSpace, isEchoObject, type ReactiveEchoObject } from '@dxos/react-client/echo';
9
+
10
+ import { AssistantPanel, AutomationPanel, ChatContainer } from '../components';
11
+ import { AUTOMATION_PLUGIN } from '../meta';
12
+ import { GptChatType } from '../types';
13
+
14
+ export default () =>
15
+ contributes(Capabilities.ReactSurface, [
16
+ createSurface({
17
+ id: `${AUTOMATION_PLUGIN}/assistant`,
18
+ role: 'complementary--assistant',
19
+ component: ({ data }) => <AssistantPanel subject={data.subject} />,
20
+ }),
21
+ createSurface({
22
+ id: `${AUTOMATION_PLUGIN}/automation`,
23
+ role: 'complementary--automation',
24
+ filter: (data): data is { subject: ReactiveEchoObject<any> } =>
25
+ isEchoObject(data.subject) && !!getSpace(data.subject),
26
+ component: ({ data }) => <AutomationPanel space={getSpace(data.subject)!} object={data.subject} />,
27
+ }),
28
+ createSurface({
29
+ id: `${AUTOMATION_PLUGIN}/gpt-chat`,
30
+ role: 'article',
31
+ filter: (data): data is { subject: GptChatType } => data.subject instanceof GptChatType,
32
+ component: ({ data, role }) => <ChatContainer role={role} chat={data.subject} />,
33
+ }),
34
+ ]);
@@ -6,8 +6,10 @@
6
6
 
7
7
  import React, { useEffect, useRef, useState } from 'react';
8
8
 
9
- import { type AIServiceClient, AIServiceClientImpl, ObjectId, type Message } from '@dxos/assistant';
9
+ import { type Message } from '@dxos/artifact';
10
+ import { type AIServiceClient, AIServiceClientImpl } from '@dxos/assistant';
10
11
  import type { ReactiveEchoObject } from '@dxos/echo-db';
12
+ import { ObjectId } from '@dxos/echo-schema';
11
13
  import { SpaceId } from '@dxos/keys';
12
14
  import { useClient, useConfig } from '@dxos/react-client';
13
15
  import { ContextMenu, type ThemedClassName } from '@dxos/react-ui';
@@ -58,13 +60,13 @@ export const AssistantPanel = ({ subject, classNames }: AssistantPanelProps) =>
58
60
  setContextSpaceId(contextSpaceId);
59
61
  setThreadId(threadId);
60
62
 
61
- const messages = await aiClient.current!.getMessagesInThread(contextSpaceId, threadId);
63
+ const messages = await aiClient.current!.getMessages(contextSpaceId, threadId);
62
64
  setHistory(messages);
63
65
  });
64
66
  }, []);
65
67
 
66
68
  const handleRequest = async (input: string) => {
67
- if (input === '') {
69
+ if (!aiClient.current || input === '') {
68
70
  return;
69
71
  }
70
72
 
@@ -78,10 +80,10 @@ export const AssistantPanel = ({ subject, classNames }: AssistantPanelProps) =>
78
80
  role: 'user',
79
81
  content: [{ type: 'text', text: input }],
80
82
  };
81
- await aiClient.current!.insertMessages([userMessage]);
83
+ await aiClient.current.appendMessages([userMessage]);
82
84
  setHistory([...history, userMessage]);
83
85
 
84
- const generationStream = await aiClient.current!.generate({
86
+ const generationStream = await aiClient.current.generate({
85
87
  model: '@anthropic/claude-3-5-sonnet-20241022',
86
88
  spaceId: contextSpaceId!,
87
89
  threadId: threadId!,
@@ -94,7 +96,7 @@ export const AssistantPanel = ({ subject, classNames }: AssistantPanelProps) =>
94
96
  setHistory([...historyBefore, ...generationStream.accumulatedMessages]);
95
97
  }
96
98
 
97
- await aiClient.current!.insertMessages(await generationStream.complete());
99
+ await aiClient.current!.appendMessages(await generationStream.complete());
98
100
  };
99
101
 
100
102
  const getSystemPrompt = async () => {
@@ -114,7 +116,7 @@ export const AssistantPanel = ({ subject, classNames }: AssistantPanelProps) =>
114
116
  // setContextSpaceId(contextSpaceId);
115
117
  setThreadId(threadId);
116
118
 
117
- const messages = await aiClient.current!.getMessagesInThread(contextSpaceId!, threadId);
119
+ const messages = await aiClient.current!.getMessages(contextSpaceId!, threadId);
118
120
  setHistory(messages);
119
121
  };
120
122
 
@@ -146,7 +148,7 @@ export const AssistantPanel = ({ subject, classNames }: AssistantPanelProps) =>
146
148
  </Toolbar.Button>
147
149
  </ContextMenu.Trigger>
148
150
  <ContextMenu.Portal>
149
- <ContextMenu.Content classNames='z-[31]'>
151
+ <ContextMenu.Content>
150
152
  <ContextMenu.Viewport>
151
153
  <ContextMenu.Item onClick={clearThread}>Clear thread</ContextMenu.Item>
152
154
  <ContextMenu.Item onClick={async () => console.log(await getSystemPrompt())}>
@@ -7,8 +7,7 @@ import '@dxos-theme';
7
7
  import { type Meta } from '@storybook/react';
8
8
  import React from 'react';
9
9
 
10
- import { FunctionTrigger } from '@dxos/functions';
11
- import { FunctionType } from '@dxos/plugin-script/types';
10
+ import { FunctionType, FunctionTrigger } from '@dxos/functions';
12
11
  import { create, useSpaces } from '@dxos/react-client/echo';
13
12
  import { withClientProvider } from '@dxos/react-client/testing';
14
13
  import { withLayout, withTheme } from '@dxos/storybook-utils';
@@ -5,8 +5,14 @@
5
5
  import React, { useState } from 'react';
6
6
 
7
7
  import { S } from '@dxos/echo-schema';
8
- import { FunctionTriggerSchema, FunctionTrigger, type FunctionTriggerType } from '@dxos/functions';
9
- import { FunctionType, ScriptType } from '@dxos/plugin-script';
8
+ import {
9
+ FunctionType,
10
+ FunctionTrigger,
11
+ FunctionTriggerSchema,
12
+ TriggerKind,
13
+ type FunctionTriggerType,
14
+ ScriptType,
15
+ } from '@dxos/functions';
10
16
  import { type Client, useClient } from '@dxos/react-client';
11
17
  import { create, Filter, useQuery, type Space, type ReactiveObject, getSpace } from '@dxos/react-client/echo';
12
18
  import { IconButton, Input, useTranslation, Button } from '@dxos/react-ui';
@@ -24,7 +30,7 @@ export type AutomationPanelProps = {
24
30
  };
25
31
 
26
32
  // TODO(burdon): Factor out common layout with ViewEditor.
27
- export const AutomationPanel = ({ space }: AutomationPanelProps) => {
33
+ export const AutomationPanel = ({ space, object }: AutomationPanelProps) => {
28
34
  const { t } = useTranslation(AUTOMATION_PLUGIN);
29
35
  const client = useClient();
30
36
  const triggers = useQuery(space, Filter.schema(FunctionTrigger));
@@ -41,7 +47,7 @@ export const AutomationPanel = ({ space }: AutomationPanelProps) => {
41
47
  };
42
48
 
43
49
  const handleAdd = () => {
44
- setTrigger(create(FunctionTriggerSchema, {}));
50
+ setTrigger(create(FunctionTriggerSchema, { meta: {} }));
45
51
  setSelected(undefined);
46
52
  };
47
53
 
@@ -71,43 +77,46 @@ export const AutomationPanel = ({ space }: AutomationPanelProps) => {
71
77
  <List.Root<FunctionTrigger> items={triggers} isItem={S.is(FunctionTrigger)} getId={(field) => field.id}>
72
78
  {({ items: triggers }) => (
73
79
  <div role='list' className='flex flex-col w-full'>
74
- {triggers?.map((trigger) => (
75
- <List.Item<FunctionTrigger>
76
- key={trigger.id}
77
- item={trigger}
78
- classNames={mx(grid, ghostHover, 'items-center')}
79
- >
80
- <Input.Root>
81
- <Input.Switch checked={trigger.enabled} onCheckedChange={(checked) => (trigger.enabled = checked)} />
82
- </Input.Root>
83
-
84
- <div className={'flex'}>
85
- <List.ItemTitle classNames='px-2 cursor-pointer w-0 shrink' onClick={() => handleSelect(trigger)}>
86
- {getFunctionName(scripts, functions, trigger)}
87
- </List.ItemTitle>
88
-
89
- {/* TODO: a better way to expose URL copy action */}
90
- <Button onClick={() => navigator.clipboard.writeText(getWebhookUrl(client, trigger))}>
91
- Copy URL
92
- </Button>
93
- </div>
94
-
95
- <List.ItemDeleteButton onClick={() => handleDelete(trigger)} />
96
- </List.Item>
97
- ))}
80
+ {triggers?.map((trigger) => {
81
+ const copyAction = getCopyAction(client, trigger);
82
+ return (
83
+ <List.Item<FunctionTrigger>
84
+ key={trigger.id}
85
+ item={trigger}
86
+ classNames={mx(grid, ghostHover, 'items-center', 'px-2')}
87
+ >
88
+ <Input.Root>
89
+ <Input.Switch
90
+ checked={trigger.enabled}
91
+ onCheckedChange={(checked) => (trigger.enabled = checked)}
92
+ />
93
+ </Input.Root>
94
+
95
+ <div className={'flex'}>
96
+ <List.ItemTitle
97
+ classNames='px-1 cursor-pointer w-0 shrink truncate'
98
+ onClick={() => handleSelect(trigger)}
99
+ >
100
+ {getFunctionName(scripts, functions, trigger) ?? '∅'}
101
+ </List.ItemTitle>
102
+
103
+ {/* TODO: a better way to expose copy action */}
104
+ {copyAction && (
105
+ <Button onClick={() => navigator.clipboard.writeText(copyAction.contentProvider())}>
106
+ {t(copyAction.translationKey)}
107
+ </Button>
108
+ )}
109
+ </div>
110
+
111
+ <List.ItemDeleteButton onClick={() => handleDelete(trigger)} />
112
+ </List.Item>
113
+ );
114
+ })}
98
115
  </div>
99
116
  )}
100
117
  </List.Root>
101
118
 
102
- {trigger && (
103
- <TriggerEditor
104
- space={space}
105
- storedTrigger={selected}
106
- trigger={trigger}
107
- onSave={handleSave}
108
- onCancel={handleCancel}
109
- />
110
- )}
119
+ {trigger && <TriggerEditor space={space} trigger={trigger} onSave={handleSave} onCancel={handleCancel} />}
111
120
 
112
121
  {!trigger && (
113
122
  <div className='flex p-2 justify-center'>
@@ -118,6 +127,18 @@ export const AutomationPanel = ({ space }: AutomationPanelProps) => {
118
127
  );
119
128
  };
120
129
 
130
+ const getCopyAction = (client: Client, trigger: FunctionTrigger | undefined) => {
131
+ if (trigger?.spec?.type === TriggerKind.Email) {
132
+ return { translationKey: 'trigger copy email', contentProvider: () => `${getSpace(trigger)!.id}@dxos.network` };
133
+ }
134
+
135
+ if (trigger?.spec?.type === TriggerKind.Webhook) {
136
+ return { translationKey: 'trigger copy url', contentProvider: () => getWebhookUrl(client, trigger) };
137
+ }
138
+
139
+ return undefined;
140
+ };
141
+
121
142
  const getWebhookUrl = (client: Client, trigger: FunctionTrigger) => {
122
143
  const spaceId = getSpace(trigger)!.id;
123
144
  const edgeUrl = new URL(client.config.values.runtime!.services!.edge!.url!);
@@ -127,9 +148,10 @@ const getWebhookUrl = (client: Client, trigger: FunctionTrigger) => {
127
148
  };
128
149
 
129
150
  const getFunctionName = (scripts: ScriptType[], functions: FunctionType[], trigger: FunctionTriggerType) => {
151
+ const shortId = trigger.function && `${trigger.function?.slice(0, 16)}…`;
130
152
  const functionObject = functions.find((fn) => fn.name === trigger.function);
131
153
  if (!functionObject) {
132
- return trigger.function;
154
+ return shortId;
133
155
  }
134
- return scripts.find((s) => functionObject.source?.id === s.id)?.name ?? functionObject.name;
156
+ return scripts.find((s) => functionObject.source?.target?.id === s.id)?.name ?? shortId;
135
157
  };