@dxos/assistant-toolkit 0.8.4-main.ae835ea

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 (200) hide show
  1. package/LICENSE +8 -0
  2. package/README.md +3 -0
  3. package/dist/lib/browser/index.mjs +2481 -0
  4. package/dist/lib/browser/index.mjs.map +7 -0
  5. package/dist/lib/browser/meta.json +1 -0
  6. package/dist/lib/node-esm/index.mjs +2483 -0
  7. package/dist/lib/node-esm/index.mjs.map +7 -0
  8. package/dist/lib/node-esm/meta.json +1 -0
  9. package/dist/types/src/blueprints/design/design-blueprint.d.ts +4 -0
  10. package/dist/types/src/blueprints/design/design-blueprint.d.ts.map +1 -0
  11. package/dist/types/src/blueprints/design/design-blueprint.test.d.ts +2 -0
  12. package/dist/types/src/blueprints/design/design-blueprint.test.d.ts.map +1 -0
  13. package/dist/types/src/blueprints/design/index.d.ts +3 -0
  14. package/dist/types/src/blueprints/design/index.d.ts.map +1 -0
  15. package/dist/types/src/blueprints/discord/discord-blueprint.d.ts +18 -0
  16. package/dist/types/src/blueprints/discord/discord-blueprint.d.ts.map +1 -0
  17. package/dist/types/src/blueprints/discord/index.d.ts +3 -0
  18. package/dist/types/src/blueprints/discord/index.d.ts.map +1 -0
  19. package/dist/types/src/blueprints/index.d.ts +7 -0
  20. package/dist/types/src/blueprints/index.d.ts.map +1 -0
  21. package/dist/types/src/blueprints/linear/index.d.ts +3 -0
  22. package/dist/types/src/blueprints/linear/index.d.ts.map +1 -0
  23. package/dist/types/src/blueprints/linear/linear-blueprint.d.ts +18 -0
  24. package/dist/types/src/blueprints/linear/linear-blueprint.d.ts.map +1 -0
  25. package/dist/types/src/blueprints/planning/index.d.ts +3 -0
  26. package/dist/types/src/blueprints/planning/index.d.ts.map +1 -0
  27. package/dist/types/src/blueprints/planning/planning-blueprint.d.ts +4 -0
  28. package/dist/types/src/blueprints/planning/planning-blueprint.d.ts.map +1 -0
  29. package/dist/types/src/blueprints/planning/planning-blueprint.test.d.ts +2 -0
  30. package/dist/types/src/blueprints/planning/planning-blueprint.test.d.ts.map +1 -0
  31. package/dist/types/src/blueprints/research/index.d.ts +3 -0
  32. package/dist/types/src/blueprints/research/index.d.ts.map +1 -0
  33. package/dist/types/src/blueprints/research/research-blueprint.d.ts +4 -0
  34. package/dist/types/src/blueprints/research/research-blueprint.d.ts.map +1 -0
  35. package/dist/types/src/blueprints/research/research-blueprint.test.d.ts +2 -0
  36. package/dist/types/src/blueprints/research/research-blueprint.test.d.ts.map +1 -0
  37. package/dist/types/src/blueprints/testing.d.ts +12 -0
  38. package/dist/types/src/blueprints/testing.d.ts.map +1 -0
  39. package/dist/types/src/blueprints/websearch/index.d.ts +4 -0
  40. package/dist/types/src/blueprints/websearch/index.d.ts.map +1 -0
  41. package/dist/types/src/blueprints/websearch/websearch-blueprint.d.ts +4 -0
  42. package/dist/types/src/blueprints/websearch/websearch-blueprint.d.ts.map +1 -0
  43. package/dist/types/src/blueprints/websearch/websearch-toolkit.d.ts +26 -0
  44. package/dist/types/src/blueprints/websearch/websearch-toolkit.d.ts.map +1 -0
  45. package/dist/types/src/experimental/feed.test.d.ts +2 -0
  46. package/dist/types/src/experimental/feed.test.d.ts.map +1 -0
  47. package/dist/types/src/functions/agent/index.d.ts +5 -0
  48. package/dist/types/src/functions/agent/index.d.ts.map +1 -0
  49. package/dist/types/src/functions/agent/prompt.d.ts +11 -0
  50. package/dist/types/src/functions/agent/prompt.d.ts.map +1 -0
  51. package/dist/types/src/functions/discord/fetch-messages.d.ts +11 -0
  52. package/dist/types/src/functions/discord/fetch-messages.d.ts.map +1 -0
  53. package/dist/types/src/functions/discord/fetch-messages.test.d.ts +2 -0
  54. package/dist/types/src/functions/discord/fetch-messages.test.d.ts.map +1 -0
  55. package/dist/types/src/functions/discord/index.d.ts +12 -0
  56. package/dist/types/src/functions/discord/index.d.ts.map +1 -0
  57. package/dist/types/src/functions/document/index.d.ts +12 -0
  58. package/dist/types/src/functions/document/index.d.ts.map +1 -0
  59. package/dist/types/src/functions/document/read.d.ts +7 -0
  60. package/dist/types/src/functions/document/read.d.ts.map +1 -0
  61. package/dist/types/src/functions/document/update.d.ts +6 -0
  62. package/dist/types/src/functions/document/update.d.ts.map +1 -0
  63. package/dist/types/src/functions/entity-extraction/entity-extraction.d.ts +173 -0
  64. package/dist/types/src/functions/entity-extraction/entity-extraction.d.ts.map +1 -0
  65. package/dist/types/src/functions/entity-extraction/entity-extraction.test.d.ts +2 -0
  66. package/dist/types/src/functions/entity-extraction/entity-extraction.test.d.ts.map +1 -0
  67. package/dist/types/src/functions/entity-extraction/index.d.ts +174 -0
  68. package/dist/types/src/functions/entity-extraction/index.d.ts.map +1 -0
  69. package/dist/types/src/functions/exa/exa.d.ts +5 -0
  70. package/dist/types/src/functions/exa/exa.d.ts.map +1 -0
  71. package/dist/types/src/functions/exa/index.d.ts +3 -0
  72. package/dist/types/src/functions/exa/index.d.ts.map +1 -0
  73. package/dist/types/src/functions/exa/mock.d.ts +5 -0
  74. package/dist/types/src/functions/exa/mock.d.ts.map +1 -0
  75. package/dist/types/src/functions/github/fetch-prs.d.ts +6 -0
  76. package/dist/types/src/functions/github/fetch-prs.d.ts.map +1 -0
  77. package/dist/types/src/functions/index.d.ts +8 -0
  78. package/dist/types/src/functions/index.d.ts.map +1 -0
  79. package/dist/types/src/functions/linear/index.d.ts +9 -0
  80. package/dist/types/src/functions/linear/index.d.ts.map +1 -0
  81. package/dist/types/src/functions/linear/linear.test.d.ts +2 -0
  82. package/dist/types/src/functions/linear/linear.test.d.ts.map +1 -0
  83. package/dist/types/src/functions/linear/sync-issues.d.ts +12 -0
  84. package/dist/types/src/functions/linear/sync-issues.d.ts.map +1 -0
  85. package/dist/types/src/functions/research/create-document.d.ts +7 -0
  86. package/dist/types/src/functions/research/create-document.d.ts.map +1 -0
  87. package/dist/types/src/functions/research/graph.d.ts +64 -0
  88. package/dist/types/src/functions/research/graph.d.ts.map +1 -0
  89. package/dist/types/src/functions/research/graph.test.d.ts +2 -0
  90. package/dist/types/src/functions/research/graph.test.d.ts.map +1 -0
  91. package/dist/types/src/functions/research/index.d.ts +19 -0
  92. package/dist/types/src/functions/research/index.d.ts.map +1 -0
  93. package/dist/types/src/functions/research/research-graph.d.ts +18 -0
  94. package/dist/types/src/functions/research/research-graph.d.ts.map +1 -0
  95. package/dist/types/src/functions/research/research.d.ts +13 -0
  96. package/dist/types/src/functions/research/research.d.ts.map +1 -0
  97. package/dist/types/src/functions/research/research.test.d.ts +2 -0
  98. package/dist/types/src/functions/research/research.test.d.ts.map +1 -0
  99. package/dist/types/src/functions/research/types.d.ts +384 -0
  100. package/dist/types/src/functions/research/types.d.ts.map +1 -0
  101. package/dist/types/src/functions/tasks/index.d.ts +15 -0
  102. package/dist/types/src/functions/tasks/index.d.ts.map +1 -0
  103. package/dist/types/src/functions/tasks/read.d.ts +7 -0
  104. package/dist/types/src/functions/tasks/read.d.ts.map +1 -0
  105. package/dist/types/src/functions/tasks/task-list.d.ts +74 -0
  106. package/dist/types/src/functions/tasks/task-list.d.ts.map +1 -0
  107. package/dist/types/src/functions/tasks/task-list.test.d.ts +2 -0
  108. package/dist/types/src/functions/tasks/task-list.test.d.ts.map +1 -0
  109. package/dist/types/src/functions/tasks/update.d.ts +9 -0
  110. package/dist/types/src/functions/tasks/update.d.ts.map +1 -0
  111. package/dist/types/src/index.d.ts +5 -0
  112. package/dist/types/src/index.d.ts.map +1 -0
  113. package/dist/types/src/plugins.d.ts +19 -0
  114. package/dist/types/src/plugins.d.ts.map +1 -0
  115. package/dist/types/src/sync/index.d.ts +2 -0
  116. package/dist/types/src/sync/index.d.ts.map +1 -0
  117. package/dist/types/src/sync/sync.d.ts +15 -0
  118. package/dist/types/src/sync/sync.d.ts.map +1 -0
  119. package/dist/types/src/testing/data/exa-search-1748337321991.d.ts +38 -0
  120. package/dist/types/src/testing/data/exa-search-1748337321991.d.ts.map +1 -0
  121. package/dist/types/src/testing/data/exa-search-1748337331526.d.ts +37 -0
  122. package/dist/types/src/testing/data/exa-search-1748337331526.d.ts.map +1 -0
  123. package/dist/types/src/testing/data/exa-search-1748337344119.d.ts +58 -0
  124. package/dist/types/src/testing/data/exa-search-1748337344119.d.ts.map +1 -0
  125. package/dist/types/src/testing/data/index.d.ts +3 -0
  126. package/dist/types/src/testing/data/index.d.ts.map +1 -0
  127. package/dist/types/src/testing/index.d.ts +2 -0
  128. package/dist/types/src/testing/index.d.ts.map +1 -0
  129. package/dist/types/src/util/graphql.d.ts +22 -0
  130. package/dist/types/src/util/graphql.d.ts.map +1 -0
  131. package/dist/types/src/util/index.d.ts +2 -0
  132. package/dist/types/src/util/index.d.ts.map +1 -0
  133. package/dist/types/tsconfig.tsbuildinfo +1 -0
  134. package/package.json +67 -0
  135. package/src/blueprints/design/design-blueprint.test.ts +108 -0
  136. package/src/blueprints/design/design-blueprint.ts +33 -0
  137. package/src/blueprints/design/index.ts +7 -0
  138. package/src/blueprints/discord/discord-blueprint.ts +34 -0
  139. package/src/blueprints/discord/index.ts +7 -0
  140. package/src/blueprints/index.ts +10 -0
  141. package/src/blueprints/linear/index.ts +7 -0
  142. package/src/blueprints/linear/linear-blueprint.ts +35 -0
  143. package/src/blueprints/planning/index.ts +7 -0
  144. package/src/blueprints/planning/planning-blueprint.test.ts +129 -0
  145. package/src/blueprints/planning/planning-blueprint.ts +98 -0
  146. package/src/blueprints/research/index.ts +7 -0
  147. package/src/blueprints/research/research-blueprint.test.ts +7 -0
  148. package/src/blueprints/research/research-blueprint.ts +45 -0
  149. package/src/blueprints/testing.ts +34 -0
  150. package/src/blueprints/websearch/index.ts +8 -0
  151. package/src/blueprints/websearch/websearch-blueprint.ts +20 -0
  152. package/src/blueprints/websearch/websearch-toolkit.ts +8 -0
  153. package/src/experimental/feed.test.ts +108 -0
  154. package/src/functions/agent/index.ts +11 -0
  155. package/src/functions/agent/prompt.ts +101 -0
  156. package/src/functions/discord/fetch-messages.test.ts +59 -0
  157. package/src/functions/discord/fetch-messages.ts +251 -0
  158. package/src/functions/discord/index.ts +9 -0
  159. package/src/functions/document/index.ts +11 -0
  160. package/src/functions/document/read.ts +29 -0
  161. package/src/functions/document/update.ts +30 -0
  162. package/src/functions/entity-extraction/entity-extraction.conversations.json +1 -0
  163. package/src/functions/entity-extraction/entity-extraction.test.ts +100 -0
  164. package/src/functions/entity-extraction/entity-extraction.ts +163 -0
  165. package/src/functions/entity-extraction/index.ts +9 -0
  166. package/src/functions/exa/exa.ts +37 -0
  167. package/src/functions/exa/index.ts +6 -0
  168. package/src/functions/exa/mock.ts +71 -0
  169. package/src/functions/github/fetch-prs.ts +30 -0
  170. package/src/functions/index.ts +11 -0
  171. package/src/functions/linear/index.ts +9 -0
  172. package/src/functions/linear/linear.test.ts +86 -0
  173. package/src/functions/linear/sync-issues.ts +189 -0
  174. package/src/functions/research/create-document.ts +69 -0
  175. package/src/functions/research/graph.test.ts +69 -0
  176. package/src/functions/research/graph.ts +388 -0
  177. package/src/functions/research/index.ts +15 -0
  178. package/src/functions/research/instructions-research.tpl +98 -0
  179. package/src/functions/research/research-graph.ts +47 -0
  180. package/src/functions/research/research.conversations.json +10714 -0
  181. package/src/functions/research/research.test.ts +240 -0
  182. package/src/functions/research/research.ts +155 -0
  183. package/src/functions/research/types.ts +24 -0
  184. package/src/functions/tasks/index.ts +11 -0
  185. package/src/functions/tasks/read.ts +34 -0
  186. package/src/functions/tasks/task-list.test.ts +99 -0
  187. package/src/functions/tasks/task-list.ts +165 -0
  188. package/src/functions/tasks/update.ts +52 -0
  189. package/src/index.ts +8 -0
  190. package/src/plugins.tsx +68 -0
  191. package/src/sync/index.ts +5 -0
  192. package/src/sync/sync.ts +87 -0
  193. package/src/testing/data/exa-search-1748337321991.ts +131 -0
  194. package/src/testing/data/exa-search-1748337331526.ts +144 -0
  195. package/src/testing/data/exa-search-1748337344119.ts +133 -0
  196. package/src/testing/data/index.ts +11 -0
  197. package/src/testing/index.ts +5 -0
  198. package/src/typedefs.d.ts +8 -0
  199. package/src/util/graphql.ts +31 -0
  200. package/src/util/index.ts +5 -0
@@ -0,0 +1,240 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { inspect } from 'node:util';
6
+
7
+ import { describe, it } from '@effect/vitest';
8
+ import * as Effect from 'effect/Effect';
9
+ import * as Layer from 'effect/Layer';
10
+
11
+ import { AiService, ConsolePrinter, MemoizedAiService } from '@dxos/ai';
12
+ import { AiServiceTestingPreset, EXA_API_KEY } from '@dxos/ai/testing';
13
+ import {
14
+ AiConversation,
15
+ type ContextBinding,
16
+ GenerationObserver,
17
+ makeToolExecutionServiceFromFunctions,
18
+ makeToolResolverFromFunctions,
19
+ } from '@dxos/assistant';
20
+ import { Blueprint } from '@dxos/blueprints';
21
+ import { Obj, Ref, Type } from '@dxos/echo';
22
+ import { TestHelpers, acquireReleaseResource } from '@dxos/effect';
23
+ import {
24
+ ComputeEventLogger,
25
+ CredentialsService,
26
+ DatabaseService,
27
+ FunctionInvocationService,
28
+ QueueService,
29
+ TracingService,
30
+ } from '@dxos/functions';
31
+ import { TestDatabaseLayer } from '@dxos/functions/testing';
32
+ import { ObjectId } from '@dxos/keys';
33
+ import { DataType } from '@dxos/schema';
34
+
35
+ import { ResearchBlueprint } from '../../blueprints';
36
+ import { testToolkit } from '../../blueprints/testing';
37
+
38
+ import createDocument from './create-document';
39
+ import { createExtractionSchema, getSanitizedSchemaName } from './graph';
40
+ import { default as research } from './research';
41
+ import { ResearchGraph, queryResearchGraph } from './research-graph';
42
+ import { ResearchDataTypes } from './types';
43
+
44
+ ObjectId.dangerouslyDisableRandomness();
45
+
46
+ const TestLayer = Layer.mergeAll(
47
+ AiService.model('@anthropic/claude-opus-4-0'),
48
+ makeToolResolverFromFunctions([research, createDocument], testToolkit),
49
+ makeToolExecutionServiceFromFunctions(testToolkit, testToolkit.toLayer({}) as any),
50
+ ComputeEventLogger.layerFromTracing,
51
+ ).pipe(
52
+ Layer.provideMerge(FunctionInvocationService.layerTest({ functions: [research, createDocument] })),
53
+ Layer.provideMerge(
54
+ Layer.mergeAll(
55
+ MemoizedAiService.layerTest().pipe(Layer.provide(AiServiceTestingPreset('direct'))),
56
+ TestDatabaseLayer({
57
+ indexing: { vector: true },
58
+ types: [...ResearchDataTypes, ResearchGraph, Blueprint.Blueprint],
59
+ }),
60
+ CredentialsService.configuredLayer([{ service: 'exa.ai', apiKey: EXA_API_KEY }]),
61
+ TracingService.layerNoop,
62
+ ),
63
+ ),
64
+ );
65
+
66
+ // TODO(dmaretskyi): Out-of-memory.
67
+ describe.skip('Research', () => {
68
+ it.effect(
69
+ 'call a function to generate a research report',
70
+ Effect.fnUntraced(
71
+ function* (_) {
72
+ yield* DatabaseService.add(
73
+ Obj.make(DataType.Organization, {
74
+ name: 'Airbnb',
75
+ website: 'https://www.airbnb.com/',
76
+ }),
77
+ );
78
+ yield* DatabaseService.flush({ indexes: true });
79
+
80
+ const result = yield* FunctionInvocationService.invokeFunction(research, {
81
+ query: 'Founders and investors of airbnb.',
82
+ mockSearch: false,
83
+ });
84
+
85
+ console.log(inspect(result, { depth: null, colors: true }));
86
+ console.log(JSON.stringify(result, null, 2));
87
+
88
+ yield* DatabaseService.flush({ indexes: true });
89
+ const researchGraph = yield* queryResearchGraph();
90
+ const data = yield* DatabaseService.load(researchGraph!.queue).pipe(
91
+ Effect.flatMap((queue) => Effect.promise(() => queue.queryObjects())),
92
+ );
93
+ console.log(inspect(data, { depth: null, colors: true }));
94
+ },
95
+ Effect.provide(TestLayer),
96
+ TestHelpers.provideTestContext,
97
+ ),
98
+ MemoizedAiService.isGenerationEnabled() ? 240_000 : undefined,
99
+ );
100
+
101
+ it.scoped(
102
+ 'research blueprint',
103
+ Effect.fnUntraced(
104
+ function* (_) {
105
+ const queue = yield* QueueService.createQueue<DataType.Message | ContextBinding>();
106
+ const conversation = yield* acquireReleaseResource(() => new AiConversation(queue));
107
+
108
+ const org = Obj.make(DataType.Organization, { name: 'Airbnb', website: 'https://www.airbnb.com/' });
109
+ yield* DatabaseService.add(org);
110
+ yield* DatabaseService.flush({ indexes: true });
111
+
112
+ const blueprint = yield* DatabaseService.add(Obj.clone(ResearchBlueprint));
113
+ yield* Effect.promise(() => conversation.context.bind({ blueprints: [Ref.make(blueprint)] }));
114
+
115
+ const observer = GenerationObserver.fromPrinter(new ConsolePrinter());
116
+ yield* conversation.createRequest({
117
+ observer,
118
+ prompt: `Research airbnb founders.`,
119
+ });
120
+
121
+ const researchGraph = yield* queryResearchGraph();
122
+ const data = yield* DatabaseService.load(researchGraph!.queue).pipe(
123
+ Effect.flatMap((queue) => Effect.promise(() => queue.queryObjects())),
124
+ );
125
+ console.log(inspect(data, { depth: null, colors: true }));
126
+ },
127
+ Effect.provide(TestLayer),
128
+ TestHelpers.provideTestContext,
129
+ ),
130
+ MemoizedAiService.isGenerationEnabled() ? 240_000 : undefined,
131
+ );
132
+ });
133
+
134
+ describe('misc', () => {
135
+ it('createExtractionSchema', () => {
136
+ const _schema = createExtractionSchema(ResearchDataTypes);
137
+ // log.info('schema', { schema });
138
+ });
139
+
140
+ it('getSanitizedSchemaName', () => {
141
+ const _names = ResearchDataTypes.map(getSanitizedSchemaName);
142
+ // log.info('names', { names }) ;
143
+ });
144
+
145
+ it('getTypeAnnotation', () => {
146
+ for (const schema of ResearchDataTypes) {
147
+ const _dxn = Type.getDXN(schema);
148
+ // log.info('dxn', { schema, dxn });
149
+ }
150
+ });
151
+
152
+ it.skip('sanitizeObjects', () => {
153
+ const _TEST_DATA = {
154
+ objects_dxos_org_type_Project: [
155
+ {
156
+ name: 'Reor',
157
+ description:
158
+ 'A private & local AI personal knowledge management app focused on high entropy thinkers. Features include Q&A chat with full context of notes, automatic connection of ideas, semantic search, and local-first architecture with LLMs and vector databases.',
159
+ image: 'https://reorhomepage-2-cwy0zagzg-reor-team.vercel.app/opengraph-image.jpg',
160
+ id: 'reor-project-01',
161
+ },
162
+ {
163
+ name: 'Personal AI',
164
+ description:
165
+ 'A platform for creating personal AI models with unlimited memory capabilities. Features include memory stacking, data uploads, and personalized language models that can be trained on individual knowledge bases.',
166
+ image: 'https://cdn.prod.website-files.com/5ff65c460ce39f5ec5681c6a/663d12aab1b425e1ad40d3a6_Memory-min.jpg',
167
+ id: 'personal-ai-project-01',
168
+ },
169
+ {
170
+ name: 'IKI AI',
171
+ description:
172
+ 'An AI-native workspace for research, strategy, and creative work. Offers features like AI summarization, content analysis, and team knowledge sharing capabilities.',
173
+ image: 'https://framerusercontent.com/assets/cI6Uo7x4q0W3uxzOt2preXjv6aE.jpg',
174
+ id: 'iki-ai-project-01',
175
+ },
176
+ {
177
+ name: 'Forethink',
178
+ description:
179
+ 'A privacy-first, on-device AI system that proactively links current activities to relevant knowledge. Features real-time learning across teams and local data processing.',
180
+ image: 'https://framerusercontent.com/assets/XDhN6tICnlElB134JWcWXOuXc.jpeg',
181
+ id: 'forethink-project-01',
182
+ },
183
+ {
184
+ name: 'Amurex',
185
+ description:
186
+ 'An invisible AI companion that handles tasks, remembers important information, and supports workflow. Includes features for meeting automation, email categorization, and cross-platform search.',
187
+ image: 'https://amurex.ai/ogimages/og_amurex.jpg',
188
+ id: 'amurex-project-01',
189
+ },
190
+ ],
191
+ objects_dxos_org_type_Organization: [
192
+ {
193
+ name: 'Reor',
194
+ description:
195
+ 'A technology company focused on developing local-first AI solutions for personal knowledge management.',
196
+ status: 'active',
197
+ website: 'https://www.reorproject.org',
198
+ id: 'reor-org-01',
199
+ },
200
+ {
201
+ name: 'Personal.ai',
202
+ description: 'A company specializing in personal AI development and memory management solutions.',
203
+ status: 'active',
204
+ website: 'https://www.personal.ai',
205
+ id: 'personal-ai-org-01',
206
+ },
207
+ {
208
+ name: 'IKI AI',
209
+ description: 'A startup backed by 500 Global, developing AI-native workspaces for knowledge workers.',
210
+ status: 'active',
211
+ website: 'https://iki.ai',
212
+ id: 'iki-ai-org-01',
213
+ },
214
+ {
215
+ name: 'Forethink.ai',
216
+ description:
217
+ 'A company focused on privacy-first AI solutions for knowledge management and team collaboration.',
218
+ status: 'active',
219
+ website: 'https://www.forethink.ai',
220
+ id: 'forethink-org-01',
221
+ },
222
+ ],
223
+ objects_dxos_org_type_Text: [
224
+ {
225
+ content:
226
+ 'Current trends in AI-powered Personal Knowledge Management (PKM) systems include: 1) Shift from document-based to graph-based systems for better knowledge connection, 2) Integration of large language models for enhanced content understanding and retrieval, 3) Focus on privacy-first and local-first architectures, 4) Emphasis on automatic knowledge connection and discovery, 5) Implementation of semantic search capabilities.',
227
+ id: 'pkm-trends-text-01',
228
+ },
229
+ {
230
+ content:
231
+ 'Key features emerging across modern PKM tools include: automated content categorization, semantic analysis and linking, personalized AI models that learn from user interaction, integration with existing workflows, and robust privacy protection. These tools are increasingly focusing on proactive knowledge surfacing rather than passive storage.',
232
+ id: 'pkm-features-text-01',
233
+ },
234
+ ],
235
+ };
236
+
237
+ // const data = await sanitizeObjects(TYPES, TEST_DATA, db);
238
+ // log.info('data', { data });
239
+ });
240
+ });
@@ -0,0 +1,155 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import * as Toolkit from '@effect/ai/Toolkit';
6
+ import * as AnthropicTool from '@effect/ai-anthropic/AnthropicTool';
7
+ import * as Array from 'effect/Array';
8
+ import * as Effect from 'effect/Effect';
9
+ import * as Layer from 'effect/Layer';
10
+ import * as Schema from 'effect/Schema';
11
+
12
+ import { AiService, ConsolePrinter } from '@dxos/ai';
13
+ import {
14
+ AiSession,
15
+ GenerationObserver,
16
+ createToolkit,
17
+ makeToolExecutionServiceFromFunctions,
18
+ makeToolResolverFromFunctions,
19
+ } from '@dxos/assistant';
20
+ import { type DXN, Obj } from '@dxos/echo';
21
+ import { DatabaseService, FunctionInvocationService, TracingService, defineFunction } from '@dxos/functions';
22
+ import { DataType } from '@dxos/schema';
23
+ import { trim } from '@dxos/util';
24
+
25
+ import { exaFunction, exaMockFunction } from '../exa';
26
+
27
+ import { LocalSearchHandler, LocalSearchToolkit, makeGraphWriterHandler, makeGraphWriterToolkit } from './graph';
28
+ // TODO(dmaretskyi): Vite build bug with instruction files with the same filename getting mixed-up.
29
+ import PROMPT from './instructions-research.tpl?raw';
30
+ import { contextQueueLayerFromResearchGraph } from './research-graph';
31
+ import { ResearchDataTypes } from './types';
32
+
33
+ /**
34
+ * Exec external service and return the results as a Subgraph.
35
+ */
36
+ export default defineFunction({
37
+ key: 'dxos.org/function/research',
38
+ name: 'Research',
39
+ description: trim`
40
+ Research the web for information.
41
+ Inserts structured data into the research graph.
42
+ Will return research summary and the objects created.
43
+ `,
44
+ inputSchema: Schema.Struct({
45
+ query: Schema.String.annotations({
46
+ description: trim`
47
+ The query to search for.
48
+ If doing research on an object, load it first and pass it as a JSON string.
49
+ `,
50
+ }),
51
+
52
+ researchInstructions: Schema.optional(Schema.String).annotations({
53
+ description: trim`
54
+ The instructions for the research agent.
55
+ E.g., preference on fast responses or in-depth analysis, number of web searcher or the objects created.
56
+ `,
57
+ }),
58
+
59
+ // TOOD(burdon): Move to context.
60
+ mockSearch: Schema.optional(Schema.Boolean).annotations({
61
+ description: 'Whether to use the mock search tool.',
62
+ default: false,
63
+ }),
64
+ }),
65
+ outputSchema: Schema.Struct({
66
+ note: Schema.optional(Schema.String).annotations({
67
+ description: 'A note from the research agent.',
68
+ }),
69
+ objects: Schema.Array(Schema.Unknown).annotations({
70
+ description: 'The structured objects created as a result of the research.',
71
+ }),
72
+ }),
73
+ handler: Effect.fnUntraced(
74
+ function* ({ data: { query, mockSearch, researchInstructions } }) {
75
+ if (mockSearch) {
76
+ const mockPerson = yield* DatabaseService.add(
77
+ Obj.make(DataType.Person, {
78
+ preferredName: 'John Doe',
79
+ emails: [{ value: 'john.doe@example.com' }],
80
+ phoneNumbers: [{ value: '123-456-7890' }],
81
+ }),
82
+ );
83
+
84
+ return {
85
+ note: trim`
86
+ The research run in test-mode and was mocked.
87
+ Proceed as usual.
88
+ We reference John Doe to test reference: ${Obj.getDXN(mockPerson)}
89
+ `,
90
+ objects: [Obj.toJSON(mockPerson)],
91
+ };
92
+ }
93
+
94
+ yield* DatabaseService.flush({ indexes: true });
95
+ yield* TracingService.emitStatus({ message: 'Researching...' });
96
+
97
+ const objectDXNs: DXN[] = [];
98
+ const GraphWriterToolkit = makeGraphWriterToolkit({ schema: ResearchDataTypes });
99
+ const GraphWriterHandler = makeGraphWriterHandler(GraphWriterToolkit, {
100
+ onAppend: (dxns) => objectDXNs.push(...dxns),
101
+ });
102
+ const NativeWebSearch = Toolkit.make(AnthropicTool.WebSearch_20250305({}));
103
+
104
+ const toolkit = yield* createToolkit({
105
+ toolkit: Toolkit.merge(LocalSearchToolkit, GraphWriterToolkit, NativeWebSearch),
106
+ // toolIds: [mockSearch ? ToolId.make(exaMockFunction.key) : ToolId.make(exaFunction.key)],
107
+ }).pipe(
108
+ Effect.provide(
109
+ Layer.mergeAll(
110
+ //
111
+ GraphWriterHandler,
112
+ LocalSearchHandler,
113
+ ).pipe(Layer.provide(contextQueueLayerFromResearchGraph)),
114
+ ),
115
+ );
116
+
117
+ const session = new AiSession();
118
+ const result = yield* session.run({
119
+ prompt: query,
120
+ system:
121
+ PROMPT +
122
+ (researchInstructions
123
+ ? '\n\n' + `<research_instructions>${researchInstructions}</research_instructions>`
124
+ : ''),
125
+ toolkit,
126
+ observer: GenerationObserver.fromPrinter(new ConsolePrinter({ tag: 'research' })),
127
+ });
128
+ const note = result
129
+ .at(-1)
130
+ ?.blocks.filter((block) => block._tag === 'text')
131
+ .at(-1)?.text;
132
+ const objects = yield* Effect.forEach(objectDXNs, (dxn) => DatabaseService.resolve(dxn)).pipe(
133
+ Effect.map(Array.map((obj) => Obj.toJSON(obj))),
134
+ );
135
+
136
+ return {
137
+ note,
138
+ objects,
139
+ };
140
+ },
141
+ Effect.provide(
142
+ Layer.mergeAll(
143
+ AiService.model('@anthropic/claude-sonnet-4-0'),
144
+ // TODO(dmaretskyi): Extract.
145
+ makeToolResolverFromFunctions([exaFunction, exaMockFunction], Toolkit.make()),
146
+ makeToolExecutionServiceFromFunctions(Toolkit.make() as any, Layer.empty as any),
147
+ ).pipe(
148
+ Layer.provide(
149
+ // TODO(dmaretskyi): This should be provided by environment.
150
+ Layer.mergeAll(FunctionInvocationService.layerTestMocked({ functions: [exaFunction, exaMockFunction] })),
151
+ ),
152
+ ),
153
+ ),
154
+ ),
155
+ });
@@ -0,0 +1,24 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { DataType } from '@dxos/schema';
6
+
7
+ /**
8
+ * Data types for research.
9
+ */
10
+ export const ResearchDataTypes = [
11
+ // Objects
12
+ DataType.Event,
13
+ DataType.LegacyOrganization,
14
+ DataType.LegacyPerson,
15
+ DataType.Project,
16
+ DataType.Task,
17
+ DataType.Text,
18
+
19
+ // Relations
20
+ // TODO(wittjosiah): Until views (e.g. table) support relations this needs to be expressed via organization ref.
21
+ // DataType.Employer,
22
+ DataType.HasRelationship,
23
+ DataType.HasConnection,
24
+ ];
@@ -0,0 +1,11 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { default as read$ } from './read';
6
+ import { default as update$ } from './update';
7
+
8
+ export namespace Tasks {
9
+ export const read = read$;
10
+ export const update = update$;
11
+ }
@@ -0,0 +1,34 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import * as Effect from 'effect/Effect';
6
+ import * as Schema from 'effect/Schema';
7
+
8
+ import { ArtifactId } from '@dxos/assistant';
9
+ import { DatabaseService, defineFunction } from '@dxos/functions';
10
+ import { Markdown } from '@dxos/plugin-markdown/types';
11
+
12
+ export default defineFunction({
13
+ key: 'dxos.org/function/markdown/read-tasks',
14
+ name: 'Read',
15
+ description: 'Read markdown tasks.',
16
+ inputSchema: Schema.Struct({
17
+ id: ArtifactId.annotations({
18
+ description: 'The ID of the document to read.',
19
+ }),
20
+ }),
21
+ outputSchema: Schema.Struct({
22
+ content: Schema.String,
23
+ }),
24
+ handler: Effect.fn(function* ({ data: { id } }) {
25
+ const doc = yield* DatabaseService.resolve(ArtifactId.toDXN(id), Markdown.Document);
26
+
27
+ // Return content with line numbers prefixed.
28
+ const { content } = yield* DatabaseService.load(doc.content);
29
+ const lines = content.split('\n');
30
+ const len = String(lines.length).length;
31
+ const numbered = lines.map((line, i) => `${String(i + 1).padStart(len, ' ')}. ${line}`).join('\n');
32
+ return { content: numbered };
33
+ }),
34
+ });
@@ -0,0 +1,99 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { describe, expect, it } from 'vitest';
6
+
7
+ import { MarkdownTasks, type TaskOperation } from './task-list';
8
+
9
+ describe('MarkdownTasks', () => {
10
+ it('should initialize with content', () => {
11
+ const content = '- [ ] Task 1\n- [x] Task 2';
12
+ const manager = new MarkdownTasks(content);
13
+ expect(manager.getRawContent()).toBe(content);
14
+ });
15
+
16
+ it('should provide numbered content', () => {
17
+ const manager = new MarkdownTasks('- [ ] Task 1\n - [ ] Subtask');
18
+ const numbered = manager.getNumberedContent();
19
+ expect(numbered).toBe(' 1→- [ ] Task 1\n 2→ - [ ] Subtask');
20
+ });
21
+
22
+ it('should insert tasks with indentation', () => {
23
+ const manager = new MarkdownTasks('- [ ] Main task');
24
+ manager.insertTask(2, 'Subtask', false, 2);
25
+ expect(manager.getRawContent()).toBe('- [ ] Main task\n - [ ] Subtask');
26
+ });
27
+
28
+ it('should insert completed tasks', () => {
29
+ const manager = new MarkdownTasks('- [ ] Task 1');
30
+ manager.insertTask(2, 'Completed task', true, 0);
31
+ expect(manager.getRawContent()).toBe('- [ ] Task 1\n- [x] Completed task');
32
+ });
33
+
34
+ it('should delete tasks', () => {
35
+ const manager = new MarkdownTasks('- [ ] Task 1\n- [ ] Task 2');
36
+ const success = manager.deleteTask(2);
37
+ expect(success).toBe(true);
38
+ expect(manager.getRawContent()).toBe('- [ ] Task 1');
39
+ });
40
+
41
+ it('should update task text', () => {
42
+ const manager = new MarkdownTasks('- [ ] Old task');
43
+ const success = manager.updateTaskText(1, 'New task description');
44
+ expect(success).toBe(true);
45
+ expect(manager.getRawContent()).toBe('- [ ] New task description');
46
+ });
47
+
48
+ it('should toggle task completion', () => {
49
+ const manager = new MarkdownTasks('- [ ] Task 1\n- [x] Task 2');
50
+
51
+ // Complete first task
52
+ manager.toggleTaskCompletion(1, true);
53
+ expect(manager.getRawContent()).toBe('- [x] Task 1\n- [x] Task 2');
54
+
55
+ // Uncomplete second task
56
+ manager.toggleTaskCompletion(2, false);
57
+ expect(manager.getRawContent()).toBe('- [x] Task 1\n- [ ] Task 2');
58
+ });
59
+
60
+ it('should change task indentation', () => {
61
+ const manager = new MarkdownTasks('- [ ] Task 1');
62
+ const success = manager.setTaskIndent(1, 4);
63
+ expect(success).toBe(true);
64
+ expect(manager.getRawContent()).toBe(' - [ ] Task 1');
65
+ });
66
+
67
+ it('should apply multiple operations', () => {
68
+ const manager = new MarkdownTasks('- [ ] Task 1');
69
+
70
+ const operations: TaskOperation[] = [
71
+ { type: 'insertTask', lineNumber: 2, text: 'New task', indent: 2 },
72
+ { type: 'toggleTaskCompletion', lineNumber: 1, completed: true },
73
+ { type: 'insertTask', lineNumber: 3, text: 'Another task', indent: 0 },
74
+ ];
75
+
76
+ manager.applyOperations(operations);
77
+ const result = manager.getRawContent();
78
+ expect(result).toBe('- [x] Task 1\n - [ ] New task\n- [ ] Another task');
79
+ });
80
+
81
+ it('should handle hierarchical task structure', () => {
82
+ const manager = new MarkdownTasks();
83
+
84
+ // Build a hierarchical structure
85
+ manager.insertTask(1, 'Main task', false, 0);
86
+ manager.insertTask(2, 'Subtask 1', false, 2);
87
+ manager.insertTask(3, 'Sub-subtask', false, 4);
88
+ manager.insertTask(4, 'Subtask 2', false, 2);
89
+
90
+ const expected = '- [ ] Main task\n - [ ] Subtask 1\n - [ ] Sub-subtask\n - [ ] Subtask 2';
91
+ expect(manager.getRawContent()).toBe(expected);
92
+ });
93
+
94
+ it('should handle line insertions at the end', () => {
95
+ const manager = new MarkdownTasks('- [ ] Task 1');
96
+ manager.insertTask(999, 'End task', false, 0);
97
+ expect(manager.getRawContent()).toBe('- [ ] Task 1\n- [ ] End task');
98
+ });
99
+ });