@dxos/assistant 0.8.4-main.72ec0f3 → 0.8.4-main.74a063c4e0
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.
- package/README.md +1 -0
- package/dist/lib/neutral/chunk-UAWIXKF6.mjs +1875 -0
- package/dist/lib/neutral/chunk-UAWIXKF6.mjs.map +7 -0
- package/dist/lib/{browser → neutral}/extraction/index.mjs +29 -35
- package/dist/lib/neutral/extraction/index.mjs.map +7 -0
- package/dist/lib/neutral/index.mjs +551 -0
- package/dist/lib/neutral/index.mjs.map +7 -0
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/lib/neutral/testing/index.mjs +114 -0
- package/dist/lib/neutral/testing/index.mjs.map +7 -0
- package/dist/types/src/conversation/context.d.ts +116 -64
- package/dist/types/src/conversation/context.d.ts.map +1 -1
- package/dist/types/src/conversation/context.test.d.ts +2 -0
- package/dist/types/src/conversation/context.test.d.ts.map +1 -0
- package/dist/types/src/conversation/conversation.d.ts +45 -18
- package/dist/types/src/conversation/conversation.d.ts.map +1 -1
- package/dist/types/src/conversation/conversation.test.d.ts +2 -0
- package/dist/types/src/conversation/conversation.test.d.ts.map +1 -0
- package/dist/types/src/diff.d.ts +3 -1
- package/dist/types/src/diff.d.ts.map +1 -1
- package/dist/types/src/errors.d.ts +12 -16
- package/dist/types/src/errors.d.ts.map +1 -1
- package/dist/types/src/execution-graph.d.ts +2 -2
- package/dist/types/src/execution-graph.d.ts.map +1 -1
- package/dist/types/src/extraction/extraction-ner-function/index.d.ts +0 -1
- package/dist/types/src/extraction/extraction-ner-function/index.d.ts.map +1 -1
- package/dist/types/src/extraction/extraction.d.ts +1059 -313
- package/dist/types/src/extraction/extraction.d.ts.map +1 -1
- package/dist/types/src/extraction/index.d.ts +0 -1
- package/dist/types/src/extraction/index.d.ts.map +1 -1
- package/dist/types/src/extraction/quotes.d.ts +2 -2
- package/dist/types/src/extraction/quotes.d.ts.map +1 -1
- package/dist/types/src/functions/services.d.ts +28 -9
- package/dist/types/src/functions/services.d.ts.map +1 -1
- package/dist/types/src/functions.test.d.ts +2 -0
- package/dist/types/src/functions.test.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +2 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/service/AgentService.d.ts +61 -0
- package/dist/types/src/service/AgentService.d.ts.map +1 -0
- package/dist/types/src/service/AgentService.test.d.ts +2 -0
- package/dist/types/src/service/AgentService.test.d.ts.map +1 -0
- package/dist/types/src/service/agent-process.d.ts +17 -0
- package/dist/types/src/service/agent-process.d.ts.map +1 -0
- package/dist/types/src/service/index.d.ts +3 -0
- package/dist/types/src/service/index.d.ts.map +1 -0
- package/dist/types/src/session/artifact-diff.d.ts +1 -1
- package/dist/types/src/session/artifact-diff.d.ts.map +1 -1
- package/dist/types/src/session/format.d.ts +3 -3
- package/dist/types/src/session/format.d.ts.map +1 -1
- package/dist/types/src/session/index.d.ts +1 -1
- package/dist/types/src/session/index.d.ts.map +1 -1
- package/dist/types/src/session/observer.d.ts +4 -3
- package/dist/types/src/session/observer.d.ts.map +1 -1
- package/dist/types/src/session/session.d.ts +72 -11
- package/dist/types/src/session/session.d.ts.map +1 -1
- package/dist/types/src/session/toolkit.d.ts +9 -6
- package/dist/types/src/session/toolkit.d.ts.map +1 -1
- package/dist/types/src/session/version-pin.d.ts +2 -2
- package/dist/types/src/session/version-pin.d.ts.map +1 -1
- package/dist/types/src/templates/system.d.ts +0 -1
- package/dist/types/src/templates/system.d.ts.map +1 -1
- package/dist/types/src/testing/index.d.ts +2 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/testing/layer.d.ts +35 -0
- package/dist/types/src/testing/layer.d.ts.map +1 -0
- package/dist/types/src/tracing.d.ts +264 -0
- package/dist/types/src/tracing.d.ts.map +1 -0
- package/dist/types/src/types/artifact.d.ts +13 -5
- package/dist/types/src/types/artifact.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +42 -33
- package/src/conversation/context.test.ts +52 -0
- package/src/conversation/context.ts +307 -90
- package/src/conversation/conversation.test.ts +58 -0
- package/src/conversation/conversation.ts +209 -62
- package/src/diff.test.ts +36 -5
- package/src/diff.ts +13 -4
- package/src/errors.ts +2 -2
- package/src/execution-graph.test.ts +3 -3
- package/src/execution-graph.ts +6 -6
- package/src/extraction/extraction-ner-function/index.ts +0 -1
- package/src/extraction/extraction.ts +40 -12
- package/src/extraction/index.ts +0 -1
- package/src/extraction/quotes.ts +4 -5
- package/src/functions/services.ts +217 -50
- package/src/functions.conversations.json +1 -0
- package/src/functions.test.ts +82 -0
- package/src/index.ts +2 -0
- package/src/service/AgentService.conversations.json +1 -0
- package/src/service/AgentService.test.ts +169 -0
- package/src/service/AgentService.ts +165 -0
- package/src/service/agent-process.ts +359 -0
- package/src/service/index.ts +6 -0
- package/src/session/artifact-diff.ts +1 -1
- package/src/session/format.test.ts +110 -39
- package/src/session/format.ts +12 -14
- package/src/session/index.ts +1 -2
- package/src/session/observer.ts +4 -1
- package/src/session/session.conversations.json +1 -1
- package/src/session/session.test.ts +58 -17
- package/src/session/session.ts +249 -113
- package/src/session/toolkit.ts +21 -25
- package/src/session/version-pin.ts +4 -3
- package/src/templates/instructions/format.tpl +0 -4
- package/src/templates/instructions/system.tpl +11 -2
- package/src/templates/system.ts +6 -5
- package/src/testing/index.ts +5 -0
- package/src/testing/layer.ts +258 -0
- package/src/tracing.ts +113 -0
- package/src/types/artifact.ts +31 -13
- package/dist/lib/browser/extraction/index.mjs.map +0 -7
- package/dist/lib/browser/index.mjs +0 -1290
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +0 -7
- package/dist/lib/node-esm/extraction/index.mjs +0 -226
- package/dist/lib/node-esm/extraction/index.mjs.map +0 -7
- package/dist/lib/node-esm/index.mjs +0 -1291
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.d.ts +0 -4
- package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.d.ts.map +0 -1
- package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.test.d.ts +0 -2
- package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.test.d.ts.map +0 -1
- package/dist/types/src/extraction/extraction-llm-function/index.d.ts +0 -2
- package/dist/types/src/extraction/extraction-llm-function/index.d.ts.map +0 -1
- package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.d.ts +0 -345
- package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.d.ts.map +0 -1
- package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.test.d.ts +0 -2
- package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.test.d.ts.map +0 -1
- package/dist/types/src/session/GenericToolkit.d.ts +0 -84
- package/dist/types/src/session/GenericToolkit.d.ts.map +0 -1
- package/src/extraction/extraction-llm-function/extraction-llm-function.test.ts +0 -84
- package/src/extraction/extraction-llm-function/extraction-llm-function.ts +0 -62
- package/src/extraction/extraction-llm-function/index.ts +0 -5
- package/src/extraction/extraction-ner-function/extraction-ner-function.test.ts +0 -116
- package/src/extraction/extraction-ner-function/extraction-ner-function.ts +0 -56
- package/src/session/GenericToolkit.ts +0 -130
- /package/dist/lib/{browser → neutral}/chunk-J5LGTIGS.mjs +0 -0
- /package/dist/lib/{browser → neutral}/chunk-J5LGTIGS.mjs.map +0 -0
|
@@ -0,0 +1,1875 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__export
|
|
3
|
+
} from "./chunk-J5LGTIGS.mjs";
|
|
4
|
+
|
|
5
|
+
// src/conversation/context.ts
|
|
6
|
+
import { Atom, Registry } from "@effect-atom/atom-react";
|
|
7
|
+
import * as EArray from "effect/Array";
|
|
8
|
+
import * as Context from "effect/Context";
|
|
9
|
+
import * as Effect from "effect/Effect";
|
|
10
|
+
import * as Function from "effect/Function";
|
|
11
|
+
import * as Runtime from "effect/Runtime";
|
|
12
|
+
import * as Schema from "effect/Schema";
|
|
13
|
+
import { Blueprint } from "@dxos/blueprints";
|
|
14
|
+
import { Resource } from "@dxos/context";
|
|
15
|
+
import { DXN, Feed, Obj, Query, Ref, Type } from "@dxos/echo";
|
|
16
|
+
import { assertArgument } from "@dxos/invariant";
|
|
17
|
+
import { log } from "@dxos/log";
|
|
18
|
+
import { ComplexSet, isNonNullable } from "@dxos/util";
|
|
19
|
+
var __dxlog_file = "/__w/dxos/dxos/packages/core/assistant/src/conversation/context.ts";
|
|
20
|
+
var ContextBinding = Schema.Struct({
|
|
21
|
+
blueprints: Schema.Struct({
|
|
22
|
+
added: Schema.Array(Ref.Ref(Blueprint.Blueprint)),
|
|
23
|
+
removed: Schema.Array(Ref.Ref(Blueprint.Blueprint))
|
|
24
|
+
}),
|
|
25
|
+
objects: Schema.Struct({
|
|
26
|
+
added: Schema.Array(Ref.Ref(Obj.Unknown)),
|
|
27
|
+
removed: Schema.Array(Ref.Ref(Obj.Unknown))
|
|
28
|
+
})
|
|
29
|
+
}).pipe(Type.object({
|
|
30
|
+
typename: "org.dxos.type.contextBinding",
|
|
31
|
+
version: "0.1.0"
|
|
32
|
+
}));
|
|
33
|
+
var Bindings = class {
|
|
34
|
+
blueprints = new ComplexSet((ref) => ref.dxn.toString());
|
|
35
|
+
// TODO(burdon): Some DXNs have the Space prefix so only compare the object ID.
|
|
36
|
+
objects = new ComplexSet((ref) => ref.dxn.asEchoDXN()?.echoId);
|
|
37
|
+
toJSON() {
|
|
38
|
+
return {
|
|
39
|
+
blueprints: EArray.fromIterable(this.blueprints).map((ref) => ref.dxn.toString()),
|
|
40
|
+
objects: EArray.fromIterable(this.objects).map((ref) => ref.dxn.toString())
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
var AiContextBinder = class extends Resource {
|
|
45
|
+
_blueprints = Atom.make([]).pipe(Atom.keepAlive);
|
|
46
|
+
_objects = Atom.make([]).pipe(Atom.keepAlive);
|
|
47
|
+
_registry;
|
|
48
|
+
_feed;
|
|
49
|
+
_runtime;
|
|
50
|
+
#bindingsQuery;
|
|
51
|
+
constructor(options) {
|
|
52
|
+
super();
|
|
53
|
+
assertArgument(options.feed, "options.feed", "Feed is required");
|
|
54
|
+
assertArgument(options.runtime, "options.runtime", "Feed runtime is required");
|
|
55
|
+
this._feed = options.feed;
|
|
56
|
+
this._runtime = options.runtime;
|
|
57
|
+
this._registry = options.registry ?? Registry.make();
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Returns the blueprints atom for subscription.
|
|
61
|
+
*/
|
|
62
|
+
get blueprints() {
|
|
63
|
+
return this._blueprints;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Returns the objects atom for subscription.
|
|
67
|
+
*/
|
|
68
|
+
get objects() {
|
|
69
|
+
return this._objects;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Gets the current blueprints value.
|
|
73
|
+
*/
|
|
74
|
+
getBlueprints() {
|
|
75
|
+
return this._registry.get(this._blueprints);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Gets the current objects value.
|
|
79
|
+
*/
|
|
80
|
+
getObjects() {
|
|
81
|
+
return this._registry.get(this._objects);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Subscribe to changes in blueprints.
|
|
85
|
+
*/
|
|
86
|
+
subscribeBlueprints(cb) {
|
|
87
|
+
return this._registry.subscribe(this._blueprints, () => cb(this._registry.get(this._blueprints)));
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Subscribe to changes in objects.
|
|
91
|
+
*/
|
|
92
|
+
subscribeObjects(cb) {
|
|
93
|
+
return this._registry.subscribe(this._objects, () => cb(this._registry.get(this._objects)));
|
|
94
|
+
}
|
|
95
|
+
async _open() {
|
|
96
|
+
this.#bindingsQuery = await Runtime.runPromise(this._runtime)(Feed.query(this._feed, Query.type(ContextBinding)));
|
|
97
|
+
const initialResults = await this.#bindingsQuery.run();
|
|
98
|
+
await this._updateBindings(initialResults);
|
|
99
|
+
this._ctx.onDispose(this.#bindingsQuery.subscribe(async () => {
|
|
100
|
+
await this._updateBindings(this.#bindingsQuery.results);
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Re-reads bindings from the feed to pick up changes made by other processes.
|
|
105
|
+
*/
|
|
106
|
+
async sync() {
|
|
107
|
+
if (this.#bindingsQuery) {
|
|
108
|
+
const results = await this.#bindingsQuery.run();
|
|
109
|
+
log("sync", {
|
|
110
|
+
bindingItems: results.length
|
|
111
|
+
}, {
|
|
112
|
+
F: __dxlog_file,
|
|
113
|
+
L: 152,
|
|
114
|
+
S: this,
|
|
115
|
+
C: (f, a) => f(...a)
|
|
116
|
+
});
|
|
117
|
+
await this._updateBindings(results);
|
|
118
|
+
log("sync complete", {
|
|
119
|
+
blueprints: this._registry.get(this._blueprints).length,
|
|
120
|
+
blueprintKeys: this._registry.get(this._blueprints).map((bp) => bp.key)
|
|
121
|
+
}, {
|
|
122
|
+
F: __dxlog_file,
|
|
123
|
+
L: 154,
|
|
124
|
+
S: this,
|
|
125
|
+
C: (f, a) => f(...a)
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async _updateBindings(items) {
|
|
130
|
+
if (items.length === 0) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const bindings = this._reduce(items);
|
|
134
|
+
log("_updateBindings", {
|
|
135
|
+
items: items.length,
|
|
136
|
+
blueprintRefs: [
|
|
137
|
+
...bindings.blueprints
|
|
138
|
+
].map((ref) => ({
|
|
139
|
+
dxn: ref.dxn.toString(),
|
|
140
|
+
available: ref.isAvailable
|
|
141
|
+
}))
|
|
142
|
+
}, {
|
|
143
|
+
F: __dxlog_file,
|
|
144
|
+
L: 169,
|
|
145
|
+
S: this,
|
|
146
|
+
C: (f, a) => f(...a)
|
|
147
|
+
});
|
|
148
|
+
const currentBlueprints = this._registry.get(this._blueprints);
|
|
149
|
+
const currentObjects = this._registry.get(this._objects);
|
|
150
|
+
const resolvedBlueprints = await this._resolve(bindings.blueprints, currentBlueprints);
|
|
151
|
+
const resolvedObjects = await this._resolve(bindings.objects, currentObjects);
|
|
152
|
+
log("_updateBindings resolved", {
|
|
153
|
+
resolvedBlueprints: resolvedBlueprints.length,
|
|
154
|
+
resolvedBlueprintKeys: resolvedBlueprints.map((bp) => bp.key)
|
|
155
|
+
}, {
|
|
156
|
+
F: __dxlog_file,
|
|
157
|
+
L: 180,
|
|
158
|
+
S: this,
|
|
159
|
+
C: (f, a) => f(...a)
|
|
160
|
+
});
|
|
161
|
+
const reducedBlueprintDxns = new ComplexSet(DXN.hash, [
|
|
162
|
+
...bindings.blueprints
|
|
163
|
+
].map((ref) => ref.dxn));
|
|
164
|
+
const reducedObjectDxns = new ComplexSet(DXN.hash, [
|
|
165
|
+
...bindings.objects
|
|
166
|
+
].map((ref) => ref.dxn));
|
|
167
|
+
const filteredBlueprints = currentBlueprints.filter((obj) => reducedBlueprintDxns.has(Obj.getDXN(obj)));
|
|
168
|
+
const filteredObjects = currentObjects.filter((obj) => reducedObjectDxns.has(Obj.getDXN(obj)));
|
|
169
|
+
const mergedBlueprints = this._mergeInto(filteredBlueprints, resolvedBlueprints);
|
|
170
|
+
const mergedObjects = this._mergeInto(filteredObjects, resolvedObjects);
|
|
171
|
+
this._registry.set(this._blueprints, mergedBlueprints);
|
|
172
|
+
this._registry.set(this._objects, mergedObjects);
|
|
173
|
+
log("updated bindings", {
|
|
174
|
+
blueprints: mergedBlueprints.length,
|
|
175
|
+
objects: mergedObjects.length
|
|
176
|
+
}, {
|
|
177
|
+
F: __dxlog_file,
|
|
178
|
+
L: 203,
|
|
179
|
+
S: this,
|
|
180
|
+
C: (f, a) => f(...a)
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
async _close() {
|
|
184
|
+
this._registry.set(this._blueprints, []);
|
|
185
|
+
this._registry.set(this._objects, []);
|
|
186
|
+
}
|
|
187
|
+
async bind({ blueprints, objects }) {
|
|
188
|
+
const currentBlueprints = this._registry.get(this._blueprints);
|
|
189
|
+
const currentObjects = this._registry.get(this._objects);
|
|
190
|
+
const { added: addedBlueprints, next: nextBlueprints } = this._processBindings(blueprints, currentBlueprints);
|
|
191
|
+
const { added: addedObjects, next: nextObjects } = this._processBindings(objects, currentObjects);
|
|
192
|
+
if (!addedBlueprints.length && !addedObjects.length) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
this._registry.set(this._blueprints, nextBlueprints);
|
|
196
|
+
this._registry.set(this._objects, nextObjects);
|
|
197
|
+
log("bind", {
|
|
198
|
+
blueprints: addedBlueprints.length,
|
|
199
|
+
objects: addedObjects.length
|
|
200
|
+
}, {
|
|
201
|
+
F: __dxlog_file,
|
|
202
|
+
L: 229,
|
|
203
|
+
S: this,
|
|
204
|
+
C: (f, a) => f(...a)
|
|
205
|
+
});
|
|
206
|
+
await Runtime.runPromise(this._runtime)(Feed.append(this._feed, [
|
|
207
|
+
Obj.make(ContextBinding, {
|
|
208
|
+
blueprints: {
|
|
209
|
+
added: addedBlueprints,
|
|
210
|
+
removed: []
|
|
211
|
+
},
|
|
212
|
+
objects: {
|
|
213
|
+
added: addedObjects,
|
|
214
|
+
removed: []
|
|
215
|
+
}
|
|
216
|
+
})
|
|
217
|
+
]));
|
|
218
|
+
}
|
|
219
|
+
async unbind({ blueprints, objects }) {
|
|
220
|
+
if (!blueprints?.length && !objects?.length) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
const removedBlueprintDxns = (blueprints ?? []).map((ref) => ref.dxn);
|
|
224
|
+
const removedObjectDxns = (objects ?? []).map((ref) => ref.dxn);
|
|
225
|
+
if (removedBlueprintDxns.length > 0) {
|
|
226
|
+
const current = this._registry.get(this._blueprints);
|
|
227
|
+
this._registry.set(this._blueprints, current.filter((obj) => !removedBlueprintDxns.some((dxn) => DXN.equalsEchoId(Obj.getDXN(obj), dxn))));
|
|
228
|
+
}
|
|
229
|
+
if (removedObjectDxns.length > 0) {
|
|
230
|
+
const current = this._registry.get(this._objects);
|
|
231
|
+
this._registry.set(this._objects, current.filter((obj) => !removedObjectDxns.some((dxn) => DXN.equalsEchoId(Obj.getDXN(obj), dxn))));
|
|
232
|
+
}
|
|
233
|
+
log("unbind", {
|
|
234
|
+
blueprints: blueprints?.length,
|
|
235
|
+
objects: objects?.length
|
|
236
|
+
}, {
|
|
237
|
+
F: __dxlog_file,
|
|
238
|
+
L: 269,
|
|
239
|
+
S: this,
|
|
240
|
+
C: (f, a) => f(...a)
|
|
241
|
+
});
|
|
242
|
+
await Runtime.runPromise(this._runtime)(Feed.append(this._feed, [
|
|
243
|
+
Obj.make(ContextBinding, {
|
|
244
|
+
blueprints: {
|
|
245
|
+
added: [],
|
|
246
|
+
removed: blueprints ?? []
|
|
247
|
+
},
|
|
248
|
+
objects: {
|
|
249
|
+
added: [],
|
|
250
|
+
removed: objects ?? []
|
|
251
|
+
}
|
|
252
|
+
})
|
|
253
|
+
]));
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Process bindings to filter duplicates and determine next state.
|
|
257
|
+
*/
|
|
258
|
+
_processBindings(refs, current) {
|
|
259
|
+
const next = [
|
|
260
|
+
...current
|
|
261
|
+
];
|
|
262
|
+
const added = [];
|
|
263
|
+
if (!refs?.length) {
|
|
264
|
+
return {
|
|
265
|
+
added,
|
|
266
|
+
next
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
const seen = new Set(current.map((obj) => Obj.getDXN(obj).toString()));
|
|
270
|
+
for (const ref of refs) {
|
|
271
|
+
const dxn = ref.dxn.toString();
|
|
272
|
+
if (!seen.has(dxn)) {
|
|
273
|
+
seen.add(dxn);
|
|
274
|
+
added.push(ref);
|
|
275
|
+
if (ref.isAvailable) {
|
|
276
|
+
const target = ref.target;
|
|
277
|
+
if (target) {
|
|
278
|
+
next.push(target);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
added,
|
|
285
|
+
next
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Reduce results into sets of blueprints and objects.
|
|
290
|
+
*/
|
|
291
|
+
_reduce(items) {
|
|
292
|
+
return Function.pipe(items, EArray.reduce(new Bindings(), (context, { blueprints, objects }) => {
|
|
293
|
+
blueprints.added.forEach((ref) => context.blueprints.add(ref));
|
|
294
|
+
blueprints.removed.forEach((ref) => context.blueprints.delete(ref));
|
|
295
|
+
objects.added.forEach((ref) => context.objects.add(ref));
|
|
296
|
+
objects.removed.forEach((ref) => {
|
|
297
|
+
for (const obj of context.objects) {
|
|
298
|
+
if (DXN.equalsEchoId(obj.dxn, ref.dxn)) {
|
|
299
|
+
context.objects.delete(obj);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
return context;
|
|
304
|
+
}));
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Merge resolved items into the current set, adding only items not already present (by DXN).
|
|
308
|
+
*/
|
|
309
|
+
_mergeInto(current, resolved) {
|
|
310
|
+
const seen = new Set(current.map((obj) => Obj.getDXN(obj).toString()));
|
|
311
|
+
const merged = [
|
|
312
|
+
...current
|
|
313
|
+
];
|
|
314
|
+
for (const obj of resolved) {
|
|
315
|
+
const dxn = Obj.getDXN(obj).toString();
|
|
316
|
+
if (!seen.has(dxn)) {
|
|
317
|
+
seen.add(dxn);
|
|
318
|
+
merged.push(obj);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return merged;
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Resolve references to objects, loading them first if needed and falling back to existing objects.
|
|
325
|
+
*/
|
|
326
|
+
async _resolve(refs, current) {
|
|
327
|
+
const refArray = [
|
|
328
|
+
...refs
|
|
329
|
+
];
|
|
330
|
+
await Promise.all(refArray.map((ref) => ref.tryLoad()));
|
|
331
|
+
return refArray.map((ref) => {
|
|
332
|
+
let target;
|
|
333
|
+
if (ref.isAvailable) {
|
|
334
|
+
target = ref.target;
|
|
335
|
+
}
|
|
336
|
+
return target ?? current.find((obj) => Obj.getDXN(obj).toString() === ref.dxn.toString());
|
|
337
|
+
}).filter(isNonNullable);
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
var AiContextService = class _AiContextService extends Context.Tag("@dxos/assistant/AiContextService")() {
|
|
341
|
+
static bindContext = ({ blueprints, objects }) => Effect.gen(function* () {
|
|
342
|
+
const { binder } = yield* _AiContextService;
|
|
343
|
+
yield* Effect.promise(() => binder.bind({
|
|
344
|
+
blueprints,
|
|
345
|
+
objects
|
|
346
|
+
}));
|
|
347
|
+
});
|
|
348
|
+
static findObjects = (type) => {
|
|
349
|
+
return Effect.gen(function* () {
|
|
350
|
+
const { binder } = yield* _AiContextService;
|
|
351
|
+
return binder.getObjects().filter(Obj.instanceOf(type));
|
|
352
|
+
});
|
|
353
|
+
};
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
// src/session/artifact-diff.ts
|
|
357
|
+
import * as Context2 from "effect/Context";
|
|
358
|
+
var ArtifactDiffResolver = class extends Context2.Tag("@dxos/assistant/ArtifactDiffResolver")() {
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
// src/session/observer.ts
|
|
362
|
+
import * as Effect2 from "effect/Effect";
|
|
363
|
+
import * as Function2 from "effect/Function";
|
|
364
|
+
var GenerationObserver = Object.freeze({
|
|
365
|
+
make: ({ onBegin = Function2.constant(Effect2.void), onPart = Function2.constant(Effect2.void), onBlock = Function2.constant(Effect2.void), onEnd = Function2.constant(Effect2.void), onMessage = Function2.constant(Effect2.void) } = {}) => ({
|
|
366
|
+
onBegin,
|
|
367
|
+
onPart,
|
|
368
|
+
onBlock,
|
|
369
|
+
onEnd,
|
|
370
|
+
onMessage
|
|
371
|
+
}),
|
|
372
|
+
noop: () => GenerationObserver.make(),
|
|
373
|
+
/**
|
|
374
|
+
* Debug printer to be used in unit-tests and browser devtools.
|
|
375
|
+
*/
|
|
376
|
+
fromPrinter: (printer) => GenerationObserver.make({
|
|
377
|
+
onBlock: (block) => Effect2.sync(() => {
|
|
378
|
+
if (block.pending) {
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
printer.printContentBlock(block);
|
|
382
|
+
}),
|
|
383
|
+
onMessage: (message) => Effect2.sync(() => {
|
|
384
|
+
if (message.sender.role === "assistant") {
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
printer.printMessage(message);
|
|
388
|
+
})
|
|
389
|
+
})
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
// src/tracing.ts
|
|
393
|
+
import * as Schema2 from "effect/Schema";
|
|
394
|
+
import { Obj as Obj2 } from "@dxos/echo";
|
|
395
|
+
import { Trace } from "@dxos/functions";
|
|
396
|
+
import { ContentBlock, Actor } from "@dxos/types";
|
|
397
|
+
var PartialBlock = Trace.EventType("assistant.partialBlock", {
|
|
398
|
+
schema: Schema2.Struct({
|
|
399
|
+
messageId: Obj2.ID,
|
|
400
|
+
role: Actor.Role,
|
|
401
|
+
block: ContentBlock.Any
|
|
402
|
+
}),
|
|
403
|
+
isEphemeral: true
|
|
404
|
+
});
|
|
405
|
+
var CompleteBlock = Trace.EventType("assistant.completeBlock", {
|
|
406
|
+
schema: Schema2.Struct({
|
|
407
|
+
messageId: Obj2.ID,
|
|
408
|
+
role: Actor.Role,
|
|
409
|
+
block: ContentBlock.Any
|
|
410
|
+
}),
|
|
411
|
+
isEphemeral: false
|
|
412
|
+
});
|
|
413
|
+
var AgentTurnStarted = Trace.EventType("assistant.agentTurnStarted", {
|
|
414
|
+
schema: Schema2.Struct({
|
|
415
|
+
/** Number of history messages. */
|
|
416
|
+
historyLength: Schema2.Number,
|
|
417
|
+
/** Number of pending messages. */
|
|
418
|
+
pendingLength: Schema2.Number,
|
|
419
|
+
/** System prompt length in characters. */
|
|
420
|
+
systemPromptLength: Schema2.Number
|
|
421
|
+
}),
|
|
422
|
+
isEphemeral: false
|
|
423
|
+
});
|
|
424
|
+
var AgentTurnCompleted = Trace.EventType("assistant.agentTurnCompleted", {
|
|
425
|
+
schema: Schema2.Struct({
|
|
426
|
+
/** Number of messages produced in this turn. */
|
|
427
|
+
messageCount: Schema2.Number,
|
|
428
|
+
/** Whether the agent is done (no more tool calls). */
|
|
429
|
+
done: Schema2.Boolean
|
|
430
|
+
}),
|
|
431
|
+
isEphemeral: false
|
|
432
|
+
});
|
|
433
|
+
var ToolCallStarted = Trace.EventType("assistant.toolCallStarted", {
|
|
434
|
+
schema: Schema2.Struct({
|
|
435
|
+
/** Tool name. */
|
|
436
|
+
name: Schema2.String,
|
|
437
|
+
/** Tool call ID. */
|
|
438
|
+
toolCallId: Schema2.String
|
|
439
|
+
}),
|
|
440
|
+
isEphemeral: false
|
|
441
|
+
});
|
|
442
|
+
var ToolCallCompleted = Trace.EventType("assistant.toolCallCompleted", {
|
|
443
|
+
schema: Schema2.Struct({
|
|
444
|
+
/** Tool name. */
|
|
445
|
+
name: Schema2.String,
|
|
446
|
+
/** Tool call ID. */
|
|
447
|
+
toolCallId: Schema2.String,
|
|
448
|
+
/** Whether the tool call resulted in an error. */
|
|
449
|
+
isError: Schema2.Boolean
|
|
450
|
+
}),
|
|
451
|
+
isEphemeral: false
|
|
452
|
+
});
|
|
453
|
+
var AgentInputReceived = Trace.EventType("assistant.agentInputReceived", {
|
|
454
|
+
schema: Schema2.Struct({
|
|
455
|
+
/** Type of input: 'prompt' or 'tool_result'. */
|
|
456
|
+
inputType: Schema2.String,
|
|
457
|
+
/** Length of input content. */
|
|
458
|
+
contentLength: Schema2.Number
|
|
459
|
+
}),
|
|
460
|
+
isEphemeral: false
|
|
461
|
+
});
|
|
462
|
+
var AgentRequestCompleted = Trace.EventType("assistant.agentRequestCompleted", {
|
|
463
|
+
schema: Schema2.Struct({
|
|
464
|
+
/** Number of pending items remaining in queue. */
|
|
465
|
+
pendingItems: Schema2.Number
|
|
466
|
+
}),
|
|
467
|
+
isEphemeral: false
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
// src/errors.ts
|
|
471
|
+
import { BaseError } from "@dxos/errors";
|
|
472
|
+
var AiModelError = class extends BaseError.extend("AiModelError", "AI model error") {
|
|
473
|
+
};
|
|
474
|
+
var AiAssistantError = class extends BaseError.extend("AiAssistantError", "AI assistant error") {
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
// src/session/format.ts
|
|
478
|
+
import * as Effect3 from "effect/Effect";
|
|
479
|
+
import * as Function3 from "effect/Function";
|
|
480
|
+
import * as Option from "effect/Option";
|
|
481
|
+
import { Template } from "@dxos/blueprints";
|
|
482
|
+
import { Obj as Obj3 } from "@dxos/echo";
|
|
483
|
+
import { ObjectVersion } from "@dxos/echo-db";
|
|
484
|
+
import { log as log2 } from "@dxos/log";
|
|
485
|
+
import { Message } from "@dxos/types";
|
|
486
|
+
import { trim } from "@dxos/util";
|
|
487
|
+
var __dxlog_file2 = "/__w/dxos/dxos/packages/core/assistant/src/session/format.ts";
|
|
488
|
+
var formatSystemPrompt = ({ system, blueprints = [], objects = [] }) => Effect3.gen(function* () {
|
|
489
|
+
const blueprintDefs = yield* Function3.pipe(blueprints, Effect3.forEach((blueprint) => Effect3.succeed(blueprint.instructions)), Effect3.flatMap(Effect3.forEach((template) => Effect3.gen(function* () {
|
|
490
|
+
return trim`
|
|
491
|
+
<blueprint>
|
|
492
|
+
${yield* Template.processTemplate(template)}
|
|
493
|
+
</blueprint>
|
|
494
|
+
`;
|
|
495
|
+
}))), Effect3.map((blueprints2) => blueprints2.length > 0 ? [
|
|
496
|
+
"## Blueprints Definitions",
|
|
497
|
+
...blueprints2
|
|
498
|
+
].join("\n\n") : void 0));
|
|
499
|
+
const objectDefs = yield* Function3.pipe(objects, Effect3.forEach((object) => Effect3.succeed(trim`
|
|
500
|
+
<object>
|
|
501
|
+
<dxn>${Obj3.getDXN(object)}</dxn>
|
|
502
|
+
<typename>${Obj3.getTypename(object)}</typename>
|
|
503
|
+
</object>
|
|
504
|
+
`)), Effect3.map((objects2) => objects2.length > 0 ? [
|
|
505
|
+
"## Context Objects",
|
|
506
|
+
...objects2
|
|
507
|
+
].join("\n\n") : void 0));
|
|
508
|
+
return yield* Function3.pipe(Effect3.succeed([
|
|
509
|
+
system,
|
|
510
|
+
blueprintDefs,
|
|
511
|
+
objectDefs
|
|
512
|
+
].filter((def) => def !== void 0)), Effect3.map((parts) => parts.join("\n\n")));
|
|
513
|
+
});
|
|
514
|
+
var formatUserPrompt = ({ prompt, history = [] }) => Effect3.gen(function* () {
|
|
515
|
+
const blocks = [];
|
|
516
|
+
const artifactDiffResolver = yield* Effect3.serviceOption(ArtifactDiffResolver);
|
|
517
|
+
if (Option.isSome(artifactDiffResolver)) {
|
|
518
|
+
const versions = gatherObjectVersions(history);
|
|
519
|
+
const artifactDiff = yield* Effect3.tryPromise({
|
|
520
|
+
try: () => artifactDiffResolver.value.resolve([
|
|
521
|
+
...versions.entries()
|
|
522
|
+
].map(([id, version]) => ({
|
|
523
|
+
id,
|
|
524
|
+
lastVersion: version
|
|
525
|
+
}))),
|
|
526
|
+
catch: AiAssistantError.wrap({
|
|
527
|
+
message: "Artifact diff resolution error"
|
|
528
|
+
})
|
|
529
|
+
});
|
|
530
|
+
log2("version", {
|
|
531
|
+
artifactDiff,
|
|
532
|
+
versions
|
|
533
|
+
}, {
|
|
534
|
+
F: __dxlog_file2,
|
|
535
|
+
L: 93,
|
|
536
|
+
S: this,
|
|
537
|
+
C: (f, a) => f(...a)
|
|
538
|
+
});
|
|
539
|
+
for (const [id, { version }] of [
|
|
540
|
+
...artifactDiff.entries()
|
|
541
|
+
]) {
|
|
542
|
+
if (ObjectVersion.equals(version, versions.get(id))) {
|
|
543
|
+
artifactDiff.delete(id);
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
blocks.push({
|
|
547
|
+
_tag: "anchor",
|
|
548
|
+
objectId: id,
|
|
549
|
+
version
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
if (artifactDiff.size > 0) {
|
|
553
|
+
blocks.push(createArtifactUpdateBlock(artifactDiff));
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
return Obj3.make(Message.Message, {
|
|
557
|
+
created: (/* @__PURE__ */ new Date()).toISOString(),
|
|
558
|
+
sender: {
|
|
559
|
+
role: "user"
|
|
560
|
+
},
|
|
561
|
+
blocks: [
|
|
562
|
+
...blocks,
|
|
563
|
+
{
|
|
564
|
+
_tag: "text",
|
|
565
|
+
text: prompt
|
|
566
|
+
}
|
|
567
|
+
]
|
|
568
|
+
});
|
|
569
|
+
});
|
|
570
|
+
var gatherObjectVersions = (messages) => {
|
|
571
|
+
const artifactIds = /* @__PURE__ */ new Map();
|
|
572
|
+
for (const message of messages) {
|
|
573
|
+
for (const block of message.blocks) {
|
|
574
|
+
if (block._tag === "anchor") {
|
|
575
|
+
artifactIds.set(block.objectId, block.version);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
return artifactIds;
|
|
580
|
+
};
|
|
581
|
+
var createArtifactUpdateBlock = (artifactDiff) => {
|
|
582
|
+
return {
|
|
583
|
+
_tag: "text",
|
|
584
|
+
// TODO(dmaretskyi): Does this need to be a special content-block?
|
|
585
|
+
disposition: "artifact-update",
|
|
586
|
+
text: trim`
|
|
587
|
+
The following artifacts have been updated since the last message:
|
|
588
|
+
${[
|
|
589
|
+
...artifactDiff.entries()
|
|
590
|
+
].map(([id, { diff }]) => `<changed-artifact id="${id}">${diff ? `
|
|
591
|
+
${diff}` : ""}</changed-artifact>`).join("\n")}
|
|
592
|
+
`
|
|
593
|
+
};
|
|
594
|
+
};
|
|
595
|
+
|
|
596
|
+
// src/session/session.ts
|
|
597
|
+
import * as LanguageModel from "@effect/ai/LanguageModel";
|
|
598
|
+
import * as Array2 from "effect/Array";
|
|
599
|
+
import * as Chunk from "effect/Chunk";
|
|
600
|
+
import * as Effect4 from "effect/Effect";
|
|
601
|
+
import { pipe as pipe3 } from "effect/Function";
|
|
602
|
+
import * as Option2 from "effect/Option";
|
|
603
|
+
import * as Stream from "effect/Stream";
|
|
604
|
+
import { AiParser, AiPreprocessor, AiSummarizer, callTool, withoutToolCallParising } from "@dxos/ai";
|
|
605
|
+
import { Obj as Obj4 } from "@dxos/echo";
|
|
606
|
+
import { Trace as Trace2, TracingService } from "@dxos/functions";
|
|
607
|
+
import { log as log3 } from "@dxos/log";
|
|
608
|
+
import { ContentBlock as ContentBlock2, Message as Message2 } from "@dxos/types";
|
|
609
|
+
var __dxlog_file3 = "/__w/dxos/dxos/packages/core/assistant/src/session/session.ts";
|
|
610
|
+
var AiSession = class {
|
|
611
|
+
_options;
|
|
612
|
+
/** Prevents concurrent execution of session. */
|
|
613
|
+
_semaphore = Effect4.runSync(Effect4.makeSemaphore(1));
|
|
614
|
+
_observer;
|
|
615
|
+
_onOutput;
|
|
616
|
+
/** Message history from queue. */
|
|
617
|
+
// TODO(burdon): Evolve into supporting a git-like graph of messages.
|
|
618
|
+
_history = [];
|
|
619
|
+
/** Pending messages for this session (incl. the current prompt). */
|
|
620
|
+
_pending = [];
|
|
621
|
+
_started = 0;
|
|
622
|
+
_ended = 0;
|
|
623
|
+
_toolCalls = 0;
|
|
624
|
+
constructor(_options = {}) {
|
|
625
|
+
this._options = _options;
|
|
626
|
+
this._observer = _options.observer ?? GenerationObserver.noop();
|
|
627
|
+
this._onOutput = _options.onOutput ?? (() => Effect4.void);
|
|
628
|
+
}
|
|
629
|
+
get duration() {
|
|
630
|
+
return this._ended - this._started;
|
|
631
|
+
}
|
|
632
|
+
get toolCalls() {
|
|
633
|
+
return this._toolCalls;
|
|
634
|
+
}
|
|
635
|
+
get pending() {
|
|
636
|
+
return this._pending;
|
|
637
|
+
}
|
|
638
|
+
_submitMessage = (message) => Effect4.gen(this, function* () {
|
|
639
|
+
this._pending.push(message);
|
|
640
|
+
yield* this._observer.onMessage(message);
|
|
641
|
+
for (const block of message.blocks) {
|
|
642
|
+
yield* Trace2.write(CompleteBlock, {
|
|
643
|
+
messageId: message.id,
|
|
644
|
+
role: message.sender.role,
|
|
645
|
+
block
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
yield* this._onOutput(message);
|
|
649
|
+
return message;
|
|
650
|
+
});
|
|
651
|
+
getToolCalls = () => pipe3([
|
|
652
|
+
...this._history,
|
|
653
|
+
...this._pending
|
|
654
|
+
], Array2.reverse, Array2.takeWhile((_) => _.sender.role === "assistant"), Array2.flatMap((_) => _.blocks.filter(ContentBlock2.is("toolCall")).map((block) => ({
|
|
655
|
+
block,
|
|
656
|
+
message: _
|
|
657
|
+
}))), Array2.filter((_) => !_.block.providerExecuted));
|
|
658
|
+
/**
|
|
659
|
+
* Initialize a session: set up history, perform summarization if needed, and submit the user prompt.
|
|
660
|
+
* Must be called before `runTurn()`.
|
|
661
|
+
*/
|
|
662
|
+
begin = ({ prompt, system, history = [], blueprints = [], objects = [] }) => Effect4.gen(this, function* () {
|
|
663
|
+
this._started = Date.now();
|
|
664
|
+
this._history = [
|
|
665
|
+
...history
|
|
666
|
+
];
|
|
667
|
+
this._pending = [];
|
|
668
|
+
const systemPrompt = yield* formatSystemPrompt({
|
|
669
|
+
system,
|
|
670
|
+
blueprints,
|
|
671
|
+
objects
|
|
672
|
+
}).pipe(Effect4.orDie);
|
|
673
|
+
if (this._options.summarizationThreshold !== void 0) {
|
|
674
|
+
const tokenCount = yield* AiPreprocessor.estimateTokens(yield* AiPreprocessor.preprocessPrompt([
|
|
675
|
+
...this._history
|
|
676
|
+
], {
|
|
677
|
+
system: systemPrompt
|
|
678
|
+
}));
|
|
679
|
+
if (tokenCount > this._options.summarizationThreshold) {
|
|
680
|
+
const summary = yield* AiSummarizer.summarize([
|
|
681
|
+
...this._history
|
|
682
|
+
]);
|
|
683
|
+
yield* this._submitMessage(summary);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
yield* this._submitMessage(yield* formatUserPrompt({
|
|
687
|
+
prompt,
|
|
688
|
+
history
|
|
689
|
+
}));
|
|
690
|
+
});
|
|
691
|
+
/**
|
|
692
|
+
* Execute a single turn: one LLM generation followed by tool execution.
|
|
693
|
+
* The toolkit and system prompt can be updated between turns to reflect context changes (e.g. dynamically enabled blueprints).
|
|
694
|
+
*/
|
|
695
|
+
runAgentTurn = ({ system, toolkit }) => Effect4.gen(this, function* () {
|
|
696
|
+
log3("request", {
|
|
697
|
+
system: {
|
|
698
|
+
snippet: createSnippet(system),
|
|
699
|
+
length: system.length
|
|
700
|
+
},
|
|
701
|
+
pending: this._pending.length,
|
|
702
|
+
history: this._history.length
|
|
703
|
+
}, {
|
|
704
|
+
F: __dxlog_file3,
|
|
705
|
+
L: 207,
|
|
706
|
+
S: this,
|
|
707
|
+
C: (f, a) => f(...a)
|
|
708
|
+
});
|
|
709
|
+
const prompt = yield* AiPreprocessor.preprocessPrompt([
|
|
710
|
+
...this._history,
|
|
711
|
+
...this._pending
|
|
712
|
+
], {
|
|
713
|
+
system,
|
|
714
|
+
cacheControl: "ephemeral"
|
|
715
|
+
});
|
|
716
|
+
const observer = this._observer;
|
|
717
|
+
let currentMessageId = null;
|
|
718
|
+
const messages = yield* LanguageModel.streamText({
|
|
719
|
+
prompt,
|
|
720
|
+
toolkit,
|
|
721
|
+
disableToolCallResolution: true
|
|
722
|
+
}).pipe(withoutToolCallParising, AiParser.parseResponse({
|
|
723
|
+
emitPartial: true,
|
|
724
|
+
onBegin: () => observer.onBegin(),
|
|
725
|
+
onBlock: (block) => observer.onBlock(block),
|
|
726
|
+
onPart: (part) => observer.onPart(part),
|
|
727
|
+
onEnd: (summary) => observer.onEnd(summary)
|
|
728
|
+
}), Stream.mapEffect((block) => Effect4.gen(this, function* () {
|
|
729
|
+
if (block.pending) {
|
|
730
|
+
currentMessageId ??= Obj4.ID.random();
|
|
731
|
+
log3("emit ephemeral message", {
|
|
732
|
+
id: currentMessageId,
|
|
733
|
+
type: block._tag
|
|
734
|
+
}, {
|
|
735
|
+
F: __dxlog_file3,
|
|
736
|
+
L: 239,
|
|
737
|
+
S: this,
|
|
738
|
+
C: (f, a) => f(...a)
|
|
739
|
+
});
|
|
740
|
+
yield* Trace2.write(PartialBlock, {
|
|
741
|
+
messageId: currentMessageId,
|
|
742
|
+
role: "assistant",
|
|
743
|
+
block
|
|
744
|
+
});
|
|
745
|
+
return Option2.none();
|
|
746
|
+
} else {
|
|
747
|
+
currentMessageId ??= Obj4.ID.random();
|
|
748
|
+
const id = currentMessageId;
|
|
749
|
+
currentMessageId = null;
|
|
750
|
+
log3("emit complete message", {
|
|
751
|
+
id,
|
|
752
|
+
type: block._tag
|
|
753
|
+
}, {
|
|
754
|
+
F: __dxlog_file3,
|
|
755
|
+
L: 250,
|
|
756
|
+
S: this,
|
|
757
|
+
C: (f, a) => f(...a)
|
|
758
|
+
});
|
|
759
|
+
const message = Obj4.make(Message2.Message, {
|
|
760
|
+
id,
|
|
761
|
+
created: (/* @__PURE__ */ new Date()).toISOString(),
|
|
762
|
+
sender: {
|
|
763
|
+
role: "assistant"
|
|
764
|
+
},
|
|
765
|
+
blocks: [
|
|
766
|
+
block
|
|
767
|
+
]
|
|
768
|
+
});
|
|
769
|
+
return Option2.some(yield* this._submitMessage(message));
|
|
770
|
+
}
|
|
771
|
+
}), {
|
|
772
|
+
concurrency: 1,
|
|
773
|
+
unordered: false
|
|
774
|
+
}), Stream.filterMap((_) => _), Stream.runCollect, Effect4.map(Chunk.toArray));
|
|
775
|
+
const toolCalls = this.getToolCalls();
|
|
776
|
+
if (toolCalls.length === 0) {
|
|
777
|
+
this._ended = Date.now();
|
|
778
|
+
return {
|
|
779
|
+
messages,
|
|
780
|
+
done: true
|
|
781
|
+
};
|
|
782
|
+
} else if (!toolkit) {
|
|
783
|
+
throw new Error("No toolkit provided");
|
|
784
|
+
}
|
|
785
|
+
return {
|
|
786
|
+
messages,
|
|
787
|
+
done: false
|
|
788
|
+
};
|
|
789
|
+
});
|
|
790
|
+
runTools = ({ toolkit }) => Effect4.gen(this, function* () {
|
|
791
|
+
const toolCalls = this.getToolCalls();
|
|
792
|
+
const toolResults = yield* Effect4.forEach(toolCalls, ({ block, message }) => {
|
|
793
|
+
if (!toolkit) {
|
|
794
|
+
throw new Error("No toolkit provided");
|
|
795
|
+
}
|
|
796
|
+
return callTool(toolkit, block).pipe(Effect4.provide(TracingService.layerSubframe((context) => ({
|
|
797
|
+
...context,
|
|
798
|
+
parentMessage: message.id,
|
|
799
|
+
toolCallId: block.toolCallId
|
|
800
|
+
}))));
|
|
801
|
+
});
|
|
802
|
+
yield* this._submitMessage(Obj4.make(Message2.Message, {
|
|
803
|
+
created: (/* @__PURE__ */ new Date()).toISOString(),
|
|
804
|
+
sender: {
|
|
805
|
+
role: "tool"
|
|
806
|
+
},
|
|
807
|
+
blocks: toolResults
|
|
808
|
+
}));
|
|
809
|
+
this._toolCalls += toolResults.length;
|
|
810
|
+
});
|
|
811
|
+
/**
|
|
812
|
+
* Run a full conversation turn loop. Equivalent to calling `begin()` then `runTurn()` in a loop.
|
|
813
|
+
*/
|
|
814
|
+
run = ({ prompt, system: systemTemplate, history = [], objects = [], blueprints = [], toolkit }) => Effect4.gen(this, function* () {
|
|
815
|
+
yield* this.begin({
|
|
816
|
+
prompt,
|
|
817
|
+
system: systemTemplate,
|
|
818
|
+
history,
|
|
819
|
+
objects,
|
|
820
|
+
blueprints
|
|
821
|
+
});
|
|
822
|
+
const system = yield* formatSystemPrompt({
|
|
823
|
+
system: systemTemplate,
|
|
824
|
+
blueprints,
|
|
825
|
+
objects
|
|
826
|
+
}).pipe(Effect4.orDie);
|
|
827
|
+
do {
|
|
828
|
+
const { done } = yield* this.runAgentTurn({
|
|
829
|
+
system,
|
|
830
|
+
toolkit
|
|
831
|
+
});
|
|
832
|
+
if (done) {
|
|
833
|
+
break;
|
|
834
|
+
}
|
|
835
|
+
yield* this.runTools({
|
|
836
|
+
toolkit
|
|
837
|
+
});
|
|
838
|
+
} while (true);
|
|
839
|
+
log3("done", {
|
|
840
|
+
pending: this._pending.length,
|
|
841
|
+
duration: this.duration,
|
|
842
|
+
tools: this._toolCalls
|
|
843
|
+
}, {
|
|
844
|
+
F: __dxlog_file3,
|
|
845
|
+
L: 334,
|
|
846
|
+
S: this,
|
|
847
|
+
C: (f, a) => f(...a)
|
|
848
|
+
});
|
|
849
|
+
return this._pending;
|
|
850
|
+
}).pipe(this._semaphore.withPermits(1), Effect4.withSpan("AiSession.run"));
|
|
851
|
+
};
|
|
852
|
+
var createSnippet = (text, len = 32) => text.length <= len * 2 ? text : [
|
|
853
|
+
text.slice(0, len),
|
|
854
|
+
"...",
|
|
855
|
+
text.slice(-len)
|
|
856
|
+
].join("");
|
|
857
|
+
|
|
858
|
+
// src/session/toolkit.ts
|
|
859
|
+
import * as Toolkit from "@effect/ai/Toolkit";
|
|
860
|
+
import * as Effect5 from "effect/Effect";
|
|
861
|
+
import { GenericToolkit, ToolExecutionService, ToolResolverService } from "@dxos/ai";
|
|
862
|
+
import { isTruthy } from "@dxos/util";
|
|
863
|
+
var createToolkit = ({ toolkit: toolkitProp, blueprints = [], genericToolkits = [] }) => Effect5.gen(function* () {
|
|
864
|
+
const blueprintToolkit = yield* ToolResolverService.resolveToolkit(blueprints.flatMap(({ tools }) => tools));
|
|
865
|
+
const blueprintToolHandler = yield* blueprintToolkit.toContext(ToolExecutionService.handlersFor(blueprintToolkit));
|
|
866
|
+
const genericToolkit = GenericToolkit.merge(...genericToolkits);
|
|
867
|
+
const toolkit = Toolkit.merge(...[
|
|
868
|
+
toolkitProp,
|
|
869
|
+
blueprintToolkit,
|
|
870
|
+
genericToolkit.toolkit
|
|
871
|
+
].filter(isTruthy));
|
|
872
|
+
return yield* toolkit.pipe(Effect5.provide(blueprintToolHandler), Effect5.provide(genericToolkit.layer));
|
|
873
|
+
});
|
|
874
|
+
|
|
875
|
+
// src/session/version-pin.ts
|
|
876
|
+
import * as Schema3 from "effect/Schema";
|
|
877
|
+
import { getVersion } from "@dxos/echo-db";
|
|
878
|
+
import { ObjectId } from "@dxos/keys";
|
|
879
|
+
var ObjectVersionSchema = Schema3.Unknown;
|
|
880
|
+
var VersionPinSchema = Schema3.Struct({
|
|
881
|
+
// TODO(dmaretskyi): Use Ref when those support encoding.
|
|
882
|
+
objectId: ObjectId,
|
|
883
|
+
// TODO(dmaretskyi): Could be opaque.
|
|
884
|
+
version: ObjectVersionSchema
|
|
885
|
+
});
|
|
886
|
+
var VersionPin = class extends VersionPinSchema {
|
|
887
|
+
static DISPOSITION = "version-pin";
|
|
888
|
+
static fromObject(object) {
|
|
889
|
+
return VersionPin.make({
|
|
890
|
+
objectId: object.id,
|
|
891
|
+
version: getVersion(object)
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
static createBlock(pin) {
|
|
895
|
+
return {
|
|
896
|
+
_tag: "json",
|
|
897
|
+
disposition: VersionPin.DISPOSITION,
|
|
898
|
+
data: JSON.stringify(pin)
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
};
|
|
902
|
+
|
|
903
|
+
// src/conversation/conversation.ts
|
|
904
|
+
import * as Array3 from "effect/Array";
|
|
905
|
+
import * as Context3 from "effect/Context";
|
|
906
|
+
import * as Effect6 from "effect/Effect";
|
|
907
|
+
import * as Either from "effect/Either";
|
|
908
|
+
import { pipe as pipe4 } from "effect/Function";
|
|
909
|
+
import * as Layer from "effect/Layer";
|
|
910
|
+
import * as Record from "effect/Record";
|
|
911
|
+
import * as Runtime2 from "effect/Runtime";
|
|
912
|
+
import { Resource as Resource2 } from "@dxos/context";
|
|
913
|
+
import { Database, Feed as Feed2, Filter, Obj as Obj5 } from "@dxos/echo";
|
|
914
|
+
import { acquireReleaseResource } from "@dxos/effect";
|
|
915
|
+
import { invariant } from "@dxos/invariant";
|
|
916
|
+
import { log as log4 } from "@dxos/log";
|
|
917
|
+
import { McpToolkit } from "@dxos/mcp-client";
|
|
918
|
+
import { Message as Message3 } from "@dxos/types";
|
|
919
|
+
var __dxlog_file4 = "/__w/dxos/dxos/packages/core/assistant/src/conversation/conversation.ts";
|
|
920
|
+
var SUMMARY_THRESHOLD = 8e4;
|
|
921
|
+
var AiConversation = class extends Resource2 {
|
|
922
|
+
/**
|
|
923
|
+
* Blueprints and objects bound to the conversation.
|
|
924
|
+
*/
|
|
925
|
+
_binder;
|
|
926
|
+
_feed;
|
|
927
|
+
_runtime;
|
|
928
|
+
_toolkit;
|
|
929
|
+
constructor(options) {
|
|
930
|
+
super();
|
|
931
|
+
this._feed = options.feed;
|
|
932
|
+
this._runtime = options.runtime;
|
|
933
|
+
invariant(this._feed, void 0, {
|
|
934
|
+
F: __dxlog_file4,
|
|
935
|
+
L: 74,
|
|
936
|
+
S: this,
|
|
937
|
+
A: [
|
|
938
|
+
"this._feed",
|
|
939
|
+
""
|
|
940
|
+
]
|
|
941
|
+
});
|
|
942
|
+
invariant(this._runtime, void 0, {
|
|
943
|
+
F: __dxlog_file4,
|
|
944
|
+
L: 75,
|
|
945
|
+
S: this,
|
|
946
|
+
A: [
|
|
947
|
+
"this._runtime",
|
|
948
|
+
""
|
|
949
|
+
]
|
|
950
|
+
});
|
|
951
|
+
this._binder = new AiContextBinder({
|
|
952
|
+
feed: this._feed,
|
|
953
|
+
runtime: this._runtime,
|
|
954
|
+
registry: options.registry
|
|
955
|
+
});
|
|
956
|
+
}
|
|
957
|
+
async _open() {
|
|
958
|
+
await this._binder.open(this._ctx);
|
|
959
|
+
}
|
|
960
|
+
get feed() {
|
|
961
|
+
return this._feed;
|
|
962
|
+
}
|
|
963
|
+
get context() {
|
|
964
|
+
return this._binder;
|
|
965
|
+
}
|
|
966
|
+
get toolkit() {
|
|
967
|
+
return this._toolkit;
|
|
968
|
+
}
|
|
969
|
+
async getHistory() {
|
|
970
|
+
const queryResult = await Runtime2.runPromise(this._runtime)(Feed2.query(this._feed, Filter.type(Message3.Message)));
|
|
971
|
+
const items = await queryResult.run();
|
|
972
|
+
return items.filter(Obj5.instanceOf(Message3.Message));
|
|
973
|
+
}
|
|
974
|
+
getTools() {
|
|
975
|
+
return Effect6.gen(this, function* () {
|
|
976
|
+
const blueprints = this.context.getBlueprints();
|
|
977
|
+
const tookit = yield* createToolkit({
|
|
978
|
+
toolkit: this._toolkit,
|
|
979
|
+
blueprints
|
|
980
|
+
});
|
|
981
|
+
return tookit.tools;
|
|
982
|
+
}).pipe(Effect6.orDie);
|
|
983
|
+
}
|
|
984
|
+
/**
|
|
985
|
+
* Creates a new cancelable request effect.
|
|
986
|
+
*/
|
|
987
|
+
createRequest(params) {
|
|
988
|
+
return Effect6.gen(this, function* () {
|
|
989
|
+
const history = yield* Effect6.promise(() => this.getHistory());
|
|
990
|
+
const blueprints = this.context.getBlueprints();
|
|
991
|
+
const objects = this.context.getObjects();
|
|
992
|
+
log4("run", {
|
|
993
|
+
history: history.length,
|
|
994
|
+
blueprints: blueprints.length,
|
|
995
|
+
objects: objects.length
|
|
996
|
+
}, {
|
|
997
|
+
F: __dxlog_file4,
|
|
998
|
+
L: 120,
|
|
999
|
+
S: this,
|
|
1000
|
+
C: (f, a) => f(...a)
|
|
1001
|
+
});
|
|
1002
|
+
const session = new AiSession({
|
|
1003
|
+
summarizationThreshold: SUMMARY_THRESHOLD,
|
|
1004
|
+
observer: params.observer,
|
|
1005
|
+
onOutput: (message) => Effect6.promise(() => Runtime2.runPromise(this._runtime)(Feed2.append(this._feed, [
|
|
1006
|
+
message
|
|
1007
|
+
])))
|
|
1008
|
+
});
|
|
1009
|
+
yield* session.begin({
|
|
1010
|
+
history,
|
|
1011
|
+
blueprints,
|
|
1012
|
+
objects,
|
|
1013
|
+
prompt: params.prompt,
|
|
1014
|
+
system: params.system
|
|
1015
|
+
});
|
|
1016
|
+
do {
|
|
1017
|
+
yield* Effect6.promise(() => this.context.sync());
|
|
1018
|
+
const currentBlueprints = this.context.getBlueprints();
|
|
1019
|
+
const mcps = yield* connectMcpServers(currentBlueprints);
|
|
1020
|
+
const toolkit = yield* createToolkit({
|
|
1021
|
+
toolkit: params.toolkit,
|
|
1022
|
+
blueprints: currentBlueprints,
|
|
1023
|
+
genericToolkits: mcps
|
|
1024
|
+
});
|
|
1025
|
+
log4("toolkit", {
|
|
1026
|
+
tools: Record.keys(toolkit.tools)
|
|
1027
|
+
}, {
|
|
1028
|
+
F: __dxlog_file4,
|
|
1029
|
+
L: 151,
|
|
1030
|
+
S: this,
|
|
1031
|
+
C: (f, a) => f(...a)
|
|
1032
|
+
});
|
|
1033
|
+
const system = yield* formatSystemPrompt({
|
|
1034
|
+
system: params.system,
|
|
1035
|
+
blueprints: currentBlueprints,
|
|
1036
|
+
objects: this.context.getObjects()
|
|
1037
|
+
}).pipe(Effect6.orDie);
|
|
1038
|
+
const { done } = yield* session.runAgentTurn({
|
|
1039
|
+
system,
|
|
1040
|
+
toolkit
|
|
1041
|
+
});
|
|
1042
|
+
if (done) {
|
|
1043
|
+
break;
|
|
1044
|
+
}
|
|
1045
|
+
yield* session.runTools({
|
|
1046
|
+
toolkit
|
|
1047
|
+
});
|
|
1048
|
+
} while (true);
|
|
1049
|
+
log4("result", {
|
|
1050
|
+
messages: session.pending.length,
|
|
1051
|
+
duration: session.duration,
|
|
1052
|
+
toolCalls: session.toolCalls
|
|
1053
|
+
}, {
|
|
1054
|
+
F: __dxlog_file4,
|
|
1055
|
+
L: 170,
|
|
1056
|
+
S: this,
|
|
1057
|
+
C: (f, a) => f(...a)
|
|
1058
|
+
});
|
|
1059
|
+
return [
|
|
1060
|
+
...session.pending
|
|
1061
|
+
];
|
|
1062
|
+
}).pipe(Effect6.provide(Layer.mergeAll(Layer.succeed(AiContextService, {
|
|
1063
|
+
binder: this.context
|
|
1064
|
+
}), Layer.succeed(AiConversationService, this))));
|
|
1065
|
+
}
|
|
1066
|
+
};
|
|
1067
|
+
var AiConversationService = class _AiConversationService extends Context3.Tag("@dxos/assistant/AiConversationService")() {
|
|
1068
|
+
/**
|
|
1069
|
+
* Create a new conversation layer from options.
|
|
1070
|
+
*/
|
|
1071
|
+
static layer = (options) => aiContextFromConversation.pipe(Layer.provideMerge(Layer.scoped(_AiConversationService, Effect6.gen(function* () {
|
|
1072
|
+
const conversation = yield* acquireReleaseResource(() => new AiConversation(options));
|
|
1073
|
+
return conversation;
|
|
1074
|
+
}))));
|
|
1075
|
+
/**
|
|
1076
|
+
* Create a new conversation with a new feed.
|
|
1077
|
+
*/
|
|
1078
|
+
static layerNewFeed = (options) => Layer.unwrapScoped(Effect6.gen(function* () {
|
|
1079
|
+
const feed = Feed2.make();
|
|
1080
|
+
yield* Database.add(feed);
|
|
1081
|
+
const runtime4 = yield* Effect6.runtime();
|
|
1082
|
+
return _AiConversationService.layer({
|
|
1083
|
+
...options,
|
|
1084
|
+
feed,
|
|
1085
|
+
runtime: runtime4
|
|
1086
|
+
});
|
|
1087
|
+
}));
|
|
1088
|
+
/**
|
|
1089
|
+
* Run a prompt in the current conversation.
|
|
1090
|
+
*/
|
|
1091
|
+
static run = (params) => Effect6.gen(function* () {
|
|
1092
|
+
const conversation = yield* _AiConversationService;
|
|
1093
|
+
return yield* conversation.createRequest(params);
|
|
1094
|
+
});
|
|
1095
|
+
};
|
|
1096
|
+
var aiContextFromConversation = Layer.effect(AiContextService, Effect6.gen(function* () {
|
|
1097
|
+
const conversation = yield* AiConversationService;
|
|
1098
|
+
return {
|
|
1099
|
+
binder: conversation.context
|
|
1100
|
+
};
|
|
1101
|
+
}));
|
|
1102
|
+
var connectMcpServers = (blueprints) => pipe4(blueprints, Array3.flatMap((_) => _.mcpServers ?? []), Effect6.forEach(({ url, protocol }) => McpToolkit.make({
|
|
1103
|
+
url,
|
|
1104
|
+
kind: protocol
|
|
1105
|
+
}).pipe(
|
|
1106
|
+
// NOTE: Type-inference fails here without explicit void return.
|
|
1107
|
+
Effect6.tap((toolkit) => log4.info("Connected to MCP server", {
|
|
1108
|
+
url,
|
|
1109
|
+
tools: Object.keys(toolkit.toolkit.tools).length
|
|
1110
|
+
}, {
|
|
1111
|
+
F: __dxlog_file4,
|
|
1112
|
+
L: 268,
|
|
1113
|
+
S: void 0,
|
|
1114
|
+
C: (f, a) => f(...a)
|
|
1115
|
+
})),
|
|
1116
|
+
Effect6.tapDefect((error) => Effect6.sync(() => log4.warn("Failed to connect to MCP server", {
|
|
1117
|
+
error
|
|
1118
|
+
}, {
|
|
1119
|
+
F: __dxlog_file4,
|
|
1120
|
+
L: 270,
|
|
1121
|
+
S: void 0,
|
|
1122
|
+
C: (f, a) => f(...a)
|
|
1123
|
+
}))),
|
|
1124
|
+
Effect6.either
|
|
1125
|
+
)), Effect6.map(Array3.filterMap((_) => Either.getRight(_))));
|
|
1126
|
+
|
|
1127
|
+
// src/types/artifact.ts
|
|
1128
|
+
import * as Schema4 from "effect/Schema";
|
|
1129
|
+
import { Obj as Obj6, Ref as Ref2 } from "@dxos/echo";
|
|
1130
|
+
import { Database as Database2 } from "@dxos/echo";
|
|
1131
|
+
import { EncodedReference } from "@dxos/echo-protocol";
|
|
1132
|
+
import { DXN as DXN2, LOCAL_SPACE_TAG } from "@dxos/keys";
|
|
1133
|
+
import { trim as trim2 } from "@dxos/util";
|
|
1134
|
+
var createArtifactElement = (id) => `<artifact id=${id} />`;
|
|
1135
|
+
var ArtifactId = class extends Schema4.String.annotations({
|
|
1136
|
+
// TODO(dmaretskyi): This section gets overriden.
|
|
1137
|
+
description: trim2`
|
|
1138
|
+
The ID of the referenced object. Formats accepted:
|
|
1139
|
+
- DXN (dxn:echo:@:01KG7R1ZXWFMWQ4DA1Q6TN1DG4). DXNs can be prepended with an @ symbol for compatibility with in-text references.
|
|
1140
|
+
- space ID, object ID tuple (spaceID:objectID)
|
|
1141
|
+
- Only object ID that is assumed to be in the current space (01KG7R1ZXWFMWQ4DA1Q6TN1DG4)
|
|
1142
|
+
`,
|
|
1143
|
+
examples: [
|
|
1144
|
+
"dxn:echo:@:01KG7R1ZXWFMWQ4DA1Q6TN1DG4",
|
|
1145
|
+
"@dxn:echo:@:01KG7R1ZXWFMWQ4DA1Q6TN1DG4",
|
|
1146
|
+
"BM3FSHFOMJCHCG5QW7JTVKGYABD2GAA7G:01KG7R1ZXWFMWQ4DA1Q6TN1DG4",
|
|
1147
|
+
"01KG7R1ZXWFMWQ4DA1Q6TN1DG4"
|
|
1148
|
+
]
|
|
1149
|
+
}) {
|
|
1150
|
+
static toDXN(reference, owningSpaceId) {
|
|
1151
|
+
if (reference.startsWith("@dxn:")) {
|
|
1152
|
+
return DXN2.parse(reference.slice(1));
|
|
1153
|
+
} else if (reference.startsWith("dxn:")) {
|
|
1154
|
+
return DXN2.parse(reference);
|
|
1155
|
+
} else if (/^[A-Z0-9]+:[A-Z0-9]+$/.test(reference)) {
|
|
1156
|
+
const [spaceId, objectId] = reference.split(":");
|
|
1157
|
+
return new DXN2(DXN2.kind.ECHO, [
|
|
1158
|
+
spaceId === owningSpaceId ? LOCAL_SPACE_TAG : spaceId,
|
|
1159
|
+
objectId
|
|
1160
|
+
]);
|
|
1161
|
+
} else if (/^[A-Z0-9]+$/.test(reference)) {
|
|
1162
|
+
return DXN2.fromLocalObjectId(reference);
|
|
1163
|
+
} else {
|
|
1164
|
+
throw new Error(`Unable to parse object reference: ${reference}`);
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Resolves an artifact ID to an object.
|
|
1169
|
+
*/
|
|
1170
|
+
static resolve(schema, ref) {
|
|
1171
|
+
const dxn = ArtifactId.toDXN(ref);
|
|
1172
|
+
return Database2.resolve(dxn, schema);
|
|
1173
|
+
}
|
|
1174
|
+
};
|
|
1175
|
+
var RefFromLLM = Schema4.transform(ArtifactId, Ref2.Ref(Obj6.Unknown), {
|
|
1176
|
+
decode: (fromA, fromI) => EncodedReference.fromDXN(ArtifactId.toDXN(fromA)),
|
|
1177
|
+
encode: (toI, toA) => EncodedReference.toDXN(toI).toString(),
|
|
1178
|
+
strict: false
|
|
1179
|
+
}).annotations({
|
|
1180
|
+
description: ArtifactId.ast.annotations.description
|
|
1181
|
+
});
|
|
1182
|
+
|
|
1183
|
+
// src/functions/services.ts
|
|
1184
|
+
import * as Tool from "@effect/ai/Tool";
|
|
1185
|
+
import * as Context4 from "effect/Context";
|
|
1186
|
+
import * as Effect7 from "effect/Effect";
|
|
1187
|
+
import * as Layer2 from "effect/Layer";
|
|
1188
|
+
import * as Option3 from "effect/Option";
|
|
1189
|
+
import * as Record2 from "effect/Record";
|
|
1190
|
+
import * as Schema5 from "effect/Schema";
|
|
1191
|
+
import * as SchemaAST from "effect/SchemaAST";
|
|
1192
|
+
import { AiToolNotFoundError, ToolExecutionService as ToolExecutionService2, ToolResolverService as ToolResolverService2 } from "@dxos/ai";
|
|
1193
|
+
import { GenericToolkit as GenericToolkit2 } from "@dxos/ai";
|
|
1194
|
+
import { todo } from "@dxos/debug";
|
|
1195
|
+
import { Ref as Ref3 } from "@dxos/echo";
|
|
1196
|
+
import { FunctionInvocationService, FunctionNotFoundError } from "@dxos/functions";
|
|
1197
|
+
import { invariant as invariant2 } from "@dxos/invariant";
|
|
1198
|
+
import { Operation, OperationRegistry } from "@dxos/operation";
|
|
1199
|
+
var __dxlog_file5 = "/__w/dxos/dxos/packages/core/assistant/src/functions/services.ts";
|
|
1200
|
+
var makeToolResolverFromFunctions = () => {
|
|
1201
|
+
return Layer2.effect(ToolResolverService2, Effect7.gen(function* () {
|
|
1202
|
+
const toolkitProvider = yield* GenericToolkit2.GenericToolkitProvider;
|
|
1203
|
+
const functionInvocationService = yield* FunctionInvocationService;
|
|
1204
|
+
return {
|
|
1205
|
+
resolve: (id) => Effect7.gen(function* () {
|
|
1206
|
+
const toolkit = toolkitProvider.getToolkit();
|
|
1207
|
+
const tool = toolkit.toolkit.tools[id];
|
|
1208
|
+
if (tool) {
|
|
1209
|
+
return tool;
|
|
1210
|
+
}
|
|
1211
|
+
return yield* functionInvocationService.resolveFunction(id).pipe(Effect7.map(projectFunctionToTool), Effect7.catchTag("FunctionNotFound", () => Effect7.fail(new AiToolNotFoundError(id))));
|
|
1212
|
+
})
|
|
1213
|
+
};
|
|
1214
|
+
}));
|
|
1215
|
+
};
|
|
1216
|
+
var makeToolExecutionServiceFromFunctions = () => {
|
|
1217
|
+
return Layer2.effect(ToolExecutionService2, Effect7.gen(function* () {
|
|
1218
|
+
const toolkitProvider = yield* GenericToolkit2.GenericToolkitProvider;
|
|
1219
|
+
const toolkit = toolkitProvider.getToolkit();
|
|
1220
|
+
const toolkitHandler = yield* toolkit.toolkit.pipe(Effect7.provide(toolkit.layer));
|
|
1221
|
+
invariant2(isHandlerLike(toolkitHandler), void 0, {
|
|
1222
|
+
F: __dxlog_file5,
|
|
1223
|
+
L: 82,
|
|
1224
|
+
S: this,
|
|
1225
|
+
A: [
|
|
1226
|
+
"isHandlerLike(toolkitHandler)",
|
|
1227
|
+
""
|
|
1228
|
+
]
|
|
1229
|
+
});
|
|
1230
|
+
const functionInvocationService = yield* FunctionInvocationService;
|
|
1231
|
+
return {
|
|
1232
|
+
handlersFor: (toolkit2) => {
|
|
1233
|
+
const makeHandler = (tool) => {
|
|
1234
|
+
return Effect7.fn(`toolFunctionHandler ${tool.name}`)(function* (input) {
|
|
1235
|
+
if (toolkitHandler.tools[tool.name]) {
|
|
1236
|
+
if (Tool.isProviderDefined(tool)) {
|
|
1237
|
+
throw new Error("Attempted to call a provider-defined tool");
|
|
1238
|
+
}
|
|
1239
|
+
const { result } = yield* toolkitHandler.handle(tool.name, input);
|
|
1240
|
+
return result;
|
|
1241
|
+
}
|
|
1242
|
+
const functionDef = getOperationFromTool(tool).pipe(Option3.getOrThrow);
|
|
1243
|
+
return yield* functionInvocationService.invokeFunction(functionDef, input).pipe(Effect7.catchAllDefect((defect) => Effect7.fail(defect)));
|
|
1244
|
+
});
|
|
1245
|
+
};
|
|
1246
|
+
return toolkit2.of(Record2.map(toolkit2.tools, (tool, _name) => Tool.isUserDefined(tool) ? makeHandler(tool) : null));
|
|
1247
|
+
}
|
|
1248
|
+
};
|
|
1249
|
+
}));
|
|
1250
|
+
};
|
|
1251
|
+
var makeToolResolverFromOperations = ({ toolkit: extraToolkit = GenericToolkit2.empty } = {}) => {
|
|
1252
|
+
return Layer2.effect(ToolResolverService2, Effect7.gen(function* () {
|
|
1253
|
+
const toolkitProvider = yield* GenericToolkit2.GenericToolkitProvider;
|
|
1254
|
+
const operationRegistry = yield* OperationRegistry.Service;
|
|
1255
|
+
return {
|
|
1256
|
+
resolve: (id) => Effect7.gen(function* () {
|
|
1257
|
+
const toolkit = GenericToolkit2.merge(extraToolkit, toolkitProvider.getToolkit());
|
|
1258
|
+
const tool = toolkit.toolkit.tools[id];
|
|
1259
|
+
if (tool) {
|
|
1260
|
+
return tool;
|
|
1261
|
+
}
|
|
1262
|
+
return yield* operationRegistry.resolve(id).pipe(Effect7.flatMap(Option3.match({
|
|
1263
|
+
onSome: (_) => Effect7.succeed(projectFunctionToTool(_)),
|
|
1264
|
+
onNone: () => Effect7.fail(new AiToolNotFoundError(id))
|
|
1265
|
+
})));
|
|
1266
|
+
})
|
|
1267
|
+
};
|
|
1268
|
+
}));
|
|
1269
|
+
};
|
|
1270
|
+
var makeToolExecutionService = (opts) => Layer2.effect(ToolExecutionService2, Effect7.gen(function* () {
|
|
1271
|
+
const toolkitProvider = yield* GenericToolkit2.GenericToolkitProvider;
|
|
1272
|
+
const toolkit = toolkitProvider.getToolkit();
|
|
1273
|
+
const toolkitHandler = yield* toolkit.toolkit.pipe(Effect7.provide(toolkit.layer));
|
|
1274
|
+
invariant2(isHandlerLike(toolkitHandler), void 0, {
|
|
1275
|
+
F: __dxlog_file5,
|
|
1276
|
+
L: 162,
|
|
1277
|
+
S: this,
|
|
1278
|
+
A: [
|
|
1279
|
+
"isHandlerLike(toolkitHandler)",
|
|
1280
|
+
""
|
|
1281
|
+
]
|
|
1282
|
+
});
|
|
1283
|
+
return {
|
|
1284
|
+
handlersFor: (toolkit2) => {
|
|
1285
|
+
const makeHandler = (tool) => {
|
|
1286
|
+
return Effect7.fn(`toolFunctionHandler ${tool.name}`)(function* (input) {
|
|
1287
|
+
if (toolkitHandler.tools[tool.name]) {
|
|
1288
|
+
if (Tool.isProviderDefined(tool)) {
|
|
1289
|
+
throw new Error("Attempted to call a provider-defined tool");
|
|
1290
|
+
}
|
|
1291
|
+
const { result } = yield* toolkitHandler.handle(tool.name, input);
|
|
1292
|
+
return result;
|
|
1293
|
+
}
|
|
1294
|
+
return yield* opts.invoke(tool, input).pipe(Effect7.catchAllDefect((defect) => Effect7.fail(defect)));
|
|
1295
|
+
});
|
|
1296
|
+
};
|
|
1297
|
+
return toolkit2.of(Record2.map(toolkit2.tools, (tool, _name) => Tool.isUserDefined(tool) ? makeHandler(tool) : null));
|
|
1298
|
+
}
|
|
1299
|
+
};
|
|
1300
|
+
}));
|
|
1301
|
+
var makeToolExecutionServiceFromOperationInvoker = () => {
|
|
1302
|
+
return Layer2.unwrapEffect(Effect7.gen(function* () {
|
|
1303
|
+
const operationInvoker = yield* Operation.Service;
|
|
1304
|
+
return makeToolExecutionService({
|
|
1305
|
+
invoke: (tool, input) => Effect7.gen(function* () {
|
|
1306
|
+
const operationDef = getOperationFromTool(tool).pipe(Option3.getOrThrow);
|
|
1307
|
+
return yield* operationInvoker.invoke(operationDef, input).pipe(Effect7.orDie);
|
|
1308
|
+
})
|
|
1309
|
+
});
|
|
1310
|
+
}));
|
|
1311
|
+
};
|
|
1312
|
+
var functionInvocationServiceFromOperations = Layer2.effect(FunctionInvocationService, Effect7.gen(function* () {
|
|
1313
|
+
const operationRegistry = yield* OperationRegistry.Service;
|
|
1314
|
+
const operationInvoker = yield* Operation.Service;
|
|
1315
|
+
return {
|
|
1316
|
+
invokeFunction: (operationDef, input) => operationInvoker.invoke(operationDef, input).pipe(Effect7.orDie),
|
|
1317
|
+
resolveFunction: (key) => operationRegistry.resolve(key).pipe(Effect7.flatten, Effect7.catchTag("NoSuchElementException", () => Effect7.fail(new FunctionNotFoundError(key))))
|
|
1318
|
+
};
|
|
1319
|
+
}));
|
|
1320
|
+
var ToolExecutionServices = Layer2.mergeAll(makeToolResolverFromFunctions(), makeToolExecutionServiceFromFunctions());
|
|
1321
|
+
var FunctionToolAnnotation = class extends Context4.Tag("@dxos/assistant/FunctionToolAnnotation")() {
|
|
1322
|
+
};
|
|
1323
|
+
var getOperationFromTool = (tool) => {
|
|
1324
|
+
return Context4.getOption(FunctionToolAnnotation)(tool.annotations).pipe(Option3.map(({ definition }) => definition));
|
|
1325
|
+
};
|
|
1326
|
+
var toolCache = /* @__PURE__ */ new WeakMap();
|
|
1327
|
+
var projectFunctionToTool = (fn3) => {
|
|
1328
|
+
if (toolCache.has(fn3)) {
|
|
1329
|
+
return toolCache.get(fn3);
|
|
1330
|
+
}
|
|
1331
|
+
const tool = Tool.make(makeToolName(fn3.meta.name ?? fn3.meta.key), {
|
|
1332
|
+
description: fn3.meta.description,
|
|
1333
|
+
parameters: createStructFieldsFromSchema(fn3.input),
|
|
1334
|
+
failure: Schema5.Any
|
|
1335
|
+
}).annotate(FunctionToolAnnotation, {
|
|
1336
|
+
definition: fn3
|
|
1337
|
+
});
|
|
1338
|
+
toolCache.set(fn3, tool);
|
|
1339
|
+
return tool;
|
|
1340
|
+
};
|
|
1341
|
+
var makeToolName = (name) => {
|
|
1342
|
+
const toolName = name.toLowerCase().replace(/[^a-zA-Z0-9]/g, "-");
|
|
1343
|
+
invariant2(toolName.match(/^[a-z_][a-z0-9-_]*$/), void 0, {
|
|
1344
|
+
F: __dxlog_file5,
|
|
1345
|
+
L: 272,
|
|
1346
|
+
S: void 0,
|
|
1347
|
+
A: [
|
|
1348
|
+
"toolName.match(/^[a-z_][a-z0-9-_]*$/)",
|
|
1349
|
+
""
|
|
1350
|
+
]
|
|
1351
|
+
});
|
|
1352
|
+
return toolName;
|
|
1353
|
+
};
|
|
1354
|
+
var createStructFieldsFromSchema = (schema) => {
|
|
1355
|
+
switch (schema.ast._tag) {
|
|
1356
|
+
case "TypeLiteral":
|
|
1357
|
+
return Object.fromEntries(schema.ast.propertySignatures.map((prop) => [
|
|
1358
|
+
prop.name,
|
|
1359
|
+
Schema5.make(mapSchemaTypeForLLM(prop.type))
|
|
1360
|
+
]));
|
|
1361
|
+
case "VoidKeyword":
|
|
1362
|
+
return {};
|
|
1363
|
+
default:
|
|
1364
|
+
return todo(`Unsupported schema AST: ${schema.ast._tag}`);
|
|
1365
|
+
}
|
|
1366
|
+
};
|
|
1367
|
+
var mapSchemaTypeForLLM = (ast) => {
|
|
1368
|
+
if (Ref3.isRefType(ast)) {
|
|
1369
|
+
const description = ast.annotations.description ? ast.annotations.description + "\n" + RefFromLLM.ast.annotations.description : RefFromLLM.ast.annotations.description;
|
|
1370
|
+
return RefFromLLM.annotations({
|
|
1371
|
+
description
|
|
1372
|
+
}).ast;
|
|
1373
|
+
} else if (SchemaAST.isTupleType(ast)) {
|
|
1374
|
+
return new SchemaAST.TupleType(ast.elements.map((t) => new SchemaAST.OptionalType(mapSchemaTypeForLLM(t.type), t.isOptional, t.annotations)), ast.rest.map((t) => new SchemaAST.Type(mapSchemaTypeForLLM(t.type), t.annotations)), ast.isReadonly, ast.annotations);
|
|
1375
|
+
} else if (SchemaAST.isTypeLiteral(ast)) {
|
|
1376
|
+
return new SchemaAST.TypeLiteral(ast.propertySignatures.map((p) => new SchemaAST.PropertySignature(p.name, mapSchemaTypeForLLM(p.type), p.isOptional, p.isReadonly, p.annotations)), ast.indexSignatures, ast.annotations);
|
|
1377
|
+
}
|
|
1378
|
+
return ast;
|
|
1379
|
+
};
|
|
1380
|
+
var isHandlerLike = (value2) => {
|
|
1381
|
+
return typeof value2.tools === "object" && typeof value2.handle === "function";
|
|
1382
|
+
};
|
|
1383
|
+
|
|
1384
|
+
// src/service/agent-process.ts
|
|
1385
|
+
import * as Tool2 from "@effect/ai/Tool";
|
|
1386
|
+
import * as Toolkit2 from "@effect/ai/Toolkit";
|
|
1387
|
+
import * as Cause from "effect/Cause";
|
|
1388
|
+
import * as Duration from "effect/Duration";
|
|
1389
|
+
import * as Effect8 from "effect/Effect";
|
|
1390
|
+
import * as Exit from "effect/Exit";
|
|
1391
|
+
import * as Layer3 from "effect/Layer";
|
|
1392
|
+
import * as Match from "effect/Match";
|
|
1393
|
+
import * as Option4 from "effect/Option";
|
|
1394
|
+
import * as Schema6 from "effect/Schema";
|
|
1395
|
+
import { AiService, GenericToolkit as GenericToolkit3 } from "@dxos/ai";
|
|
1396
|
+
import { Database as Database3, DXN as DXN3, Feed as Feed3, Obj as Obj7 } from "@dxos/echo";
|
|
1397
|
+
import { acquireReleaseResource as acquireReleaseResource2 } from "@dxos/effect";
|
|
1398
|
+
import { TracingService as TracingService2 } from "@dxos/functions";
|
|
1399
|
+
import { Process, ProcessManager, StorageService } from "@dxos/functions-runtime";
|
|
1400
|
+
import { log as log5 } from "@dxos/log";
|
|
1401
|
+
import { Operation as Operation2, OperationRegistry as OperationRegistry2 } from "@dxos/operation";
|
|
1402
|
+
import { trim as trim3 } from "@dxos/util";
|
|
1403
|
+
var __dxlog_file6 = "/__w/dxos/dxos/packages/core/assistant/src/service/agent-process.ts";
|
|
1404
|
+
var AGENT_PROCESS_KEY = "org.dxos.testing.process.agent";
|
|
1405
|
+
var AgentProcess = (options) => Process.make({
|
|
1406
|
+
key: AGENT_PROCESS_KEY,
|
|
1407
|
+
input: Schema6.String,
|
|
1408
|
+
output: Schema6.Void,
|
|
1409
|
+
services: [
|
|
1410
|
+
Database3.Service,
|
|
1411
|
+
GenericToolkit3.GenericToolkitProvider,
|
|
1412
|
+
Operation2.Service,
|
|
1413
|
+
OperationRegistry2.Service,
|
|
1414
|
+
StorageService.StorageService,
|
|
1415
|
+
Feed3.FeedService,
|
|
1416
|
+
ProcessManager.ProcessOperationInvoker.Service,
|
|
1417
|
+
AiService.AiService,
|
|
1418
|
+
// @deprecated Required by AiSessionRunRequirements for backward compat with tool handlers.
|
|
1419
|
+
TracingService2
|
|
1420
|
+
]
|
|
1421
|
+
}, (ctx) => Effect8.gen(function* () {
|
|
1422
|
+
const feedDxn = ctx.params.target;
|
|
1423
|
+
if (feedDxn == null) {
|
|
1424
|
+
return yield* Effect8.die(new Error("Agent executable requires spawn options.target set to a queue DXN."));
|
|
1425
|
+
}
|
|
1426
|
+
const feed = yield* Database3.resolve(DXN3.parse(feedDxn), Feed3.Feed).pipe(Effect8.orDie);
|
|
1427
|
+
const runtime4 = yield* Effect8.runtime();
|
|
1428
|
+
const conversation = yield* acquireReleaseResource2(() => new AiConversation({
|
|
1429
|
+
feed,
|
|
1430
|
+
runtime: runtime4
|
|
1431
|
+
}));
|
|
1432
|
+
let inputQueue = [
|
|
1433
|
+
...yield* AgentEventsKey.get
|
|
1434
|
+
];
|
|
1435
|
+
const storageService = yield* StorageService.StorageService;
|
|
1436
|
+
const toolCallManager = new ToolCallManager(storageService);
|
|
1437
|
+
yield* toolCallManager.load();
|
|
1438
|
+
return {
|
|
1439
|
+
onInput: Effect8.fnUntraced(function* (prompt) {
|
|
1440
|
+
log5("agent onInput received", {
|
|
1441
|
+
promptLength: prompt.length,
|
|
1442
|
+
backlog: inputQueue.length
|
|
1443
|
+
}, {
|
|
1444
|
+
F: __dxlog_file6,
|
|
1445
|
+
L: 79,
|
|
1446
|
+
S: this,
|
|
1447
|
+
C: (f, a) => f(...a)
|
|
1448
|
+
});
|
|
1449
|
+
inputQueue.push({
|
|
1450
|
+
_tag: "prompt",
|
|
1451
|
+
content: prompt
|
|
1452
|
+
});
|
|
1453
|
+
log5("agent onInput persisting queue", {
|
|
1454
|
+
depth: inputQueue.length
|
|
1455
|
+
}, {
|
|
1456
|
+
F: __dxlog_file6,
|
|
1457
|
+
L: 81,
|
|
1458
|
+
S: this,
|
|
1459
|
+
C: (f, a) => f(...a)
|
|
1460
|
+
});
|
|
1461
|
+
yield* AgentEventsKey.set(inputQueue);
|
|
1462
|
+
log5("agent onInput persisted", {
|
|
1463
|
+
depth: inputQueue.length
|
|
1464
|
+
}, {
|
|
1465
|
+
F: __dxlog_file6,
|
|
1466
|
+
L: 83,
|
|
1467
|
+
S: this,
|
|
1468
|
+
C: (f, a) => f(...a)
|
|
1469
|
+
});
|
|
1470
|
+
ctx.setAlarm();
|
|
1471
|
+
log5("agent onInput alarm scheduled", void 0, {
|
|
1472
|
+
F: __dxlog_file6,
|
|
1473
|
+
L: 85,
|
|
1474
|
+
S: this,
|
|
1475
|
+
C: (f, a) => f(...a)
|
|
1476
|
+
});
|
|
1477
|
+
}),
|
|
1478
|
+
onAlarm: Effect8.fnUntraced(function* () {
|
|
1479
|
+
log5("agent onAlarm fired", {
|
|
1480
|
+
pending: inputQueue.length
|
|
1481
|
+
}, {
|
|
1482
|
+
F: __dxlog_file6,
|
|
1483
|
+
L: 89,
|
|
1484
|
+
S: this,
|
|
1485
|
+
C: (f, a) => f(...a)
|
|
1486
|
+
});
|
|
1487
|
+
const item = inputQueue.shift();
|
|
1488
|
+
if (!item) {
|
|
1489
|
+
log5("agent onAlarm empty queue", {}, {
|
|
1490
|
+
F: __dxlog_file6,
|
|
1491
|
+
L: 92,
|
|
1492
|
+
S: this,
|
|
1493
|
+
C: (f, a) => f(...a)
|
|
1494
|
+
});
|
|
1495
|
+
return;
|
|
1496
|
+
}
|
|
1497
|
+
if (item._tag === "tool_result" && toolCallManager.isReported(item.pid)) {
|
|
1498
|
+
log5.info("skip tool result that was reported synchronously", {
|
|
1499
|
+
pid: item.pid
|
|
1500
|
+
}, {
|
|
1501
|
+
F: __dxlog_file6,
|
|
1502
|
+
L: 97,
|
|
1503
|
+
S: this,
|
|
1504
|
+
C: (f, a) => f(...a)
|
|
1505
|
+
});
|
|
1506
|
+
return;
|
|
1507
|
+
}
|
|
1508
|
+
log5("agent onAlarm handling", {
|
|
1509
|
+
tag: item._tag
|
|
1510
|
+
}, {
|
|
1511
|
+
F: __dxlog_file6,
|
|
1512
|
+
L: 102,
|
|
1513
|
+
S: this,
|
|
1514
|
+
C: (f, a) => f(...a)
|
|
1515
|
+
});
|
|
1516
|
+
const prompt = Match.value(item).pipe(Match.tag("prompt", (item2) => item2.content), Match.tag("tool_result", (item2) => item2.isError ? toolErrorResponse(item2.pid, item2.result) : toolResultResponse(item2.pid, item2.result)), Match.exhaustive);
|
|
1517
|
+
log5("begin request", {
|
|
1518
|
+
prompt
|
|
1519
|
+
}, {
|
|
1520
|
+
F: __dxlog_file6,
|
|
1521
|
+
L: 114,
|
|
1522
|
+
S: this,
|
|
1523
|
+
C: (f, a) => f(...a)
|
|
1524
|
+
});
|
|
1525
|
+
yield* conversation.createRequest({
|
|
1526
|
+
prompt,
|
|
1527
|
+
// TODO(dmaretskyi): Polling currently broken, agent relies on completion notifications being delivered.
|
|
1528
|
+
// toolkit: AsynchronousExectionToolkit,
|
|
1529
|
+
system: options.systemPrompt
|
|
1530
|
+
});
|
|
1531
|
+
log5("end request", void 0, {
|
|
1532
|
+
F: __dxlog_file6,
|
|
1533
|
+
L: 121,
|
|
1534
|
+
S: this,
|
|
1535
|
+
C: (f, a) => f(...a)
|
|
1536
|
+
});
|
|
1537
|
+
yield* AgentEventsKey.set(inputQueue);
|
|
1538
|
+
if (inputQueue.length > 0) {
|
|
1539
|
+
ctx.setAlarm();
|
|
1540
|
+
}
|
|
1541
|
+
}, Effect8.orDie, Effect8.provide(Layer3.mergeAll(makeToolResolverFromOperations(), ToolExecutionService3({
|
|
1542
|
+
toolCallManager,
|
|
1543
|
+
feed
|
|
1544
|
+
}), functionInvocationServiceFromOperations, AsynchronousExectionToolkitLayer, AiService.model(options.model ?? "@anthropic/claude-opus-4-6")).pipe(Layer3.orDie))),
|
|
1545
|
+
onChildEvent: Effect8.fnUntraced(function* (event) {
|
|
1546
|
+
log5.info("childEvent", {
|
|
1547
|
+
event
|
|
1548
|
+
}, {
|
|
1549
|
+
F: __dxlog_file6,
|
|
1550
|
+
L: 142,
|
|
1551
|
+
S: this,
|
|
1552
|
+
C: (f, a) => f(...a)
|
|
1553
|
+
});
|
|
1554
|
+
if (event._tag === "exited") {
|
|
1555
|
+
if (!toolCallManager.isToolCall(event.pid)) {
|
|
1556
|
+
log5.verbose("childEvent ignored non-tool call", {
|
|
1557
|
+
pid: event.pid
|
|
1558
|
+
}, {
|
|
1559
|
+
F: __dxlog_file6,
|
|
1560
|
+
L: 145,
|
|
1561
|
+
S: this,
|
|
1562
|
+
C: (f, a) => f(...a)
|
|
1563
|
+
});
|
|
1564
|
+
return;
|
|
1565
|
+
}
|
|
1566
|
+
const operationInvoker = yield* ProcessManager.ProcessOperationInvoker.Service;
|
|
1567
|
+
const fiber = yield* operationInvoker.attachFiber(event.pid).pipe(Effect8.orDie);
|
|
1568
|
+
const result = yield* fiber.await.pipe(Effect8.orDie).pipe(Effect8.map(Exit.match({
|
|
1569
|
+
onSuccess: (value2) => ({
|
|
1570
|
+
_tag: "tool_result",
|
|
1571
|
+
pid: event.pid,
|
|
1572
|
+
result: value2,
|
|
1573
|
+
isError: false
|
|
1574
|
+
}),
|
|
1575
|
+
onFailure: (cause) => ({
|
|
1576
|
+
_tag: "tool_result",
|
|
1577
|
+
pid: event.pid,
|
|
1578
|
+
result: Cause.pretty(cause),
|
|
1579
|
+
isError: true
|
|
1580
|
+
})
|
|
1581
|
+
})));
|
|
1582
|
+
inputQueue.push(result);
|
|
1583
|
+
log5("agent onChildEvent persisted tool result", {
|
|
1584
|
+
depth: inputQueue.length,
|
|
1585
|
+
childPid: event.pid
|
|
1586
|
+
}, {
|
|
1587
|
+
F: __dxlog_file6,
|
|
1588
|
+
L: 170,
|
|
1589
|
+
S: this,
|
|
1590
|
+
C: (f, a) => f(...a)
|
|
1591
|
+
});
|
|
1592
|
+
yield* AgentEventsKey.set(inputQueue);
|
|
1593
|
+
ctx.setAlarm();
|
|
1594
|
+
log5("agent onChildEvent alarm scheduled", {
|
|
1595
|
+
depth: inputQueue.length
|
|
1596
|
+
}, {
|
|
1597
|
+
F: __dxlog_file6,
|
|
1598
|
+
L: 173,
|
|
1599
|
+
S: this,
|
|
1600
|
+
C: (f, a) => f(...a)
|
|
1601
|
+
});
|
|
1602
|
+
}
|
|
1603
|
+
})
|
|
1604
|
+
};
|
|
1605
|
+
}));
|
|
1606
|
+
var AgentEvent = Schema6.Union(Schema6.TaggedStruct("prompt", {
|
|
1607
|
+
content: Schema6.String
|
|
1608
|
+
}), Schema6.TaggedStruct("tool_result", {
|
|
1609
|
+
pid: Process.ID,
|
|
1610
|
+
result: Schema6.Unknown,
|
|
1611
|
+
isError: Schema6.Boolean
|
|
1612
|
+
}));
|
|
1613
|
+
var AgentEventsKey = StorageService.key(Schema6.parseJson(Schema6.Array(AgentEvent).pipe(Schema6.mutable)), "inputQueue").pipe(StorageService.withDefault(() => []));
|
|
1614
|
+
var ToolCallState = Schema6.Struct({
|
|
1615
|
+
activeCalls: Schema6.Array(Schema6.Struct({
|
|
1616
|
+
pid: Process.ID,
|
|
1617
|
+
// Whether the result was reported to the agent.
|
|
1618
|
+
reported: Schema6.Boolean
|
|
1619
|
+
}).pipe(Schema6.mutable)).pipe(Schema6.mutable)
|
|
1620
|
+
});
|
|
1621
|
+
var ToolCallStateKey = StorageService.key(Schema6.parseJson(ToolCallState.pipe(Schema6.mutable)), "toolCallState").pipe(StorageService.withDefault(() => ({
|
|
1622
|
+
activeCalls: []
|
|
1623
|
+
})));
|
|
1624
|
+
var ToolCallManager = class {
|
|
1625
|
+
#storageService;
|
|
1626
|
+
#state = {
|
|
1627
|
+
activeCalls: []
|
|
1628
|
+
};
|
|
1629
|
+
constructor(storageService) {
|
|
1630
|
+
this.#storageService = storageService;
|
|
1631
|
+
}
|
|
1632
|
+
load() {
|
|
1633
|
+
return Effect8.gen(this, function* () {
|
|
1634
|
+
this.#state = yield* ToolCallStateKey.get;
|
|
1635
|
+
}).pipe(Effect8.provideService(StorageService.StorageService, this.#storageService));
|
|
1636
|
+
}
|
|
1637
|
+
beginCall(pid) {
|
|
1638
|
+
return Effect8.gen(this, function* () {
|
|
1639
|
+
this.#state.activeCalls.push({
|
|
1640
|
+
pid,
|
|
1641
|
+
reported: false
|
|
1642
|
+
});
|
|
1643
|
+
yield* ToolCallStateKey.set(this.#state);
|
|
1644
|
+
}).pipe(Effect8.provideService(StorageService.StorageService, this.#storageService));
|
|
1645
|
+
}
|
|
1646
|
+
markAsReported(pid) {
|
|
1647
|
+
return Effect8.gen(this, function* () {
|
|
1648
|
+
const call = this.#state.activeCalls.find((call2) => call2.pid === pid);
|
|
1649
|
+
if (!call) {
|
|
1650
|
+
return;
|
|
1651
|
+
}
|
|
1652
|
+
call.reported = true;
|
|
1653
|
+
yield* ToolCallStateKey.set(this.#state);
|
|
1654
|
+
}).pipe(Effect8.provideService(StorageService.StorageService, this.#storageService));
|
|
1655
|
+
}
|
|
1656
|
+
isToolCall(pid) {
|
|
1657
|
+
return this.#state.activeCalls.some((call) => call.pid === pid);
|
|
1658
|
+
}
|
|
1659
|
+
isReported(pid) {
|
|
1660
|
+
return this.#state.activeCalls.some((call) => call.pid === pid && call.reported);
|
|
1661
|
+
}
|
|
1662
|
+
};
|
|
1663
|
+
var ToolExecutionService3 = ({ backgroundThreshold = Duration.seconds(1), toolCallManager, feed }) => Layer3.unwrapEffect(Effect8.gen(function* () {
|
|
1664
|
+
const operationInvoker = yield* ProcessManager.ProcessOperationInvoker.Service;
|
|
1665
|
+
return makeToolExecutionService({
|
|
1666
|
+
invoke: (tool, input) => Effect8.gen(function* () {
|
|
1667
|
+
const operationDef = getOperationFromTool(tool).pipe(Option4.getOrThrow);
|
|
1668
|
+
log5("invoking operation", {
|
|
1669
|
+
operationDef,
|
|
1670
|
+
input
|
|
1671
|
+
}, {
|
|
1672
|
+
F: __dxlog_file6,
|
|
1673
|
+
L: 277,
|
|
1674
|
+
S: this,
|
|
1675
|
+
C: (f, a) => f(...a)
|
|
1676
|
+
});
|
|
1677
|
+
const fiber = yield* operationInvoker.invokeFiber(operationDef, input, {
|
|
1678
|
+
environment: {
|
|
1679
|
+
conversation: Obj7.getDXN(feed).toString()
|
|
1680
|
+
}
|
|
1681
|
+
});
|
|
1682
|
+
toolCallManager.beginCall(fiber.pid);
|
|
1683
|
+
log5("invoked operation", {
|
|
1684
|
+
operationDef,
|
|
1685
|
+
input,
|
|
1686
|
+
fiber
|
|
1687
|
+
}, {
|
|
1688
|
+
F: __dxlog_file6,
|
|
1689
|
+
L: 284,
|
|
1690
|
+
S: this,
|
|
1691
|
+
C: (f, a) => f(...a)
|
|
1692
|
+
});
|
|
1693
|
+
const result = yield* fiber.await.pipe(Effect8.tap(() => toolCallManager.markAsReported(fiber.pid)), Effect8.timeout(backgroundThreshold), Effect8.catchTag("TimeoutException", () => Effect8.succeed(toolIsRunningInBackgroundResponse(fiber.pid))));
|
|
1694
|
+
log5("result", {
|
|
1695
|
+
result
|
|
1696
|
+
}, {
|
|
1697
|
+
F: __dxlog_file6,
|
|
1698
|
+
L: 291,
|
|
1699
|
+
S: this,
|
|
1700
|
+
C: (f, a) => f(...a)
|
|
1701
|
+
});
|
|
1702
|
+
return result;
|
|
1703
|
+
})
|
|
1704
|
+
});
|
|
1705
|
+
}));
|
|
1706
|
+
var AsynchronousExectionToolkit = class extends Toolkit2.make(Tool2.make("poll-tools", {
|
|
1707
|
+
description: trim3`
|
|
1708
|
+
Poll tool calls running in the background.
|
|
1709
|
+
Set wait to true to wait for the tool call to complete before returning.
|
|
1710
|
+
Only set wait to true if you dont have other tasks to perform in parallel.
|
|
1711
|
+
Set an appropriate timeout to avoid waiting forever.
|
|
1712
|
+
You will also be notified about the job completion separatelly, so you do not always need to inspect the job if you dont need the result right now.
|
|
1713
|
+
`,
|
|
1714
|
+
parameters: {
|
|
1715
|
+
ids: Schema6.Array(Schema6.String).annotations({
|
|
1716
|
+
description: "The IDs of the jobs to inspect."
|
|
1717
|
+
}),
|
|
1718
|
+
wait: Schema6.optional(Schema6.Boolean).annotations({
|
|
1719
|
+
description: "Whether to wait for the job to complete before returning.",
|
|
1720
|
+
default: false
|
|
1721
|
+
}),
|
|
1722
|
+
timeout: Schema6.optional(Schema6.Number).annotations({
|
|
1723
|
+
description: "Maximum time to wait for the job to complete. If the job does not complete within the timeout, the current state is returned.",
|
|
1724
|
+
default: 1e4
|
|
1725
|
+
})
|
|
1726
|
+
}
|
|
1727
|
+
})) {
|
|
1728
|
+
};
|
|
1729
|
+
var AsynchronousExectionToolkitLayer = AsynchronousExectionToolkit.toLayer(Effect8.gen(function* () {
|
|
1730
|
+
const invoker = yield* ProcessManager.ProcessOperationInvoker.Service;
|
|
1731
|
+
return {
|
|
1732
|
+
"poll-tools": ({ ids, wait, timeout: timeout2 = 1e4 }) => Effect8.gen(function* () {
|
|
1733
|
+
return yield* Effect8.forEach(ids, (pid) => invoker.attachFiber(Process.ID.make(pid)).pipe(Effect8.flatMap((_) => _.await), Effect8.timeout(Duration.millis(timeout2)), Effect8.flatMap(Exit.match({
|
|
1734
|
+
onSuccess: (value2) => Effect8.succeed(toolResultResponse(pid, value2)),
|
|
1735
|
+
onFailure: (cause) => Effect8.succeed(toolErrorResponse(pid, Cause.pretty(cause)))
|
|
1736
|
+
})), Effect8.catchTag("ProcessNotFoundError", () => Effect8.succeed(`Process not found: ${pid}`)), Effect8.catchTag("TimeoutException", () => Effect8.succeed(`Process still running: ${pid}`))));
|
|
1737
|
+
})
|
|
1738
|
+
};
|
|
1739
|
+
}));
|
|
1740
|
+
var toolIsRunningInBackgroundResponse = (pid) => `Tool is running in the background id=${pid}; wait for the completion notification to get the result.`;
|
|
1741
|
+
var toolResultResponse = (pid, value2) => `<result pid=${pid}>${JSON.stringify(value2)}</result>`;
|
|
1742
|
+
var toolErrorResponse = (pid, cause) => `<error pid=${pid}>${cause}</error>`;
|
|
1743
|
+
|
|
1744
|
+
// src/service/AgentService.ts
|
|
1745
|
+
var AgentService_exports = {};
|
|
1746
|
+
__export(AgentService_exports, {
|
|
1747
|
+
AgentService: () => AgentService,
|
|
1748
|
+
createSession: () => createSession,
|
|
1749
|
+
getSession: () => getSession,
|
|
1750
|
+
layer: () => layer
|
|
1751
|
+
});
|
|
1752
|
+
import * as Context5 from "effect/Context";
|
|
1753
|
+
import * as Effect9 from "effect/Effect";
|
|
1754
|
+
import * as Layer4 from "effect/Layer";
|
|
1755
|
+
import { Blueprint as Blueprint2 } from "@dxos/blueprints";
|
|
1756
|
+
import { Database as Database4, Feed as Feed4, Obj as Obj8, Ref as Ref4 } from "@dxos/echo";
|
|
1757
|
+
import { acquireReleaseResource as acquireReleaseResource3 } from "@dxos/effect";
|
|
1758
|
+
import { ProcessManager as ProcessManager2 } from "@dxos/functions-runtime";
|
|
1759
|
+
var AgentService = class extends Context5.Tag("@dxos/assistant/AgentService")() {
|
|
1760
|
+
};
|
|
1761
|
+
var getSession = Effect9.serviceFunctionEffect(AgentService, (service) => service.getSession);
|
|
1762
|
+
var createSession = Effect9.fn("createSession")(function* (opts) {
|
|
1763
|
+
const blueprints = yield* Effect9.forEach(opts?.blueprints ?? [], (blueprint) => Blueprint2.upsert(blueprint.key).pipe(Effect9.map(Ref4.make)));
|
|
1764
|
+
const feed = yield* Database4.add(Feed4.make());
|
|
1765
|
+
const runtime4 = yield* Effect9.runtime();
|
|
1766
|
+
const binder = yield* acquireReleaseResource3(() => new AiContextBinder({
|
|
1767
|
+
feed,
|
|
1768
|
+
runtime: runtime4
|
|
1769
|
+
}));
|
|
1770
|
+
yield* Effect9.promise(() => binder.bind({
|
|
1771
|
+
blueprints,
|
|
1772
|
+
objects: opts?.context ?? []
|
|
1773
|
+
}));
|
|
1774
|
+
return yield* getSession(feed);
|
|
1775
|
+
}, Effect9.scoped);
|
|
1776
|
+
var layer = (opts) => Layer4.effect(AgentService, Effect9.gen(function* () {
|
|
1777
|
+
const processManager = yield* ProcessManager2.ProcessManagerService;
|
|
1778
|
+
const sessionCache = /* @__PURE__ */ new Map();
|
|
1779
|
+
return {
|
|
1780
|
+
getSession: (feed) => Effect9.gen(function* () {
|
|
1781
|
+
const cached = sessionCache.get(feed.id);
|
|
1782
|
+
if (cached) {
|
|
1783
|
+
return cached;
|
|
1784
|
+
}
|
|
1785
|
+
const target = Obj8.getDXN(feed).toString();
|
|
1786
|
+
const executable = AgentProcess({
|
|
1787
|
+
systemPrompt: opts?.systemPrompt
|
|
1788
|
+
});
|
|
1789
|
+
const processes = yield* processManager.list({
|
|
1790
|
+
target,
|
|
1791
|
+
key: executable.key
|
|
1792
|
+
});
|
|
1793
|
+
let handle;
|
|
1794
|
+
if (processes.length > 0) {
|
|
1795
|
+
handle = processes[0];
|
|
1796
|
+
} else {
|
|
1797
|
+
handle = yield* processManager.spawn(executable, {
|
|
1798
|
+
name: "agent",
|
|
1799
|
+
target,
|
|
1800
|
+
traceMeta: {
|
|
1801
|
+
conversationId: feed.id
|
|
1802
|
+
}
|
|
1803
|
+
});
|
|
1804
|
+
}
|
|
1805
|
+
const session = makeSession(handle, feed);
|
|
1806
|
+
sessionCache.set(feed.id, session);
|
|
1807
|
+
return session;
|
|
1808
|
+
})
|
|
1809
|
+
};
|
|
1810
|
+
}));
|
|
1811
|
+
var makeSession = (process, feed) => ({
|
|
1812
|
+
feed,
|
|
1813
|
+
submitPrompt: (prompt) => process.submitInput(prompt),
|
|
1814
|
+
waitForCompletion: () => process.runToCompletion(),
|
|
1815
|
+
subscribeEphemeral: () => process.subscribeEphemeral(),
|
|
1816
|
+
addContext: (context) => Effect9.gen(function* () {
|
|
1817
|
+
const runtime4 = yield* Effect9.runtime();
|
|
1818
|
+
const binder = yield* acquireReleaseResource3(() => new AiContextBinder({
|
|
1819
|
+
feed,
|
|
1820
|
+
runtime: runtime4
|
|
1821
|
+
}));
|
|
1822
|
+
yield* Effect9.promise(() => binder.bind({
|
|
1823
|
+
blueprints: [],
|
|
1824
|
+
objects: context
|
|
1825
|
+
}));
|
|
1826
|
+
}).pipe(Effect9.scoped),
|
|
1827
|
+
getContext: () => Effect9.gen(function* () {
|
|
1828
|
+
const runtime4 = yield* Effect9.runtime();
|
|
1829
|
+
const binder = yield* acquireReleaseResource3(() => new AiContextBinder({
|
|
1830
|
+
feed,
|
|
1831
|
+
runtime: runtime4
|
|
1832
|
+
}));
|
|
1833
|
+
return binder.getObjects().map((object) => Ref4.make(object));
|
|
1834
|
+
}).pipe(Effect9.scoped)
|
|
1835
|
+
});
|
|
1836
|
+
|
|
1837
|
+
export {
|
|
1838
|
+
ContextBinding,
|
|
1839
|
+
Bindings,
|
|
1840
|
+
AiContextBinder,
|
|
1841
|
+
AiContextService,
|
|
1842
|
+
ArtifactDiffResolver,
|
|
1843
|
+
GenerationObserver,
|
|
1844
|
+
PartialBlock,
|
|
1845
|
+
CompleteBlock,
|
|
1846
|
+
AgentTurnStarted,
|
|
1847
|
+
AgentTurnCompleted,
|
|
1848
|
+
ToolCallStarted,
|
|
1849
|
+
ToolCallCompleted,
|
|
1850
|
+
AgentInputReceived,
|
|
1851
|
+
AgentRequestCompleted,
|
|
1852
|
+
AiModelError,
|
|
1853
|
+
AiAssistantError,
|
|
1854
|
+
formatSystemPrompt,
|
|
1855
|
+
formatUserPrompt,
|
|
1856
|
+
AiSession,
|
|
1857
|
+
createToolkit,
|
|
1858
|
+
VersionPin,
|
|
1859
|
+
AiConversation,
|
|
1860
|
+
AiConversationService,
|
|
1861
|
+
createArtifactElement,
|
|
1862
|
+
ArtifactId,
|
|
1863
|
+
RefFromLLM,
|
|
1864
|
+
makeToolResolverFromFunctions,
|
|
1865
|
+
makeToolExecutionServiceFromFunctions,
|
|
1866
|
+
makeToolResolverFromOperations,
|
|
1867
|
+
makeToolExecutionService,
|
|
1868
|
+
makeToolExecutionServiceFromOperationInvoker,
|
|
1869
|
+
functionInvocationServiceFromOperations,
|
|
1870
|
+
ToolExecutionServices,
|
|
1871
|
+
getOperationFromTool,
|
|
1872
|
+
AGENT_PROCESS_KEY,
|
|
1873
|
+
AgentService_exports
|
|
1874
|
+
};
|
|
1875
|
+
//# sourceMappingURL=chunk-UAWIXKF6.mjs.map
|