@dxos/assistant 0.8.4-main.c4373fc → 0.8.4-main.c85a9c8dae

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 (123) hide show
  1. package/README.md +1 -0
  2. package/dist/lib/neutral/chunk-BE6SRJ4N.mjs +193 -0
  3. package/dist/lib/neutral/chunk-BE6SRJ4N.mjs.map +7 -0
  4. package/dist/lib/neutral/extraction/index.mjs +223 -0
  5. package/dist/lib/neutral/extraction/index.mjs.map +7 -0
  6. package/dist/lib/neutral/index.mjs +1328 -0
  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 +38 -0
  10. package/dist/lib/neutral/testing/index.mjs.map +7 -0
  11. package/dist/types/src/conversation/context.d.ts +109 -60
  12. package/dist/types/src/conversation/context.d.ts.map +1 -1
  13. package/dist/types/src/conversation/context.test.d.ts +2 -0
  14. package/dist/types/src/conversation/context.test.d.ts.map +1 -0
  15. package/dist/types/src/conversation/conversation.d.ts +41 -16
  16. package/dist/types/src/conversation/conversation.d.ts.map +1 -1
  17. package/dist/types/src/conversation/conversation.test.d.ts +2 -0
  18. package/dist/types/src/conversation/conversation.test.d.ts.map +1 -0
  19. package/dist/types/src/diff.d.ts +3 -1
  20. package/dist/types/src/diff.d.ts.map +1 -1
  21. package/dist/types/src/errors.d.ts +22 -30
  22. package/dist/types/src/errors.d.ts.map +1 -1
  23. package/dist/types/src/execution-graph.d.ts +5 -2
  24. package/dist/types/src/execution-graph.d.ts.map +1 -1
  25. package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.d.ts.map +1 -1
  26. package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.d.ts +282 -268
  27. package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.d.ts.map +1 -1
  28. package/dist/types/src/extraction/extraction.d.ts +348 -303
  29. package/dist/types/src/extraction/extraction.d.ts.map +1 -1
  30. package/dist/types/src/extraction/quotes.d.ts +2 -2
  31. package/dist/types/src/extraction/quotes.d.ts.map +1 -1
  32. package/dist/types/src/functions/services.d.ts +6 -6
  33. package/dist/types/src/functions/services.d.ts.map +1 -1
  34. package/dist/types/src/functions.test.d.ts +2 -0
  35. package/dist/types/src/functions.test.d.ts.map +1 -0
  36. package/dist/types/src/session/artifact-diff.d.ts +1 -1
  37. package/dist/types/src/session/artifact-diff.d.ts.map +1 -1
  38. package/dist/types/src/session/format.d.ts +4 -176
  39. package/dist/types/src/session/format.d.ts.map +1 -1
  40. package/dist/types/src/session/index.d.ts +1 -0
  41. package/dist/types/src/session/index.d.ts.map +1 -1
  42. package/dist/types/src/session/observer.d.ts +6 -5
  43. package/dist/types/src/session/observer.d.ts.map +1 -1
  44. package/dist/types/src/session/session.d.ts +25 -15
  45. package/dist/types/src/session/session.d.ts.map +1 -1
  46. package/dist/types/src/session/toolkit.d.ts +9 -6
  47. package/dist/types/src/session/toolkit.d.ts.map +1 -1
  48. package/dist/types/src/session/version-pin.d.ts +3 -3
  49. package/dist/types/src/session/version-pin.d.ts.map +1 -1
  50. package/dist/types/src/testing/index.d.ts +2 -0
  51. package/dist/types/src/testing/index.d.ts.map +1 -0
  52. package/dist/types/src/testing/layer.d.ts +22 -0
  53. package/dist/types/src/testing/layer.d.ts.map +1 -0
  54. package/dist/types/src/types/artifact.d.ts +28 -0
  55. package/dist/types/src/types/artifact.d.ts.map +1 -0
  56. package/dist/types/src/types/index.d.ts +2 -0
  57. package/dist/types/src/types/index.d.ts.map +1 -0
  58. package/dist/types/tsconfig.tsbuildinfo +1 -1
  59. package/package.json +41 -31
  60. package/src/conversation/context.test.ts +46 -0
  61. package/src/conversation/context.ts +223 -49
  62. package/src/conversation/conversation.test.ts +65 -0
  63. package/src/conversation/conversation.ts +179 -53
  64. package/src/diff.test.ts +37 -6
  65. package/src/diff.ts +18 -4
  66. package/src/errors.ts +2 -2
  67. package/src/execution-graph.test.ts +46 -44
  68. package/src/execution-graph.ts +25 -16
  69. package/src/extraction/extraction-llm-function/extraction-llm-function.test.ts +2 -2
  70. package/src/extraction/extraction-llm-function/extraction-llm-function.ts +44 -54
  71. package/src/extraction/extraction-ner-function/extraction-ner-function.test.ts +15 -14
  72. package/src/extraction/extraction-ner-function/extraction-ner-function.ts +36 -44
  73. package/src/extraction/extraction-ner-function/named-entity-recognition.test.ts +10 -5
  74. package/src/extraction/extraction.ts +9 -8
  75. package/src/extraction/quotes.ts +4 -5
  76. package/src/functions/services.ts +88 -37
  77. package/src/functions.conversations.json +1 -0
  78. package/src/functions.test.ts +74 -0
  79. package/src/session/artifact-diff.ts +1 -1
  80. package/src/session/format.test.ts +103 -39
  81. package/src/session/format.ts +17 -16
  82. package/src/session/index.ts +1 -0
  83. package/src/session/observer.ts +6 -3
  84. package/src/session/session.conversations.json +1 -1
  85. package/src/session/session.test.ts +60 -18
  86. package/src/session/session.ts +99 -60
  87. package/src/session/toolkit.ts +21 -25
  88. package/src/session/version-pin.ts +6 -6
  89. package/src/templates/{format.tpl → instructions/format.tpl} +1 -4
  90. package/src/templates/system.ts +2 -2
  91. package/src/testing/index.ts +5 -0
  92. package/src/testing/layer.ts +85 -0
  93. package/src/{types.ts → types/artifact.ts} +43 -6
  94. package/src/types/index.ts +5 -0
  95. package/dist/lib/browser/chunk-CWGVG235.mjs +0 -380
  96. package/dist/lib/browser/chunk-CWGVG235.mjs.map +0 -7
  97. package/dist/lib/browser/chunk-UB26N5UF.mjs +0 -89
  98. package/dist/lib/browser/chunk-UB26N5UF.mjs.map +0 -7
  99. package/dist/lib/browser/extraction/index.mjs +0 -272
  100. package/dist/lib/browser/extraction/index.mjs.map +0 -7
  101. package/dist/lib/browser/index.mjs +0 -869
  102. package/dist/lib/browser/index.mjs.map +0 -7
  103. package/dist/lib/browser/meta.json +0 -1
  104. package/dist/lib/browser/named-entity-recognition-QPYHML66.mjs +0 -13
  105. package/dist/lib/browser/named-entity-recognition-QPYHML66.mjs.map +0 -7
  106. package/dist/lib/node-esm/chunk-BCFXTNSK.mjs +0 -91
  107. package/dist/lib/node-esm/chunk-BCFXTNSK.mjs.map +0 -7
  108. package/dist/lib/node-esm/chunk-OV5PMQPB.mjs +0 -382
  109. package/dist/lib/node-esm/chunk-OV5PMQPB.mjs.map +0 -7
  110. package/dist/lib/node-esm/extraction/index.mjs +0 -273
  111. package/dist/lib/node-esm/extraction/index.mjs.map +0 -7
  112. package/dist/lib/node-esm/index.mjs +0 -870
  113. package/dist/lib/node-esm/index.mjs.map +0 -7
  114. package/dist/lib/node-esm/meta.json +0 -1
  115. package/dist/lib/node-esm/named-entity-recognition-2HDF46YX.mjs +0 -14
  116. package/dist/lib/node-esm/named-entity-recognition-2HDF46YX.mjs.map +0 -7
  117. package/dist/types/src/summarization/summarization.test.d.ts +0 -2
  118. package/dist/types/src/summarization/summarization.test.d.ts.map +0 -1
  119. package/dist/types/src/types.d.ts +0 -16
  120. package/dist/types/src/types.d.ts.map +0 -1
  121. package/src/summarization/summarization.test.ts +0 -202
  122. /package/src/extraction/extraction-llm-function/{instructions.tpl → transcription-instructions.tpl} +0 -0
  123. /package/src/templates/{system.tpl → instructions/system.tpl} +0 -0
@@ -2,16 +2,28 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
+ import type * as Tool from '@effect/ai/Tool';
5
6
  import type * as Toolkit from '@effect/ai/Toolkit';
7
+ import type { Registry } from '@effect-atom/atom-react';
6
8
  import * as Array from 'effect/Array';
9
+ import * as Context from 'effect/Context';
7
10
  import * as Effect from 'effect/Effect';
8
- import * as Option from 'effect/Option';
9
-
11
+ import * as Either from 'effect/Either';
12
+ import { pipe } from 'effect/Function';
13
+ import * as Layer from 'effect/Layer';
14
+
15
+ import { type ToolExecutionService, type ToolResolverService } from '@dxos/ai';
16
+ import { type GenericToolkit } from '@dxos/ai';
17
+ import { type Blueprint } from '@dxos/blueprints';
18
+ import { Resource } from '@dxos/context';
10
19
  import { Obj } from '@dxos/echo';
11
20
  import { type Queue } from '@dxos/echo-db';
12
- import { DatabaseService } from '@dxos/functions';
21
+ import { acquireReleaseResource } from '@dxos/effect';
22
+ import { QueueService } from '@dxos/functions';
23
+ import { invariant } from '@dxos/invariant';
13
24
  import { log } from '@dxos/log';
14
- import { DataType } from '@dxos/schema';
25
+ import { McpToolkit } from '@dxos/mcp-client';
26
+ import { Message } from '@dxos/types';
15
27
 
16
28
  import {
17
29
  AiSession,
@@ -23,99 +35,213 @@ import {
23
35
 
24
36
  import { AiContextBinder, AiContextService, type ContextBinding } from './context';
25
37
 
26
- export interface AiConversationRunParams {
38
+ export interface AiConversationRunProps {
27
39
  prompt: string;
28
40
  system?: string;
29
41
  observer?: GenerationObserver;
30
42
  }
31
43
 
32
44
  export type AiConversationOptions = {
33
- queue: Queue<DataType.Message | ContextBinding>;
45
+ queue: Queue<Message.Message | ContextBinding>;
46
+ toolkit?: Toolkit.Any;
47
+ registry?: Registry.Registry;
34
48
  };
35
49
 
50
+ // TODO(dmaretskyi): Take from model characteristics. opus has 200k max tokens.
51
+ /**
52
+ * Summarization threshold in tokens.
53
+ */
54
+ const SUMMARY_THRESHOLD = 80_000;
55
+
36
56
  /**
37
57
  * Durable conversation state (initiated by users and agents) backed by a Queue.
38
58
  * Executes tools based on AI responses and supports cancellation of in-progress requests.
39
59
  */
40
- export class AiConversation {
41
- /**
42
- * Message and binding queue.
43
- */
44
- private readonly _queue: Queue<DataType.Message | ContextBinding>;
45
-
46
- /**
47
- * Blueprints bound to the conversation.
48
- */
49
- private readonly _context: AiContextBinder;
50
-
60
+ export class AiConversation extends Resource {
51
61
  /**
52
- * Toolkit from the current session request.
62
+ * Blueprints and objects bound to the conversation.
53
63
  */
54
- private _toolkit: Toolkit.WithHandler<any> | undefined;
64
+ private readonly _binder: AiContextBinder;
65
+ private readonly _queue: Queue<Message.Message | ContextBinding>;
66
+ private readonly _toolkit?: Toolkit.Any;
55
67
 
56
68
  public constructor(options: AiConversationOptions) {
69
+ super();
57
70
  this._queue = options.queue;
58
- this._context = new AiContextBinder(this._queue);
71
+ this._toolkit = options.toolkit;
72
+ invariant(this._queue);
73
+ this._binder = new AiContextBinder({ queue: this._queue, registry: options.registry });
74
+ }
75
+
76
+ protected override async _open(): Promise<void> {
77
+ await this._binder.open(this._ctx);
78
+ }
79
+
80
+ public get queue() {
81
+ return this._queue;
59
82
  }
60
83
 
61
84
  public get context() {
62
- return this._context;
85
+ return this._binder;
63
86
  }
64
87
 
65
88
  public get toolkit() {
66
89
  return this._toolkit;
67
90
  }
68
91
 
69
- public async getHistory(): Promise<DataType.Message[]> {
70
- const queueItems = await this._queue.queryObjects();
71
- return queueItems.filter(Obj.instanceOf(DataType.Message));
92
+ public async getHistory(): Promise<Message.Message[]> {
93
+ const queueItems = await this._queue.queryObjects(); // TODO(burdon): Update.
94
+ return queueItems.filter(Obj.instanceOf(Message.Message));
95
+ }
96
+
97
+ getTools(): Effect.Effect<Record<string, Tool.Any>, never, ToolExecutionService | ToolResolverService> {
98
+ return Effect.gen(this, function* () {
99
+ const blueprints = this.context.getBlueprints();
100
+ const tookit = yield* createToolkit({ toolkit: this._toolkit, blueprints });
101
+ return tookit.tools;
102
+ }).pipe(Effect.orDie);
72
103
  }
73
104
 
74
105
  /**
75
106
  * Creates a new cancelable request effect.
76
107
  */
77
- createRequest(
78
- params: AiConversationRunParams,
79
- ): Effect.Effect<DataType.Message[], AiSessionRunError, AiSessionRunRequirements> {
108
+ public createRequest(
109
+ params: AiConversationRunProps,
110
+ ): Effect.Effect<Message.Message[], AiSessionRunError, AiSessionRunRequirements> {
80
111
  return Effect.gen(this, function* () {
81
- const session = new AiSession();
82
112
  const history = yield* Effect.promise(() => this.getHistory());
83
113
 
84
- // Get context objects.
85
- const context = yield* Effect.promise(() => this.context.query());
86
- const blueprints = yield* Effect.forEach(context.blueprints.values(), DatabaseService.loadOption).pipe(
87
- Effect.map(Array.filter(Option.isSome)),
88
- Effect.map(Array.map((option) => option.value)),
89
- );
90
- const objects = yield* Effect.forEach(context.objects.values(), DatabaseService.loadOption).pipe(
91
- Effect.map(Array.filter(Option.isSome)),
92
- Effect.map(Array.map((option) => option.value)),
93
- );
94
-
95
114
  // Create toolkit.
96
- const toolkit = yield* createToolkit({ blueprints });
97
- this._toolkit = toolkit;
115
+ const blueprints = this.context.getBlueprints();
116
+
117
+ const mcps = yield* connectMcpServers(blueprints);
118
+
119
+ const toolkit = yield* createToolkit({
120
+ toolkit: this._toolkit,
121
+ blueprints,
122
+ genericToolkits: mcps,
123
+ });
124
+
125
+ // Context objects.
126
+ const objects = this.context.getObjects();
98
127
 
99
- const start = Date.now();
100
128
  log('run', {
101
129
  history: history.length,
102
130
  blueprints: blueprints.length,
131
+ tools: Object.keys(toolkit.tools).length,
103
132
  objects: objects.length,
104
- tools: this._toolkit?.tools.length ?? 0,
105
133
  });
106
134
 
107
135
  // Process request.
108
- const messages = yield* session.run({ history, blueprints, objects, toolkit, ...params }).pipe(
109
- Effect.provideService(AiContextService, {
110
- binder: this.context,
111
- }),
112
- );
113
-
114
- log('result', { messages: messages.length, duration: Date.now() - start });
136
+ const session = new AiSession({
137
+ summarizationThreshold: SUMMARY_THRESHOLD,
138
+ });
139
+ const messages = yield* session
140
+ .run({
141
+ history,
142
+ blueprints,
143
+ toolkit,
144
+ objects,
145
+ ...params,
146
+ onOutput: (message) => Effect.promise(() => this._queue.append([message])),
147
+ })
148
+ .pipe(
149
+ Effect.provide(
150
+ Layer.mergeAll(
151
+ Layer.succeed(AiContextService, {
152
+ binder: this.context,
153
+ }),
154
+ Layer.succeed(AiConversationService, this),
155
+ ),
156
+ ),
157
+ );
158
+
159
+ log('result', {
160
+ messages: messages.length,
161
+ duration: session.duration,
162
+ toolCalls: session.toolCalls,
163
+ });
115
164
 
116
- // Append to queue.
117
- yield* Effect.promise(() => this._queue.append(messages));
118
165
  return messages;
119
- }).pipe(Effect.withSpan('AiConversation.request'));
166
+ });
120
167
  }
121
168
  }
169
+
170
+ /**
171
+ * Gives access to the ai conversation.
172
+ */
173
+ export class AiConversationService extends Context.Tag('@dxos/assistant/AiConversationService')<
174
+ AiConversationService,
175
+ AiConversation
176
+ >() {
177
+ /**
178
+ * Create a new conversation layer from options.
179
+ */
180
+ static layer = (options: AiConversationOptions): Layer.Layer<AiConversationService | AiContextService> =>
181
+ aiContextFromConversation.pipe(
182
+ Layer.provideMerge(
183
+ Layer.scoped(
184
+ AiConversationService,
185
+ Effect.gen(function* () {
186
+ const conversation = yield* acquireReleaseResource(() => new AiConversation(options));
187
+ return conversation;
188
+ }),
189
+ ),
190
+ ),
191
+ );
192
+
193
+ /**
194
+ * Create a new conversation with a new queue.
195
+ */
196
+ static layerNewQueue = (
197
+ options?: Omit<AiConversationOptions, 'queue'>,
198
+ ): Layer.Layer<AiConversationService | AiContextService, never, QueueService> =>
199
+ Layer.unwrapScoped(
200
+ Effect.gen(function* () {
201
+ return AiConversationService.layer({
202
+ ...options,
203
+ queue: yield* QueueService.createQueue<Message.Message | ContextBinding>(),
204
+ });
205
+ }),
206
+ );
207
+
208
+ /**
209
+ * Run a prompt in the current conversation.
210
+ */
211
+ static run = (
212
+ params: AiConversationRunProps,
213
+ ): Effect.Effect<Message.Message[], AiSessionRunError, AiSessionRunRequirements | AiConversationService> =>
214
+ Effect.gen(function* () {
215
+ const conversation = yield* AiConversationService;
216
+ return yield* conversation.createRequest(params);
217
+ });
218
+ }
219
+
220
+ const aiContextFromConversation = Layer.effect(
221
+ AiContextService,
222
+ Effect.gen(function* () {
223
+ const conversation = yield* AiConversationService;
224
+ return {
225
+ binder: conversation.context,
226
+ };
227
+ }),
228
+ );
229
+
230
+ const connectMcpServers = (
231
+ blueprints: readonly Blueprint.Blueprint[],
232
+ ): Effect.Effect<GenericToolkit.GenericToolkit[]> =>
233
+ pipe(
234
+ blueprints,
235
+ Array.flatMap((_) => _.mcpServers ?? []),
236
+ Effect.forEach(({ url, protocol }) =>
237
+ McpToolkit.make({ url, kind: protocol }).pipe(
238
+ // NOTE: Type-inference fails here without explicit void return.
239
+ Effect.tap((toolkit): void =>
240
+ log.info('Connected to MCP server', { url, tools: Object.keys(toolkit.toolkit.tools).length }),
241
+ ),
242
+ Effect.tapDefect((error) => Effect.sync(() => log.warn('Failed to connect to MCP server', { error }))),
243
+ Effect.either,
244
+ ),
245
+ ),
246
+ Effect.map(Array.filterMap((_) => Either.getRight(_))),
247
+ );
package/src/diff.test.ts CHANGED
@@ -2,20 +2,27 @@
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';
9
- import { DataType } from '@dxos/schema';
9
+ import { Text } from '@dxos/schema';
10
10
  import { trim } from '@dxos/util';
11
11
 
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
- graph.schemaRegistry.addSchema([DataType.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(DataType.makeText(document));
34
+ const text = db.add(Text.make(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(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(''));
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
@@ -11,6 +11,11 @@ import { isNonNullable } from '@dxos/util';
11
11
  // Relevant info:
12
12
  // https://github.com/Minahil-official/GPT-4.1-Promting-Guide?tab=readme-ov-file#-apply-patch-tool-format-v4a-diff
13
13
 
14
+ // TODO(burdon): Anthropic only.
15
+ // https://docs.anthropic.com/en/docs/agents-and-tools/tool-use/text-editor-tool#example-str-replace-command
16
+ // AI_TOOL_NOT_FOUND: str_replace_based_edit_tool
17
+ // 'str_replace_based_edit_tool',
18
+
14
19
  // TOOD(burdon): Support append to end (no "-").
15
20
 
16
21
  export type Diff = {
@@ -42,9 +47,10 @@ export const reduceDiffs = (diffs: readonly string[]): Diff[] => {
42
47
  }
43
48
  return acc;
44
49
  }
50
+ default: {
51
+ throw new Error(`Invalid diff format: each line must start with "- " or "+ ", got: ${JSON.stringify(diff)}`);
52
+ }
45
53
  }
46
-
47
- return acc;
48
54
  }, []);
49
55
  };
50
56
 
@@ -60,7 +66,11 @@ export const computeDiffsWithCursors = <T>(accessor: DocAccessor<T>, diffs: read
60
66
  .filter(isNonNullable);
61
67
  };
62
68
 
63
- 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 => {
64
74
  for (const diff of reduceDiffs(diffs)) {
65
75
  accessor.handle.change((doc: Doc<T>) => {
66
76
  const text = DocAccessor.getValue<string>(accessor);
@@ -70,7 +80,11 @@ export const applyDiffs = <T>(accessor: DocAccessor<T>, diffs: readonly string[]
70
80
  // NOTE: This only replaces the first match.
71
81
  A.splice(doc, accessor.path as A.Prop[], idx, diff.match.length, diff.replace);
72
82
  } else {
73
- 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
+ }
74
88
  }
75
89
  });
76
90
  }
package/src/errors.ts CHANGED
@@ -7,9 +7,9 @@ import { BaseError } from '@dxos/errors';
7
7
  /**
8
8
  * Generic error from AI model.
9
9
  */
10
- export class AiModelError extends BaseError.extend('AI_MODEL_ERROR', 'AI model error') {}
10
+ export class AiModelError extends BaseError.extend('AiModelError', 'AI model error') {}
11
11
 
12
12
  /**
13
13
  * Generic error for AI agent execution.
14
14
  */
15
- export class AiAssistantError extends BaseError.extend('AI_ASSISTANT_ERROR', 'AI assistant error') {}
15
+ export class AiAssistantError extends BaseError.extend('AiAssistantError', 'AI assistant error') {}