@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
@@ -6,22 +6,17 @@ import { describe, expect, it } from '@effect/vitest';
6
6
  import * as Effect from 'effect/Effect';
7
7
  import * as Layer from 'effect/Layer';
8
8
 
9
- import { AiService, MemoizedAiService } from '@dxos/ai';
10
- import { AiServiceTestingPreset } from '@dxos/ai/testing';
9
+ import { AiService } from '@dxos/ai';
10
+ import { MemoizedAiService, TestAiService } from '@dxos/ai/testing';
11
11
  import { makeToolExecutionServiceFromFunctions, makeToolResolverFromFunctions } from '@dxos/assistant';
12
12
  import { Blueprint } from '@dxos/blueprints';
13
13
  import { Obj } from '@dxos/echo';
14
- import { TestHelpers } from '@dxos/effect';
15
- import {
16
- ComputeEventLogger,
17
- CredentialsService,
18
- DatabaseService,
19
- FunctionInvocationService,
20
- TracingService,
21
- } from '@dxos/functions';
22
- import { TestDatabaseLayer } from '@dxos/functions/testing';
23
- import { ObjectId, PublicKey } from '@dxos/keys';
24
- import { DataType } from '@dxos/schema';
14
+ import { Database } from '@dxos/echo';
15
+ import { TestHelpers } from '@dxos/effect/testing';
16
+ import { CredentialsService, FunctionInvocationService, TracingService } from '@dxos/functions';
17
+ import { FunctionInvocationServiceLayerTest, TestDatabaseLayer } from '@dxos/functions-runtime/testing';
18
+ import { ObjectId } from '@dxos/keys';
19
+ import { Message, Organization, Person } from '@dxos/types';
25
20
 
26
21
  import { testToolkit } from '../../blueprints/testing';
27
22
  import { ResearchGraph } from '../research';
@@ -34,17 +29,15 @@ const TestLayer = Layer.mergeAll(
34
29
  AiService.model('@anthropic/claude-opus-4-0'),
35
30
  makeToolResolverFromFunctions([], testToolkit),
36
31
  makeToolExecutionServiceFromFunctions(testToolkit, testToolkit.toLayer({}) as any),
37
- ComputeEventLogger.layerFromTracing,
38
32
  ).pipe(
39
- Layer.provideMerge(FunctionInvocationService.layerTest({ functions: [entityExtraction] })),
33
+ Layer.provideMerge(FunctionInvocationServiceLayerTest({ functions: [entityExtraction] })),
40
34
  Layer.provideMerge(
41
35
  Layer.mergeAll(
42
- MemoizedAiService.layerTest().pipe(Layer.provide(AiServiceTestingPreset('direct'))),
36
+ TestAiService(),
43
37
  TestDatabaseLayer({
44
- // TODO(dmaretskyi): define the constant space key in the test-builder.
45
- spaceKey: PublicKey.from('665c420e0dec9aa36c2bedca567afb0778701920e346eaf83ab2bd3403859723'),
38
+ spaceKey: 'fixed',
46
39
  indexing: { vector: true },
47
- types: [Blueprint.Blueprint, DataType.Message, DataType.Person, DataType.Organization, ResearchGraph],
40
+ types: [Blueprint.Blueprint, Message.Message, Person.Person, Organization.Organization, ResearchGraph],
48
41
  }),
49
42
  CredentialsService.configuredLayer([]),
50
43
  TracingService.layerNoop,
@@ -52,13 +45,13 @@ const TestLayer = Layer.mergeAll(
52
45
  ),
53
46
  );
54
47
 
55
- describe.skip('Entity extraction', () => {
48
+ describe('Entity extraction', () => {
56
49
  it.effect(
57
50
  'call a function to generate a research report',
58
51
  Effect.fnUntraced(
59
52
  function* (_) {
60
- const email = yield* DatabaseService.add(
61
- Obj.make(DataType.Message, {
53
+ const email = yield* Database.Service.add(
54
+ Obj.make(Message.Message, {
62
55
  [Obj.Meta]: {
63
56
  tags: ['important'],
64
57
  },
@@ -83,7 +76,7 @@ describe.skip('Entity extraction', () => {
83
76
  ],
84
77
  }),
85
78
  );
86
- yield* DatabaseService.flush({ indexes: true });
79
+ yield* Database.Service.flush({ indexes: true });
87
80
  const result = yield* FunctionInvocationService.invokeFunction(entityExtraction, {
88
81
  source: email,
89
82
  });
@@ -10,40 +10,50 @@ import * as Schema from 'effect/Schema';
10
10
 
11
11
  import { AiService } from '@dxos/ai';
12
12
  import { AiSession, makeToolExecutionServiceFromFunctions, makeToolResolverFromFunctions } from '@dxos/assistant';
13
- import { Filter, Obj, Ref } from '@dxos/echo';
14
- import { DatabaseService, FunctionInvocationService, defineFunction } from '@dxos/functions';
13
+ import { Filter, Obj, Ref, Type } from '@dxos/echo';
14
+ import { Database } from '@dxos/echo';
15
+ import { defineFunction } from '@dxos/functions';
16
+ import { FunctionInvocationServiceLayerTest } from '@dxos/functions-runtime/testing';
15
17
  import { type DXN } from '@dxos/keys';
16
18
  import { log } from '@dxos/log';
17
- import { DataType } from '@dxos/schema';
19
+ import { type Actor, LegacyOrganization, Message, Organization, Person } from '@dxos/types';
18
20
  import { trim } from '@dxos/util';
19
21
 
20
- import { contextQueueLayerFromResearchGraph, makeGraphWriterHandler, makeGraphWriterToolkit } from '../research';
22
+ import { makeGraphWriterHandler, makeGraphWriterToolkit } from '../../crud';
23
+ import { contextQueueLayerFromResearchGraph } from '../research';
21
24
 
22
25
  export default defineFunction({
23
26
  key: 'dxos.org/functions/entity-extraction',
24
27
  name: 'Entity Extraction',
25
28
  description: 'Extracts entities from emails and transcripts.',
26
29
  inputSchema: Schema.Struct({
27
- source: DataType.Message.annotations({ description: 'Email or transcript to extract entities from.' }),
30
+ source: Message.Message.annotations({
31
+ description: 'Email or transcript to extract entities from.',
32
+ }),
28
33
 
29
34
  // TODO(dmaretskyi): Consider making this an array of blueprints instead.
30
- instructions: Schema.optional(Schema.String).annotations({ description: 'Instructions extraction process.' }),
35
+ instructions: Schema.optional(Schema.String).annotations({
36
+ description: 'Instructions extraction process.',
37
+ }),
31
38
  }),
32
39
  outputSchema: Schema.Struct({
33
40
  entities: Schema.optional(
34
- Schema.Array(Obj.Any).annotations({
41
+ Schema.Array(Type.Obj).annotations({
35
42
  description: 'Extracted entities.',
36
43
  }),
37
44
  ),
38
45
  }),
39
46
  handler: Effect.fnUntraced(
40
- function* ({ data: { source, instructions } }) {
41
- const contact = yield* extractContact(source);
42
- let organization: DataType.Organization | null = null;
47
+ function* ({ data: { source: message, instructions } }) {
48
+ const tags = Obj.getMeta(message)?.tags;
49
+ const contact = yield* extractContact(message.sender, tags);
50
+ let organization: Organization.Organization | null = null;
43
51
 
44
52
  if (contact && !contact.organization) {
45
53
  const created: DXN[] = [];
46
- const GraphWriterToolkit = makeGraphWriterToolkit({ schema: [DataType.LegacyOrganization] }).pipe();
54
+ const GraphWriterToolkit = makeGraphWriterToolkit({
55
+ schema: [LegacyOrganization],
56
+ }).pipe();
47
57
  const GraphWriterHandler = makeGraphWriterHandler(GraphWriterToolkit, {
48
58
  onAppend: (dxns) => created.push(...dxns),
49
59
  });
@@ -57,17 +67,22 @@ export default defineFunction({
57
67
  The extracted organization URL must match the sender's email domain.
58
68
  ${instructions ? '<user_intructions>' + instructions + '</user_intructions>' : ''},
59
69
  `,
60
- prompt: JSON.stringify({ source, contact }),
70
+ prompt: JSON.stringify({ source: message, contact }),
61
71
  toolkit,
62
72
  });
63
73
 
64
74
  if (created.length > 1) {
65
75
  throw new Error('Multiple organizations created');
66
76
  } else if (created.length === 1) {
67
- organization = yield* DatabaseService.resolve(created[0], DataType.Organization);
68
- Obj.getMeta(organization).tags ??= [];
69
- Obj.getMeta(organization).tags!.push(...(Obj.getMeta(source)?.tags ?? []));
70
- contact.organization = Ref.make(organization);
77
+ organization = yield* Database.Service.resolve(created[0], Organization.Organization);
78
+ Obj.change(organization, (org) => {
79
+ const meta = Obj.getMeta(org);
80
+ meta.tags ??= [];
81
+ meta.tags.push(...(tags ?? []));
82
+ });
83
+ Obj.change(contact, (c) => {
84
+ c.organization = Ref.make(organization!);
85
+ });
71
86
  }
72
87
  }
73
88
 
@@ -83,22 +98,22 @@ export default defineFunction({
83
98
  ).pipe(
84
99
  Layer.provide(
85
100
  // TODO(dmaretskyi): This should be provided by environment.
86
- Layer.mergeAll(FunctionInvocationService.layerTest()),
101
+ Layer.mergeAll(FunctionInvocationServiceLayerTest()),
87
102
  ),
88
103
  ),
89
104
  ),
90
105
  ),
91
106
  });
92
107
 
93
- const extractContact = Effect.fn('extractContact')(function* (message: DataType.Message) {
94
- const name = message.sender.name;
95
- const email = message.sender.email;
108
+ const extractContact = Effect.fn('extractContact')(function* (actor: Actor.Actor, tags?: readonly string[]) {
109
+ const name = actor.name;
110
+ const email = actor.email;
96
111
  if (!email) {
97
- log.warn('email is required for contact extraction', { sender: message.sender });
112
+ log.warn('email is required for contact extraction', { actor });
98
113
  return undefined;
99
114
  }
100
115
 
101
- const { objects: existingContacts } = yield* DatabaseService.runQuery(Filter.type(DataType.Person));
116
+ const existingContacts = yield* Database.Service.runQuery(Filter.type(Person.Person));
102
117
 
103
118
  // Check for existing contact
104
119
  // TODO(dmaretskyi): Query filter DSL - https://linear.app/dxos/issue/DX-541/filtercontains-should-work-with-partial-objects
@@ -111,16 +126,16 @@ const extractContact = Effect.fn('extractContact')(function* (message: DataType.
111
126
  return existingContact;
112
127
  }
113
128
 
114
- const newContact = Obj.make(DataType.Person, {
115
- [Obj.Meta]: {
116
- tags: Obj.getMeta(message)?.tags,
117
- },
129
+ const newContact = Obj.make(Person.Person, {
130
+ ...(tags ? { [Obj.Meta]: { tags: [...tags] } } : {}),
118
131
  emails: [{ value: email }],
119
132
  });
120
- yield* DatabaseService.add(newContact);
133
+ yield* Database.Service.add(newContact);
121
134
 
122
135
  if (name) {
123
- newContact.fullName = name;
136
+ Obj.change(newContact, (c) => {
137
+ c.fullName = name;
138
+ });
124
139
  }
125
140
 
126
141
  const emailDomain = email.split('@')[1]?.toLowerCase();
@@ -131,7 +146,7 @@ const extractContact = Effect.fn('extractContact')(function* (message: DataType.
131
146
 
132
147
  log.info('extracted email domain', { emailDomain });
133
148
 
134
- const { objects: existingOrganisations } = yield* DatabaseService.runQuery(Filter.type(DataType.Organization));
149
+ const existingOrganisations = yield* Database.Service.runQuery(Filter.type(Organization.Organization));
135
150
  const matchingOrg = existingOrganisations.find((org) => {
136
151
  if (org.website) {
137
152
  try {
@@ -147,7 +162,10 @@ const extractContact = Effect.fn('extractContact')(function* (message: DataType.
147
162
  emailDomain.endsWith(`.${websiteDomain}`)
148
163
  );
149
164
  } catch (e) {
150
- log.warn('Error parsing website URL', { website: org.website, error: e });
165
+ log.warn('Error parsing website URL', {
166
+ website: org.website,
167
+ error: e,
168
+ });
151
169
  return false;
152
170
  }
153
171
  }
@@ -156,7 +174,9 @@ const extractContact = Effect.fn('extractContact')(function* (message: DataType.
156
174
 
157
175
  if (matchingOrg) {
158
176
  log.info('found matching organization', { organization: matchingOrg });
159
- newContact.organization = Ref.make(matchingOrg);
177
+ Obj.change(newContact, (c) => {
178
+ c.organization = Ref.make(matchingOrg);
179
+ });
160
180
  }
161
181
 
162
182
  return newContact;
@@ -7,7 +7,7 @@ import * as Schema from 'effect/Schema';
7
7
 
8
8
  import { defineFunction } from '@dxos/functions';
9
9
 
10
- import { SEARCH_RESULTS } from '../../testing';
10
+ import { SEARCH_RESULTS } from './data';
11
11
 
12
12
  export default defineFunction({
13
13
  key: 'dxos.org/function/exa-mock',
@@ -6,7 +6,7 @@ import * as HttpClient from '@effect/platform/HttpClient';
6
6
  import * as Effect from 'effect/Effect';
7
7
  import * as Schema from 'effect/Schema';
8
8
 
9
- import { defineFunction, withAuthorization } from '@dxos/functions';
9
+ import { CredentialsService, defineFunction, withAuthorization } from '@dxos/functions';
10
10
 
11
11
  export default defineFunction({
12
12
  key: 'dxos.org/function/github/fetch-prs',
@@ -21,7 +21,8 @@ export default defineFunction({
21
21
  }),
22
22
  }),
23
23
  handler: Effect.fnUntraced(function* ({ data }) {
24
- const client = yield* HttpClient.HttpClient.pipe(Effect.map(withAuthorization({ service: 'github.com' })));
24
+ const credential = yield* CredentialsService.getCredential({ service: 'github.com' });
25
+ const client = yield* HttpClient.HttpClient.pipe(Effect.map(withAuthorization(credential.apiKey!)));
25
26
 
26
27
  const response = yield* client.get(`https://api.github.com/repos/${data.owner}/${data.repo}/pulls`);
27
28
  const json: any = yield* response.json;
@@ -13,16 +13,16 @@ import { AiService } from '@dxos/ai';
13
13
  import { AiServiceTestingPreset } from '@dxos/ai/testing';
14
14
  import { makeToolExecutionServiceFromFunctions, makeToolResolverFromFunctions } from '@dxos/assistant';
15
15
  import { Obj, Query } from '@dxos/echo';
16
- import { TestHelpers } from '@dxos/effect';
16
+ import { Database } from '@dxos/echo';
17
+ import { TestHelpers } from '@dxos/effect/testing';
18
+ import { CredentialsService, FunctionInvocationService } from '@dxos/functions';
19
+ import { TracingServiceExt } from '@dxos/functions-runtime';
17
20
  import {
18
- ComputeEventLogger,
19
- CredentialsService,
20
- DatabaseService,
21
- FunctionInvocationService,
22
- TracingService,
23
- } from '@dxos/functions';
24
- import { TestDatabaseLayer, testStoragePath } from '@dxos/functions/testing';
25
- import { DataType } from '@dxos/schema';
21
+ FunctionInvocationServiceLayerTestMocked,
22
+ TestDatabaseLayer,
23
+ testStoragePath,
24
+ } from '@dxos/functions-runtime/testing';
25
+ import { Person, Project, Task } from '@dxos/types';
26
26
 
27
27
  import { LINEAR_ID_KEY, default as fetchLinearIssues } from './sync-issues';
28
28
 
@@ -30,21 +30,19 @@ const TestLayer = Layer.mergeAll(
30
30
  AiService.model('@anthropic/claude-opus-4-0'),
31
31
  makeToolResolverFromFunctions([], Toolkit.make()),
32
32
  makeToolExecutionServiceFromFunctions(Toolkit.make() as any, Layer.empty as any),
33
- ComputeEventLogger.layerFromTracing,
34
33
  ).pipe(
35
34
  Layer.provideMerge(
36
35
  Layer.mergeAll(
37
36
  AiServiceTestingPreset('direct'),
38
37
  TestDatabaseLayer({
39
38
  // indexing: { vector: true },
40
- types: [DataType.Task, DataType.Person, DataType.Project],
39
+ types: [Task.Task, Person.Person, Project.Project],
41
40
  storagePath: testStoragePath({ name: 'feed-test-13' }),
42
41
  }),
43
42
  CredentialsService.layerConfig([{ service: 'linear.app', apiKey: Config.redacted('LINEAR_API_KEY') }]),
44
- FunctionInvocationService.layerTestMocked({ functions: [fetchLinearIssues] }).pipe(
45
- Layer.provideMerge(ComputeEventLogger.layerFromTracing),
46
- Layer.provideMerge(TracingService.layerLogInfo()),
47
- ),
43
+ FunctionInvocationServiceLayerTestMocked({
44
+ functions: [fetchLinearIssues],
45
+ }).pipe(Layer.provideMerge(TracingServiceExt.layerLogInfo())),
48
46
  FetchHttpClient.layer,
49
47
  ),
50
48
  ),
@@ -55,29 +53,29 @@ describe('Linear', { timeout: 600_000 }, () => {
55
53
  'sync',
56
54
  Effect.fnUntraced(
57
55
  function* (_) {
58
- yield* DatabaseService.flush({ indexes: true });
56
+ yield* Database.Service.flush({ indexes: true });
59
57
 
60
58
  yield* FunctionInvocationService.invokeFunction(fetchLinearIssues, {
61
59
  team: '1127c63a-6f77-4725-9229-50f6cd47321c',
62
60
  });
63
61
 
64
- const { objects: persons } = yield* DatabaseService.runQuery(Query.type(DataType.Person));
62
+ const persons = yield* Database.Service.runQuery(Query.type(Person.Person));
65
63
  console.log('people', {
66
64
  count: persons.length,
67
65
  people: persons.map((_) => `(${_.id}) ${Obj.getLabel(_)} [${Obj.getKeys(_, LINEAR_ID_KEY)[0]?.id}]`),
68
66
  });
69
- const { objects: projects } = yield* DatabaseService.runQuery(Query.type(DataType.Project));
67
+ const projects = yield* Database.Service.runQuery(Query.type(Project.Project));
70
68
  console.log('projects', {
71
69
  count: projects.length,
72
70
  projects: projects.map((_) => `(${_.id}) ${Obj.getLabel(_)} [${Obj.getKeys(_, LINEAR_ID_KEY)[0]?.id}]`),
73
71
  });
74
- const { objects: tasks } = yield* DatabaseService.runQuery(Query.type(DataType.Task));
72
+ const tasks = yield* Database.Service.runQuery(Query.type(Task.Task));
75
73
  console.log('tasks', {
76
74
  count: tasks.length,
77
75
  tasks: tasks.map((_) => `(${_.id}) ${Obj.getLabel(_)} [${Obj.getKeys(_, LINEAR_ID_KEY)[0]?.id}]`),
78
76
  });
79
77
 
80
- yield* DatabaseService.flush({ indexes: true });
78
+ yield* Database.Service.flush({ indexes: true });
81
79
  },
82
80
  Effect.provide(TestLayer),
83
81
  TestHelpers.taggedTest('sync'),
@@ -10,9 +10,10 @@ import * as Function from 'effect/Function';
10
10
  import * as Schema from 'effect/Schema';
11
11
 
12
12
  import { Filter, Obj, Query, Ref, type Type } from '@dxos/echo';
13
- import { DatabaseService, defineFunction, withAuthorization } from '@dxos/functions';
13
+ import { Database } from '@dxos/echo';
14
+ import { CredentialsService, defineFunction, withAuthorization } from '@dxos/functions';
14
15
  import { log } from '@dxos/log';
15
- import { DataType } from '@dxos/schema';
16
+ import { Person, Project, Task } from '@dxos/types';
16
17
 
17
18
  import { syncObjects } from '../../sync';
18
19
  import { graphqlRequestBody } from '../../util';
@@ -35,7 +36,7 @@ query Issues($teamId: String!, $after: DateTimeOrDuration!) {
35
36
  updatedAt
36
37
  description
37
38
  assignee { id, name }
38
- state {
39
+ state {
39
40
  name
40
41
  }
41
42
  project {
@@ -84,10 +85,11 @@ export default defineFunction({
84
85
  }),
85
86
  }),
86
87
  handler: Effect.fnUntraced(function* ({ data }) {
87
- const client = yield* HttpClient.HttpClient.pipe(Effect.map(withAuthorization({ service: 'linear.app' })));
88
+ const credential = yield* CredentialsService.getCredential({ service: 'linear.app' });
89
+ const client = yield* HttpClient.HttpClient.pipe(Effect.map(withAuthorization(credential.apiKey!)));
88
90
 
89
91
  // Get the timestamp that was previosly synced.
90
- const after = yield* getLatestUpdateTimestamp(data.team, DataType.Task);
92
+ const after = yield* getLatestUpdateTimestamp(data.team, Task.Task);
91
93
  log.info('will fetch', { after });
92
94
 
93
95
  // Fetch the issues that have changed since the last sync.
@@ -114,8 +116,8 @@ export default defineFunction({
114
116
  const getLatestUpdateTimestamp: (
115
117
  teamId: string,
116
118
  dataType: Type.Obj.Any,
117
- ) => Effect.Effect<string, never, DatabaseService> = Effect.fnUntraced(function* (teamId, dataType) {
118
- const { objects: existingTasks } = yield* DatabaseService.runQuery(
119
+ ) => Effect.Effect<string, never, Database.Service> = Effect.fnUntraced(function* (teamId, dataType) {
120
+ const existingTasks = yield* Database.Service.runQuery(
119
121
  Query.type(dataType).select(Filter.foreignKeys(dataType, [{ source: LINEAR_TEAM_ID_KEY, id: teamId }])),
120
122
  );
121
123
  return Function.pipe(
@@ -126,8 +128,8 @@ const getLatestUpdateTimestamp: (
126
128
  );
127
129
  });
128
130
 
129
- const mapLinearPerson = (person: LinearPerson, { teamId }: { teamId: string }): DataType.Person =>
130
- Obj.make(DataType.Person, {
131
+ const mapLinearPerson = (person: LinearPerson, { teamId }: { teamId: string }): Person.Person =>
132
+ Obj.make(Person.Person, {
131
133
  [Obj.Meta]: {
132
134
  keys: [
133
135
  {
@@ -143,8 +145,8 @@ const mapLinearPerson = (person: LinearPerson, { teamId }: { teamId: string }):
143
145
  nickname: person.name,
144
146
  });
145
147
 
146
- const mapLinearIssue = (issue: LinearIssue, { teamId }: { teamId: string }): DataType.Task =>
147
- Obj.make(DataType.Task, {
148
+ const mapLinearIssue = (issue: LinearIssue, { teamId }: { teamId: string }): Task.Task =>
149
+ Obj.make(Task.Task, {
148
150
  [Obj.Meta]: {
149
151
  keys: [
150
152
  {
@@ -170,7 +172,7 @@ const mapLinearIssue = (issue: LinearIssue, { teamId }: { teamId: string }): Dat
170
172
  project: !issue.project
171
173
  ? undefined
172
174
  : Ref.make(
173
- DataType.makeProject({
175
+ Project.make({
174
176
  [Obj.Meta]: {
175
177
  keys: [
176
178
  {
@@ -0,0 +1,76 @@
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 { Obj, Relation } from '@dxos/echo';
10
+ import { Database } from '@dxos/echo';
11
+ import { TracingService, defineFunction } from '@dxos/functions';
12
+ import { log } from '@dxos/log';
13
+ import { Markdown } from '@dxos/plugin-markdown/types';
14
+ import { HasSubject } from '@dxos/types';
15
+ import { trim } from '@dxos/util';
16
+
17
+ export default defineFunction({
18
+ key: 'dxos.org/function/research/document-create',
19
+ name: 'Create research document',
20
+ description: 'Creates a note summarizing the research.',
21
+ inputSchema: Schema.Struct({
22
+ subject: ArtifactId.annotations({
23
+ description: trim`
24
+ ID of the object (organization, contact, etc.) for which the research was performed.
25
+ `,
26
+ }),
27
+ name: Schema.String.annotations({
28
+ description: 'Name of the document.',
29
+ }),
30
+ content: Schema.String.annotations({
31
+ description: trim`
32
+ Content of the note.
33
+ Supports (and are prefered) references to research objects using @ syntax and <object> tags (refer to research blueprint instructions).
34
+ `,
35
+ }),
36
+ }),
37
+ outputSchema: Schema.Struct({
38
+ document: ArtifactId.annotations({
39
+ description: 'DXN of the created document.',
40
+ }),
41
+ }),
42
+ handler: Effect.fnUntraced(function* ({ data: { subject, name, content } }) {
43
+ log.info('Creating research document', { subject, name, content });
44
+
45
+ // TODO(burdon): Auto flush before and after calling function?
46
+ yield* Database.Service.flush({ indexes: true });
47
+ yield* TracingService.emitStatus({ message: 'Creating research document...' });
48
+
49
+ // TODO(burdon): Type check.
50
+ const target = (yield* Database.Service.resolve(ArtifactId.toDXN(subject))) as Obj.Unknown;
51
+
52
+ // Create document.
53
+ const object = yield* Database.Service.add(
54
+ Markdown.make({
55
+ name,
56
+ content,
57
+ }),
58
+ );
59
+
60
+ // Create relation.
61
+ yield* Database.Service.add(
62
+ Relation.make(HasSubject.HasSubject, {
63
+ [Relation.Source]: object,
64
+ [Relation.Target]: target,
65
+ completedAt: new Date().toISOString(),
66
+ }),
67
+ );
68
+
69
+ yield* Database.Service.flush({ indexes: true });
70
+ log.info('Created research document', { subject, object });
71
+
72
+ return {
73
+ document: Obj.getDXN(object).toString(),
74
+ };
75
+ }),
76
+ });
@@ -2,10 +2,9 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { default as create$ } from './create-document';
5
+ import { default as create$ } from './document-create';
6
6
  import { default as research$ } from './research';
7
7
 
8
- export * from './graph';
9
8
  export * from './research-graph';
10
9
  export * from './types';
11
10
 
@@ -7,8 +7,10 @@ import * as Layer from 'effect/Layer';
7
7
  import * as Schema from 'effect/Schema';
8
8
 
9
9
  import { Obj, Query, Ref, Type } from '@dxos/echo';
10
+ import { Database } from '@dxos/echo';
11
+ import { SystemTypeAnnotation } from '@dxos/echo/internal';
10
12
  import { Queue } from '@dxos/echo-db';
11
- import { ContextQueueService, DatabaseService, QueueService } from '@dxos/functions';
13
+ import { ContextQueueService, QueueService } from '@dxos/functions';
12
14
 
13
15
  /**
14
16
  * Container for a set of ephemeral research results.
@@ -16,32 +18,32 @@ import { ContextQueueService, DatabaseService, QueueService } from '@dxos/functi
16
18
  export const ResearchGraph = Schema.Struct({
17
19
  queue: Type.Ref(Queue),
18
20
  }).pipe(
19
- Type.Obj({
21
+ Type.object({
20
22
  typename: 'dxos.org/type/ResearchGraph',
21
23
  version: '0.1.0',
22
24
  }),
25
+ SystemTypeAnnotation.set(true),
23
26
  );
24
27
 
25
28
  export interface ResearchGraph extends Schema.Schema.Type<typeof ResearchGraph> {}
26
29
 
27
- export const queryResearchGraph: () => Effect.Effect<ResearchGraph | undefined, never, DatabaseService> = Effect.fn(
30
+ export const queryResearchGraph: () => Effect.Effect<ResearchGraph | undefined, never, Database.Service> = Effect.fn(
28
31
  'queryResearchGraph',
29
32
  )(function* () {
30
- const { objects } = yield* DatabaseService.runQuery(Query.type(ResearchGraph));
33
+ const objects = yield* Database.Service.runQuery(Query.type(ResearchGraph));
31
34
  return objects.at(0);
32
35
  });
33
36
 
34
- export const createResearchGraph: () => Effect.Effect<ResearchGraph, never, DatabaseService | QueueService> = Effect.fn(
35
- 'createResearchGraph',
36
- )(function* () {
37
- const queue = yield* QueueService.createQueue();
38
- return yield* DatabaseService.add(Obj.make(ResearchGraph, { queue: Ref.fromDXN(queue.dxn) }));
39
- });
37
+ export const createResearchGraph: () => Effect.Effect<ResearchGraph, never, Database.Service | QueueService> =
38
+ Effect.fn('createResearchGraph')(function* () {
39
+ const queue = yield* QueueService.createQueue();
40
+ return yield* Database.Service.add(Obj.make(ResearchGraph, { queue: Ref.fromDXN(queue.dxn) }));
41
+ });
40
42
 
41
43
  export const contextQueueLayerFromResearchGraph = Layer.unwrapEffect(
42
44
  Effect.gen(function* () {
43
45
  const researchGraph = (yield* queryResearchGraph()) ?? (yield* createResearchGraph());
44
- const researchQueue = yield* DatabaseService.load(researchGraph.queue);
46
+ const researchQueue = yield* Database.Service.load(researchGraph.queue);
45
47
  return ContextQueueService.layer(researchQueue);
46
48
  }),
47
49
  );