@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
package/package.json
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/assistant",
|
|
3
|
-
"version": "0.8.4-main.
|
|
3
|
+
"version": "0.8.4-main.74a063c4e0",
|
|
4
4
|
"description": "Natural language interface to DXOS",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/dxos/dxos"
|
|
10
|
+
},
|
|
7
11
|
"license": "MIT",
|
|
8
12
|
"author": "info@dxos.org",
|
|
9
13
|
"sideEffects": true,
|
|
@@ -12,14 +16,17 @@
|
|
|
12
16
|
".": {
|
|
13
17
|
"source": "./src/index.ts",
|
|
14
18
|
"types": "./dist/types/src/index.d.ts",
|
|
15
|
-
"
|
|
16
|
-
"node": "./dist/lib/node-esm/index.mjs"
|
|
19
|
+
"default": "./dist/lib/neutral/index.mjs"
|
|
17
20
|
},
|
|
18
21
|
"./extraction": {
|
|
19
22
|
"source": "./src/extraction/index.ts",
|
|
20
23
|
"types": "./dist/types/src/extraction/index.d.ts",
|
|
21
|
-
"
|
|
22
|
-
|
|
24
|
+
"default": "./dist/lib/neutral/extraction/index.mjs"
|
|
25
|
+
},
|
|
26
|
+
"./testing": {
|
|
27
|
+
"source": "./src/testing/index.ts",
|
|
28
|
+
"types": "./dist/types/src/testing/index.d.ts",
|
|
29
|
+
"default": "./dist/lib/neutral/testing/index.mjs"
|
|
23
30
|
}
|
|
24
31
|
},
|
|
25
32
|
"types": "dist/types/src/index.d.ts",
|
|
@@ -38,41 +45,43 @@
|
|
|
38
45
|
"src"
|
|
39
46
|
],
|
|
40
47
|
"dependencies": {
|
|
41
|
-
"@automerge/automerge": "3.
|
|
42
|
-
"@effect/
|
|
43
|
-
"@effect/
|
|
44
|
-
"@effect/
|
|
45
|
-
"@
|
|
48
|
+
"@automerge/automerge": "3.2.3",
|
|
49
|
+
"@effect-atom/atom": "^0.5.1",
|
|
50
|
+
"@effect-atom/atom-react": "^0.5.0",
|
|
51
|
+
"@effect/ai": "0.33.2",
|
|
52
|
+
"@effect/experimental": "0.58.0",
|
|
53
|
+
"@effect/platform": "0.94.4",
|
|
46
54
|
"@xenova/transformers": "^2.17.2",
|
|
47
55
|
"lodash.defaultsdeep": "^4.6.1",
|
|
48
|
-
"@dxos/ai": "0.8.4-main.
|
|
49
|
-
"@dxos/async": "0.8.4-main.
|
|
50
|
-
"@dxos/blueprints": "0.8.4-main.
|
|
51
|
-
"@dxos/
|
|
52
|
-
"@dxos/echo-db": "0.8.4-main.
|
|
53
|
-
"@dxos/
|
|
54
|
-
"@dxos/
|
|
55
|
-
"@dxos/
|
|
56
|
-
"@dxos/
|
|
57
|
-
"@dxos/
|
|
58
|
-
"@dxos/
|
|
59
|
-
"@dxos/
|
|
60
|
-
"@dxos/
|
|
61
|
-
"@dxos/
|
|
62
|
-
"@dxos/log": "0.8.4-main.
|
|
63
|
-
"@dxos/
|
|
64
|
-
"@dxos/
|
|
65
|
-
"@dxos/
|
|
66
|
-
"@dxos/
|
|
67
|
-
"@dxos/
|
|
68
|
-
"@dxos/
|
|
56
|
+
"@dxos/ai": "0.8.4-main.74a063c4e0",
|
|
57
|
+
"@dxos/async": "0.8.4-main.74a063c4e0",
|
|
58
|
+
"@dxos/blueprints": "0.8.4-main.74a063c4e0",
|
|
59
|
+
"@dxos/echo": "0.8.4-main.74a063c4e0",
|
|
60
|
+
"@dxos/echo-db": "0.8.4-main.74a063c4e0",
|
|
61
|
+
"@dxos/debug": "0.8.4-main.74a063c4e0",
|
|
62
|
+
"@dxos/echo-protocol": "0.8.4-main.74a063c4e0",
|
|
63
|
+
"@dxos/effect": "0.8.4-main.74a063c4e0",
|
|
64
|
+
"@dxos/functions": "0.8.4-main.74a063c4e0",
|
|
65
|
+
"@dxos/functions-runtime": "0.8.4-main.74a063c4e0",
|
|
66
|
+
"@dxos/errors": "0.8.4-main.74a063c4e0",
|
|
67
|
+
"@dxos/context": "0.8.4-main.74a063c4e0",
|
|
68
|
+
"@dxos/keys": "0.8.4-main.74a063c4e0",
|
|
69
|
+
"@dxos/invariant": "0.8.4-main.74a063c4e0",
|
|
70
|
+
"@dxos/log": "0.8.4-main.74a063c4e0",
|
|
71
|
+
"@dxos/mcp-client": "0.8.4-main.74a063c4e0",
|
|
72
|
+
"@dxos/operation": "0.8.4-main.74a063c4e0",
|
|
73
|
+
"@dxos/types": "0.8.4-main.74a063c4e0",
|
|
74
|
+
"@dxos/schema": "0.8.4-main.74a063c4e0",
|
|
75
|
+
"@dxos/util": "0.8.4-main.74a063c4e0",
|
|
76
|
+
"@dxos/node-std": "0.8.4-main.74a063c4e0",
|
|
77
|
+
"@dxos/graph": "0.8.4-main.74a063c4e0"
|
|
69
78
|
},
|
|
70
79
|
"devDependencies": {
|
|
71
80
|
"@types/lodash.defaultsdeep": "^4.6.6",
|
|
72
|
-
"effect": "3.
|
|
81
|
+
"effect": "3.20.0"
|
|
73
82
|
},
|
|
74
83
|
"peerDependencies": {
|
|
75
|
-
"effect": "
|
|
84
|
+
"effect": "3.20.0"
|
|
76
85
|
},
|
|
77
86
|
"publishConfig": {
|
|
78
87
|
"access": "public"
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Context from 'effect/Context';
|
|
6
|
+
import * as Effect from 'effect/Effect';
|
|
7
|
+
import * as Layer from 'effect/Layer';
|
|
8
|
+
import * as Runtime from 'effect/Runtime';
|
|
9
|
+
import * as Schema from 'effect/Schema';
|
|
10
|
+
import { describe, it } from 'vitest';
|
|
11
|
+
|
|
12
|
+
import { Feed, Obj, Ref, Type } from '@dxos/echo';
|
|
13
|
+
|
|
14
|
+
import { AiContextBinder } from './context';
|
|
15
|
+
|
|
16
|
+
const createMockFeedRuntime = (): Runtime.Runtime<Feed.FeedService> => {
|
|
17
|
+
const mockFeedService: Context.Tag.Service<Feed.FeedService> = {
|
|
18
|
+
append: async () => {},
|
|
19
|
+
remove: async () => {},
|
|
20
|
+
query: () =>
|
|
21
|
+
({
|
|
22
|
+
subscribe: () => () => {},
|
|
23
|
+
results: [],
|
|
24
|
+
run: async () => [],
|
|
25
|
+
}) as any,
|
|
26
|
+
};
|
|
27
|
+
const layer = Layer.succeed(Feed.FeedService, mockFeedService);
|
|
28
|
+
return Effect.runSync(Effect.runtime<Feed.FeedService>().pipe(Effect.provide(layer)));
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
describe('AiContextBinder', () => {
|
|
32
|
+
it('should handle bind with Ref', async () => {
|
|
33
|
+
const feed = Feed.make();
|
|
34
|
+
const runtime = createMockFeedRuntime();
|
|
35
|
+
const binder = new AiContextBinder({ feed, runtime });
|
|
36
|
+
|
|
37
|
+
const TestSchema = Schema.Struct({}).pipe(
|
|
38
|
+
Type.object({
|
|
39
|
+
typename: 'org.dxos.type.example',
|
|
40
|
+
version: '0.1.0',
|
|
41
|
+
}),
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const obj = Obj.make(TestSchema, {});
|
|
45
|
+
const ref = Ref.make(obj);
|
|
46
|
+
|
|
47
|
+
await binder.bind({
|
|
48
|
+
blueprints: [],
|
|
49
|
+
objects: [ref],
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -2,18 +2,20 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import * as
|
|
5
|
+
import { Atom, Registry } from '@effect-atom/atom-react';
|
|
6
|
+
import * as EArray from 'effect/Array';
|
|
7
7
|
import * as Context from 'effect/Context';
|
|
8
|
+
import * as Effect from 'effect/Effect';
|
|
8
9
|
import * as Function from 'effect/Function';
|
|
10
|
+
import * as Runtime from 'effect/Runtime';
|
|
9
11
|
import * as Schema from 'effect/Schema';
|
|
10
12
|
|
|
11
13
|
import { Blueprint } from '@dxos/blueprints';
|
|
12
14
|
import { Resource } from '@dxos/context';
|
|
13
|
-
import { DXN,
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import { ComplexSet } from '@dxos/util';
|
|
15
|
+
import { DXN, Feed, Obj, type QueryResult, 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';
|
|
17
19
|
|
|
18
20
|
/**
|
|
19
21
|
* Thread message that binds or unbinds contextual objects to a conversation.
|
|
@@ -21,18 +23,17 @@ import { ComplexSet } from '@dxos/util';
|
|
|
21
23
|
// TODO(burdon): Move to @dxos/schema ContentBlock?
|
|
22
24
|
export const ContextBinding = Schema.Struct({
|
|
23
25
|
blueprints: Schema.Struct({
|
|
24
|
-
added: Schema.Array(
|
|
25
|
-
removed: Schema.Array(
|
|
26
|
+
added: Schema.Array(Ref.Ref(Blueprint.Blueprint)),
|
|
27
|
+
removed: Schema.Array(Ref.Ref(Blueprint.Blueprint)),
|
|
26
28
|
}),
|
|
27
29
|
|
|
28
|
-
// TODO(burdon): Type.Expando => Type.Obj (or Obj.Any?)
|
|
29
30
|
objects: Schema.Struct({
|
|
30
|
-
added: Schema.Array(
|
|
31
|
-
removed: Schema.Array(
|
|
31
|
+
added: Schema.Array(Ref.Ref(Obj.Unknown)),
|
|
32
|
+
removed: Schema.Array(Ref.Ref(Obj.Unknown)),
|
|
32
33
|
}),
|
|
33
34
|
}).pipe(
|
|
34
|
-
Type.
|
|
35
|
-
typename: 'dxos.
|
|
35
|
+
Type.object({
|
|
36
|
+
typename: 'org.dxos.type.contextBinding',
|
|
36
37
|
version: '0.1.0',
|
|
37
38
|
}),
|
|
38
39
|
);
|
|
@@ -41,141 +42,342 @@ export interface ContextBinding extends Schema.Schema.Type<typeof ContextBinding
|
|
|
41
42
|
|
|
42
43
|
export type BindingProps = Partial<{
|
|
43
44
|
blueprints: Ref.Ref<Blueprint.Blueprint>[];
|
|
44
|
-
objects: Ref.Ref<
|
|
45
|
+
objects: Ref.Ref<Obj.Unknown>[];
|
|
45
46
|
}>;
|
|
46
47
|
|
|
47
48
|
export class Bindings {
|
|
48
49
|
readonly blueprints = new ComplexSet<Ref.Ref<Blueprint.Blueprint>>((ref) => ref.dxn.toString());
|
|
50
|
+
|
|
49
51
|
// TODO(burdon): Some DXNs have the Space prefix so only compare the object ID.
|
|
50
|
-
readonly objects = new ComplexSet<Ref.Ref<
|
|
52
|
+
readonly objects = new ComplexSet<Ref.Ref<Obj.Unknown>>((ref) => ref.dxn.asEchoDXN()?.echoId);
|
|
51
53
|
|
|
52
54
|
toJSON() {
|
|
53
55
|
return {
|
|
54
|
-
blueprints:
|
|
55
|
-
objects:
|
|
56
|
+
blueprints: EArray.fromIterable(this.blueprints).map((ref) => ref.dxn.toString()),
|
|
57
|
+
objects: EArray.fromIterable(this.objects).map((ref) => ref.dxn.toString()),
|
|
56
58
|
};
|
|
57
59
|
}
|
|
58
60
|
}
|
|
59
61
|
|
|
62
|
+
export type AiContextBinderOptions = {
|
|
63
|
+
feed: Feed.Feed;
|
|
64
|
+
runtime: Runtime.Runtime<Feed.FeedService>;
|
|
65
|
+
registry?: Registry.Registry;
|
|
66
|
+
};
|
|
67
|
+
|
|
60
68
|
/**
|
|
61
69
|
* Manages bindings of blueprints and objects to a conversation.
|
|
62
70
|
*/
|
|
63
71
|
// TODO(burdon): Context should manage ephemeral state of bindings until prompt is issued?
|
|
64
72
|
export class AiContextBinder extends Resource {
|
|
73
|
+
private readonly _blueprints = Atom.make<Blueprint.Blueprint[]>([]).pipe(Atom.keepAlive);
|
|
74
|
+
private readonly _objects = Atom.make<Obj.Unknown[]>([]).pipe(Atom.keepAlive);
|
|
75
|
+
private readonly _registry: Registry.Registry;
|
|
76
|
+
private readonly _feed: Feed.Feed;
|
|
77
|
+
private readonly _runtime: Runtime.Runtime<Feed.FeedService>;
|
|
78
|
+
#bindingsQuery: QueryResult.QueryResult<ContextBinding> | undefined;
|
|
79
|
+
|
|
80
|
+
constructor(options: AiContextBinderOptions) {
|
|
81
|
+
super();
|
|
82
|
+
assertArgument(options.feed, 'options.feed', 'Feed is required');
|
|
83
|
+
assertArgument(options.runtime, 'options.runtime', 'Feed runtime is required');
|
|
84
|
+
this._feed = options.feed;
|
|
85
|
+
this._runtime = options.runtime;
|
|
86
|
+
this._registry = options.registry ?? Registry.make();
|
|
87
|
+
}
|
|
88
|
+
|
|
65
89
|
/**
|
|
66
|
-
*
|
|
90
|
+
* Returns the blueprints atom for subscription.
|
|
67
91
|
*/
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
private _objects?: ReadonlySignal<Ref.Ref<Type.Expando>[]>;
|
|
92
|
+
get blueprints(): Atom.Atom<Blueprint.Blueprint[]> {
|
|
93
|
+
return this._blueprints;
|
|
94
|
+
}
|
|
72
95
|
|
|
73
|
-
|
|
74
|
-
|
|
96
|
+
/**
|
|
97
|
+
* Returns the objects atom for subscription.
|
|
98
|
+
*/
|
|
99
|
+
get objects(): Atom.Atom<Obj.Unknown[]> {
|
|
100
|
+
return this._objects;
|
|
75
101
|
}
|
|
76
102
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
103
|
+
/**
|
|
104
|
+
* Gets the current blueprints value.
|
|
105
|
+
*/
|
|
106
|
+
getBlueprints(): Blueprint.Blueprint[] {
|
|
107
|
+
return this._registry.get(this._blueprints);
|
|
80
108
|
}
|
|
81
109
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
110
|
+
/**
|
|
111
|
+
* Gets the current objects value.
|
|
112
|
+
*/
|
|
113
|
+
getObjects(): Obj.Unknown[] {
|
|
114
|
+
return this._registry.get(this._objects);
|
|
85
115
|
}
|
|
86
116
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
117
|
+
/**
|
|
118
|
+
* Subscribe to changes in blueprints.
|
|
119
|
+
*/
|
|
120
|
+
subscribeBlueprints(cb: (blueprints: Blueprint.Blueprint[]) => void): () => void {
|
|
121
|
+
return this._registry.subscribe(this._blueprints, () => cb(this._registry.get(this._blueprints)));
|
|
90
122
|
}
|
|
91
123
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
this.
|
|
97
|
-
this._blueprints = computed(() => [...this.bindings.value.blueprints]);
|
|
98
|
-
this._objects = computed(() => [...this.bindings.value.objects]);
|
|
124
|
+
/**
|
|
125
|
+
* Subscribe to changes in objects.
|
|
126
|
+
*/
|
|
127
|
+
subscribeObjects(cb: (objects: Obj.Unknown[]) => void): () => void {
|
|
128
|
+
return this._registry.subscribe(this._objects, () => cb(this._registry.get(this._objects)));
|
|
99
129
|
}
|
|
100
130
|
|
|
101
|
-
protected override async
|
|
102
|
-
this
|
|
103
|
-
|
|
104
|
-
|
|
131
|
+
protected override async _open(): Promise<void> {
|
|
132
|
+
this.#bindingsQuery = await Runtime.runPromise(this._runtime)(Feed.query(this._feed, Query.type(ContextBinding)));
|
|
133
|
+
|
|
134
|
+
// Process initial state before returning.
|
|
135
|
+
const initialResults = await this.#bindingsQuery.run();
|
|
136
|
+
await this._updateBindings(initialResults);
|
|
137
|
+
|
|
138
|
+
// Subscribe to future changes.
|
|
139
|
+
this._ctx.onDispose(
|
|
140
|
+
this.#bindingsQuery.subscribe(async () => {
|
|
141
|
+
await this._updateBindings(this.#bindingsQuery!.results);
|
|
142
|
+
}),
|
|
143
|
+
);
|
|
105
144
|
}
|
|
106
145
|
|
|
107
146
|
/**
|
|
108
|
-
*
|
|
147
|
+
* Re-reads bindings from the feed to pick up changes made by other processes.
|
|
109
148
|
*/
|
|
110
|
-
async
|
|
111
|
-
|
|
112
|
-
|
|
149
|
+
async sync(): Promise<void> {
|
|
150
|
+
if (this.#bindingsQuery) {
|
|
151
|
+
const results = await this.#bindingsQuery.run();
|
|
152
|
+
log('sync', { bindingItems: results.length });
|
|
153
|
+
await this._updateBindings(results);
|
|
154
|
+
log('sync complete', {
|
|
155
|
+
blueprints: this._registry.get(this._blueprints).length,
|
|
156
|
+
blueprintKeys: this._registry.get(this._blueprints).map((bp) => (bp as any).key),
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private async _updateBindings(items: ContextBinding[]): Promise<void> {
|
|
162
|
+
// Skip update if no items - preserve existing state set by bind().
|
|
163
|
+
if (items.length === 0) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const bindings = this._reduce(items);
|
|
168
|
+
|
|
169
|
+
log('_updateBindings', {
|
|
170
|
+
items: items.length,
|
|
171
|
+
blueprintRefs: [...bindings.blueprints].map((ref) => ({ dxn: ref.dxn.toString(), available: ref.isAvailable })),
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// Resolve references (loading them first if needed).
|
|
175
|
+
const currentBlueprints = this._registry.get(this._blueprints);
|
|
176
|
+
const currentObjects = this._registry.get(this._objects);
|
|
177
|
+
const resolvedBlueprints = await this._resolve(bindings.blueprints, currentBlueprints);
|
|
178
|
+
const resolvedObjects = await this._resolve(bindings.objects, currentObjects);
|
|
179
|
+
|
|
180
|
+
log('_updateBindings resolved', {
|
|
181
|
+
resolvedBlueprints: resolvedBlueprints.length,
|
|
182
|
+
resolvedBlueprintKeys: resolvedBlueprints.map((bp) => (bp as any).key),
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Filter current state to only items still in the reduced binding set,
|
|
186
|
+
// then merge in newly resolved items. This ensures unbind events are respected.
|
|
187
|
+
const reducedBlueprintDxns = new ComplexSet<DXN>(
|
|
188
|
+
DXN.hash,
|
|
189
|
+
[...bindings.blueprints].map((ref) => ref.dxn),
|
|
190
|
+
);
|
|
191
|
+
const reducedObjectDxns = new ComplexSet<DXN>(
|
|
192
|
+
DXN.hash,
|
|
193
|
+
[...bindings.objects].map((ref) => ref.dxn),
|
|
194
|
+
);
|
|
195
|
+
const filteredBlueprints = currentBlueprints.filter((obj) => reducedBlueprintDxns.has(Obj.getDXN(obj)));
|
|
196
|
+
const filteredObjects = currentObjects.filter((obj) => reducedObjectDxns.has(Obj.getDXN(obj)));
|
|
197
|
+
const mergedBlueprints = this._mergeInto(filteredBlueprints, resolvedBlueprints);
|
|
198
|
+
const mergedObjects = this._mergeInto(filteredObjects, resolvedObjects);
|
|
199
|
+
|
|
200
|
+
this._registry.set(this._blueprints, mergedBlueprints);
|
|
201
|
+
this._registry.set(this._objects, mergedObjects);
|
|
202
|
+
|
|
203
|
+
log('updated bindings', {
|
|
204
|
+
blueprints: mergedBlueprints.length,
|
|
205
|
+
objects: mergedObjects.length,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
protected override async _close(): Promise<void> {
|
|
210
|
+
// Reset atoms to empty state.
|
|
211
|
+
this._registry.set(this._blueprints, []);
|
|
212
|
+
this._registry.set(this._objects, []);
|
|
113
213
|
}
|
|
114
214
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
props.blueprints?.filter((ref) => !this.blueprints.peek().find((b) => b.dxn.toString() === ref.dxn.toString())) ??
|
|
119
|
-
[];
|
|
120
|
-
const objects =
|
|
121
|
-
props.objects?.filter((ref) => !this.objects.peek().find((o) => o.dxn.toString() === ref.dxn.toString())) ?? [];
|
|
215
|
+
async bind({ blueprints, objects }: BindingProps): Promise<void> {
|
|
216
|
+
const currentBlueprints = this._registry.get(this._blueprints);
|
|
217
|
+
const currentObjects = this._registry.get(this._objects);
|
|
122
218
|
|
|
123
|
-
|
|
219
|
+
const { added: addedBlueprints, next: nextBlueprints } = this._processBindings(blueprints, currentBlueprints);
|
|
220
|
+
const { added: addedObjects, next: nextObjects } = this._processBindings(objects, currentObjects);
|
|
221
|
+
if (!addedBlueprints.length && !addedObjects.length) {
|
|
124
222
|
return;
|
|
125
223
|
}
|
|
126
224
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
225
|
+
// Atomic updates - subscribers notified automatically.
|
|
226
|
+
this._registry.set(this._blueprints, nextBlueprints);
|
|
227
|
+
this._registry.set(this._objects, nextObjects);
|
|
228
|
+
|
|
229
|
+
log('bind', { blueprints: addedBlueprints.length, objects: addedObjects.length });
|
|
230
|
+
await Runtime.runPromise(this._runtime)(
|
|
231
|
+
Feed.append(this._feed, [
|
|
232
|
+
Obj.make(ContextBinding, {
|
|
233
|
+
blueprints: {
|
|
234
|
+
added: addedBlueprints,
|
|
235
|
+
removed: [],
|
|
236
|
+
},
|
|
237
|
+
objects: {
|
|
238
|
+
added: addedObjects,
|
|
239
|
+
removed: [],
|
|
240
|
+
},
|
|
241
|
+
}),
|
|
242
|
+
]),
|
|
243
|
+
);
|
|
139
244
|
}
|
|
140
245
|
|
|
141
|
-
async unbind(
|
|
142
|
-
if (!
|
|
246
|
+
async unbind({ blueprints, objects }: BindingProps): Promise<void> {
|
|
247
|
+
if (!blueprints?.length && !objects?.length) {
|
|
143
248
|
return;
|
|
144
249
|
}
|
|
145
250
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
251
|
+
// Immediately update atom state so removals are reflected before the queue round-trips.
|
|
252
|
+
const removedBlueprintDxns = (blueprints ?? []).map((ref) => ref.dxn);
|
|
253
|
+
const removedObjectDxns = (objects ?? []).map((ref) => ref.dxn);
|
|
254
|
+
if (removedBlueprintDxns.length > 0) {
|
|
255
|
+
const current = this._registry.get(this._blueprints);
|
|
256
|
+
this._registry.set(
|
|
257
|
+
this._blueprints,
|
|
258
|
+
current.filter((obj) => !removedBlueprintDxns.some((dxn) => DXN.equalsEchoId(Obj.getDXN(obj), dxn))),
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
if (removedObjectDxns.length > 0) {
|
|
262
|
+
const current = this._registry.get(this._objects);
|
|
263
|
+
this._registry.set(
|
|
264
|
+
this._objects,
|
|
265
|
+
current.filter((obj) => !removedObjectDxns.some((dxn) => DXN.equalsEchoId(Obj.getDXN(obj), dxn))),
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
log('unbind', { blueprints: blueprints?.length, objects: objects?.length });
|
|
270
|
+
await Runtime.runPromise(this._runtime)(
|
|
271
|
+
Feed.append(this._feed, [
|
|
272
|
+
Obj.make(ContextBinding, {
|
|
273
|
+
blueprints: {
|
|
274
|
+
added: [],
|
|
275
|
+
removed: blueprints ?? [],
|
|
276
|
+
},
|
|
277
|
+
objects: {
|
|
278
|
+
added: [],
|
|
279
|
+
removed: objects ?? [],
|
|
280
|
+
},
|
|
281
|
+
}),
|
|
282
|
+
]),
|
|
283
|
+
);
|
|
158
284
|
}
|
|
159
285
|
|
|
160
|
-
|
|
286
|
+
/**
|
|
287
|
+
* Process bindings to filter duplicates and determine next state.
|
|
288
|
+
*/
|
|
289
|
+
private _processBindings<T extends Obj.Unknown>(
|
|
290
|
+
refs: Ref.Ref<T>[] | undefined,
|
|
291
|
+
current: T[],
|
|
292
|
+
): { added: Ref.Ref<T>[]; next: T[] } {
|
|
293
|
+
const next = [...current];
|
|
294
|
+
const added: Ref.Ref<T>[] = [];
|
|
295
|
+
if (!refs?.length) {
|
|
296
|
+
return { added, next };
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const seen = new Set(current.map((obj) => Obj.getDXN(obj).toString()));
|
|
300
|
+
for (const ref of refs) {
|
|
301
|
+
const dxn = ref.dxn.toString();
|
|
302
|
+
if (!seen.has(dxn)) {
|
|
303
|
+
seen.add(dxn);
|
|
304
|
+
added.push(ref);
|
|
305
|
+
|
|
306
|
+
// Only resolve target if available (has target or resolver).
|
|
307
|
+
if (ref.isAvailable) {
|
|
308
|
+
const target = ref.target;
|
|
309
|
+
if (target) {
|
|
310
|
+
next.push(target);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return { added, next };
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Reduce results into sets of blueprints and objects.
|
|
321
|
+
*/
|
|
322
|
+
private _reduce(items: ContextBinding[]): Bindings {
|
|
161
323
|
return Function.pipe(
|
|
162
324
|
items,
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
325
|
+
EArray.reduce(new Bindings(), (context, { blueprints, objects }) => {
|
|
326
|
+
blueprints.added.forEach((ref) => context.blueprints.add(ref));
|
|
327
|
+
blueprints.removed.forEach((ref) => context.blueprints.delete(ref));
|
|
328
|
+
|
|
329
|
+
objects.added.forEach((ref) => context.objects.add(ref));
|
|
330
|
+
objects.removed.forEach((ref) => {
|
|
168
331
|
for (const obj of context.objects) {
|
|
169
332
|
if (DXN.equalsEchoId(obj.dxn, ref.dxn)) {
|
|
170
333
|
context.objects.delete(obj);
|
|
171
334
|
}
|
|
172
335
|
}
|
|
173
336
|
});
|
|
174
|
-
|
|
337
|
+
|
|
175
338
|
return context;
|
|
176
339
|
}),
|
|
177
340
|
);
|
|
178
341
|
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Merge resolved items into the current set, adding only items not already present (by DXN).
|
|
345
|
+
*/
|
|
346
|
+
private _mergeInto<T extends Obj.Unknown>(current: T[], resolved: T[]): T[] {
|
|
347
|
+
const seen = new Set(current.map((obj) => Obj.getDXN(obj).toString()));
|
|
348
|
+
const merged = [...current];
|
|
349
|
+
for (const obj of resolved) {
|
|
350
|
+
const dxn = Obj.getDXN(obj).toString();
|
|
351
|
+
if (!seen.has(dxn)) {
|
|
352
|
+
seen.add(dxn);
|
|
353
|
+
merged.push(obj);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return merged;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Resolve references to objects, loading them first if needed and falling back to existing objects.
|
|
361
|
+
*/
|
|
362
|
+
private async _resolve<T extends Obj.Unknown>(refs: Iterable<Ref.Ref<T>>, current: T[]): Promise<T[]> {
|
|
363
|
+
const refArray = [...refs];
|
|
364
|
+
|
|
365
|
+
// Load all refs that need loading.
|
|
366
|
+
await Promise.all(refArray.map((ref) => ref.tryLoad()));
|
|
367
|
+
|
|
368
|
+
return refArray
|
|
369
|
+
.map((ref) => {
|
|
370
|
+
let target: T | undefined;
|
|
371
|
+
// Only resolve target if available (has target or resolver).
|
|
372
|
+
if (ref.isAvailable) {
|
|
373
|
+
target = ref.target;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Fallback to existing object.
|
|
377
|
+
return target ?? current.find((obj) => Obj.getDXN(obj as any).toString() === ref.dxn.toString());
|
|
378
|
+
})
|
|
379
|
+
.filter(isNonNullable);
|
|
380
|
+
}
|
|
179
381
|
}
|
|
180
382
|
|
|
181
383
|
export class AiContextService extends Context.Tag('@dxos/assistant/AiContextService')<
|
|
@@ -183,4 +385,19 @@ export class AiContextService extends Context.Tag('@dxos/assistant/AiContextServ
|
|
|
183
385
|
{
|
|
184
386
|
binder: AiContextBinder;
|
|
185
387
|
}
|
|
186
|
-
>() {
|
|
388
|
+
>() {
|
|
389
|
+
static bindContext = ({ blueprints, objects }: BindingProps): Effect.Effect<void, never, AiContextService> =>
|
|
390
|
+
Effect.gen(function* () {
|
|
391
|
+
const { binder } = yield* AiContextService;
|
|
392
|
+
yield* Effect.promise(() => binder.bind({ blueprints, objects }));
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
static findObjects = <T extends Type.AnyEntity>(
|
|
396
|
+
type: T,
|
|
397
|
+
): Effect.Effect<Schema.Schema.Type<T>[], never, AiContextService> => {
|
|
398
|
+
return Effect.gen(function* () {
|
|
399
|
+
const { binder } = yield* AiContextService;
|
|
400
|
+
return binder.getObjects().filter(Obj.instanceOf(type));
|
|
401
|
+
});
|
|
402
|
+
};
|
|
403
|
+
}
|