@dxos/assistant 0.8.4-main.937b3ca → 0.8.4-main.9be5663bfe

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 (124) hide show
  1. package/README.md +1 -0
  2. package/dist/lib/neutral/chunk-UAWIXKF6.mjs +1875 -0
  3. package/dist/lib/neutral/chunk-UAWIXKF6.mjs.map +7 -0
  4. package/dist/lib/{browser → neutral}/extraction/index.mjs +26 -32
  5. package/dist/lib/neutral/extraction/index.mjs.map +7 -0
  6. package/dist/lib/{browser → neutral}/index.mjs +73 -372
  7. package/dist/lib/neutral/index.mjs.map +7 -0
  8. package/dist/lib/neutral/meta.json +1 -0
  9. package/dist/lib/neutral/testing/index.mjs +114 -0
  10. package/dist/lib/neutral/testing/index.mjs.map +7 -0
  11. package/dist/types/src/conversation/context.d.ts +46 -20
  12. package/dist/types/src/conversation/context.d.ts.map +1 -1
  13. package/dist/types/src/conversation/conversation.d.ts +37 -10
  14. package/dist/types/src/conversation/conversation.d.ts.map +1 -1
  15. package/dist/types/src/diff.d.ts +3 -1
  16. package/dist/types/src/diff.d.ts.map +1 -1
  17. package/dist/types/src/extraction/extraction-ner-function/index.d.ts +0 -1
  18. package/dist/types/src/extraction/extraction-ner-function/index.d.ts.map +1 -1
  19. package/dist/types/src/extraction/extraction.d.ts +933 -221
  20. package/dist/types/src/extraction/extraction.d.ts.map +1 -1
  21. package/dist/types/src/extraction/index.d.ts +0 -1
  22. package/dist/types/src/extraction/index.d.ts.map +1 -1
  23. package/dist/types/src/functions/services.d.ts +27 -8
  24. package/dist/types/src/functions/services.d.ts.map +1 -1
  25. package/dist/types/src/index.d.ts +2 -0
  26. package/dist/types/src/index.d.ts.map +1 -1
  27. package/dist/types/src/service/AgentService.d.ts +61 -0
  28. package/dist/types/src/service/AgentService.d.ts.map +1 -0
  29. package/dist/types/src/service/AgentService.test.d.ts +2 -0
  30. package/dist/types/src/service/AgentService.test.d.ts.map +1 -0
  31. package/dist/types/src/service/agent-process.d.ts +17 -0
  32. package/dist/types/src/service/agent-process.d.ts.map +1 -0
  33. package/dist/types/src/service/index.d.ts +3 -0
  34. package/dist/types/src/service/index.d.ts.map +1 -0
  35. package/dist/types/src/session/format.d.ts.map +1 -1
  36. package/dist/types/src/session/index.d.ts +1 -1
  37. package/dist/types/src/session/index.d.ts.map +1 -1
  38. package/dist/types/src/session/observer.d.ts.map +1 -1
  39. package/dist/types/src/session/session.d.ts +64 -8
  40. package/dist/types/src/session/session.d.ts.map +1 -1
  41. package/dist/types/src/session/toolkit.d.ts +6 -2
  42. package/dist/types/src/session/toolkit.d.ts.map +1 -1
  43. package/dist/types/src/templates/system.d.ts +0 -1
  44. package/dist/types/src/templates/system.d.ts.map +1 -1
  45. package/dist/types/src/testing/layer.d.ts +23 -9
  46. package/dist/types/src/testing/layer.d.ts.map +1 -1
  47. package/dist/types/src/tracing.d.ts +264 -0
  48. package/dist/types/src/tracing.d.ts.map +1 -0
  49. package/dist/types/src/types/artifact.d.ts +5 -5
  50. package/dist/types/src/types/artifact.d.ts.map +1 -1
  51. package/dist/types/tsconfig.tsbuildinfo +1 -1
  52. package/package.json +33 -33
  53. package/src/conversation/context.test.ts +19 -13
  54. package/src/conversation/context.ts +150 -50
  55. package/src/conversation/conversation.test.ts +25 -32
  56. package/src/conversation/conversation.ts +197 -47
  57. package/src/diff.test.ts +36 -5
  58. package/src/diff.ts +13 -4
  59. package/src/execution-graph.test.ts +2 -2
  60. package/src/execution-graph.ts +3 -3
  61. package/src/extraction/extraction-ner-function/index.ts +0 -1
  62. package/src/extraction/extraction.ts +38 -10
  63. package/src/extraction/index.ts +0 -1
  64. package/src/functions/services.ts +199 -50
  65. package/src/functions.conversations.json +1 -1
  66. package/src/functions.test.ts +28 -20
  67. package/src/index.ts +2 -0
  68. package/src/service/AgentService.conversations.json +1 -0
  69. package/src/service/AgentService.test.ts +169 -0
  70. package/src/service/AgentService.ts +165 -0
  71. package/src/service/agent-process.ts +359 -0
  72. package/src/service/index.ts +6 -0
  73. package/src/session/format.test.ts +23 -15
  74. package/src/session/format.ts +0 -1
  75. package/src/session/index.ts +1 -2
  76. package/src/session/observer.ts +3 -2
  77. package/src/session/session.conversations.json +1 -1
  78. package/src/session/session.test.ts +52 -5
  79. package/src/session/session.ts +232 -117
  80. package/src/session/toolkit.ts +12 -7
  81. package/src/templates/instructions/format.tpl +0 -4
  82. package/src/templates/instructions/system.tpl +39 -2
  83. package/src/templates/system.ts +6 -5
  84. package/src/testing/layer.ts +205 -24
  85. package/src/tracing.ts +113 -0
  86. package/src/types/artifact.ts +17 -10
  87. package/dist/lib/browser/chunk-7CDDG6LL.mjs +0 -578
  88. package/dist/lib/browser/chunk-7CDDG6LL.mjs.map +0 -7
  89. package/dist/lib/browser/extraction/index.mjs.map +0 -7
  90. package/dist/lib/browser/index.mjs.map +0 -7
  91. package/dist/lib/browser/meta.json +0 -1
  92. package/dist/lib/browser/testing/index.mjs +0 -32
  93. package/dist/lib/browser/testing/index.mjs.map +0 -7
  94. package/dist/lib/node-esm/chunk-CC7ER6Q3.mjs +0 -579
  95. package/dist/lib/node-esm/chunk-CC7ER6Q3.mjs.map +0 -7
  96. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
  97. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +0 -7
  98. package/dist/lib/node-esm/extraction/index.mjs +0 -226
  99. package/dist/lib/node-esm/extraction/index.mjs.map +0 -7
  100. package/dist/lib/node-esm/index.mjs +0 -851
  101. package/dist/lib/node-esm/index.mjs.map +0 -7
  102. package/dist/lib/node-esm/meta.json +0 -1
  103. package/dist/lib/node-esm/testing/index.mjs +0 -33
  104. package/dist/lib/node-esm/testing/index.mjs.map +0 -7
  105. package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.d.ts +0 -4
  106. package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.d.ts.map +0 -1
  107. package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.test.d.ts +0 -2
  108. package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.test.d.ts.map +0 -1
  109. package/dist/types/src/extraction/extraction-llm-function/index.d.ts +0 -2
  110. package/dist/types/src/extraction/extraction-llm-function/index.d.ts.map +0 -1
  111. package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.d.ts +0 -347
  112. package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.d.ts.map +0 -1
  113. package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.test.d.ts +0 -2
  114. package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.test.d.ts.map +0 -1
  115. package/dist/types/src/session/GenericToolkit.d.ts +0 -85
  116. package/dist/types/src/session/GenericToolkit.d.ts.map +0 -1
  117. package/src/extraction/extraction-llm-function/extraction-llm-function.test.ts +0 -84
  118. package/src/extraction/extraction-llm-function/extraction-llm-function.ts +0 -62
  119. package/src/extraction/extraction-llm-function/index.ts +0 -5
  120. package/src/extraction/extraction-ner-function/extraction-ner-function.test.ts +0 -116
  121. package/src/extraction/extraction-ner-function/extraction-ner-function.ts +0 -56
  122. package/src/session/GenericToolkit.ts +0 -131
  123. /package/dist/lib/{browser → neutral}/chunk-J5LGTIGS.mjs +0 -0
  124. /package/dist/lib/{browser → neutral}/chunk-J5LGTIGS.mjs.map +0 -0
@@ -2,15 +2,27 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import type * as Toolkit from '@effect/ai/Toolkit';
6
5
  import type { Registry } from '@effect-atom/atom-react';
6
+ import type * as Tool from '@effect/ai/Tool';
7
+ import type * as Toolkit from '@effect/ai/Toolkit';
8
+ import * as Array from 'effect/Array';
9
+ import * as Context from 'effect/Context';
7
10
  import * as Effect from 'effect/Effect';
11
+ import * as Either from 'effect/Either';
12
+ import { pipe } from 'effect/Function';
13
+ import * as Layer from 'effect/Layer';
14
+ import * as Record from 'effect/Record';
15
+ import * as Runtime from 'effect/Runtime';
8
16
 
17
+ import { type ToolExecutionService, type ToolResolverService } from '@dxos/ai';
18
+ import { type GenericToolkit } from '@dxos/ai';
19
+ import { type Blueprint } from '@dxos/blueprints';
9
20
  import { Resource } from '@dxos/context';
10
- import { Obj } from '@dxos/echo';
11
- import { type Queue } from '@dxos/echo-db';
21
+ import { Database, Feed, Filter, Obj } from '@dxos/echo';
22
+ import { acquireReleaseResource } from '@dxos/effect';
12
23
  import { invariant } from '@dxos/invariant';
13
24
  import { log } from '@dxos/log';
25
+ import { McpToolkit } from '@dxos/mcp-client';
14
26
  import { Message } from '@dxos/types';
15
27
 
16
28
  import {
@@ -19,24 +31,31 @@ import {
19
31
  type AiSessionRunRequirements,
20
32
  type GenerationObserver,
21
33
  createToolkit,
34
+ formatSystemPrompt,
22
35
  } from '../session';
36
+ import { AiContextBinder, AiContextService } from './context';
23
37
 
24
- import { AiContextBinder, AiContextService, type ContextBinding } from './context';
25
-
26
- export interface AiConversationRunProps {
38
+ export interface AiConversationRunProps<Tools extends Record<string, Tool.Any>> {
27
39
  prompt: string;
28
40
  system?: string;
29
41
  observer?: GenerationObserver;
42
+ toolkit?: Toolkit.Toolkit<Tools>;
30
43
  }
31
44
 
32
45
  export type AiConversationOptions = {
33
- queue: Queue<Message.Message | ContextBinding>;
34
- toolkit?: Toolkit.Any;
46
+ feed: Feed.Feed;
47
+ runtime: Runtime.Runtime<Feed.FeedService>;
35
48
  registry?: Registry.Registry;
36
49
  };
37
50
 
51
+ // TODO(dmaretskyi): Take from model characteristics. opus has 200k max tokens.
38
52
  /**
39
- * Durable conversation state (initiated by users and agents) backed by a Queue.
53
+ * Summarization threshold in tokens.
54
+ */
55
+ const SUMMARY_THRESHOLD = 80_000;
56
+
57
+ /**
58
+ * Durable conversation state (initiated by users and agents) backed by a Feed.
40
59
  * Executes tools based on AI responses and supports cancellation of in-progress requests.
41
60
  */
42
61
  export class AiConversation extends Resource {
@@ -44,23 +63,25 @@ export class AiConversation extends Resource {
44
63
  * Blueprints and objects bound to the conversation.
45
64
  */
46
65
  private readonly _binder: AiContextBinder;
47
- private readonly _queue: Queue<Message.Message | ContextBinding>;
66
+ private readonly _feed: Feed.Feed;
67
+ private readonly _runtime: Runtime.Runtime<Feed.FeedService>;
48
68
  private readonly _toolkit?: Toolkit.Any;
49
69
 
50
70
  public constructor(options: AiConversationOptions) {
51
71
  super();
52
- this._queue = options.queue;
53
- this._toolkit = options.toolkit;
54
- invariant(this._queue);
55
- this._binder = new AiContextBinder({ queue: this._queue, registry: options.registry });
72
+ this._feed = options.feed;
73
+ this._runtime = options.runtime;
74
+ invariant(this._feed);
75
+ invariant(this._runtime);
76
+ this._binder = new AiContextBinder({ feed: this._feed, runtime: this._runtime, registry: options.registry });
56
77
  }
57
78
 
58
79
  protected override async _open(): Promise<void> {
59
80
  await this._binder.open(this._ctx);
60
81
  }
61
82
 
62
- public get queue() {
63
- return this._queue;
83
+ public get feed() {
84
+ return this._feed;
64
85
  }
65
86
 
66
87
  public get context() {
@@ -72,54 +93,183 @@ export class AiConversation extends Resource {
72
93
  }
73
94
 
74
95
  public async getHistory(): Promise<Message.Message[]> {
75
- const queueItems = await this._queue.queryObjects(); // TODO(burdon): Update.
76
- return queueItems.filter(Obj.instanceOf(Message.Message));
96
+ const queryResult = await Runtime.runPromise(this._runtime)(Feed.query(this._feed, Filter.type(Message.Message)));
97
+ const items = await queryResult.run();
98
+ return items.filter(Obj.instanceOf(Message.Message));
99
+ }
100
+
101
+ getTools(): Effect.Effect<Record<string, Tool.Any>, never, ToolExecutionService | ToolResolverService> {
102
+ return Effect.gen(this, function* () {
103
+ const blueprints = this.context.getBlueprints();
104
+ const tookit = yield* createToolkit({ toolkit: this._toolkit, blueprints });
105
+ return tookit.tools;
106
+ }).pipe(Effect.orDie);
77
107
  }
78
108
 
79
109
  /**
80
110
  * Creates a new cancelable request effect.
81
111
  */
82
- public createRequest(
83
- params: AiConversationRunProps,
84
- ): Effect.Effect<Message.Message[], AiSessionRunError, AiSessionRunRequirements> {
85
- const self = this;
86
- return Effect.gen(function* () {
87
- const history = yield* Effect.promise(() => self.getHistory());
88
-
89
- // Create toolkit.
90
- const blueprints = self.context.getBlueprints();
91
- const toolkit = yield* createToolkit({
92
- toolkit: self._toolkit,
93
- blueprints,
94
- });
95
-
96
- // Context objects.
97
- const objects = self.context.getObjects();
112
+ public createRequest<Tools extends Record<string, Tool.Any> = {}>(
113
+ params: AiConversationRunProps<Tools>,
114
+ ): Effect.Effect<Message.Message[], AiSessionRunError, AiSessionRunRequirements | Tool.HandlersFor<Tools>> {
115
+ return Effect.gen(this, function* () {
116
+ const history = yield* Effect.promise(() => this.getHistory());
117
+ const blueprints = this.context.getBlueprints();
118
+ const objects = this.context.getObjects();
98
119
 
99
120
  log('run', {
100
121
  history: history.length,
101
122
  blueprints: blueprints.length,
102
- tools: Object.keys(toolkit.tools).length,
103
123
  objects: objects.length,
104
124
  });
105
125
 
106
- // Process request.
107
- const session = new AiSession();
108
- const messages = yield* session.run({ history, blueprints, toolkit, objects, ...params }).pipe(
109
- Effect.provideService(AiContextService, {
110
- binder: self.context,
111
- }),
112
- );
126
+ const session = new AiSession({
127
+ summarizationThreshold: SUMMARY_THRESHOLD,
128
+ observer: params.observer,
129
+ onOutput: (message) =>
130
+ Effect.promise(() => Runtime.runPromise(this._runtime)(Feed.append(this._feed, [message]))),
131
+ });
132
+
133
+ yield* session.begin({
134
+ history,
135
+ blueprints,
136
+ objects,
137
+ prompt: params.prompt,
138
+ system: params.system,
139
+ });
140
+
141
+ // Turn loop: recompute toolkit and system prompt between turns to pick up dynamically enabled blueprints.
142
+ do {
143
+ yield* Effect.promise(() => this.context.sync());
144
+ const currentBlueprints = this.context.getBlueprints();
145
+ const mcps = yield* connectMcpServers(currentBlueprints);
146
+ const toolkit = yield* createToolkit({
147
+ toolkit: params.toolkit,
148
+ blueprints: currentBlueprints,
149
+ genericToolkits: mcps,
150
+ });
151
+ log('toolkit', { tools: Record.keys(toolkit.tools) });
152
+ const system = yield* formatSystemPrompt({
153
+ system: params.system,
154
+ blueprints: currentBlueprints,
155
+ objects: this.context.getObjects(),
156
+ }).pipe(Effect.orDie);
157
+
158
+ const { done } = yield* session.runAgentTurn({
159
+ system,
160
+ toolkit,
161
+ });
162
+
163
+ if (done) {
164
+ break;
165
+ }
166
+
167
+ yield* session.runTools({ toolkit });
168
+ } while (true);
113
169
 
114
170
  log('result', {
115
- messages: messages.length,
171
+ messages: session.pending.length,
116
172
  duration: session.duration,
117
173
  toolCalls: session.toolCalls,
118
174
  });
119
175
 
120
- // Append to queue.
121
- yield* Effect.promise(() => self._queue.append(messages));
122
- return messages;
123
- });
176
+ return [...session.pending];
177
+ }).pipe(
178
+ Effect.provide(
179
+ Layer.mergeAll(
180
+ Layer.succeed(AiContextService, {
181
+ binder: this.context,
182
+ }),
183
+ Layer.succeed(AiConversationService, this),
184
+ ),
185
+ ),
186
+ );
124
187
  }
125
188
  }
189
+
190
+ /**
191
+ * Gives access to the ai conversation.
192
+ */
193
+ export class AiConversationService extends Context.Tag('@dxos/assistant/AiConversationService')<
194
+ AiConversationService,
195
+ AiConversation
196
+ >() {
197
+ /**
198
+ * Create a new conversation layer from options.
199
+ */
200
+ static layer = (options: AiConversationOptions): Layer.Layer<AiConversationService | AiContextService> =>
201
+ aiContextFromConversation.pipe(
202
+ Layer.provideMerge(
203
+ Layer.scoped(
204
+ AiConversationService,
205
+ Effect.gen(function* () {
206
+ const conversation = yield* acquireReleaseResource(() => new AiConversation(options));
207
+ return conversation;
208
+ }),
209
+ ),
210
+ ),
211
+ );
212
+
213
+ /**
214
+ * Create a new conversation with a new feed.
215
+ */
216
+ static layerNewFeed = (
217
+ options?: Omit<AiConversationOptions, 'feed' | 'runtime'>,
218
+ ): Layer.Layer<AiConversationService | AiContextService, never, Database.Service | Feed.FeedService> =>
219
+ Layer.unwrapScoped(
220
+ Effect.gen(function* () {
221
+ const feed = Feed.make();
222
+ yield* Database.add(feed);
223
+ const runtime = yield* Effect.runtime<Feed.FeedService>();
224
+ return AiConversationService.layer({
225
+ ...options,
226
+ feed,
227
+ runtime,
228
+ });
229
+ }),
230
+ );
231
+
232
+ /**
233
+ * Run a prompt in the current conversation.
234
+ */
235
+ static run = <Tools extends Record<string, Tool.Any> = {}>(
236
+ params: AiConversationRunProps<Tools>,
237
+ ): Effect.Effect<
238
+ Message.Message[],
239
+ AiSessionRunError,
240
+ AiSessionRunRequirements | Tool.HandlersFor<Tools> | AiConversationService
241
+ > =>
242
+ Effect.gen(function* () {
243
+ const conversation = yield* AiConversationService;
244
+ return yield* conversation.createRequest(params);
245
+ });
246
+ }
247
+
248
+ const aiContextFromConversation = Layer.effect(
249
+ AiContextService,
250
+ Effect.gen(function* () {
251
+ const conversation = yield* AiConversationService;
252
+ return {
253
+ binder: conversation.context,
254
+ };
255
+ }),
256
+ );
257
+
258
+ const connectMcpServers = (
259
+ blueprints: readonly Blueprint.Blueprint[],
260
+ ): Effect.Effect<GenericToolkit.GenericToolkit[]> =>
261
+ pipe(
262
+ blueprints,
263
+ Array.flatMap((_) => _.mcpServers ?? []),
264
+ Effect.forEach(({ url, protocol }) =>
265
+ McpToolkit.make({ url, kind: protocol }).pipe(
266
+ // NOTE: Type-inference fails here without explicit void return.
267
+ Effect.tap((toolkit): void =>
268
+ log.info('Connected to MCP server', { url, tools: Object.keys(toolkit.toolkit.tools).length }),
269
+ ),
270
+ Effect.tapDefect((error) => Effect.sync(() => log.warn('Failed to connect to MCP server', { error }))),
271
+ Effect.either,
272
+ ),
273
+ ),
274
+ Effect.map(Array.filterMap((_) => Either.getRight(_))),
275
+ );
package/src/diff.test.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { describe, expect, it } from 'vitest';
5
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
6
6
 
7
7
  import { createDocAccessor } from '@dxos/echo-db';
8
8
  import { EchoTestBuilder } from '@dxos/echo-db/testing';
@@ -12,10 +12,17 @@ import { trim } from '@dxos/util';
12
12
  import { applyDiffs } from './diff';
13
13
 
14
14
  describe('diff', () => {
15
+ let builder: EchoTestBuilder;
16
+ beforeEach(async () => {
17
+ builder = await new EchoTestBuilder().open();
18
+ });
19
+
20
+ afterEach(async () => {
21
+ await builder.close();
22
+ });
23
+
15
24
  it('should apply diffs', async () => {
16
- const builder = new EchoTestBuilder();
17
- const { db, graph } = await builder.createDatabase();
18
- await graph.schemaRegistry.register([Text.Text]);
25
+ const { db } = await builder.createDatabase({ types: [Text.Text] });
19
26
 
20
27
  const document = trim`
21
28
  # Hello World
@@ -24,7 +31,7 @@ describe('diff', () => {
24
31
  But not this one.
25
32
  `;
26
33
 
27
- const text = db.add(Text.make(document));
34
+ const text = db.add(Text.make({ content: document }));
28
35
  const accessor = createDocAccessor(text, ['content']);
29
36
  const result = applyDiffs(accessor, [
30
37
  `- There'z a typo in this sentence.`,
@@ -40,4 +47,28 @@ describe('diff', () => {
40
47
  But not this one.
41
48
  `);
42
49
  });
50
+
51
+ it('should apply diffs that append content at the end', async () => {
52
+ const { db } = await builder.createDatabase({ types: [Text.Text] });
53
+
54
+ const document = '# Shopping list';
55
+
56
+ const text = db.add(Text.make({ content: document }));
57
+ const accessor = createDocAccessor(text, ['content']);
58
+ const result = applyDiffs(accessor, ['- # Shopping list', '+ # Shopping list\n- Milk']);
59
+
60
+ expect(result).toBe('# Shopping list\n- Milk');
61
+ });
62
+
63
+ it('should append text to an empty document', async () => {
64
+ const builder = new EchoTestBuilder();
65
+ const { db, graph } = await builder.createDatabase();
66
+ await graph.schemaRegistry.register([Text.Text]);
67
+
68
+ const text = db.add(Text.make({ content: '' }));
69
+ const accessor = createDocAccessor(text, ['content']);
70
+ const result = applyDiffs(accessor, ['+ # Shopping list']);
71
+
72
+ expect(result).toBe('# Shopping list');
73
+ });
43
74
  });
package/src/diff.ts CHANGED
@@ -47,9 +47,10 @@ export const reduceDiffs = (diffs: readonly string[]): Diff[] => {
47
47
  }
48
48
  return acc;
49
49
  }
50
+ default: {
51
+ throw new Error(`Invalid diff format: each line must start with "- " or "+ ", got: ${JSON.stringify(diff)}`);
52
+ }
50
53
  }
51
-
52
- return acc;
53
54
  }, []);
54
55
  };
55
56
 
@@ -65,7 +66,11 @@ export const computeDiffsWithCursors = <T>(accessor: DocAccessor<T>, diffs: read
65
66
  .filter(isNonNullable);
66
67
  };
67
68
 
68
- export const applyDiffs = <T>(accessor: DocAccessor<T>, diffs: readonly string[]): string => {
69
+ export const applyDiffs = <T>(
70
+ accessor: DocAccessor<T>,
71
+ diffs: readonly string[],
72
+ { errorOnNotFound = false }: { errorOnNotFound?: boolean } = {},
73
+ ): string => {
69
74
  for (const diff of reduceDiffs(diffs)) {
70
75
  accessor.handle.change((doc: Doc<T>) => {
71
76
  const text = DocAccessor.getValue<string>(accessor);
@@ -75,7 +80,11 @@ export const applyDiffs = <T>(accessor: DocAccessor<T>, diffs: readonly string[]
75
80
  // NOTE: This only replaces the first match.
76
81
  A.splice(doc, accessor.path as A.Prop[], idx, diff.match.length, diff.replace);
77
82
  } else {
78
- log.warn('diff not found', diff);
83
+ if (errorOnNotFound) {
84
+ throw new Error(`Diff not found: ${diff.match}`);
85
+ } else {
86
+ log.warn('diff not found', diff);
87
+ }
79
88
  }
80
89
  });
81
90
  }
@@ -71,7 +71,7 @@ describe('ExecutionGraph', () => {
71
71
  blocks: [
72
72
  {
73
73
  _tag: 'text',
74
- text: '{"id":"01K6AXZKK79H4E54RKP1YZDARK","@type":"dxn:type:dxos.org/type/Organization:0.1.0","@meta":{"keys":[]},"name":"Blue Yard","website":"https://blueyard.com"}',
74
+ text: '{"id":"01K6AXZKK79H4E54RKP1YZDARK","@type":"dxn:type:org.dxos.type.organization:0.1.0","@meta":{"keys":[]},"name":"Blue Yard","website":"https://blueyard.com"}',
75
75
  },
76
76
  ],
77
77
  properties: {},
@@ -619,7 +619,7 @@ describe('ExecutionGraph', () => {
619
619
  { _tag: 'reasoning', reasoningText: 'I need to think about this' },
620
620
  { _tag: 'toolCall', toolCallId: 'tool1', name: 'analyze', input: '{}', providerExecuted: false },
621
621
  {
622
- _tag: 'summary',
622
+ _tag: 'stats',
623
623
  message: 'OK',
624
624
  duration: 1000,
625
625
  toolCalls: 1,
@@ -40,7 +40,7 @@ enum IconType {
40
40
  // Interactions.
41
41
  USER = 'ph--user--regular',
42
42
  USER_INTERACTION = 'ph--user-sound--regular',
43
- AGENT = 'ph--robot--regular',
43
+ AGENT = 'ph--drone--regular',
44
44
  THINKING = 'ph--brain--regular',
45
45
  LINK = 'ph--link--regular',
46
46
  TOOL = 'ph--wrench--regular',
@@ -412,7 +412,7 @@ const createBlockCommit = (
412
412
  message: block.error ? 'Tool error: ' + block.error : 'Tool call succeeded',
413
413
  } satisfies Commit;
414
414
 
415
- case 'summary':
415
+ case 'stats':
416
416
  return {
417
417
  id: getGenericBlockId(message.id, idx),
418
418
  branch,
@@ -420,7 +420,7 @@ const createBlockCommit = (
420
420
  timestamp,
421
421
  icon: IconType.ROCKET,
422
422
  level: LogLevel.INFO,
423
- message: ContentBlock.createSummaryMessage(block),
423
+ message: ContentBlock.createStatsMessage(block),
424
424
  } satisfies Commit;
425
425
 
426
426
  case 'status':
@@ -2,5 +2,4 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- export * from './extraction-ner-function';
6
5
  export * from './named-entity-recognition';
@@ -4,16 +4,14 @@
4
4
 
5
5
  import * as Schema from 'effect/Schema';
6
6
 
7
- import { asyncTimeout } from '@dxos/async';
8
- import { Type } from '@dxos/echo';
9
- import { type FunctionDefinition } from '@dxos/functions';
10
- import { type FunctionExecutor } from '@dxos/functions-runtime';
7
+ import { Obj } from '@dxos/echo';
11
8
  import { log } from '@dxos/log';
9
+ import { Operation } from '@dxos/operation';
12
10
  import { Message } from '@dxos/types';
13
11
 
14
12
  export const ExtractionInput = Schema.Struct({
15
13
  message: Message.Message,
16
- objects: Schema.optional(Schema.Array(Type.Obj)),
14
+ objects: Schema.optional(Schema.Array(Obj.Unknown)),
17
15
  options: Schema.optional(
18
16
  Schema.Struct({
19
17
  timeout: Schema.optional(Schema.Number),
@@ -31,8 +29,7 @@ export interface ExtractionOutput extends Schema.Schema.Type<typeof ExtractionOu
31
29
 
32
30
  export type ProcessTranscriptMessageProps = {
33
31
  input: ExtractionInput;
34
- function: FunctionDefinition<ExtractionInput, ExtractionOutput>;
35
- executor: FunctionExecutor;
32
+ function: Operation.Definition<ExtractionInput, ExtractionOutput>;
36
33
 
37
34
  options?: {
38
35
  /**
@@ -49,7 +46,35 @@ export type ProcessTranscriptMessageProps = {
49
46
  };
50
47
  };
51
48
 
52
- export type ExtractionFunction = FunctionDefinition<ExtractionInput, ExtractionOutput>;
49
+ export type ExtractionFunction = Operation.Definition<ExtractionInput, ExtractionOutput>;
50
+
51
+ /**
52
+ * Placeholder for Anthropic-based entity extraction.
53
+ * Implementation is currently disabled pending restoration of extraction logic.
54
+ */
55
+ export const extractionAnthropicFunction = Operation.make({
56
+ meta: {
57
+ key: 'org.dxos.function.extraction.anthropic',
58
+ name: 'Entity Extraction (Anthropic)',
59
+ description: 'Extract entities from transcript using Anthropic LLM.',
60
+ },
61
+ input: ExtractionInput,
62
+ output: ExtractionOutput,
63
+ });
64
+
65
+ /**
66
+ * Placeholder for NER-based entity extraction.
67
+ * Implementation is currently disabled pending restoration of extraction logic.
68
+ */
69
+ export const extractionNerFunction = Operation.make({
70
+ meta: {
71
+ key: 'org.dxos.function.extraction.ner',
72
+ name: 'Entity Extraction (NER)',
73
+ description: 'Extract entities from transcript using Named Entity Recognition.',
74
+ },
75
+ input: ExtractionInput,
76
+ output: ExtractionOutput,
77
+ });
53
78
 
54
79
  /**
55
80
  * Extract entities from the transcript message and add them to the message.
@@ -58,9 +83,9 @@ export type ExtractionFunction = FunctionDefinition<ExtractionInput, ExtractionO
58
83
  export const processTranscriptMessage = async (params: ProcessTranscriptMessageProps): Promise<ExtractionOutput> => {
59
84
  try {
60
85
  if (params.options?.timeout && params.options.timeout > 0) {
61
- return await asyncTimeout(params.executor.invoke(params.function, params.input), params.options.timeout);
86
+ // return await asyncTimeout(params.executor.invoke(params.function, params.input), params.options.timeout);
62
87
  } else {
63
- return await params.executor.invoke(params.function, params.input);
88
+ // return await params.executor.invoke(params.function, params.input);
64
89
  }
65
90
  } catch (error) {
66
91
  if (params.options?.fallbackToRaw) {
@@ -73,4 +98,7 @@ export const processTranscriptMessage = async (params: ProcessTranscriptMessageP
73
98
  throw error;
74
99
  }
75
100
  }
101
+
102
+ // TODO(dmaretskyi): Restore extraction logic.
103
+ return { message: params.input.message, timeElapsed: 0 };
76
104
  };
@@ -3,6 +3,5 @@
3
3
  //
4
4
 
5
5
  export * from './extraction';
6
- export * from './extraction-llm-function';
7
6
  export * from './extraction-ner-function';
8
7
  export * from './quotes';