@dxos/assistant-toolkit 0.8.4-main.ae835ea → 0.8.4-main.bc674ce

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 (204) hide show
  1. package/dist/lib/browser/chunk-J5LGTIGS.mjs +10 -0
  2. package/dist/lib/browser/chunk-J5LGTIGS.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +1091 -692
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/testing/index.mjs +43 -0
  7. package/dist/lib/browser/testing/index.mjs.map +7 -0
  8. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +11 -0
  9. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +7 -0
  10. package/dist/lib/node-esm/index.mjs +1090 -692
  11. package/dist/lib/node-esm/index.mjs.map +4 -4
  12. package/dist/lib/node-esm/meta.json +1 -1
  13. package/dist/lib/node-esm/testing/index.mjs +44 -0
  14. package/dist/lib/node-esm/testing/index.mjs.map +7 -0
  15. package/dist/types/src/blueprints/design/design-blueprint.d.ts +17 -3
  16. package/dist/types/src/blueprints/design/design-blueprint.d.ts.map +1 -1
  17. package/dist/types/src/blueprints/design/index.d.ts +1 -1
  18. package/dist/types/src/blueprints/design/index.d.ts.map +1 -1
  19. package/dist/types/src/blueprints/discord/discord-blueprint.d.ts +13 -13
  20. package/dist/types/src/blueprints/discord/discord-blueprint.d.ts.map +1 -1
  21. package/dist/types/src/blueprints/discord/index.d.ts +1 -1
  22. package/dist/types/src/blueprints/discord/index.d.ts.map +1 -1
  23. package/dist/types/src/blueprints/linear/index.d.ts +1 -1
  24. package/dist/types/src/blueprints/linear/index.d.ts.map +1 -1
  25. package/dist/types/src/blueprints/linear/linear-blueprint.d.ts +13 -13
  26. package/dist/types/src/blueprints/linear/linear-blueprint.d.ts.map +1 -1
  27. package/dist/types/src/blueprints/planning/index.d.ts +1 -1
  28. package/dist/types/src/blueprints/planning/index.d.ts.map +1 -1
  29. package/dist/types/src/blueprints/planning/planning-blueprint.d.ts +17 -3
  30. package/dist/types/src/blueprints/planning/planning-blueprint.d.ts.map +1 -1
  31. package/dist/types/src/blueprints/research/index.d.ts +1 -1
  32. package/dist/types/src/blueprints/research/index.d.ts.map +1 -1
  33. package/dist/types/src/blueprints/research/research-blueprint.d.ts +17 -3
  34. package/dist/types/src/blueprints/research/research-blueprint.d.ts.map +1 -1
  35. package/dist/types/src/blueprints/testing.d.ts +2 -2
  36. package/dist/types/src/blueprints/testing.d.ts.map +1 -1
  37. package/dist/types/src/blueprints/websearch/index.d.ts +1 -1
  38. package/dist/types/src/blueprints/websearch/index.d.ts.map +1 -1
  39. package/dist/types/src/blueprints/websearch/websearch-blueprint.d.ts +17 -3
  40. package/dist/types/src/blueprints/websearch/websearch-blueprint.d.ts.map +1 -1
  41. package/dist/types/src/blueprints/websearch/websearch-toolkit.d.ts +1 -0
  42. package/dist/types/src/blueprints/websearch/websearch-toolkit.d.ts.map +1 -1
  43. package/dist/types/src/{functions/research → crud}/graph.d.ts +14 -11
  44. package/dist/types/src/crud/graph.d.ts.map +1 -0
  45. package/dist/types/src/crud/graph.test.d.ts.map +1 -0
  46. package/dist/types/src/crud/index.d.ts +2 -0
  47. package/dist/types/src/crud/index.d.ts.map +1 -0
  48. package/dist/types/src/functions/agent/prompt.d.ts +73 -8
  49. package/dist/types/src/functions/agent/prompt.d.ts.map +1 -1
  50. package/dist/types/src/functions/discord/fetch-messages.d.ts +1 -1
  51. package/dist/types/src/functions/discord/index.d.ts +1 -1
  52. package/dist/types/src/functions/discord/index.d.ts.map +1 -1
  53. package/dist/types/src/functions/document/index.d.ts +3 -2
  54. package/dist/types/src/functions/document/index.d.ts.map +1 -1
  55. package/dist/types/src/functions/document/read.d.ts +1 -1
  56. package/dist/types/src/functions/document/read.d.ts.map +1 -1
  57. package/dist/types/src/functions/document/update.d.ts +1 -1
  58. package/dist/types/src/functions/document/update.d.ts.map +1 -1
  59. package/dist/types/src/functions/entity-extraction/entity-extraction.d.ts +126 -125
  60. package/dist/types/src/functions/entity-extraction/entity-extraction.d.ts.map +1 -1
  61. package/dist/types/src/functions/entity-extraction/index.d.ts +126 -125
  62. package/dist/types/src/functions/entity-extraction/index.d.ts.map +1 -1
  63. package/dist/types/src/functions/exa/data/exa-search-1748337321991.d.ts.map +1 -0
  64. package/dist/types/src/functions/exa/data/exa-search-1748337331526.d.ts.map +1 -0
  65. package/dist/types/src/functions/exa/data/exa-search-1748337344119.d.ts.map +1 -0
  66. package/dist/types/src/functions/exa/data/index.d.ts.map +1 -0
  67. package/dist/types/src/functions/exa/exa.d.ts +1 -1
  68. package/dist/types/src/functions/exa/mock.d.ts +1 -1
  69. package/dist/types/src/functions/github/fetch-prs.d.ts +1 -1
  70. package/dist/types/src/functions/github/fetch-prs.d.ts.map +1 -1
  71. package/dist/types/src/functions/linear/index.d.ts +2 -2
  72. package/dist/types/src/functions/linear/index.d.ts.map +1 -1
  73. package/dist/types/src/functions/linear/sync-issues.d.ts +2 -2
  74. package/dist/types/src/functions/linear/sync-issues.d.ts.map +1 -1
  75. package/dist/types/src/functions/research/document-create.d.ts +9 -0
  76. package/dist/types/src/functions/research/document-create.d.ts.map +1 -0
  77. package/dist/types/src/functions/research/index.d.ts +8 -6
  78. package/dist/types/src/functions/research/index.d.ts.map +1 -1
  79. package/dist/types/src/functions/research/research-graph.d.ts +9 -8
  80. package/dist/types/src/functions/research/research-graph.d.ts.map +1 -1
  81. package/dist/types/src/functions/research/research.d.ts +4 -3
  82. package/dist/types/src/functions/research/research.d.ts.map +1 -1
  83. package/dist/types/src/functions/research/types.d.ts +2 -380
  84. package/dist/types/src/functions/research/types.d.ts.map +1 -1
  85. package/dist/types/src/functions/tasks/index.d.ts +2 -2
  86. package/dist/types/src/functions/tasks/index.d.ts.map +1 -1
  87. package/dist/types/src/functions/tasks/read.d.ts +1 -1
  88. package/dist/types/src/functions/tasks/read.d.ts.map +1 -1
  89. package/dist/types/src/functions/tasks/update.d.ts +1 -1
  90. package/dist/types/src/functions/tasks/update.d.ts.map +1 -1
  91. package/dist/types/src/index.d.ts +2 -1
  92. package/dist/types/src/index.d.ts.map +1 -1
  93. package/dist/types/src/initiative/Initiative.d.ts +84 -0
  94. package/dist/types/src/initiative/Initiative.d.ts.map +1 -0
  95. package/dist/types/src/initiative/InitiativeSchema.d.ts +19 -0
  96. package/dist/types/src/initiative/InitiativeSchema.d.ts.map +1 -0
  97. package/dist/types/src/initiative/functions/agent.d.ts +37 -0
  98. package/dist/types/src/initiative/functions/agent.d.ts.map +1 -0
  99. package/dist/types/src/initiative/functions/getContext.d.ts +17 -0
  100. package/dist/types/src/initiative/functions/getContext.d.ts.map +1 -0
  101. package/dist/types/src/initiative/functions/index.d.ts +4 -0
  102. package/dist/types/src/initiative/functions/index.d.ts.map +1 -0
  103. package/dist/types/src/{functions/research/create-document.d.ts → initiative/functions/update.d.ts} +3 -3
  104. package/dist/types/src/initiative/functions/update.d.ts.map +1 -0
  105. package/dist/types/src/initiative/index.d.ts +1 -0
  106. package/dist/types/src/initiative/index.d.ts.map +1 -0
  107. package/dist/types/src/initiative/initiative.test.d.ts +2 -0
  108. package/dist/types/src/initiative/initiative.test.d.ts.map +1 -0
  109. package/dist/types/src/sync/sync.d.ts +3 -3
  110. package/dist/types/src/sync/sync.d.ts.map +1 -1
  111. package/dist/types/src/testing/index.d.ts +1 -1
  112. package/dist/types/src/testing/index.d.ts.map +1 -1
  113. package/dist/types/src/testing/plugins.d.ts +19 -0
  114. package/dist/types/src/testing/plugins.d.ts.map +1 -0
  115. package/dist/types/src/toolkits/AssistantToolkit.d.ts +43 -0
  116. package/dist/types/src/toolkits/AssistantToolkit.d.ts.map +1 -0
  117. package/dist/types/src/toolkits/AssistantToolkit.test.d.ts +2 -0
  118. package/dist/types/src/toolkits/AssistantToolkit.test.d.ts.map +1 -0
  119. package/dist/types/src/toolkits/SystemToolkit.d.ts +99 -0
  120. package/dist/types/src/toolkits/SystemToolkit.d.ts.map +1 -0
  121. package/dist/types/src/toolkits/index.d.ts +3 -0
  122. package/dist/types/src/toolkits/index.d.ts.map +1 -0
  123. package/dist/types/tsconfig.tsbuildinfo +1 -1
  124. package/package.json +49 -33
  125. package/src/blueprints/design/design-blueprint.test.ts +17 -20
  126. package/src/blueprints/design/design-blueprint.ts +4 -6
  127. package/src/blueprints/design/index.ts +1 -1
  128. package/src/blueprints/discord/discord-blueprint.ts +4 -6
  129. package/src/blueprints/discord/index.ts +1 -1
  130. package/src/blueprints/linear/index.ts +1 -1
  131. package/src/blueprints/linear/linear-blueprint.ts +4 -6
  132. package/src/blueprints/planning/index.ts +1 -1
  133. package/src/blueprints/planning/planning-blueprint.test.ts +17 -20
  134. package/src/blueprints/planning/planning-blueprint.ts +4 -6
  135. package/src/blueprints/research/index.ts +1 -1
  136. package/src/blueprints/research/research-blueprint.ts +25 -19
  137. package/src/blueprints/testing.ts +2 -2
  138. package/src/blueprints/websearch/index.ts +2 -1
  139. package/src/blueprints/websearch/websearch-blueprint.ts +4 -6
  140. package/src/crud/graph.test.ts +46 -0
  141. package/src/{functions/research → crud}/graph.ts +32 -40
  142. package/src/crud/index.ts +5 -0
  143. package/src/experimental/feed.test.ts +12 -9
  144. package/src/functions/agent/prompt.ts +39 -22
  145. package/src/functions/discord/fetch-messages.test.ts +6 -7
  146. package/src/functions/discord/fetch-messages.ts +11 -10
  147. package/src/functions/document/index.ts +1 -0
  148. package/src/functions/document/read.ts +4 -3
  149. package/src/functions/document/update.ts +7 -4
  150. package/src/functions/entity-extraction/entity-extraction.conversations.json +6597 -1
  151. package/src/functions/entity-extraction/entity-extraction.test.ts +16 -23
  152. package/src/functions/entity-extraction/entity-extraction.ts +51 -31
  153. package/src/functions/exa/mock.ts +1 -1
  154. package/src/functions/github/fetch-prs.ts +3 -2
  155. package/src/functions/linear/linear.test.ts +18 -20
  156. package/src/functions/linear/sync-issues.ts +14 -12
  157. package/src/functions/research/document-create.ts +76 -0
  158. package/src/functions/research/index.ts +1 -2
  159. package/src/functions/research/research-graph.ts +13 -11
  160. package/src/functions/research/{instructions-research.tpl → research-instructions.tpl} +14 -6
  161. package/src/functions/research/research.conversations.json +1 -10714
  162. package/src/functions/research/research.test.ts +92 -161
  163. package/src/functions/research/research.ts +88 -54
  164. package/src/functions/research/types.ts +14 -12
  165. package/src/functions/tasks/read.ts +4 -3
  166. package/src/functions/tasks/update.ts +4 -3
  167. package/src/index.ts +2 -1
  168. package/src/initiative/Initiative.ts +99 -0
  169. package/src/initiative/InitiativeSchema.ts +37 -0
  170. package/src/initiative/functions/agent.ts +57 -0
  171. package/src/initiative/functions/getContext.ts +74 -0
  172. package/src/initiative/functions/index.ts +7 -0
  173. package/src/initiative/functions/update.ts +63 -0
  174. package/src/initiative/index.ts +3 -0
  175. package/src/initiative/initiative.conversations.json +1 -0
  176. package/src/initiative/initiative.test.ts +485 -0
  177. package/src/sync/sync.ts +38 -30
  178. package/src/testing/index.ts +1 -1
  179. package/src/{plugins.tsx → testing/plugins.tsx} +15 -15
  180. package/src/toolkits/AssistantToolkit.conversations.json +1 -0
  181. package/src/toolkits/AssistantToolkit.test.ts +94 -0
  182. package/src/toolkits/AssistantToolkit.ts +70 -0
  183. package/src/toolkits/SystemToolkit.ts +299 -0
  184. package/src/toolkits/index.ts +6 -0
  185. package/dist/types/src/functions/research/create-document.d.ts.map +0 -1
  186. package/dist/types/src/functions/research/graph.d.ts.map +0 -1
  187. package/dist/types/src/functions/research/graph.test.d.ts.map +0 -1
  188. package/dist/types/src/plugins.d.ts +0 -19
  189. package/dist/types/src/plugins.d.ts.map +0 -1
  190. package/dist/types/src/testing/data/exa-search-1748337321991.d.ts.map +0 -1
  191. package/dist/types/src/testing/data/exa-search-1748337331526.d.ts.map +0 -1
  192. package/dist/types/src/testing/data/exa-search-1748337344119.d.ts.map +0 -1
  193. package/dist/types/src/testing/data/index.d.ts.map +0 -1
  194. package/src/functions/research/create-document.ts +0 -69
  195. package/src/functions/research/graph.test.ts +0 -69
  196. /package/dist/types/src/{functions/research → crud}/graph.test.d.ts +0 -0
  197. /package/dist/types/src/{testing → functions/exa}/data/exa-search-1748337321991.d.ts +0 -0
  198. /package/dist/types/src/{testing → functions/exa}/data/exa-search-1748337331526.d.ts +0 -0
  199. /package/dist/types/src/{testing → functions/exa}/data/exa-search-1748337344119.d.ts +0 -0
  200. /package/dist/types/src/{testing → functions/exa}/data/index.d.ts +0 -0
  201. /package/src/{testing → functions/exa}/data/exa-search-1748337321991.ts +0 -0
  202. /package/src/{testing → functions/exa}/data/exa-search-1748337331526.ts +0 -0
  203. /package/src/{testing → functions/exa}/data/exa-search-1748337344119.ts +0 -0
  204. /package/src/{testing → functions/exa}/data/index.ts +0 -0
@@ -8,8 +8,8 @@ import { describe, it } from '@effect/vitest';
8
8
  import * as Effect from 'effect/Effect';
9
9
  import * as Layer from 'effect/Layer';
10
10
 
11
- import { AiService, ConsolePrinter, MemoizedAiService } from '@dxos/ai';
12
- import { AiServiceTestingPreset, EXA_API_KEY } from '@dxos/ai/testing';
11
+ import { AiService, ConsolePrinter } from '@dxos/ai';
12
+ import { MemoizedAiService, TestAiService } from '@dxos/ai/testing';
13
13
  import {
14
14
  AiConversation,
15
15
  type ContextBinding,
@@ -18,25 +18,21 @@ import {
18
18
  makeToolResolverFromFunctions,
19
19
  } from '@dxos/assistant';
20
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';
21
+ import { Database, Filter, Obj, Query, Ref } from '@dxos/echo';
22
+ import { acquireReleaseResource } from '@dxos/effect';
23
+ import { TestHelpers } from '@dxos/effect/testing';
24
+ import { CredentialsService, FunctionInvocationService, QueueService, TracingService } from '@dxos/functions';
25
+ import { FunctionInvocationServiceLayerTest, TestDatabaseLayer } from '@dxos/functions-runtime/testing';
26
+ import { invariant } from '@dxos/invariant';
32
27
  import { ObjectId } from '@dxos/keys';
33
- import { DataType } from '@dxos/schema';
28
+ import { MarkdownBlueprint } from '@dxos/plugin-markdown/blueprints';
29
+ import { Markdown } from '@dxos/plugin-markdown/types';
30
+ import { HasSubject, type Message, Organization } from '@dxos/types';
34
31
 
35
32
  import { ResearchBlueprint } from '../../blueprints';
36
33
  import { testToolkit } from '../../blueprints/testing';
37
34
 
38
- import createDocument from './create-document';
39
- import { createExtractionSchema, getSanitizedSchemaName } from './graph';
35
+ import { default as createDocument } from './document-create';
40
36
  import { default as research } from './research';
41
37
  import { ResearchGraph, queryResearchGraph } from './research-graph';
42
38
  import { ResearchDataTypes } from './types';
@@ -45,196 +41,131 @@ ObjectId.dangerouslyDisableRandomness();
45
41
 
46
42
  const TestLayer = Layer.mergeAll(
47
43
  AiService.model('@anthropic/claude-opus-4-0'),
48
- makeToolResolverFromFunctions([research, createDocument], testToolkit),
44
+ makeToolResolverFromFunctions([research, createDocument, ...MarkdownBlueprint.functions], testToolkit),
49
45
  makeToolExecutionServiceFromFunctions(testToolkit, testToolkit.toLayer({}) as any),
50
- ComputeEventLogger.layerFromTracing,
51
46
  ).pipe(
52
- Layer.provideMerge(FunctionInvocationService.layerTest({ functions: [research, createDocument] })),
47
+ Layer.provideMerge(
48
+ FunctionInvocationServiceLayerTest({
49
+ functions: [research, createDocument, ...MarkdownBlueprint.functions],
50
+ }),
51
+ ),
53
52
  Layer.provideMerge(
54
53
  Layer.mergeAll(
55
- MemoizedAiService.layerTest().pipe(Layer.provide(AiServiceTestingPreset('direct'))),
54
+ TestAiService(),
56
55
  TestDatabaseLayer({
56
+ spaceKey: 'fixed',
57
57
  indexing: { vector: true },
58
- types: [...ResearchDataTypes, ResearchGraph, Blueprint.Blueprint],
58
+ types: [...ResearchDataTypes, ResearchGraph, Blueprint.Blueprint, Markdown.Document, HasSubject.HasSubject],
59
59
  }),
60
- CredentialsService.configuredLayer([{ service: 'exa.ai', apiKey: EXA_API_KEY }]),
60
+ CredentialsService.configuredLayer([]),
61
61
  TracingService.layerNoop,
62
62
  ),
63
63
  ),
64
64
  );
65
65
 
66
- // TODO(dmaretskyi): Out-of-memory.
67
- describe.skip('Research', () => {
66
+ describe('Research', () => {
68
67
  it.effect(
69
68
  'call a function to generate a research report',
70
69
  Effect.fnUntraced(
71
70
  function* (_) {
72
- yield* DatabaseService.add(
73
- Obj.make(DataType.Organization, {
74
- name: 'Airbnb',
75
- website: 'https://www.airbnb.com/',
71
+ yield* Database.Service.add(
72
+ Obj.make(Organization.Organization, {
73
+ name: 'BlueYard',
74
+ website: 'https://blueyard.com',
76
75
  }),
77
76
  );
78
- yield* DatabaseService.flush({ indexes: true });
79
-
77
+ yield* Database.Service.flush({ indexes: true });
80
78
  const result = yield* FunctionInvocationService.invokeFunction(research, {
81
- query: 'Founders and investors of airbnb.',
82
- mockSearch: false,
79
+ query: 'Founders and portfolio of BlueYard.',
83
80
  });
84
81
 
85
82
  console.log(inspect(result, { depth: null, colors: true }));
86
83
  console.log(JSON.stringify(result, null, 2));
87
84
 
88
- yield* DatabaseService.flush({ indexes: true });
85
+ yield* Database.Service.flush({ indexes: true });
89
86
  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 }));
87
+ if (researchGraph) {
88
+ const data = yield* Database.Service.load(researchGraph.queue).pipe(
89
+ Effect.flatMap((queue) => Effect.promise(() => queue.queryObjects())),
90
+ );
91
+ console.log(inspect(data, { depth: null, colors: true }));
92
+ }
94
93
  },
95
94
  Effect.provide(TestLayer),
96
95
  TestHelpers.provideTestContext,
97
96
  ),
98
- MemoizedAiService.isGenerationEnabled() ? 240_000 : undefined,
97
+ MemoizedAiService.isGenerationEnabled() ? 240_000 : 30_000,
99
98
  );
100
99
 
101
100
  it.scoped(
102
- 'research blueprint',
101
+ 'create and update research report',
103
102
  Effect.fnUntraced(
104
103
  function* (_) {
105
- const queue = yield* QueueService.createQueue<DataType.Message | ContextBinding>();
106
- const conversation = yield* acquireReleaseResource(() => new AiConversation(queue));
104
+ const organization = yield* Database.Service.add(
105
+ Obj.make(Organization.Organization, {
106
+ name: 'BlueYard',
107
+ website: 'https://blueyard.com',
108
+ }),
109
+ );
107
110
 
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
+ const queue = yield* QueueService.createQueue<Message.Message | ContextBinding>();
112
+ const conversation = yield* acquireReleaseResource(() => new AiConversation({ queue }));
111
113
 
112
- const blueprint = yield* DatabaseService.add(Obj.clone(ResearchBlueprint));
113
- yield* Effect.promise(() => conversation.context.bind({ blueprints: [Ref.make(blueprint)] }));
114
+ yield* Database.Service.flush({ indexes: true });
115
+ const researchBlueprint = yield* Database.Service.add(Obj.clone(ResearchBlueprint));
116
+ const markdownBlueprint = yield* Database.Service.add(Obj.clone(MarkdownBlueprint.make()));
117
+ yield* Effect.promise(() =>
118
+ conversation.context.bind({
119
+ blueprints: [Ref.make(researchBlueprint), Ref.make(markdownBlueprint)],
120
+ objects: [Ref.make(organization)],
121
+ }),
122
+ );
114
123
 
115
124
  const observer = GenerationObserver.fromPrinter(new ConsolePrinter());
125
+
116
126
  yield* conversation.createRequest({
117
127
  observer,
118
- prompt: `Research airbnb founders.`,
128
+ prompt: `Create a research summary about ${organization.name}.`,
119
129
  });
130
+ {
131
+ const docs = yield* Database.Service.runQuery(
132
+ Query.select(Filter.id(organization.id)).targetOf(HasSubject.HasSubject).source(),
133
+ );
134
+ if (docs.length !== 1) {
135
+ throw new Error(`Expected 1 research document; got ${docs.length}: ${docs.map((_) => _.name)}`);
136
+ }
137
+
138
+ const doc = docs[0];
139
+ invariant(Obj.instanceOf(Markdown.Document, doc));
140
+ console.log({
141
+ name: doc.name,
142
+ content: yield* Database.Service.load(doc.content).pipe(Effect.map((_) => _.content)),
143
+ });
144
+ }
120
145
 
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 }));
146
+ yield* conversation.createRequest({
147
+ observer,
148
+ prompt: 'Add a section about their portfolio.',
149
+ });
150
+ {
151
+ const docs = yield* Database.Service.runQuery(
152
+ Query.select(Filter.id(organization.id)).targetOf(HasSubject.HasSubject).source(),
153
+ );
154
+ if (docs.length !== 1) {
155
+ throw new Error(`Expected 1 research document; got ${docs.length}: ${docs.map((_) => _.name)}`);
156
+ }
157
+
158
+ const doc = docs[0];
159
+ invariant(Obj.instanceOf(Markdown.Document, doc));
160
+ console.log({
161
+ name: doc.name,
162
+ content: yield* Database.Service.load(doc.content).pipe(Effect.map((_) => _.content)),
163
+ });
164
+ }
126
165
  },
127
166
  Effect.provide(TestLayer),
128
167
  TestHelpers.provideTestContext,
129
168
  ),
130
- MemoizedAiService.isGenerationEnabled() ? 240_000 : undefined,
169
+ MemoizedAiService.isGenerationEnabled() ? 240_000 : 30_000,
131
170
  );
132
171
  });
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
- });
@@ -6,8 +6,11 @@ import * as Toolkit from '@effect/ai/Toolkit';
6
6
  import * as AnthropicTool from '@effect/ai-anthropic/AnthropicTool';
7
7
  import * as Array from 'effect/Array';
8
8
  import * as Effect from 'effect/Effect';
9
+ import * as Function from 'effect/Function';
9
10
  import * as Layer from 'effect/Layer';
11
+ import * as Option from 'effect/Option';
10
12
  import * as Schema from 'effect/Schema';
13
+ import * as String from 'effect/String';
11
14
 
12
15
  import { AiService, ConsolePrinter } from '@dxos/ai';
13
16
  import {
@@ -17,17 +20,19 @@ import {
17
20
  makeToolExecutionServiceFromFunctions,
18
21
  makeToolResolverFromFunctions,
19
22
  } 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 { Template } from '@dxos/blueprints';
24
+ import { type DXN, Entity, Obj } from '@dxos/echo';
25
+ import { Database } from '@dxos/echo';
26
+ import { TracingService, defineFunction } from '@dxos/functions';
27
+ import { FunctionInvocationServiceLayerTestMocked } from '@dxos/functions-runtime/testing';
28
+ import { type Message, Person } from '@dxos/types';
23
29
  import { trim } from '@dxos/util';
24
30
 
31
+ import { LocalSearchHandler, LocalSearchToolkit, makeGraphWriterHandler, makeGraphWriterToolkit } from '../../crud';
25
32
  import { exaFunction, exaMockFunction } from '../exa';
26
33
 
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
34
  import { contextQueueLayerFromResearchGraph } from './research-graph';
35
+ import PROMPT from './research-instructions.tpl?raw';
31
36
  import { ResearchDataTypes } from './types';
32
37
 
33
38
  /**
@@ -37,22 +42,21 @@ export default defineFunction({
37
42
  key: 'dxos.org/function/research',
38
43
  name: 'Research',
39
44
  description: trim`
40
- Research the web for information.
45
+ Search the web to research information about the given subject.
41
46
  Inserts structured data into the research graph.
42
- Will return research summary and the objects created.
47
+ Creates a research summary and returns the objects created.
43
48
  `,
44
49
  inputSchema: Schema.Struct({
45
50
  query: Schema.String.annotations({
46
51
  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.
52
+ The search query.
53
+ If doing research on an object then load it first and pass it as JSON.
49
54
  `,
50
55
  }),
51
56
 
52
- researchInstructions: Schema.optional(Schema.String).annotations({
57
+ instructions: Schema.optional(Schema.String).annotations({
53
58
  description: trim`
54
59
  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
60
  `,
57
61
  }),
58
62
 
@@ -61,20 +65,28 @@ export default defineFunction({
61
65
  description: 'Whether to use the mock search tool.',
62
66
  default: false,
63
67
  }),
68
+
69
+ entityExtraction: Schema.optional(Schema.Boolean).annotations({
70
+ description: trim`
71
+ Whether to extract structured entities from the research.
72
+ Experimental feature only enable if user explicitly requests it.
73
+ `,
74
+ default: false,
75
+ }),
64
76
  }),
65
77
  outputSchema: Schema.Struct({
66
- note: Schema.optional(Schema.String).annotations({
67
- description: 'A note from the research agent.',
78
+ document: Schema.optional(Schema.String).annotations({
79
+ description: 'The generated research document.',
68
80
  }),
69
81
  objects: Schema.Array(Schema.Unknown).annotations({
70
- description: 'The structured objects created as a result of the research.',
82
+ description: 'Structured objects created during the research process.',
71
83
  }),
72
84
  }),
73
85
  handler: Effect.fnUntraced(
74
- function* ({ data: { query, mockSearch, researchInstructions } }) {
86
+ function* ({ data: { query, instructions, mockSearch = false, entityExtraction = false } }) {
75
87
  if (mockSearch) {
76
- const mockPerson = yield* DatabaseService.add(
77
- Obj.make(DataType.Person, {
88
+ const mockPerson = yield* Database.Service.add(
89
+ Obj.make(Person.Person, {
78
90
  preferredName: 'John Doe',
79
91
  emails: [{ value: 'john.doe@example.com' }],
80
92
  phoneNumbers: [{ value: '123-456-7890' }],
@@ -82,59 +94,54 @@ export default defineFunction({
82
94
  );
83
95
 
84
96
  return {
85
- note: trim`
86
- The research run in test-mode and was mocked.
87
- Proceed as usual.
97
+ document: trim`
98
+ The research ran in test-mode and was mocked. Proceed as usual.
88
99
  We reference John Doe to test reference: ${Obj.getDXN(mockPerson)}
89
100
  `,
90
101
  objects: [Obj.toJSON(mockPerson)],
91
102
  };
92
103
  }
93
104
 
94
- yield* DatabaseService.flush({ indexes: true });
95
- yield* TracingService.emitStatus({ message: 'Researching...' });
105
+ yield* Database.Service.flush({ indexes: true });
106
+ yield* TracingService.emitStatus({ message: 'Starting research...' });
96
107
 
97
- const objectDXNs: DXN[] = [];
98
- const GraphWriterToolkit = makeGraphWriterToolkit({ schema: ResearchDataTypes });
99
- const GraphWriterHandler = makeGraphWriterHandler(GraphWriterToolkit, {
100
- onAppend: (dxns) => objectDXNs.push(...dxns),
101
- });
102
108
  const NativeWebSearch = Toolkit.make(AnthropicTool.WebSearch_20250305({}));
103
109
 
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
- );
110
+ let toolkit: Toolkit.Any = NativeWebSearch;
111
+ let handlers: Layer.Layer<any, any> = Layer.empty as any;
112
+
113
+ const objectDXNs: DXN[] = [];
114
+ if (entityExtraction) {
115
+ const GraphWriterToolkit = makeGraphWriterToolkit({ schema: ResearchDataTypes });
116
+ const GraphWriterHandler = makeGraphWriterHandler(GraphWriterToolkit, {
117
+ onAppend: (dxns) => objectDXNs.push(...dxns),
118
+ });
119
+
120
+ toolkit = Toolkit.merge(toolkit, LocalSearchToolkit, GraphWriterToolkit);
121
+ handlers = Layer.mergeAll(handlers, LocalSearchHandler, GraphWriterHandler).pipe(
122
+ Layer.provide(contextQueueLayerFromResearchGraph),
123
+ ) as any;
124
+ }
125
+
126
+ const finishedToolkit = yield* createToolkit({ toolkit }).pipe(Effect.provide(handlers));
116
127
 
117
128
  const session = new AiSession();
118
129
  const result = yield* session.run({
119
130
  prompt: query,
120
- system:
121
- PROMPT +
122
- (researchInstructions
123
- ? '\n\n' + `<research_instructions>${researchInstructions}</research_instructions>`
124
- : ''),
125
- toolkit,
131
+ system: join(
132
+ Template.process(PROMPT, { entityExtraction }),
133
+ instructions && `<instructions>${instructions}</instructions>`,
134
+ ),
135
+ toolkit: finishedToolkit,
126
136
  observer: GenerationObserver.fromPrinter(new ConsolePrinter({ tag: 'research' })),
127
137
  });
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))),
138
+
139
+ const objects = yield* Effect.forEach(objectDXNs, (dxn) => Database.Service.resolve(dxn)).pipe(
140
+ Effect.map(Array.map((obj) => Entity.toJSON(obj))),
134
141
  );
135
142
 
136
143
  return {
137
- note,
144
+ document: extractLastTextBlock(result),
138
145
  objects,
139
146
  };
140
147
  },
@@ -147,9 +154,36 @@ export default defineFunction({
147
154
  ).pipe(
148
155
  Layer.provide(
149
156
  // TODO(dmaretskyi): This should be provided by environment.
150
- Layer.mergeAll(FunctionInvocationService.layerTestMocked({ functions: [exaFunction, exaMockFunction] })),
157
+ Layer.mergeAll(FunctionInvocationServiceLayerTestMocked({ functions: [exaFunction, exaMockFunction] })),
151
158
  ),
152
159
  ),
153
160
  ),
154
161
  ),
155
162
  });
163
+
164
+ // TODO(burdon): Factor out.
165
+ const join = (...strings: (string | undefined)[]) => strings.filter(Boolean).join('\n\n');
166
+
167
+ /**
168
+ * Extracts the last text block from the result.
169
+ * Skips citations.
170
+ */
171
+ // TODO(burdon): Factor out.
172
+ const extractLastTextBlock = (result: Message.Message[]) => {
173
+ return Function.pipe(
174
+ result,
175
+ Array.last,
176
+ Option.map(
177
+ Function.flow(
178
+ (_: Message.Message) => _.blocks,
179
+ Array.reverse,
180
+ Array.dropWhile((_: any) => _._tag === 'summary'),
181
+ Array.takeWhile((_: any) => _._tag === 'text'),
182
+ Array.reverse,
183
+ Array.map((_: any) => _.text),
184
+ Array.reduce('', String.concat),
185
+ ),
186
+ ),
187
+ Option.getOrElse(() => ''),
188
+ );
189
+ };
@@ -2,23 +2,25 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { DataType } from '@dxos/schema';
5
+ import { type Type } from '@dxos/echo';
6
+ import { Text } from '@dxos/schema';
7
+ import { Event, HasConnection, HasRelationship, LegacyOrganization, LegacyPerson, Project, Task } from '@dxos/types';
6
8
 
7
9
  /**
8
10
  * Data types for research.
9
11
  */
10
- export const ResearchDataTypes = [
12
+ export const ResearchDataTypes: Type.Entity.Any[] = [
11
13
  // Objects
12
- DataType.Event,
13
- DataType.LegacyOrganization,
14
- DataType.LegacyPerson,
15
- DataType.Project,
16
- DataType.Task,
17
- DataType.Text,
14
+ Event.Event,
15
+ LegacyOrganization,
16
+ Task.Task,
17
+ Text.Text,
18
18
 
19
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,
20
+ // TODO(wittjosiah): Until views (e.g., Table) support relations this needs to be expressed via organization ref.
21
+ // Employer.Employer,
22
+ LegacyPerson,
23
+ Project.Project,
24
+ HasRelationship.HasRelationship,
25
+ HasConnection.HasConnection,
24
26
  ];
@@ -6,7 +6,8 @@ import * as Effect from 'effect/Effect';
6
6
  import * as Schema from 'effect/Schema';
7
7
 
8
8
  import { ArtifactId } from '@dxos/assistant';
9
- import { DatabaseService, defineFunction } from '@dxos/functions';
9
+ import { Database } from '@dxos/echo';
10
+ import { defineFunction } from '@dxos/functions';
10
11
  import { Markdown } from '@dxos/plugin-markdown/types';
11
12
 
12
13
  export default defineFunction({
@@ -22,10 +23,10 @@ export default defineFunction({
22
23
  content: Schema.String,
23
24
  }),
24
25
  handler: Effect.fn(function* ({ data: { id } }) {
25
- const doc = yield* DatabaseService.resolve(ArtifactId.toDXN(id), Markdown.Document);
26
+ const doc = yield* Database.Service.resolve(ArtifactId.toDXN(id), Markdown.Document);
26
27
 
27
28
  // Return content with line numbers prefixed.
28
- const { content } = yield* DatabaseService.load(doc.content);
29
+ const { content } = yield* Database.Service.load(doc.content);
29
30
  const lines = content.split('\n');
30
31
  const len = String(lines.length).length;
31
32
  const numbered = lines.map((line, i) => `${String(i + 1).padStart(len, ' ')}. ${line}`).join('\n');
@@ -6,7 +6,8 @@ import * as Effect from 'effect/Effect';
6
6
  import * as Schema from 'effect/Schema';
7
7
 
8
8
  import { ArtifactId } from '@dxos/assistant';
9
- import { DatabaseService, defineFunction } from '@dxos/functions';
9
+ import { Database } from '@dxos/echo';
10
+ import { defineFunction } from '@dxos/functions';
10
11
  import { Markdown } from '@dxos/plugin-markdown/types';
11
12
 
12
13
  import { MarkdownTasks, type TaskOperation } from './task-list';
@@ -34,10 +35,10 @@ export default defineFunction({
34
35
  }),
35
36
  }),
36
37
  handler: Effect.fn(function* ({ data: { id, operations = [] } }) {
37
- const doc = yield* DatabaseService.resolve(ArtifactId.toDXN(id), Markdown.Document);
38
+ const doc = yield* Database.Service.resolve(ArtifactId.toDXN(id), Markdown.Document);
38
39
 
39
40
  // Create task manager and apply operations if provided.
40
- const { content } = yield* DatabaseService.load(doc.content);
41
+ const { content } = yield* Database.Service.load(doc.content);
41
42
  const taskManager = new MarkdownTasks(content);
42
43
  if (operations.length > 0) {
43
44
  taskManager.applyOperations(operations as TaskOperation[]);
package/src/index.ts CHANGED
@@ -4,5 +4,6 @@
4
4
 
5
5
  export * from './blueprints';
6
6
  export * from './functions';
7
- export * from './plugins';
8
7
  export * from './sync';
8
+ export * from './crud';
9
+ export * from './toolkits';