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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/LICENSE +102 -5
  2. package/README.md +1 -1
  3. package/dist/lib/neutral/chunk-J5LGTIGS.mjs +10 -0
  4. package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +7 -0
  5. package/dist/lib/neutral/extraction/index.mjs +31 -50
  6. package/dist/lib/neutral/extraction/index.mjs.map +4 -4
  7. package/dist/lib/neutral/index.mjs +1531 -1080
  8. package/dist/lib/neutral/index.mjs.map +4 -4
  9. package/dist/lib/neutral/meta.json +1 -1
  10. package/dist/types/src/extraction/extraction-ner-function/index.d.ts +0 -1
  11. package/dist/types/src/extraction/extraction-ner-function/index.d.ts.map +1 -1
  12. package/dist/types/src/extraction/extraction-ner-function/named-entity-recognition.d.ts.map +1 -1
  13. package/dist/types/src/extraction/extraction.d.ts +879 -153
  14. package/dist/types/src/extraction/extraction.d.ts.map +1 -1
  15. package/dist/types/src/extraction/index.d.ts +0 -1
  16. package/dist/types/src/extraction/index.d.ts.map +1 -1
  17. package/dist/types/src/extraction/quotes.d.ts.map +1 -1
  18. package/dist/types/src/functions/services.d.ts +14 -16
  19. package/dist/types/src/functions/services.d.ts.map +1 -1
  20. package/dist/types/src/index.d.ts +2 -5
  21. package/dist/types/src/index.d.ts.map +1 -1
  22. package/dist/types/src/request/AiRequest.d.ts +106 -0
  23. package/dist/types/src/request/AiRequest.d.ts.map +1 -0
  24. package/dist/types/src/request/artifact-diff.d.ts.map +1 -0
  25. package/dist/types/src/request/format.d.ts +15 -0
  26. package/dist/types/src/request/format.d.ts.map +1 -0
  27. package/dist/types/src/request/index.d.ts +4 -0
  28. package/dist/types/src/request/index.d.ts.map +1 -0
  29. package/dist/types/src/{session → request}/observer.d.ts.map +1 -1
  30. package/dist/types/src/{session → request}/version-pin.d.ts.map +1 -1
  31. package/dist/types/src/{conversation/context.d.ts → session/AiContext.d.ts} +41 -33
  32. package/dist/types/src/session/AiContext.d.ts.map +1 -0
  33. package/dist/types/src/session/AiContext.test.d.ts +2 -0
  34. package/dist/types/src/session/AiContext.test.d.ts.map +1 -0
  35. package/dist/types/src/session/AiSession.d.ts +73 -0
  36. package/dist/types/src/session/AiSession.d.ts.map +1 -0
  37. package/dist/types/src/session/index.d.ts +2 -5
  38. package/dist/types/src/session/index.d.ts.map +1 -1
  39. package/dist/types/src/session/toolkit.d.ts +5 -7
  40. package/dist/types/src/session/toolkit.d.ts.map +1 -1
  41. package/dist/types/src/templates/system.d.ts +1 -2
  42. package/dist/types/src/templates/system.d.ts.map +1 -1
  43. package/dist/types/src/{types → util}/artifact.d.ts +1 -2
  44. package/dist/types/src/util/artifact.d.ts.map +1 -0
  45. package/dist/types/src/util/diff.d.ts.map +1 -0
  46. package/dist/types/src/{diff.test.d.ts.map → util/diff.test.d.ts.map} +1 -1
  47. package/dist/types/src/util/error-handling.d.ts.map +1 -0
  48. package/dist/types/src/{errors.d.ts → util/errors.d.ts} +12 -12
  49. package/dist/types/src/util/errors.d.ts.map +1 -0
  50. package/dist/types/src/util/execution-graph.d.ts.map +1 -0
  51. package/dist/types/src/util/execution-graph.test.d.ts.map +1 -0
  52. package/dist/types/src/util/index.d.ts +7 -0
  53. package/dist/types/src/util/index.d.ts.map +1 -0
  54. package/dist/types/src/util/tracing.d.ts +237 -0
  55. package/dist/types/src/util/tracing.d.ts.map +1 -0
  56. package/dist/types/tsconfig.tsbuildinfo +1 -1
  57. package/package.json +25 -41
  58. package/src/extraction/extraction-ner-function/index.ts +0 -1
  59. package/src/extraction/extraction.ts +36 -8
  60. package/src/extraction/index.ts +0 -1
  61. package/src/functions/services.ts +58 -53
  62. package/src/index.ts +2 -5
  63. package/src/request/AiRequest.ts +386 -0
  64. package/src/{session → request}/format.ts +15 -11
  65. package/src/request/index.ts +8 -0
  66. package/src/session/AiContext.test.ts +53 -0
  67. package/src/{conversation/context.ts → session/AiContext.ts} +145 -72
  68. package/src/session/AiSession.ts +324 -0
  69. package/src/session/index.ts +3 -5
  70. package/src/session/toolkit.ts +18 -15
  71. package/src/templates/instructions/system.tpl +50 -2
  72. package/src/templates/system.ts +7 -6
  73. package/src/{types → util}/artifact.ts +2 -3
  74. package/src/{diff.test.ts → util/diff.test.ts} +3 -3
  75. package/src/{session → util}/error-handling.ts +3 -2
  76. package/src/{execution-graph.test.ts → util/execution-graph.test.ts} +1 -1
  77. package/src/{execution-graph.ts → util/execution-graph.ts} +16 -6
  78. package/src/util/index.ts +10 -0
  79. package/src/util/tracing.ts +57 -0
  80. package/dist/lib/neutral/chunk-BE6SRJ4N.mjs +0 -193
  81. package/dist/lib/neutral/chunk-BE6SRJ4N.mjs.map +0 -7
  82. package/dist/lib/neutral/testing/index.mjs +0 -38
  83. package/dist/lib/neutral/testing/index.mjs.map +0 -7
  84. package/dist/types/src/conversation/context.d.ts.map +0 -1
  85. package/dist/types/src/conversation/context.test.d.ts +0 -2
  86. package/dist/types/src/conversation/context.test.d.ts.map +0 -1
  87. package/dist/types/src/conversation/conversation.d.ts +0 -66
  88. package/dist/types/src/conversation/conversation.d.ts.map +0 -1
  89. package/dist/types/src/conversation/conversation.test.d.ts +0 -2
  90. package/dist/types/src/conversation/conversation.test.d.ts.map +0 -1
  91. package/dist/types/src/conversation/index.d.ts +0 -3
  92. package/dist/types/src/conversation/index.d.ts.map +0 -1
  93. package/dist/types/src/diff.d.ts.map +0 -1
  94. package/dist/types/src/errors.d.ts.map +0 -1
  95. package/dist/types/src/execution-graph.d.ts.map +0 -1
  96. package/dist/types/src/execution-graph.test.d.ts.map +0 -1
  97. package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.d.ts +0 -4
  98. package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.d.ts.map +0 -1
  99. package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.test.d.ts +0 -2
  100. package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.test.d.ts.map +0 -1
  101. package/dist/types/src/extraction/extraction-llm-function/index.d.ts +0 -2
  102. package/dist/types/src/extraction/extraction-llm-function/index.d.ts.map +0 -1
  103. package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.d.ts +0 -355
  104. package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.d.ts.map +0 -1
  105. package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.test.d.ts +0 -2
  106. package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.test.d.ts.map +0 -1
  107. package/dist/types/src/functions.test.d.ts +0 -2
  108. package/dist/types/src/functions.test.d.ts.map +0 -1
  109. package/dist/types/src/session/artifact-diff.d.ts.map +0 -1
  110. package/dist/types/src/session/error-handling.d.ts.map +0 -1
  111. package/dist/types/src/session/format.d.ts +0 -12
  112. package/dist/types/src/session/format.d.ts.map +0 -1
  113. package/dist/types/src/session/format.test.d.ts +0 -2
  114. package/dist/types/src/session/format.test.d.ts.map +0 -1
  115. package/dist/types/src/session/session.d.ts +0 -61
  116. package/dist/types/src/session/session.d.ts.map +0 -1
  117. package/dist/types/src/session/session.test.d.ts +0 -2
  118. package/dist/types/src/session/session.test.d.ts.map +0 -1
  119. package/dist/types/src/testing/index.d.ts +0 -2
  120. package/dist/types/src/testing/index.d.ts.map +0 -1
  121. package/dist/types/src/testing/layer.d.ts +0 -22
  122. package/dist/types/src/testing/layer.d.ts.map +0 -1
  123. package/dist/types/src/types/artifact.d.ts.map +0 -1
  124. package/dist/types/src/types/index.d.ts +0 -2
  125. package/dist/types/src/types/index.d.ts.map +0 -1
  126. package/src/conversation/context.test.ts +0 -46
  127. package/src/conversation/conversation.test.ts +0 -65
  128. package/src/conversation/conversation.ts +0 -247
  129. package/src/conversation/index.ts +0 -6
  130. package/src/extraction/extraction-llm-function/extraction-llm-function.test.ts +0 -84
  131. package/src/extraction/extraction-llm-function/extraction-llm-function.ts +0 -62
  132. package/src/extraction/extraction-llm-function/index.ts +0 -5
  133. package/src/extraction/extraction-ner-function/extraction-ner-function.test.ts +0 -116
  134. package/src/extraction/extraction-ner-function/extraction-ner-function.ts +0 -56
  135. package/src/functions.conversations.json +0 -1
  136. package/src/functions.test.ts +0 -74
  137. package/src/session/format.test.ts +0 -124
  138. package/src/session/session.conversations.json +0 -1
  139. package/src/session/session.test.ts +0 -191
  140. package/src/session/session.ts +0 -251
  141. package/src/testing/index.ts +0 -5
  142. package/src/testing/layer.ts +0 -85
  143. package/src/types/index.ts +0 -5
  144. /package/dist/types/src/{session → request}/artifact-diff.d.ts +0 -0
  145. /package/dist/types/src/{session → request}/observer.d.ts +0 -0
  146. /package/dist/types/src/{session → request}/version-pin.d.ts +0 -0
  147. /package/dist/types/src/{diff.d.ts → util/diff.d.ts} +0 -0
  148. /package/dist/types/src/{diff.test.d.ts → util/diff.test.d.ts} +0 -0
  149. /package/dist/types/src/{session → util}/error-handling.d.ts +0 -0
  150. /package/dist/types/src/{execution-graph.d.ts → util/execution-graph.d.ts} +0 -0
  151. /package/dist/types/src/{execution-graph.test.d.ts → util/execution-graph.test.d.ts} +0 -0
  152. /package/src/{session → request}/artifact-diff.ts +0 -0
  153. /package/src/{session → request}/observer.ts +0 -0
  154. /package/src/{session → request}/version-pin.ts +0 -0
  155. /package/src/{diff.ts → util/diff.ts} +0 -0
  156. /package/src/{errors.ts → util/errors.ts} +0 -0
@@ -1,391 +1,769 @@
1
1
  import {
2
- ArtifactId,
3
- RefFromLLM,
4
- ToolExecutionServices,
5
- createArtifactElement,
6
- makeToolExecutionServiceFromFunctions,
7
- makeToolResolverFromFunctions
8
- } from "./chunk-BE6SRJ4N.mjs";
2
+ __export
3
+ } from "./chunk-J5LGTIGS.mjs";
9
4
 
10
- // src/conversation/context.ts
11
- import { Atom, Registry } from "@effect-atom/atom-react";
12
- import * as EArray from "effect/Array";
5
+ // src/functions/services.ts
6
+ import * as Tool from "@effect/ai/Tool";
13
7
  import * as Context from "effect/Context";
14
- import * as Effect from "effect/Effect";
15
- import * as Function from "effect/Function";
8
+ import * as Effect2 from "effect/Effect";
9
+ import * as Layer from "effect/Layer";
10
+ import * as Option from "effect/Option";
11
+ import * as Record from "effect/Record";
12
+ import * as Schema4 from "effect/Schema";
13
+ import * as SchemaAST from "effect/SchemaAST";
14
+ import { AiToolNotFoundError, ToolExecutionService, ToolResolverService } from "@dxos/ai";
15
+ import { OpaqueToolkit } from "@dxos/ai";
16
+ import { Operation, OperationRegistry } from "@dxos/compute";
17
+ import { todo } from "@dxos/debug";
18
+ import { Ref as Ref2 } from "@dxos/echo";
19
+ import { invariant } from "@dxos/invariant";
20
+
21
+ // src/util/artifact.ts
16
22
  import * as Schema from "effect/Schema";
17
- import { Blueprint } from "@dxos/blueprints";
18
- import { Resource } from "@dxos/context";
19
- import { DXN, Obj, Query, Ref, Type } from "@dxos/echo";
20
- import { assertArgument } from "@dxos/invariant";
23
+ import { Database, Obj, Ref } from "@dxos/echo";
24
+ import { EncodedReference } from "@dxos/echo-protocol";
25
+ import { DXN, LOCAL_SPACE_TAG } from "@dxos/keys";
26
+ import { trim } from "@dxos/util";
27
+ var createArtifactElement = (id) => `<artifact id=${id} />`;
28
+ var ArtifactId = class extends Schema.String.annotations({
29
+ // TODO(dmaretskyi): This section gets overriden.
30
+ description: trim`
31
+ The ID of the referenced object. Formats accepted:
32
+ - DXN (dxn:echo:@:01KG7R1ZXWFMWQ4DA1Q6TN1DG4). DXNs can be prepended with an @ symbol for compatibility with in-text references.
33
+ - space ID, object ID tuple (spaceID:objectID)
34
+ - Only object ID that is assumed to be in the current space (01KG7R1ZXWFMWQ4DA1Q6TN1DG4)
35
+ `,
36
+ examples: [
37
+ "dxn:echo:@:01KG7R1ZXWFMWQ4DA1Q6TN1DG4",
38
+ "@dxn:echo:@:01KG7R1ZXWFMWQ4DA1Q6TN1DG4",
39
+ "BM3FSHFOMJCHCG5QW7JTVKGYABD2GAA7G:01KG7R1ZXWFMWQ4DA1Q6TN1DG4",
40
+ "01KG7R1ZXWFMWQ4DA1Q6TN1DG4"
41
+ ]
42
+ }) {
43
+ static toDXN(reference, owningSpaceId) {
44
+ if (reference.startsWith("@dxn:")) {
45
+ return DXN.parse(reference.slice(1));
46
+ } else if (reference.startsWith("dxn:")) {
47
+ return DXN.parse(reference);
48
+ } else if (/^[A-Z0-9]+:[A-Z0-9]+$/.test(reference)) {
49
+ const [spaceId, objectId] = reference.split(":");
50
+ return new DXN(DXN.kind.ECHO, [
51
+ spaceId === owningSpaceId ? LOCAL_SPACE_TAG : spaceId,
52
+ objectId
53
+ ]);
54
+ } else if (/^[A-Z0-9]+$/.test(reference)) {
55
+ return DXN.fromLocalObjectId(reference);
56
+ } else {
57
+ throw new Error(`Unable to parse object reference: ${reference}`);
58
+ }
59
+ }
60
+ /**
61
+ * Resolves an artifact ID to an object.
62
+ */
63
+ static resolve(schema, ref) {
64
+ const dxn = ArtifactId.toDXN(ref);
65
+ return Database.resolve(dxn, schema);
66
+ }
67
+ };
68
+ var RefFromLLM = Schema.transform(ArtifactId, Ref.Ref(Obj.Unknown), {
69
+ decode: (fromA, fromI) => EncodedReference.fromDXN(ArtifactId.toDXN(fromA)),
70
+ encode: (toI, toA) => EncodedReference.toDXN(toI).toString(),
71
+ strict: false
72
+ }).annotations({
73
+ description: ArtifactId.ast.annotations.description
74
+ });
75
+
76
+ // src/util/diff.ts
77
+ import { next as A } from "@automerge/automerge";
78
+ import { DocAccessor, toCursorRange } from "@dxos/echo-db";
21
79
  import { log } from "@dxos/log";
22
- import { ComplexSet, isTruthy } from "@dxos/util";
23
- var __dxlog_file = "/__w/dxos/dxos/packages/core/assistant/src/conversation/context.ts";
24
- var ContextBinding = Schema.Struct({
25
- blueprints: Schema.Struct({
26
- added: Schema.Array(Ref.Ref(Blueprint.Blueprint)),
27
- removed: Schema.Array(Ref.Ref(Blueprint.Blueprint))
28
- }),
29
- objects: Schema.Struct({
30
- added: Schema.Array(Ref.Ref(Obj.Unknown)),
31
- removed: Schema.Array(Ref.Ref(Obj.Unknown))
32
- })
33
- }).pipe(Type.object({
34
- typename: "dxos.org/type/ContextBinding",
35
- version: "0.1.0"
36
- }));
37
- var Bindings = class {
38
- blueprints = new ComplexSet((ref) => ref.dxn.toString());
39
- // TODO(burdon): Some DXNs have the Space prefix so only compare the object ID.
40
- objects = new ComplexSet((ref) => ref.dxn.asEchoDXN()?.echoId);
41
- toJSON() {
42
- return {
43
- blueprints: EArray.fromIterable(this.blueprints).map((ref) => ref.dxn.toString()),
44
- objects: EArray.fromIterable(this.objects).map((ref) => ref.dxn.toString())
45
- };
80
+ import { isNonNullable } from "@dxos/util";
81
+ var __dxlog_file = "/__w/dxos/dxos/packages/core/compute/assistant/src/util/diff.ts";
82
+ var reduceDiffs = (diffs) => {
83
+ return diffs.reduce((acc, diff) => {
84
+ const current = acc[acc.length - 1];
85
+ const [op, ...rest] = diff.split(" ");
86
+ switch (op) {
87
+ case "-": {
88
+ if (current && !current.match) {
89
+ current.match += rest.join(" ");
90
+ } else {
91
+ acc.push({
92
+ match: rest.join(" "),
93
+ replace: ""
94
+ });
95
+ }
96
+ return acc;
97
+ }
98
+ case "+": {
99
+ if (current && !current.replace) {
100
+ current.replace += rest.join(" ");
101
+ } else {
102
+ acc.push({
103
+ match: "",
104
+ replace: rest.join(" ")
105
+ });
106
+ }
107
+ return acc;
108
+ }
109
+ default: {
110
+ throw new Error(`Invalid diff format: each line must start with "- " or "+ ", got: ${JSON.stringify(diff)}`);
111
+ }
112
+ }
113
+ }, []);
114
+ };
115
+ var computeDiffsWithCursors = (accessor, diffs) => {
116
+ return reduceDiffs(diffs).map((diff) => {
117
+ const text = DocAccessor.getValue(accessor);
118
+ const idx = text.indexOf(diff.match);
119
+ if (idx !== -1) {
120
+ return {
121
+ cursor: toCursorRange(accessor, idx, idx + diff.match.length),
122
+ text: diff.replace
123
+ };
124
+ }
125
+ }).filter(isNonNullable);
126
+ };
127
+ var applyDiffs = (accessor, diffs, { errorOnNotFound = false } = {}) => {
128
+ for (const diff of reduceDiffs(diffs)) {
129
+ accessor.handle.change((doc) => {
130
+ const text = DocAccessor.getValue(accessor);
131
+ const idx = text.indexOf(diff.match);
132
+ if (idx !== -1) {
133
+ A.splice(doc, accessor.path, idx, diff.match.length, diff.replace);
134
+ } else {
135
+ if (errorOnNotFound) {
136
+ throw new Error(`Diff not found: ${diff.match}`);
137
+ } else {
138
+ log.warn("diff not found", diff, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 71, S: void 0 });
139
+ }
140
+ }
141
+ });
46
142
  }
143
+ return DocAccessor.getValue(accessor);
47
144
  };
48
- var AiContextBinder = class extends Resource {
49
- _blueprints = Atom.make([]).pipe(Atom.keepAlive);
50
- _objects = Atom.make([]).pipe(Atom.keepAlive);
51
- _registry;
52
- _queue;
53
- constructor(options) {
54
- super();
55
- assertArgument(options.queue, "options.queue", "Queue is required");
56
- this._queue = options.queue;
57
- this._registry = options.registry ?? Registry.make();
145
+
146
+ // src/util/error-handling.ts
147
+ import * as AiError from "@effect/ai/AiError";
148
+ import * as HttpClientError from "@effect/platform/HttpClientError";
149
+ import * as Effect from "effect/Effect";
150
+ import * as Schema2 from "effect/Schema";
151
+
152
+ // src/util/errors.ts
153
+ import { BaseError } from "@dxos/errors";
154
+ var AiModelError = class extends BaseError.extend("AiModelError", "AI model error") {
155
+ };
156
+ var AiAssistantError = class extends BaseError.extend("AiAssistantError", "AI assistant error") {
157
+ };
158
+
159
+ // src/util/error-handling.ts
160
+ var AnthropicErrorResponse = Schema2.Struct({
161
+ type: Schema2.Literal("error"),
162
+ error: Schema2.Struct({
163
+ type: Schema2.String,
164
+ message: Schema2.String
165
+ })
166
+ });
167
+ var mapAiError = (err) => Effect.gen(function* () {
168
+ const cause = err.cause;
169
+ if (HttpClientError.isHttpClientError(cause) && cause.reason === "StatusCode") {
170
+ const body = yield* cause.response.json.pipe(Effect.flatMap(Schema2.decodeUnknown(AnthropicErrorResponse, {
171
+ exact: false
172
+ })));
173
+ const parsedCause = new AiModelError({
174
+ message: body.error.message,
175
+ context: {
176
+ model: "anthropic",
177
+ type: body.error.type
178
+ }
179
+ });
180
+ return AiError.UnknownError.make({
181
+ description: body.error.message,
182
+ module: err.module,
183
+ method: err.method,
184
+ cause: parsedCause
185
+ });
186
+ }
187
+ return err;
188
+ }).pipe(Effect.catchAll(() => Effect.succeed(err)));
189
+
190
+ // src/util/execution-graph.ts
191
+ import { AgentStatus } from "@dxos/ai";
192
+ import { Obj as Obj2 } from "@dxos/echo";
193
+ import { MESSAGE_PROPERTY_TOOL_CALL_ID } from "@dxos/functions";
194
+ import { LogLevel } from "@dxos/log";
195
+ import { ContentBlock, Message } from "@dxos/types";
196
+ import { isNonNullable as isNonNullable2 } from "@dxos/util";
197
+ var SKIP_BLOCKS = [
198
+ "text"
199
+ ];
200
+ var ExecutionGraph = class {
201
+ _skipBlocks;
202
+ // TODO(dmaretskyi): Evolve the internal state to support chats, functions, circuit workflows, etc.
203
+ _commits = [];
204
+ _branchNames = /* @__PURE__ */ new Set();
205
+ _lastBlockId;
206
+ // Tool call tracking.
207
+ _toolCallCommitIds = /* @__PURE__ */ new Map();
208
+ _lastCommitByBranch = /* @__PURE__ */ new Map();
209
+ _pendingToolResults = /* @__PURE__ */ new Map();
210
+ constructor(_skipBlocks = SKIP_BLOCKS) {
211
+ this._skipBlocks = _skipBlocks;
58
212
  }
59
213
  /**
60
- * Returns the blueprints atom for subscription.
214
+ * Adds events to the graph.
61
215
  */
62
- get blueprints() {
63
- return this._blueprints;
216
+ addEvents(events) {
217
+ const sortedEvents = this.sortObjectsByCreated(events);
218
+ for (const event of sortedEvents) {
219
+ if (Obj2.instanceOf(Message.Message, event)) {
220
+ this._processMessage(event);
221
+ } else if (Obj2.instanceOf(AgentStatus, event)) {
222
+ this._processAgentStatus(event);
223
+ }
224
+ }
64
225
  }
65
226
  /**
66
- * Returns the objects atom for subscription.
227
+ * Processes a message event and creates commits for its blocks.
67
228
  */
68
- get objects() {
69
- return this._objects;
229
+ _processMessage(message) {
230
+ const messageCommits = messageToCommits(message, this._lastBlockId, this._toolCallCommitIds, this._lastCommitByBranch, this._skipBlocks);
231
+ this._commits.push(...messageCommits);
232
+ messageCommits.forEach((commit) => this._branchNames.add(commit.branch));
233
+ this._trackToolCalls(messageCommits);
234
+ if (messageCommits.length > 0) {
235
+ this._lastBlockId = messageCommits[messageCommits.length - 1].id;
236
+ }
70
237
  }
71
238
  /**
72
- * Gets the current blueprints value.
239
+ * Processes an AgentStatus event.
73
240
  */
74
- getBlueprints() {
75
- return this._registry.get(this._blueprints);
241
+ _processAgentStatus(event) {
242
+ const branch = this._getToolCallBranch(event.parentMessage, event.toolCallId);
243
+ this._branchNames.add(branch);
244
+ const agentStatusCommit = {
245
+ id: event.id,
246
+ branch,
247
+ message: event.message,
248
+ icon: "ph--flag--regular",
249
+ parents: this._getAgentStatusParents(event),
250
+ timestamp: new Date(event.created)
251
+ };
252
+ this._commits.push(agentStatusCommit);
253
+ this._lastBlockId = event.id;
254
+ if (event.toolCallId) {
255
+ this._lastCommitByBranch.set(branch, event.id);
256
+ this._updatePendingToolResults(event.toolCallId, event.id);
257
+ }
76
258
  }
77
259
  /**
78
- * Gets the current objects value.
260
+ * Stable sort: objects with 'created' field are sorted by it, others remain in place.
79
261
  */
80
- getObjects() {
81
- return this._registry.get(this._objects);
262
+ sortObjectsByCreated(objects) {
263
+ const sortedObjects = objects.slice();
264
+ const createdObjects = [];
265
+ sortedObjects.forEach((obj, idx) => {
266
+ if (obj && typeof obj.created === "string") {
267
+ createdObjects.push({
268
+ idx,
269
+ obj
270
+ });
271
+ }
272
+ });
273
+ createdObjects.sort((a, b) => {
274
+ const aDate = new Date(a.obj.created).getTime();
275
+ const bDate = new Date(b.obj.created).getTime();
276
+ return aDate - bDate;
277
+ });
278
+ let createdIdx = 0;
279
+ return sortedObjects.map((obj, _idx) => {
280
+ if (obj && typeof obj.created === "string") {
281
+ return createdObjects[createdIdx++].obj;
282
+ }
283
+ return obj;
284
+ });
82
285
  }
83
286
  /**
84
- * Subscribe to changes in blueprints.
287
+ * Returns the current state of the graph.
85
288
  */
86
- subscribeBlueprints(cb) {
87
- return this._registry.subscribe(this._blueprints, () => cb(this._registry.get(this._blueprints)));
289
+ getGraph(lastRequest = false) {
290
+ const idx = lastRequest ? this._commits.findLastIndex((c) => c.tags?.includes("user")) : -1;
291
+ const commits = idx === -1 ? this._commits : this._commits.slice(idx);
292
+ return {
293
+ branches: Array.from(this._branchNames),
294
+ commits
295
+ };
88
296
  }
89
297
  /**
90
- * Subscribe to changes in objects.
298
+ * Tracks tool calls and handles pending tool result updates.
91
299
  */
92
- subscribeObjects(cb) {
93
- return this._registry.subscribe(this._objects, () => cb(this._registry.get(this._objects)));
94
- }
95
- async _open() {
96
- const query = this._queue.query(Query.type(ContextBinding));
97
- const initialResults = await query.run();
98
- await this._updateBindings(initialResults);
99
- this._ctx.onDispose(query.subscribe(async () => {
100
- await this._updateBindings(query.results);
101
- }));
300
+ _trackToolCalls(commits) {
301
+ commits.forEach((commit) => {
302
+ if (this._isToolCallCommit(commit)) {
303
+ const toolCallId = this._extractToolCallId(commit.id);
304
+ if (toolCallId) {
305
+ this._toolCallCommitIds.set(toolCallId, commit.id);
306
+ }
307
+ }
308
+ if (this._isToolResultCommit(commit)) {
309
+ const toolCallId = this._extractToolCallId(commit.id);
310
+ if (toolCallId) {
311
+ this._pendingToolResults.set(toolCallId, commit.id);
312
+ this._handleLateToolResultUpdate(toolCallId, commit.id);
313
+ }
314
+ }
315
+ this._lastCommitByBranch.set(commit.branch, commit.id);
316
+ });
102
317
  }
103
- async _updateBindings(items) {
104
- if (items.length === 0) {
105
- return;
106
- }
107
- const bindings = this._reduce(items);
108
- const currentBlueprints = this._registry.get(this._blueprints);
109
- const currentObjects = this._registry.get(this._objects);
110
- const newBlueprints = await this._resolve(bindings.blueprints, currentBlueprints);
111
- const newObjects = await this._resolve(bindings.objects, currentObjects);
112
- this._registry.set(this._blueprints, newBlueprints);
113
- this._registry.set(this._objects, newObjects);
114
- log("updated bindings", {
115
- blueprints: newBlueprints.length,
116
- objects: newObjects.length
117
- }, {
118
- F: __dxlog_file,
119
- L: 159,
120
- S: this,
121
- C: (f, a) => f(...a)
122
- });
123
- }
124
- async _close() {
125
- this._registry.set(this._blueprints, []);
126
- this._registry.set(this._objects, []);
127
- }
128
- async bind({ blueprints, objects }) {
129
- const currentBlueprints = this._registry.get(this._blueprints);
130
- const currentObjects = this._registry.get(this._objects);
131
- const { added: addedBlueprints, next: nextBlueprints } = this._processBindings(blueprints, currentBlueprints);
132
- const { added: addedObjects, next: nextObjects } = this._processBindings(objects, currentObjects);
133
- if (!addedBlueprints.length && !addedObjects.length) {
318
+ /**
319
+ * Updates pending tool results to point to an AgentStatus event.
320
+ */
321
+ _updatePendingToolResults(toolCallId, agentStatusId) {
322
+ const toolResultId = this._pendingToolResults.get(toolCallId);
323
+ if (!toolResultId) {
134
324
  return;
135
325
  }
136
- this._registry.set(this._blueprints, nextBlueprints);
137
- this._registry.set(this._objects, nextObjects);
138
- log("bind", {
139
- blueprints: addedBlueprints.length,
140
- objects: addedObjects.length
141
- }, {
142
- F: __dxlog_file,
143
- L: 185,
144
- S: this,
145
- C: (f, a) => f(...a)
146
- });
147
- await this._queue.append([
148
- Obj.make(ContextBinding, {
149
- blueprints: {
150
- added: addedBlueprints,
151
- removed: []
152
- },
153
- objects: {
154
- added: addedObjects,
155
- removed: []
156
- }
157
- })
158
- ]);
159
- }
160
- async unbind({ blueprints, objects }) {
161
- if (!blueprints?.length && !objects?.length) {
326
+ const toolResultCommit = this._commits.find((c) => c.id === toolResultId);
327
+ if (!toolResultCommit) {
162
328
  return;
163
329
  }
164
- log("unbind", {
165
- blueprints: blueprints?.length,
166
- objects: objects?.length
167
- }, {
168
- F: __dxlog_file,
169
- L: 205,
170
- S: this,
171
- C: (f, a) => f(...a)
172
- });
173
- await this._queue.append([
174
- Obj.make(ContextBinding, {
175
- blueprints: {
176
- added: [],
177
- removed: blueprints ?? []
178
- },
179
- objects: {
180
- added: [],
181
- removed: objects ?? []
182
- }
183
- })
184
- ]);
330
+ const toolCallCommitId = this._toolCallCommitIds.get(toolCallId);
331
+ if (!toolCallCommitId) {
332
+ return;
333
+ }
334
+ const toolCallIndex = toolResultCommit.parents.indexOf(toolCallCommitId);
335
+ if (toolCallIndex !== -1) {
336
+ toolResultCommit.parents[toolCallIndex] = agentStatusId;
337
+ }
338
+ this._pendingToolResults.delete(toolCallId);
185
339
  }
186
340
  /**
187
- * Process bindings to filter duplicates and determine next state.
341
+ * Handles late updates when AgentStatus is processed before tool result.
188
342
  */
189
- _processBindings(refs, current) {
190
- const next = [
191
- ...current
192
- ];
193
- const added = [];
194
- if (!refs?.length) {
195
- return {
196
- added,
197
- next
198
- };
343
+ _handleLateToolResultUpdate(toolCallId, toolResultId) {
344
+ const toolCallCommitId = this._toolCallCommitIds.get(toolCallId);
345
+ if (!toolCallCommitId) {
346
+ return;
199
347
  }
200
- const seen = new Set(current.map((obj) => Obj.getDXN(obj).toString()));
201
- for (const ref of refs) {
202
- const dxn = ref.dxn.toString();
203
- if (!seen.has(dxn)) {
204
- seen.add(dxn);
205
- added.push(ref);
206
- if (ref.isAvailable) {
207
- const target = ref.target;
208
- if (target) {
209
- next.push(target);
210
- }
348
+ const messageId = toolCallCommitId.split("_toolCall_")[0];
349
+ const toolCallBranch = `${messageId}_${toolCallId}`;
350
+ const agentStatusCommitId = this._lastCommitByBranch.get(toolCallBranch);
351
+ if (agentStatusCommitId && agentStatusCommitId !== toolCallCommitId) {
352
+ const toolResultCommit = this._commits.find((c) => c.id === toolResultId);
353
+ if (toolResultCommit) {
354
+ const toolCallIndex = toolResultCommit.parents.indexOf(toolCallCommitId);
355
+ if (toolCallIndex !== -1) {
356
+ toolResultCommit.parents[toolCallIndex] = agentStatusCommitId;
211
357
  }
212
358
  }
359
+ this._pendingToolResults.delete(toolCallId);
213
360
  }
214
- return {
215
- added,
216
- next
217
- };
218
361
  }
219
362
  /**
220
- * Reduce results into sets of blueprints and objects.
363
+ * Gets the branch name for a tool call.
221
364
  */
222
- _reduce(items) {
223
- return Function.pipe(items, EArray.reduce(new Bindings(), (context, { blueprints, objects }) => {
224
- blueprints.added.forEach((ref) => context.blueprints.add(ref));
225
- blueprints.removed.forEach((ref) => context.blueprints.delete(ref));
226
- objects.added.forEach((ref) => context.objects.add(ref));
227
- objects.removed.forEach((ref) => {
228
- for (const obj of context.objects) {
229
- if (DXN.equalsEchoId(obj.dxn, ref.dxn)) {
230
- context.objects.delete(obj);
231
- }
232
- }
233
- });
234
- return context;
235
- }));
365
+ _getToolCallBranch(parentMessage, toolCallId) {
366
+ if (toolCallId) {
367
+ return toolCallId;
368
+ }
369
+ return parentMessage || "main";
236
370
  }
237
371
  /**
238
- * Resolve references to objects, loading them first if needed and falling back to existing objects.
372
+ * Gets the parents for an AgentStatus commit.
239
373
  */
240
- async _resolve(refs, current) {
241
- const refArray = [
242
- ...refs
243
- ];
244
- await Promise.all(refArray.map((ref) => ref.tryLoad()));
245
- return refArray.map((ref) => {
246
- let target;
247
- if (ref.isAvailable) {
248
- target = ref.target;
249
- }
250
- return target ?? current.find((obj) => Obj.getDXN(obj).toString() === ref.dxn.toString());
251
- }).filter(isTruthy);
374
+ _getAgentStatusParents(event) {
375
+ if (event.parentMessage && event.toolCallId) {
376
+ return [
377
+ getToolCallId(event.parentMessage, event.toolCallId)
378
+ ];
379
+ }
380
+ return [];
252
381
  }
253
- };
254
- var AiContextService = class _AiContextService extends Context.Tag("@dxos/assistant/AiContextService")() {
255
- static bindContext = ({ blueprints, objects }) => Effect.gen(function* () {
256
- const { binder } = yield* _AiContextService;
257
- yield* Effect.promise(() => binder.bind({
258
- blueprints,
259
- objects
260
- }));
261
- });
262
- static findObjects = (type) => {
263
- return Effect.gen(function* () {
264
- const { binder } = yield* _AiContextService;
265
- return binder.getObjects().filter(Obj.instanceOf(type));
266
- });
267
- };
268
382
  /**
269
- * Glorified type cast until we figure out function service typing.
270
- * For context, the AiContextService is provided to functions called from tool calls, but it's not implemented in types yet.
271
- * @deprecated
383
+ * Checks if a commit is a tool call commit.
272
384
  */
273
- static fixFunctionHandlerType = (eff) => eff;
274
- };
275
-
276
- // src/conversation/conversation.ts
277
- import * as Array3 from "effect/Array";
278
- import * as Context3 from "effect/Context";
279
- import * as Effect6 from "effect/Effect";
280
- import * as Either from "effect/Either";
281
- import { pipe as pipe3 } from "effect/Function";
282
- import * as Layer from "effect/Layer";
283
- import { Resource as Resource2 } from "@dxos/context";
284
- import { Obj as Obj4 } from "@dxos/echo";
285
- import { acquireReleaseResource } from "@dxos/effect";
286
- import { QueueService } from "@dxos/functions";
287
- import { invariant } from "@dxos/invariant";
288
- import { log as log4 } from "@dxos/log";
289
- import { McpToolkit } from "@dxos/mcp-client";
290
- import { Message as Message3 } from "@dxos/types";
291
-
292
- // src/session/artifact-diff.ts
293
- import * as Context2 from "effect/Context";
294
- var ArtifactDiffResolver = class extends Context2.Tag("@dxos/assistant/ArtifactDiffResolver")() {
295
- };
296
-
297
- // src/session/observer.ts
298
- import * as Effect2 from "effect/Effect";
299
- import * as Function2 from "effect/Function";
300
- var GenerationObserver = Object.freeze({
301
- 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) } = {}) => ({
302
- onBegin,
303
- onPart,
304
- onBlock,
305
- onEnd,
306
- onMessage
307
- }),
308
- noop: () => GenerationObserver.make(),
385
+ _isToolCallCommit(commit) {
386
+ return commit.message.includes("Calling tool");
387
+ }
309
388
  /**
310
- * Debug printer to be used in unit-tests and browser devtools.
389
+ * Checks if a commit is a tool result commit.
311
390
  */
312
- fromPrinter: (printer) => GenerationObserver.make({
313
- onBlock: (block) => Effect2.sync(() => {
314
- if (block.pending) {
315
- return;
316
- }
317
- printer.printContentBlock(block);
318
- }),
319
- onMessage: (message) => Effect2.sync(() => {
320
- if (message.sender.role === "assistant") {
321
- return;
322
- }
323
- printer.printMessage(message);
324
- })
325
- })
326
- });
327
-
328
- // src/session/session.ts
329
- import * as LanguageModel from "@effect/ai/LanguageModel";
330
- import * as Chunk from "effect/Chunk";
331
- import * as Effect4 from "effect/Effect";
332
- import * as Stream from "effect/Stream";
333
- import { AiParser, AiPreprocessor, AiSummarizer, callTool, withoutToolCallParising } from "@dxos/ai";
334
- import { Obj as Obj3 } from "@dxos/echo";
335
- import { TracingService } from "@dxos/functions";
336
- import { log as log3 } from "@dxos/log";
337
- import { Message as Message2 } from "@dxos/types";
338
-
339
- // src/session/format.ts
340
- import * as Effect3 from "effect/Effect";
341
- import * as Function3 from "effect/Function";
342
- import * as Option from "effect/Option";
343
- import { Template } from "@dxos/blueprints";
344
- import { Obj as Obj2 } from "@dxos/echo";
345
- import { ObjectVersion } from "@dxos/echo-db";
346
- import { log as log2 } from "@dxos/log";
347
- import { Message } from "@dxos/types";
348
- import { trim } from "@dxos/util";
349
-
350
- // src/errors.ts
351
- import { BaseError } from "@dxos/errors";
352
- var AiModelError = class extends BaseError.extend("AiModelError", "AI model error") {
391
+ _isToolResultCommit(commit) {
392
+ return commit.message.includes("Tool call succeeded") || commit.message.includes("Tool error:");
393
+ }
394
+ /**
395
+ * Extracts tool call ID from a commit ID.
396
+ */
397
+ _extractToolCallId(commitId) {
398
+ const toolCallMatch = commitId.match(/_toolCall_(.+)$/);
399
+ const toolResultMatch = commitId.match(/_toolResult_(.+)$/);
400
+ return toolCallMatch?.[1] || toolResultMatch?.[1];
401
+ }
353
402
  };
354
- var AiAssistantError = class extends BaseError.extend("AiAssistantError", "AI assistant error") {
403
+ var messageToCommits = (message, lastBlockId, toolCallIds, lastCommitByBranch, skipBlocks) => {
404
+ let previousBlockId = lastBlockId;
405
+ return message.blocks.map((block, idx) => {
406
+ const branch = getMessageBranch(message);
407
+ const parents = getBlockParents(block, previousBlockId, message, toolCallIds, lastCommitByBranch);
408
+ if (skipBlocks?.includes(block._tag)) {
409
+ return null;
410
+ }
411
+ const commit = createBlockCommit(block, message, branch, parents, idx);
412
+ if (commit) {
413
+ previousBlockId = commit.id;
414
+ }
415
+ return commit;
416
+ }).filter(isNonNullable2);
355
417
  };
356
-
357
- // src/session/format.ts
358
- var __dxlog_file2 = "/__w/dxos/dxos/packages/core/assistant/src/session/format.ts";
359
- var formatSystemPrompt = ({ system, blueprints = [], objects = [] }) => Effect3.gen(function* () {
360
- const blueprintDefs = yield* Function3.pipe(blueprints, Effect3.forEach((blueprint) => Effect3.succeed(blueprint.instructions)), Effect3.flatMap(Effect3.forEach((template) => Effect3.gen(function* () {
361
- return trim`
362
- <blueprint>
363
- ${yield* Template.processTemplate(template)}
364
- </blueprint>
365
- `;
366
- }))), Effect3.map((blueprints2) => blueprints2.length > 0 ? [
367
- "## Blueprints Definitions",
368
- ...blueprints2
369
- ].join("\n\n") : void 0));
370
- const objectDefs = yield* Function3.pipe(objects, Effect3.forEach((object) => Effect3.succeed(trim`
371
- <object>
372
- <dxn>${Obj2.getDXN(object)}</dxn>
373
- <typename>${Obj2.getTypename(object)}</typename>
374
- </object>
375
- `)), Effect3.map((objects2) => objects2.length > 0 ? [
376
- "## Context Objects",
377
- ...objects2
378
- ].join("\n\n") : void 0));
379
- return yield* Function3.pipe(Effect3.succeed([
380
- system,
381
- blueprintDefs,
382
- objectDefs
383
- ].filter((def) => def !== void 0)), Effect3.map((parts) => parts.join("\n\n")));
384
- });
385
- var formatUserPrompt = ({ prompt, history = [] }) => Effect3.gen(function* () {
418
+ var getBlockParents = (block, previousBlockId, message, toolCallIds, lastCommitByBranch) => {
419
+ const parents = [];
420
+ if (block._tag === "toolResult") {
421
+ if (previousBlockId) {
422
+ parents.push(previousBlockId);
423
+ }
424
+ const toolCallCommitId = toolCallIds?.get(block.toolCallId);
425
+ if (toolCallCommitId) {
426
+ const toolCallBranch = getToolCallBranchFromCommitId(toolCallCommitId, block.toolCallId);
427
+ const lastBlockFromToolCallBranch = lastCommitByBranch?.get(toolCallBranch);
428
+ if (lastBlockFromToolCallBranch) {
429
+ parents.push(lastBlockFromToolCallBranch);
430
+ } else {
431
+ parents.push(toolCallCommitId);
432
+ }
433
+ }
434
+ } else {
435
+ if (previousBlockId) {
436
+ parents.push(previousBlockId);
437
+ } else {
438
+ const toolCallParent = getParentId(message);
439
+ if (toolCallParent) {
440
+ parents.push(toolCallParent);
441
+ }
442
+ }
443
+ }
444
+ return parents;
445
+ };
446
+ var createBlockCommit = (block, message, branch, parents, idx) => {
447
+ const timestamp = new Date(message.created);
448
+ switch (block._tag) {
449
+ case "text":
450
+ if (!block.text.trim().length) {
451
+ return null;
452
+ }
453
+ return {
454
+ id: getGenericBlockId(message.id, idx),
455
+ branch,
456
+ parents,
457
+ timestamp,
458
+ ...message.sender.role === "user" ? {
459
+ icon: "ph--user--regular",
460
+ tags: [
461
+ "user"
462
+ ],
463
+ message: "Processing request..."
464
+ } : {
465
+ icon: "ph--drone--regular",
466
+ message: `Response (${block.text.split(" ").length} words)`
467
+ }
468
+ };
469
+ case "toolCall":
470
+ return {
471
+ id: getToolCallId(message.id, block.toolCallId),
472
+ branch,
473
+ parents,
474
+ timestamp,
475
+ icon: "ph--wrench--regular",
476
+ level: LogLevel.INFO,
477
+ message: `Calling tool (${block.name})`
478
+ };
479
+ case "toolResult":
480
+ return {
481
+ id: getToolResultId(message.id, block.toolCallId),
482
+ branch,
483
+ parents,
484
+ timestamp,
485
+ icon: block.error ? "ph--x-circle--regular" : "ph--check-circle--regular",
486
+ level: block.error ? LogLevel.ERROR : LogLevel.INFO,
487
+ message: block.error ? "Tool error: " + block.error : "Tool call succeeded"
488
+ };
489
+ case "stats":
490
+ return {
491
+ id: getGenericBlockId(message.id, idx),
492
+ branch,
493
+ parents,
494
+ timestamp,
495
+ icon: "ph--rocket--regular",
496
+ level: LogLevel.INFO,
497
+ message: ContentBlock.createStatsMessage(block)
498
+ };
499
+ case "status":
500
+ return {
501
+ id: getGenericBlockId(message.id, idx),
502
+ branch,
503
+ parents,
504
+ timestamp,
505
+ message: block.statusText,
506
+ level: LogLevel.INFO,
507
+ icon: "ph--flag--regular"
508
+ };
509
+ case "reasoning":
510
+ return {
511
+ id: getGenericBlockId(message.id, idx),
512
+ branch,
513
+ parents,
514
+ timestamp,
515
+ message: block.reasoningText ?? "Thinking...",
516
+ icon: "ph--brain--regular"
517
+ };
518
+ case "reference":
519
+ return {
520
+ id: getGenericBlockId(message.id, idx),
521
+ branch,
522
+ parents,
523
+ timestamp,
524
+ icon: "ph--link--regular",
525
+ message: stringifyRef(block.reference)
526
+ };
527
+ default:
528
+ return null;
529
+ }
530
+ };
531
+ var getToolCallBranchFromCommitId = (toolCallCommitId, toolCallId) => {
532
+ const messageId = toolCallCommitId.split("_toolCall_")[0];
533
+ return `${messageId}_${toolCallId}`;
534
+ };
535
+ var getToolCallId = (messageId, toolCallId) => `${messageId}_toolCall_${toolCallId}`;
536
+ var getToolResultId = (messageId, toolCallId) => `${messageId}_toolResult_${toolCallId}`;
537
+ var getGenericBlockId = (messageId, idx) => `${messageId}_block_${idx}`;
538
+ var getBranchName = (options) => {
539
+ if (options.toolCallId) {
540
+ return options.toolCallId;
541
+ } else if (options.parentMessage) {
542
+ return options.parentMessage;
543
+ } else {
544
+ return "main";
545
+ }
546
+ };
547
+ var getMessageBranch = (message) => {
548
+ return getBranchName({
549
+ parentMessage: message.parentMessage,
550
+ toolCallId: message.properties?.[MESSAGE_PROPERTY_TOOL_CALL_ID]
551
+ });
552
+ };
553
+ var getParentId = (message) => {
554
+ if (message.parentMessage && message.properties?.[MESSAGE_PROPERTY_TOOL_CALL_ID]) {
555
+ return getToolCallId(message.parentMessage, message.properties[MESSAGE_PROPERTY_TOOL_CALL_ID]);
556
+ } else {
557
+ return void 0;
558
+ }
559
+ };
560
+ var stringifyRef = (ref) => {
561
+ if (ref.target) {
562
+ return stringifyObject(ref.target);
563
+ }
564
+ return ref.dxn.asEchoDXN()?.echoId ?? ref.dxn.asQueueDXN()?.objectId ?? "";
565
+ };
566
+ var stringifyObject = (obj) => {
567
+ return Obj2.getLabel(obj) ?? Obj2.getTypename(obj) ?? obj.id;
568
+ };
569
+
570
+ // src/util/tracing.ts
571
+ import * as Schema3 from "effect/Schema";
572
+ import { Trace } from "@dxos/compute";
573
+ import { Obj as Obj3 } from "@dxos/echo";
574
+ import { ContentBlock as ContentBlock2, Actor } from "@dxos/types";
575
+ var PartialBlock = Trace.EventType("assistant.partialBlock", {
576
+ schema: Schema3.Struct({
577
+ messageId: Obj3.ID,
578
+ role: Actor.Role,
579
+ block: ContentBlock2.Any
580
+ }),
581
+ isEphemeral: true
582
+ });
583
+ var CompleteBlock = Trace.EventType("assistant.completeBlock", {
584
+ schema: Schema3.Struct({
585
+ messageId: Obj3.ID,
586
+ role: Actor.Role,
587
+ block: ContentBlock2.Any
588
+ }),
589
+ isEphemeral: false
590
+ });
591
+ var AgentRequestBegin = Trace.EventType("assistant.agentRequestBegin", {
592
+ schema: Schema3.Struct({}),
593
+ isEphemeral: false
594
+ });
595
+ var AgentRequestEnd = Trace.EventType("assistant.agentRequestEnd", {
596
+ schema: Schema3.Struct({}),
597
+ isEphemeral: false
598
+ });
599
+ var McpServerError = Trace.EventType("assistant.mcpServerError", {
600
+ schema: Schema3.Struct({
601
+ url: Schema3.String,
602
+ protocol: Schema3.Literal("sse", "http"),
603
+ message: Schema3.String
604
+ }),
605
+ isEphemeral: true
606
+ });
607
+
608
+ // src/functions/services.ts
609
+ var __dxlog_file2 = "/__w/dxos/dxos/packages/core/compute/assistant/src/functions/services.ts";
610
+ var makeToolResolverFromOperations = ({ toolkit: extraToolkit = OpaqueToolkit.empty } = {}) => {
611
+ return Layer.effect(ToolResolverService, Effect2.gen(function* () {
612
+ const toolkitProvider = yield* OpaqueToolkit.OpaqueToolkitProvider;
613
+ const operationRegistry = yield* OperationRegistry.Service;
614
+ return {
615
+ resolve: (id) => Effect2.gen(function* () {
616
+ const toolkit = OpaqueToolkit.merge(extraToolkit, toolkitProvider.getToolkit());
617
+ const tool = toolkit.toolkit.tools[id];
618
+ if (tool) {
619
+ return tool;
620
+ }
621
+ return yield* operationRegistry.resolve(id).pipe(Effect2.flatMap(Option.match({
622
+ onSome: (_) => Effect2.succeed(projectFunctionToTool(_)),
623
+ onNone: () => Effect2.fail(new AiToolNotFoundError(id))
624
+ })));
625
+ })
626
+ };
627
+ }));
628
+ };
629
+ var makeToolExecutionService = (opts) => Layer.effect(ToolExecutionService, Effect2.gen(function* () {
630
+ const toolkitProvider = yield* OpaqueToolkit.OpaqueToolkitProvider;
631
+ const toolkit = toolkitProvider.getToolkit();
632
+ const toolkitHandler = yield* toolkit.toolkit.pipe(Effect2.provide(toolkit.layer));
633
+ invariant(isHandlerLike(toolkitHandler), void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 42, S: this, A: ["isHandlerLike(toolkitHandler)", ""] });
634
+ return {
635
+ handlersFor: (toolkit2) => {
636
+ const makeHandler = (tool) => {
637
+ return Effect2.fn(`toolFunctionHandler ${tool.name}`)(function* (input) {
638
+ if (toolkitHandler.tools[tool.name]) {
639
+ if (Tool.isProviderDefined(tool)) {
640
+ throw new Error("Attempted to call a provider-defined tool");
641
+ }
642
+ const { result } = yield* toolkitHandler.handle(tool.name, input);
643
+ return result;
644
+ }
645
+ return yield* opts.invoke(tool, input).pipe(Effect2.catchAllDefect((defect) => Effect2.fail(defect)));
646
+ });
647
+ };
648
+ return toolkit2.of(Record.map(toolkit2.tools, (tool, _name) => Tool.isUserDefined(tool) ? makeHandler(tool) : null));
649
+ }
650
+ };
651
+ }));
652
+ var makeToolExecutionServiceFromOperationInvoker = () => {
653
+ return Layer.unwrapEffect(Effect2.gen(function* () {
654
+ const operationInvoker = yield* Operation.Service;
655
+ return makeToolExecutionService({
656
+ invoke: (tool, input) => Effect2.gen(function* () {
657
+ const operationDef = getOperationFromTool(tool).pipe(Option.getOrThrow);
658
+ return yield* operationInvoker.invoke(operationDef, input).pipe(Effect2.orDie);
659
+ })
660
+ });
661
+ }));
662
+ };
663
+ var ToolExecutionServices = Layer.mergeAll(makeToolResolverFromOperations(), makeToolExecutionServiceFromOperationInvoker());
664
+ var FunctionToolAnnotation = class extends Context.Tag("@dxos/assistant/FunctionToolAnnotation")() {
665
+ };
666
+ var getOperationFromTool = (tool) => {
667
+ return Context.getOption(FunctionToolAnnotation)(tool.annotations).pipe(Option.map(({ definition }) => definition));
668
+ };
669
+ var toolCache = /* @__PURE__ */ new WeakMap();
670
+ var projectFunctionToTool = (fn2) => {
671
+ if (toolCache.has(fn2)) {
672
+ return toolCache.get(fn2);
673
+ }
674
+ const tool = Tool.make(makeToolName(fn2.meta.name ?? fn2.meta.key), {
675
+ description: fn2.meta.description,
676
+ parameters: createStructFieldsFromSchema(fn2.input),
677
+ failure: Schema4.Any
678
+ }).annotate(FunctionToolAnnotation, {
679
+ definition: fn2
680
+ });
681
+ toolCache.set(fn2, tool);
682
+ return tool;
683
+ };
684
+ var makeToolName = (name) => {
685
+ const toolName = name.toLowerCase().replace(/[^a-zA-Z0-9]/g, "-");
686
+ invariant(toolName.match(/^[a-z_][a-z0-9-_]*$/), void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 100, S: void 0, A: ["toolName.match(/^[a-z_][a-z0-9-_]*$/)", ""] });
687
+ return toolName;
688
+ };
689
+ var createStructFieldsFromSchema = (schema) => {
690
+ switch (schema.ast._tag) {
691
+ case "TypeLiteral":
692
+ return Object.fromEntries(schema.ast.propertySignatures.map((prop) => [
693
+ prop.name,
694
+ Schema4.make(mapSchemaTypeForLLM(prop.type))
695
+ ]));
696
+ case "VoidKeyword":
697
+ return {};
698
+ default:
699
+ return todo(`Unsupported schema AST: ${schema.ast._tag}`);
700
+ }
701
+ };
702
+ var mapSchemaTypeForLLM = (ast) => {
703
+ if (Ref2.isRefType(ast)) {
704
+ const description = ast.annotations.description ? ast.annotations.description + "\n" + RefFromLLM.ast.annotations.description : RefFromLLM.ast.annotations.description;
705
+ return RefFromLLM.annotations({
706
+ description
707
+ }).ast;
708
+ } else if (SchemaAST.isTupleType(ast)) {
709
+ 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);
710
+ } else if (SchemaAST.isTypeLiteral(ast)) {
711
+ 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);
712
+ }
713
+ return ast;
714
+ };
715
+ var isHandlerLike = (value) => {
716
+ return typeof value.tools === "object" && typeof value.handle === "function";
717
+ };
718
+
719
+ // src/request/format.ts
720
+ import * as Effect3 from "effect/Effect";
721
+ import * as Function from "effect/Function";
722
+ import * as Option2 from "effect/Option";
723
+ import { Template } from "@dxos/compute";
724
+ import { Obj as Obj4 } from "@dxos/echo";
725
+ import { ObjectVersion } from "@dxos/echo-db";
726
+ import { log as log2 } from "@dxos/log";
727
+ import { Message as Message2 } from "@dxos/types";
728
+ import { trim as trim2 } from "@dxos/util";
729
+
730
+ // src/request/artifact-diff.ts
731
+ import * as Context2 from "effect/Context";
732
+ var ArtifactDiffResolver = class extends Context2.Tag("@dxos/assistant/ArtifactDiffResolver")() {
733
+ };
734
+
735
+ // src/request/format.ts
736
+ var __dxlog_file3 = "/__w/dxos/dxos/packages/core/compute/assistant/src/request/format.ts";
737
+ var formatSystemPrompt = ({ system, blueprints = [], objects = [] }) => Effect3.gen(function* () {
738
+ const blueprintDefs = yield* Function.pipe(blueprints, Effect3.forEach((blueprint) => Effect3.succeed(blueprint.instructions)), Effect3.flatMap(Effect3.forEach((template) => Effect3.gen(function* () {
739
+ return trim2`
740
+ <blueprint>
741
+ ${yield* Template.processTemplate(template)}
742
+ </blueprint>
743
+ `;
744
+ }))), Effect3.map((blueprints2) => blueprints2.length > 0 ? [
745
+ "## Blueprints Definitions",
746
+ ...blueprints2
747
+ ].join("\n\n") : void 0));
748
+ const objectDefs = yield* Function.pipe(objects, Effect3.forEach((object) => Effect3.succeed(trim2`
749
+ <object>
750
+ <dxn>${Obj4.getDXN(object)}</dxn>
751
+ <typename>${Obj4.getTypename(object)}</typename>
752
+ </object>
753
+ `)), Effect3.map((objects2) => objects2.length > 0 ? [
754
+ "## Context Objects",
755
+ ...objects2
756
+ ].join("\n\n") : void 0));
757
+ return yield* Function.pipe(Effect3.succeed([
758
+ system,
759
+ blueprintDefs,
760
+ objectDefs
761
+ ].filter((def) => def !== void 0)), Effect3.map((parts) => parts.join("\n\n")));
762
+ }).pipe(Effect3.withSpan("formatSystemPrompt"));
763
+ var formatUserPrompt = ({ prompt, history = [] }) => Effect3.gen(function* () {
386
764
  const blocks = [];
387
765
  const artifactDiffResolver = yield* Effect3.serviceOption(ArtifactDiffResolver);
388
- if (Option.isSome(artifactDiffResolver)) {
766
+ if (Option2.isSome(artifactDiffResolver)) {
389
767
  const versions = gatherObjectVersions(history);
390
768
  const artifactDiff = yield* Effect3.tryPromise({
391
769
  try: () => artifactDiffResolver.value.resolve([
@@ -401,12 +779,7 @@ var formatUserPrompt = ({ prompt, history = [] }) => Effect3.gen(function* () {
401
779
  log2("version", {
402
780
  artifactDiff,
403
781
  versions
404
- }, {
405
- F: __dxlog_file2,
406
- L: 94,
407
- S: this,
408
- C: (f, a) => f(...a)
409
- });
782
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 65, S: this });
410
783
  for (const [id, { version }] of [
411
784
  ...artifactDiff.entries()
412
785
  ]) {
@@ -424,20 +797,23 @@ var formatUserPrompt = ({ prompt, history = [] }) => Effect3.gen(function* () {
424
797
  blocks.push(createArtifactUpdateBlock(artifactDiff));
425
798
  }
426
799
  }
427
- return Obj2.make(Message.Message, {
800
+ return Obj4.make(Message2.Message, {
428
801
  created: (/* @__PURE__ */ new Date()).toISOString(),
429
802
  sender: {
430
803
  role: "user"
431
804
  },
432
- blocks: [
805
+ blocks: typeof prompt === "string" ? [
433
806
  ...blocks,
434
807
  {
435
808
  _tag: "text",
436
809
  text: prompt
437
810
  }
811
+ ] : [
812
+ ...blocks,
813
+ ...prompt
438
814
  ]
439
815
  });
440
- });
816
+ }).pipe(Effect3.withSpan("formatUserPrompt"));
441
817
  var gatherObjectVersions = (messages) => {
442
818
  const artifactIds = /* @__PURE__ */ new Map();
443
819
  for (const message of messages) {
@@ -453,8 +829,8 @@ var createArtifactUpdateBlock = (artifactDiff) => {
453
829
  return {
454
830
  _tag: "text",
455
831
  // TODO(dmaretskyi): Does this need to be a special content-block?
456
- disposition: "artifact-update",
457
- text: trim`
832
+ disposition: "synthetic",
833
+ text: trim2`
458
834
  The following artifacts have been updated since the last message:
459
835
  ${[
460
836
  ...artifactDiff.entries()
@@ -464,153 +840,306 @@ ${diff}` : ""}</changed-artifact>`).join("\n")}
464
840
  };
465
841
  };
466
842
 
467
- // src/session/session.ts
468
- var __dxlog_file3 = "/__w/dxos/dxos/packages/core/assistant/src/session/session.ts";
469
- var AiSession = class {
470
- _options;
471
- /** Prevents concurrent execution of session. */
472
- _semaphore = Effect4.runSync(Effect4.makeSemaphore(1));
473
- /** Message history from queue. */
474
- // TODO(burdon): Evolve into supporting a git-like graph of messages.
475
- _history = [];
476
- /** Pending messages for this session (incl. the current prompt). */
477
- _pending = [];
478
- _started = 0;
479
- _ended = 0;
480
- _toolCalls = 0;
481
- constructor(_options = {}) {
482
- this._options = _options;
483
- }
484
- get duration() {
485
- return this._ended - this._started;
486
- }
487
- get toolCalls() {
488
- return this._toolCalls;
489
- }
490
- run = ({ prompt, system: systemTemplate, history = [], objects = [], blueprints = [], toolkit, observer = GenerationObserver.noop(), onOutput = () => Effect4.void }) => Effect4.gen(this, function* () {
491
- this._started = Date.now();
492
- this._history = [
493
- ...history
494
- ];
495
- this._pending = [];
496
- const submitMessage = (message) => Effect4.gen(this, function* () {
497
- this._pending.push(message);
498
- yield* observer.onMessage(message);
499
- yield* TracingService.emitConverationMessage(message);
500
- yield* onOutput(message);
501
- return message;
502
- });
503
- const system = yield* formatSystemPrompt({
504
- system: systemTemplate,
505
- blueprints,
506
- objects
507
- }).pipe(Effect4.orDie);
508
- if (this._options.summarizationThreshold !== void 0) {
509
- const tokenCount = yield* AiPreprocessor.estimateTokens(yield* AiPreprocessor.preprocessPrompt([
510
- ...this._history
511
- ], {
512
- system
513
- }));
843
+ // src/request/observer.ts
844
+ import * as Effect4 from "effect/Effect";
845
+ import * as Function2 from "effect/Function";
846
+ var GenerationObserver = Object.freeze({
847
+ make: ({ onBegin = Function2.constant(Effect4.void), onPart = Function2.constant(Effect4.void), onBlock = Function2.constant(Effect4.void), onEnd = Function2.constant(Effect4.void), onMessage = Function2.constant(Effect4.void) } = {}) => ({
848
+ onBegin,
849
+ onPart,
850
+ onBlock,
851
+ onEnd,
852
+ onMessage
853
+ }),
854
+ noop: () => GenerationObserver.make(),
855
+ /**
856
+ * Debug printer to be used in unit-tests and browser devtools.
857
+ */
858
+ fromPrinter: (printer) => GenerationObserver.make({
859
+ onBlock: (block) => Effect4.sync(() => {
860
+ if (block.pending) {
861
+ return;
862
+ }
863
+ printer.printContentBlock(block);
864
+ }),
865
+ onMessage: (message) => Effect4.sync(() => {
866
+ if (message.sender.role === "assistant") {
867
+ return;
868
+ }
869
+ printer.printMessage(message);
870
+ })
871
+ })
872
+ });
873
+
874
+ // src/request/AiRequest.ts
875
+ var AiRequest_exports = {};
876
+ __export(AiRequest_exports, {
877
+ Request: () => Request
878
+ });
879
+ import * as LanguageModel from "@effect/ai/LanguageModel";
880
+ import * as Array2 from "effect/Array";
881
+ import * as Chunk from "effect/Chunk";
882
+ import * as Effect5 from "effect/Effect";
883
+ import { pipe as pipe2 } from "effect/Function";
884
+ import * as Option3 from "effect/Option";
885
+ import * as Stream from "effect/Stream";
886
+ import { AiParser, AiPreprocessor, AiSummarizer, callTool, withoutToolCallParising } from "@dxos/ai";
887
+ import { Trace as Trace2 } from "@dxos/compute";
888
+ import { Obj as Obj5 } from "@dxos/echo";
889
+ import { log as log3 } from "@dxos/log";
890
+ import { ContentBlock as ContentBlock3, Message as Message3 } from "@dxos/types";
891
+ var __dxlog_file4 = "/__w/dxos/dxos/packages/core/compute/assistant/src/request/AiRequest.ts";
892
+ var Request = class {
893
+ _options;
894
+ /** Prevents concurrent execution of session. */
895
+ _semaphore = Effect5.runSync(Effect5.makeSemaphore(1));
896
+ _observer;
897
+ _onOutput;
898
+ /** Message history from queue. */
899
+ // TODO(burdon): Evolve into supporting a git-like graph of messages.
900
+ _history = [];
901
+ /** Pending messages for this session (incl. the current prompt). */
902
+ _pending = [];
903
+ _started = 0;
904
+ _ended = 0;
905
+ _toolCalls = 0;
906
+ constructor(_options = {}) {
907
+ this._options = _options;
908
+ this._observer = _options.observer ?? GenerationObserver.noop();
909
+ this._onOutput = _options.onOutput ?? (() => Effect5.void);
910
+ }
911
+ get duration() {
912
+ return this._ended - this._started;
913
+ }
914
+ get toolCalls() {
915
+ return this._toolCalls;
916
+ }
917
+ get pending() {
918
+ return this._pending;
919
+ }
920
+ _submitMessage = (message) => Effect5.gen(this, function* () {
921
+ this._pending.push(message);
922
+ yield* this._observer.onMessage(message);
923
+ for (const block of message.blocks) {
924
+ log3("write complete block", {
925
+ messageId: message.id,
926
+ role: message.sender.role,
927
+ block: JSON.stringify(block)
928
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 59, S: this });
929
+ yield* Trace2.write(CompleteBlock, {
930
+ messageId: message.id,
931
+ role: message.sender.role,
932
+ block
933
+ });
934
+ }
935
+ yield* this._onOutput(message);
936
+ return message;
937
+ });
938
+ getToolCalls = () => pipe2([
939
+ ...this._history,
940
+ ...this._pending
941
+ ], Array2.reverse, Array2.takeWhile((_) => _.sender.role === "assistant"), Array2.flatMap((_) => _.blocks.filter(ContentBlock3.is("toolCall")).map((block) => ({
942
+ block,
943
+ message: _
944
+ }))), Array2.filter((_) => !_.block.providerExecuted), Array2.reverse);
945
+ /**
946
+ * Initialize a session: set up history, perform summarization if needed, and submit the user prompt.
947
+ * Must be called before `runTurn()`.
948
+ */
949
+ begin = ({ prompt, system, history = [], blueprints = [], objects = [] }) => Effect5.gen(this, function* () {
950
+ this._started = Date.now();
951
+ this._history = [
952
+ ...history
953
+ ];
954
+ this._pending = [];
955
+ const systemPrompt = yield* formatSystemPrompt({
956
+ system,
957
+ blueprints,
958
+ objects
959
+ }).pipe(Effect5.orDie);
960
+ if (this._options.summarizationThreshold !== void 0) {
961
+ const tokenCount = yield* AiPreprocessor.estimateTokens(yield* AiPreprocessor.preprocessPrompt([
962
+ ...this._history
963
+ ], {
964
+ system: systemPrompt
965
+ }));
514
966
  if (tokenCount > this._options.summarizationThreshold) {
515
967
  const summary = yield* AiSummarizer.summarize([
516
968
  ...this._history
517
969
  ]);
518
- yield* submitMessage(summary);
970
+ yield* this._submitMessage(summary);
519
971
  }
520
972
  }
521
- const promptMessage = yield* submitMessage(yield* formatUserPrompt({
973
+ yield* this._submitMessage(yield* formatUserPrompt({
522
974
  prompt,
523
975
  history
524
976
  }));
977
+ }).pipe(Effect5.withSpan("AiRequest.begin"));
978
+ /**
979
+ * Execute a single turn: one LLM generation followed by tool execution.
980
+ * The toolkit and system prompt can be updated between turns to reflect context changes (e.g. dynamically enabled blueprints).
981
+ */
982
+ runAgentTurn = ({ system, toolkit: opaqueToolkit }) => Effect5.gen(this, function* () {
983
+ log3.info("request", {
984
+ system: {
985
+ snippet: createSnippet(system),
986
+ length: system.length
987
+ },
988
+ pending: this._pending.length,
989
+ history: this._history.length
990
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 116, S: this });
991
+ const prompt = yield* AiPreprocessor.preprocessPrompt([
992
+ ...this._history,
993
+ ...this._pending
994
+ ], {
995
+ system,
996
+ cacheControl: "ephemeral"
997
+ });
998
+ const toolkit = opaqueToolkit ? yield* opaqueToolkit.handlers : void 0;
999
+ const observer = this._observer;
1000
+ let currentMessageId = null;
1001
+ const messages = yield* LanguageModel.streamText({
1002
+ prompt,
1003
+ toolkit,
1004
+ disableToolCallResolution: true
1005
+ }).pipe(withoutToolCallParising, AiParser.parseResponse({
1006
+ emitPartial: true,
1007
+ onBegin: () => observer.onBegin(),
1008
+ onBlock: (block) => observer.onBlock(block),
1009
+ onPart: (part) => observer.onPart(part),
1010
+ onEnd: (summary) => observer.onEnd(summary)
1011
+ }), Stream.map((block) => enrichToolCallBlock(block, toolkit)), Stream.mapEffect((block) => Effect5.gen(this, function* () {
1012
+ if (block.pending) {
1013
+ currentMessageId ??= Obj5.ID.random();
1014
+ log3("emit ephemeral message", {
1015
+ id: currentMessageId,
1016
+ type: block._tag
1017
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 147, S: this });
1018
+ yield* Trace2.write(PartialBlock, {
1019
+ messageId: currentMessageId,
1020
+ role: "assistant",
1021
+ block
1022
+ });
1023
+ return Option3.none();
1024
+ } else {
1025
+ currentMessageId ??= Obj5.ID.random();
1026
+ const id = currentMessageId;
1027
+ currentMessageId = null;
1028
+ log3("emit complete message", {
1029
+ id,
1030
+ type: block._tag
1031
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 161, S: this });
1032
+ const message = Obj5.make(Message3.Message, {
1033
+ id,
1034
+ created: (/* @__PURE__ */ new Date()).toISOString(),
1035
+ sender: {
1036
+ role: "assistant"
1037
+ },
1038
+ blocks: [
1039
+ block
1040
+ ]
1041
+ });
1042
+ return Option3.some(yield* this._submitMessage(message));
1043
+ }
1044
+ }), {
1045
+ concurrency: 1,
1046
+ unordered: false
1047
+ }), Stream.filterMap((_) => _), Stream.runCollect, Effect5.map(Chunk.toArray));
1048
+ log3.info("messages", {
1049
+ messages
1050
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 181, S: this });
1051
+ const toolCalls = this.getToolCalls();
1052
+ if (toolCalls.length === 0) {
1053
+ this._ended = Date.now();
1054
+ return {
1055
+ messages,
1056
+ done: true
1057
+ };
1058
+ } else if (!toolkit) {
1059
+ throw new Error("No toolkit provided");
1060
+ }
1061
+ return {
1062
+ messages,
1063
+ done: false
1064
+ };
1065
+ }).pipe(Effect5.withSpan("AiRequest.runAgentTurn"));
1066
+ runTools = ({ toolkit: opaqueToolkit }) => Effect5.gen(this, function* () {
1067
+ const toolkit = opaqueToolkit ? yield* opaqueToolkit.handlers : void 0;
1068
+ const toolCalls = this.getToolCalls();
1069
+ const toolResults = yield* Effect5.forEach(toolCalls, ({ block, message }) => {
1070
+ if (!toolkit) {
1071
+ throw new Error("No toolkit provided");
1072
+ }
1073
+ return callTool(toolkit, block);
1074
+ });
1075
+ yield* this._submitMessage(Obj5.make(Message3.Message, {
1076
+ created: (/* @__PURE__ */ new Date()).toISOString(),
1077
+ sender: {
1078
+ role: "tool"
1079
+ },
1080
+ blocks: toolResults
1081
+ }));
1082
+ this._toolCalls += toolResults.length;
1083
+ }).pipe(Effect5.withSpan("AiRequest.runTools"));
1084
+ /**
1085
+ * Run a full conversation turn loop. Equivalent to calling `begin()` then `runTurn()` in a loop.
1086
+ */
1087
+ run = ({ prompt, system: systemTemplate, history = [], objects = [], blueprints = [], toolkit }) => Effect5.gen(this, function* () {
1088
+ yield* this.begin({
1089
+ prompt,
1090
+ system: systemTemplate,
1091
+ history,
1092
+ objects,
1093
+ blueprints
1094
+ });
1095
+ const system = yield* formatSystemPrompt({
1096
+ system: systemTemplate,
1097
+ blueprints,
1098
+ objects
1099
+ }).pipe(Effect5.orDie);
525
1100
  do {
526
- log3("request", {
527
- prompt: promptMessage,
528
- system: {
529
- snippet: createSnippet(system),
530
- length: system.length
531
- },
532
- pending: this._pending.length,
533
- history: this._history.length,
534
- objects: objects?.length ?? 0
535
- }, {
536
- F: __dxlog_file3,
537
- L: 149,
538
- S: this,
539
- C: (f, a) => f(...a)
540
- });
541
- const prompt2 = yield* AiPreprocessor.preprocessPrompt([
542
- ...this._history,
543
- ...this._pending
544
- ], {
1101
+ const { done } = yield* this.runAgentTurn({
545
1102
  system,
546
- cacheControl: "ephemeral"
1103
+ toolkit
547
1104
  });
548
- const messages = yield* LanguageModel.streamText({
549
- prompt: prompt2,
550
- toolkit,
551
- disableToolCallResolution: true
552
- }).pipe(
553
- // Disable tool call parsing so that we can handle parameter schema errors on a per-tool call basis.
554
- withoutToolCallParising,
555
- // TOOD(dmaretskyi): Error mapping.
556
- AiParser.parseResponse({
557
- onBegin: () => observer.onBegin(),
558
- onBlock: (block) => observer.onBlock(block),
559
- onPart: (part) => observer.onPart(part),
560
- onEnd: (summary) => observer.onEnd(summary)
561
- }),
562
- Stream.mapEffect((block) => Effect4.gen(function* () {
563
- return yield* submitMessage(Obj3.make(Message2.Message, {
564
- created: (/* @__PURE__ */ new Date()).toISOString(),
565
- sender: {
566
- role: "assistant"
567
- },
568
- blocks: [
569
- block
570
- ]
571
- }));
572
- })),
573
- Stream.runCollect,
574
- Effect4.map(Chunk.toArray)
575
- );
576
- const toolCalls = messages.flatMap((message) => message.blocks.filter((block) => block._tag === "toolCall" && block.providerExecuted === false).map((block) => ({
577
- block,
578
- message
579
- })));
580
- if (toolCalls.length === 0) {
1105
+ if (done) {
581
1106
  break;
582
- } else if (!toolkit) {
583
- throw new Error("No toolkit provided");
584
1107
  }
585
- const toolResults = yield* Effect4.forEach(toolCalls, ({ block, message }) => {
586
- return callTool(toolkit, block).pipe(Effect4.provide(TracingService.layerSubframe((context) => ({
587
- ...context,
588
- parentMessage: message.id,
589
- toolCallId: block.toolCallId
590
- }))));
1108
+ yield* this.runTools({
1109
+ toolkit
591
1110
  });
592
- yield* submitMessage(Obj3.make(Message2.Message, {
593
- created: (/* @__PURE__ */ new Date()).toISOString(),
594
- sender: {
595
- role: "tool"
596
- },
597
- blocks: toolResults
598
- }));
599
- this._toolCalls++;
600
1111
  } while (true);
601
- this._ended = Date.now();
602
1112
  log3("done", {
603
1113
  pending: this._pending.length,
604
1114
  duration: this.duration,
605
1115
  tools: this._toolCalls
606
- }, {
607
- F: __dxlog_file3,
608
- L: 236,
609
- S: this,
610
- C: (f, a) => f(...a)
611
- });
1116
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 244, S: this });
612
1117
  return this._pending;
613
- }).pipe(this._semaphore.withPermits(1), Effect4.withSpan("AiSession.run"));
1118
+ }).pipe(this._semaphore.withPermits(1), Effect5.withSpan("AiRequest.run"));
1119
+ };
1120
+ var enrichToolCallBlock = (block, toolkit) => {
1121
+ if (block._tag !== "toolCall" || !toolkit) {
1122
+ return block;
1123
+ }
1124
+ const tool = toolkit.tools[block.name];
1125
+ if (!tool) {
1126
+ return block;
1127
+ }
1128
+ let operationOpt;
1129
+ try {
1130
+ operationOpt = getOperationFromTool(tool);
1131
+ } catch {
1132
+ return block;
1133
+ }
1134
+ if (Option3.isNone(operationOpt)) {
1135
+ return block;
1136
+ }
1137
+ const { meta } = operationOpt.value;
1138
+ return {
1139
+ ...block,
1140
+ operationKey: meta.key,
1141
+ operationName: meta.name
1142
+ };
614
1143
  };
615
1144
  var createSnippet = (text, len = 32) => text.length <= len * 2 ? text : [
616
1145
  text.slice(0, len),
@@ -620,699 +1149,618 @@ var createSnippet = (text, len = 32) => text.length <= len * 2 ? text : [
620
1149
 
621
1150
  // src/session/toolkit.ts
622
1151
  import * as Toolkit from "@effect/ai/Toolkit";
623
- import * as Effect5 from "effect/Effect";
624
- import { GenericToolkit, ToolExecutionService, ToolResolverService } from "@dxos/ai";
625
- import { isTruthy as isTruthy2 } from "@dxos/util";
626
- var createToolkit = ({ toolkit: toolkitProp, blueprints = [], genericToolkits = [] }) => Effect5.gen(function* () {
627
- const blueprintToolkit = yield* ToolResolverService.resolveToolkit(blueprints.flatMap(({ tools }) => tools));
628
- const blueprintToolHandler = yield* blueprintToolkit.toContext(ToolExecutionService.handlersFor(blueprintToolkit));
629
- const genericToolkit = GenericToolkit.merge(...genericToolkits);
630
- const toolkit = Toolkit.merge(...[
631
- toolkitProp,
1152
+ import * as Effect6 from "effect/Effect";
1153
+ import * as Layer2 from "effect/Layer";
1154
+ import { OpaqueToolkit as OpaqueToolkit2, ToolExecutionService as ToolExecutionService2, ToolResolverService as ToolResolverService2 } from "@dxos/ai";
1155
+ import { isTruthy } from "@dxos/util";
1156
+ var createToolkit = ({ toolkit: toolkitProp, blueprints = [], opaqueToolkits = [] }) => Effect6.gen(function* () {
1157
+ const blueprintToolkit = yield* ToolResolverService2.resolveToolkit(blueprints.flatMap(({ tools }) => tools));
1158
+ const blueprintToolHandler = yield* blueprintToolkit.toContext(ToolExecutionService2.handlersFor(blueprintToolkit));
1159
+ const opaqueToolkit = OpaqueToolkit2.merge(...opaqueToolkits);
1160
+ const toolkitDefs = [
1161
+ toolkitProp?.toolkit,
632
1162
  blueprintToolkit,
633
- genericToolkit.toolkit
634
- ].filter(isTruthy2));
635
- return yield* toolkit.pipe(Effect5.provide(blueprintToolHandler), Effect5.provide(genericToolkit.layer));
1163
+ opaqueToolkit.toolkit
1164
+ ].filter(isTruthy);
1165
+ const mergedToolkit = Toolkit.merge(...toolkitDefs);
1166
+ const combinedHandlerLayer = Layer2.mergeAll(Layer2.succeedContext(blueprintToolHandler), toolkitProp?.layer ?? OpaqueToolkit2.empty.layer, opaqueToolkit.layer);
1167
+ return OpaqueToolkit2.make(mergedToolkit, combinedHandlerLayer);
636
1168
  });
637
1169
 
638
- // src/session/version-pin.ts
639
- import * as Schema2 from "effect/Schema";
640
- import { getVersion } from "@dxos/echo-db";
641
- import { ObjectId } from "@dxos/keys";
642
- var ObjectVersionSchema = Schema2.Unknown;
643
- var VersionPinSchema = Schema2.Struct({
644
- // TODO(dmaretskyi): Use Ref when those support encoding.
645
- objectId: ObjectId,
646
- // TODO(dmaretskyi): Could be opaque.
647
- version: ObjectVersionSchema
1170
+ // src/session/AiContext.ts
1171
+ var AiContext_exports = {};
1172
+ __export(AiContext_exports, {
1173
+ Binder: () => Binder,
1174
+ Binding: () => Binding,
1175
+ Bindings: () => Bindings,
1176
+ Service: () => Service
648
1177
  });
649
- var VersionPin = class extends VersionPinSchema {
650
- static DISPOSITION = "version-pin";
651
- static fromObject(object) {
652
- return VersionPin.make({
653
- objectId: object.id,
654
- version: getVersion(object)
655
- });
656
- }
657
- static createBlock(pin) {
1178
+ import { Atom, Registry } from "@effect-atom/atom-react";
1179
+ import * as EArray from "effect/Array";
1180
+ import * as Context3 from "effect/Context";
1181
+ import * as Effect7 from "effect/Effect";
1182
+ import * as Function3 from "effect/Function";
1183
+ import * as Runtime from "effect/Runtime";
1184
+ import * as Schema5 from "effect/Schema";
1185
+ import { Blueprint } from "@dxos/compute";
1186
+ import { Resource } from "@dxos/context";
1187
+ import { DXN as DXN2, Feed, Obj as Obj6, Query, Ref as Ref3, Type as Type2 } from "@dxos/echo";
1188
+ import { assertArgument } from "@dxos/invariant";
1189
+ import { log as log4 } from "@dxos/log";
1190
+ import { ComplexSet, isNonNullable as isNonNullable3 } from "@dxos/util";
1191
+ var __dxlog_file5 = "/__w/dxos/dxos/packages/core/compute/assistant/src/session/AiContext.ts";
1192
+ var Binding = Schema5.Struct({
1193
+ blueprints: Schema5.Struct({
1194
+ added: Schema5.Array(Ref3.Ref(Blueprint.Blueprint)),
1195
+ removed: Schema5.Array(Ref3.Ref(Blueprint.Blueprint))
1196
+ }),
1197
+ objects: Schema5.Struct({
1198
+ added: Schema5.Array(Ref3.Ref(Obj6.Unknown)),
1199
+ removed: Schema5.Array(Ref3.Ref(Obj6.Unknown))
1200
+ })
1201
+ }).pipe(Type2.object({
1202
+ typename: "org.dxos.type.contextBinding",
1203
+ version: "0.1.0"
1204
+ }));
1205
+ var Bindings = class {
1206
+ blueprints = new ComplexSet((ref) => ref.dxn.toString());
1207
+ // TODO(burdon): Some DXNs have the Space prefix so only compare the object ID.
1208
+ objects = new ComplexSet((ref) => ref.dxn.asEchoDXN()?.echoId);
1209
+ toJSON() {
658
1210
  return {
659
- _tag: "json",
660
- disposition: VersionPin.DISPOSITION,
661
- data: JSON.stringify(pin)
1211
+ blueprints: EArray.fromIterable(this.blueprints).map((ref) => ref.dxn.toString()),
1212
+ objects: EArray.fromIterable(this.objects).map((ref) => ref.dxn.toString())
662
1213
  };
663
1214
  }
664
1215
  };
665
-
666
- // src/conversation/conversation.ts
667
- var __dxlog_file4 = "/__w/dxos/dxos/packages/core/assistant/src/conversation/conversation.ts";
668
- var SUMMARY_THRESHOLD = 8e4;
669
- var AiConversation = class extends Resource2 {
670
- /**
671
- * Blueprints and objects bound to the conversation.
672
- */
673
- _binder;
674
- _queue;
675
- _toolkit;
1216
+ var Binder = class extends Resource {
1217
+ _blueprints = Atom.make([]).pipe(Atom.keepAlive);
1218
+ _objects = Atom.make([]).pipe(Atom.keepAlive);
1219
+ _registry;
1220
+ _feed;
1221
+ _runtime;
1222
+ #bindingsQuery;
676
1223
  constructor(options) {
677
1224
  super();
678
- this._queue = options.queue;
679
- this._toolkit = options.toolkit;
680
- invariant(this._queue, void 0, {
681
- F: __dxlog_file4,
682
- L: 72,
683
- S: this,
684
- A: [
685
- "this._queue",
686
- ""
687
- ]
688
- });
689
- this._binder = new AiContextBinder({
690
- queue: this._queue,
691
- registry: options.registry
692
- });
693
- }
694
- async _open() {
695
- await this._binder.open(this._ctx);
696
- }
697
- get queue() {
698
- return this._queue;
699
- }
700
- get context() {
701
- return this._binder;
1225
+ assertArgument(options.feed, "options.feed", "Feed is required");
1226
+ assertArgument(options.runtime, "options.runtime", "Feed runtime is required");
1227
+ this._feed = options.feed;
1228
+ this._runtime = options.runtime;
1229
+ this._registry = options.registry ?? Registry.make();
702
1230
  }
703
- get toolkit() {
704
- return this._toolkit;
1231
+ /**
1232
+ * Returns the blueprints atom for subscription.
1233
+ */
1234
+ get blueprints() {
1235
+ return this._blueprints;
705
1236
  }
706
- async getHistory() {
707
- const queueItems = await this._queue.queryObjects();
708
- return queueItems.filter(Obj4.instanceOf(Message3.Message));
1237
+ /**
1238
+ * Returns the objects atom for subscription.
1239
+ */
1240
+ get objects() {
1241
+ return this._objects;
709
1242
  }
710
- getTools() {
711
- return Effect6.gen(this, function* () {
712
- const blueprints = this.context.getBlueprints();
713
- const tookit = yield* createToolkit({
714
- toolkit: this._toolkit,
715
- blueprints
716
- });
717
- return tookit.tools;
718
- }).pipe(Effect6.orDie);
1243
+ /**
1244
+ * Gets the current blueprints value.
1245
+ */
1246
+ getBlueprints() {
1247
+ return this._registry.get(this._blueprints);
719
1248
  }
720
1249
  /**
721
- * Creates a new cancelable request effect.
1250
+ * Gets the current objects value.
722
1251
  */
723
- createRequest(params) {
724
- return Effect6.gen(this, function* () {
725
- const history = yield* Effect6.promise(() => this.getHistory());
726
- const blueprints = this.context.getBlueprints();
727
- const mcps = yield* connectMcpServers(blueprints);
728
- const toolkit = yield* createToolkit({
729
- toolkit: this._toolkit,
730
- blueprints,
731
- genericToolkits: mcps
732
- });
733
- const objects = this.context.getObjects();
734
- log4("run", {
735
- history: history.length,
736
- blueprints: blueprints.length,
737
- tools: Object.keys(toolkit.tools).length,
738
- objects: objects.length
739
- }, {
740
- F: __dxlog_file4,
741
- L: 128,
742
- S: this,
743
- C: (f, a) => f(...a)
744
- });
745
- const session = new AiSession({
746
- summarizationThreshold: SUMMARY_THRESHOLD
747
- });
748
- const messages = yield* session.run({
749
- history,
750
- blueprints,
751
- toolkit,
752
- objects,
753
- ...params,
754
- onOutput: (message) => Effect6.promise(() => this._queue.append([
755
- message
756
- ]))
757
- }).pipe(Effect6.provide(Layer.mergeAll(Layer.succeed(AiContextService, {
758
- binder: this.context
759
- }), Layer.succeed(AiConversationService, this))));
760
- log4("result", {
761
- messages: messages.length,
762
- duration: session.duration,
763
- toolCalls: session.toolCalls
764
- }, {
765
- F: __dxlog_file4,
766
- L: 159,
767
- S: this,
768
- C: (f, a) => f(...a)
769
- });
770
- return messages;
771
- });
1252
+ getObjects() {
1253
+ return this._registry.get(this._objects);
772
1254
  }
773
- };
774
- var AiConversationService = class _AiConversationService extends Context3.Tag("@dxos/assistant/AiConversationService")() {
775
1255
  /**
776
- * Create a new conversation layer from options.
1256
+ * Subscribe to changes in blueprints.
777
1257
  */
778
- static layer = (options) => aiContextFromConversation.pipe(Layer.provideMerge(Layer.scoped(_AiConversationService, Effect6.gen(function* () {
779
- const conversation = yield* acquireReleaseResource(() => new AiConversation(options));
780
- return conversation;
781
- }))));
1258
+ subscribeBlueprints(cb) {
1259
+ return this._registry.subscribe(this._blueprints, () => cb(this._registry.get(this._blueprints)));
1260
+ }
782
1261
  /**
783
- * Create a new conversation with a new queue.
1262
+ * Subscribe to changes in objects.
784
1263
  */
785
- static layerNewQueue = (options) => Layer.unwrapScoped(Effect6.gen(function* () {
786
- return _AiConversationService.layer({
787
- ...options,
788
- queue: yield* QueueService.createQueue()
789
- });
790
- }));
1264
+ subscribeObjects(cb) {
1265
+ return this._registry.subscribe(this._objects, () => cb(this._registry.get(this._objects)));
1266
+ }
1267
+ async _open() {
1268
+ this.#bindingsQuery = await Runtime.runPromise(this._runtime)(Feed.query(this._feed, Query.type(Binding)));
1269
+ const initialResults = await this.#bindingsQuery.run();
1270
+ await this._updateBindings(initialResults);
1271
+ this._ctx.onDispose(this.#bindingsQuery.subscribe(async () => {
1272
+ await this._updateBindings(this.#bindingsQuery.results);
1273
+ }));
1274
+ }
791
1275
  /**
792
- * Run a prompt in the current conversation.
1276
+ * Re-reads bindings from the feed to pick up changes made by other processes.
793
1277
  */
794
- static run = (params) => Effect6.gen(function* () {
795
- const conversation = yield* _AiConversationService;
796
- return yield* conversation.createRequest(params);
797
- });
798
- };
799
- var aiContextFromConversation = Layer.effect(AiContextService, Effect6.gen(function* () {
800
- const conversation = yield* AiConversationService;
801
- return {
802
- binder: conversation.context
803
- };
804
- }));
805
- var connectMcpServers = (blueprints) => pipe3(blueprints, Array3.flatMap((_) => _.mcpServers ?? []), Effect6.forEach(({ url, protocol }) => McpToolkit.make({
806
- url,
807
- kind: protocol
808
- }).pipe(
809
- // NOTE: Type-inference fails here without explicit void return.
810
- Effect6.tap((toolkit) => log4.info("Connected to MCP server", {
811
- url,
812
- tools: Object.keys(toolkit.toolkit.tools).length
813
- }, {
814
- F: __dxlog_file4,
815
- L: 240,
816
- S: void 0,
817
- C: (f, a) => f(...a)
818
- })),
819
- Effect6.tapDefect((error) => Effect6.sync(() => log4.warn("Failed to connect to MCP server", {
820
- error
821
- }, {
822
- F: __dxlog_file4,
823
- L: 242,
824
- S: void 0,
825
- C: (f, a) => f(...a)
826
- }))),
827
- Effect6.either
828
- )), Effect6.map(Array3.filterMap((_) => Either.getRight(_))));
829
-
830
- // src/diff.ts
831
- import { next as A } from "@automerge/automerge";
832
- import { DocAccessor, toCursorRange } from "@dxos/echo-db";
833
- import { log as log5 } from "@dxos/log";
834
- import { isNonNullable } from "@dxos/util";
835
- var __dxlog_file5 = "/__w/dxos/dxos/packages/core/assistant/src/diff.ts";
836
- var reduceDiffs = (diffs) => {
837
- return diffs.reduce((acc, diff) => {
838
- const current = acc[acc.length - 1];
839
- const [op, ...rest] = diff.split(" ");
840
- switch (op) {
841
- case "-": {
842
- if (current && !current.match) {
843
- current.match += rest.join(" ");
844
- } else {
845
- acc.push({
846
- match: rest.join(" "),
847
- replace: ""
848
- });
849
- }
850
- return acc;
851
- }
852
- case "+": {
853
- if (current && !current.replace) {
854
- current.replace += rest.join(" ");
855
- } else {
856
- acc.push({
857
- match: "",
858
- replace: rest.join(" ")
859
- });
860
- }
861
- return acc;
862
- }
863
- default: {
864
- throw new Error(`Invalid diff format: each line must start with "- " or "+ ", got: ${JSON.stringify(diff)}`);
865
- }
1278
+ async sync() {
1279
+ if (this.#bindingsQuery) {
1280
+ const results = await this.#bindingsQuery.run();
1281
+ log4("sync", {
1282
+ bindingItems: results.length
1283
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 107, S: this });
1284
+ await this._updateBindings(results);
1285
+ log4("sync complete", {
1286
+ blueprints: this._registry.get(this._blueprints).length,
1287
+ blueprintKeys: this._registry.get(this._blueprints).map((bp) => Blueprint.getKey(bp))
1288
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 111, S: this });
866
1289
  }
867
- }, []);
868
- };
869
- var computeDiffsWithCursors = (accessor, diffs) => {
870
- return reduceDiffs(diffs).map((diff) => {
871
- const text = DocAccessor.getValue(accessor);
872
- const idx = text.indexOf(diff.match);
873
- if (idx !== -1) {
874
- return {
875
- cursor: toCursorRange(accessor, idx, idx + diff.match.length),
876
- text: diff.replace
877
- };
1290
+ }
1291
+ async _updateBindings(items) {
1292
+ if (items.length === 0) {
1293
+ return;
878
1294
  }
879
- }).filter(isNonNullable);
880
- };
881
- var applyDiffs = (accessor, diffs, { errorOnNotFound = false } = {}) => {
882
- for (const diff of reduceDiffs(diffs)) {
883
- accessor.handle.change((doc) => {
884
- const text = DocAccessor.getValue(accessor);
885
- const idx = text.indexOf(diff.match);
886
- if (idx !== -1) {
887
- A.splice(doc, accessor.path, idx, diff.match.length, diff.replace);
888
- } else {
889
- if (errorOnNotFound) {
890
- throw new Error(`Diff not found: ${diff.match}`);
891
- } else {
892
- log5.warn("diff not found", diff, {
893
- F: __dxlog_file5,
894
- L: 86,
895
- S: void 0,
896
- C: (f, a) => f(...a)
897
- });
1295
+ const bindings = this._reduce(items);
1296
+ log4("_updateBindings", {
1297
+ items: items.length,
1298
+ blueprintRefs: [
1299
+ ...bindings.blueprints
1300
+ ].map((ref) => ({
1301
+ dxn: ref.dxn.toString(),
1302
+ available: ref.isAvailable
1303
+ }))
1304
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 123, S: this });
1305
+ const currentBlueprints = this._registry.get(this._blueprints);
1306
+ const currentObjects = this._registry.get(this._objects);
1307
+ const resolvedBlueprints = await this._resolve(bindings.blueprints, currentBlueprints);
1308
+ const resolvedObjects = await this._resolve(bindings.objects, currentObjects);
1309
+ log4("_updateBindings resolved", {
1310
+ resolvedBlueprints: resolvedBlueprints.length,
1311
+ resolvedBlueprintKeys: resolvedBlueprints.map((bp) => Blueprint.getKey(bp))
1312
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 137, S: this });
1313
+ const reducedBlueprintDxns = new ComplexSet(DXN2.hash, [
1314
+ ...bindings.blueprints
1315
+ ].map((ref) => ref.dxn));
1316
+ const reducedObjectDxns = new ComplexSet(DXN2.hash, [
1317
+ ...bindings.objects
1318
+ ].map((ref) => ref.dxn));
1319
+ const filteredBlueprints = currentBlueprints.filter((obj) => reducedBlueprintDxns.has(Obj6.getDXN(obj)));
1320
+ const filteredObjects = currentObjects.filter((obj) => reducedObjectDxns.has(Obj6.getDXN(obj)));
1321
+ const mergedBlueprints = this._mergeInto(filteredBlueprints, resolvedBlueprints);
1322
+ const mergedObjects = this._mergeInto(filteredObjects, resolvedObjects);
1323
+ this._registry.set(this._blueprints, mergedBlueprints);
1324
+ this._registry.set(this._objects, mergedObjects);
1325
+ log4("updated bindings", {
1326
+ blueprints: mergedBlueprints.length,
1327
+ objects: mergedObjects.length
1328
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 155, S: this });
1329
+ }
1330
+ async _close() {
1331
+ this._registry.set(this._blueprints, []);
1332
+ this._registry.set(this._objects, []);
1333
+ }
1334
+ async bind({ blueprints, objects }) {
1335
+ const currentBlueprints = this._registry.get(this._blueprints);
1336
+ const currentObjects = this._registry.get(this._objects);
1337
+ const { added: addedBlueprints, next: nextBlueprints } = this._processBindings(blueprints, currentBlueprints);
1338
+ const { added: addedObjects, next: nextObjects } = this._processBindings(objects, currentObjects);
1339
+ if (!addedBlueprints.length && !addedObjects.length) {
1340
+ return;
1341
+ }
1342
+ this._registry.set(this._blueprints, nextBlueprints);
1343
+ this._registry.set(this._objects, nextObjects);
1344
+ log4("bind", {
1345
+ blueprints: addedBlueprints.length,
1346
+ objects: addedObjects.length
1347
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 176, S: this });
1348
+ await Runtime.runPromise(this._runtime)(Feed.append(this._feed, [
1349
+ Obj6.make(Binding, {
1350
+ blueprints: {
1351
+ added: addedBlueprints,
1352
+ removed: []
1353
+ },
1354
+ objects: {
1355
+ added: addedObjects,
1356
+ removed: []
898
1357
  }
899
- }
900
- });
1358
+ })
1359
+ ]));
901
1360
  }
902
- return DocAccessor.getValue(accessor);
903
- };
904
-
905
- // src/execution-graph.ts
906
- import { AgentStatus } from "@dxos/ai";
907
- import { Obj as Obj5 } from "@dxos/echo";
908
- import { MESSAGE_PROPERTY_TOOL_CALL_ID } from "@dxos/functions";
909
- import { LogLevel } from "@dxos/log";
910
- import { ContentBlock, Message as Message4 } from "@dxos/types";
911
- import { isNonNullable as isNonNullable2 } from "@dxos/util";
912
- var SKIP_BLOCKS = [
913
- "text"
914
- ];
915
- var ExecutionGraph = class {
916
- _skipBlocks;
917
- // TODO(dmaretskyi): Evolve the internal state to support chats, functions, circuit workflows, etc.
918
- _commits = [];
919
- _branchNames = /* @__PURE__ */ new Set();
920
- _lastBlockId;
921
- // Tool call tracking.
922
- _toolCallCommitIds = /* @__PURE__ */ new Map();
923
- _lastCommitByBranch = /* @__PURE__ */ new Map();
924
- _pendingToolResults = /* @__PURE__ */ new Map();
925
- constructor(_skipBlocks = SKIP_BLOCKS) {
926
- this._skipBlocks = _skipBlocks;
1361
+ async unbind({ blueprints, objects }) {
1362
+ if (!blueprints?.length && !objects?.length) {
1363
+ return;
1364
+ }
1365
+ const removedBlueprintDxns = (blueprints ?? []).map((ref) => ref.dxn);
1366
+ const removedObjectDxns = (objects ?? []).map((ref) => ref.dxn);
1367
+ if (removedBlueprintDxns.length > 0) {
1368
+ const current = this._registry.get(this._blueprints);
1369
+ this._registry.set(this._blueprints, current.filter((obj) => !removedBlueprintDxns.some((dxn) => DXN2.equalsEchoId(Obj6.getDXN(obj), dxn))));
1370
+ }
1371
+ if (removedObjectDxns.length > 0) {
1372
+ const current = this._registry.get(this._objects);
1373
+ this._registry.set(this._objects, current.filter((obj) => !removedObjectDxns.some((dxn) => DXN2.equalsEchoId(Obj6.getDXN(obj), dxn))));
1374
+ }
1375
+ log4("unbind", {
1376
+ blueprints: blueprints?.length,
1377
+ objects: objects?.length
1378
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 208, S: this });
1379
+ await Runtime.runPromise(this._runtime)(Feed.append(this._feed, [
1380
+ Obj6.make(Binding, {
1381
+ blueprints: {
1382
+ added: [],
1383
+ removed: blueprints ?? []
1384
+ },
1385
+ objects: {
1386
+ added: [],
1387
+ removed: objects ?? []
1388
+ }
1389
+ })
1390
+ ]));
927
1391
  }
928
1392
  /**
929
- * Adds events to the graph.
1393
+ * Process bindings to filter duplicates and determine next state.
930
1394
  */
931
- addEvents(events) {
932
- const sortedEvents = this.sortObjectsByCreated(events);
933
- for (const event of sortedEvents) {
934
- if (Obj5.instanceOf(Message4.Message, event)) {
935
- this._processMessage(event);
936
- } else if (Obj5.instanceOf(AgentStatus, event)) {
937
- this._processAgentStatus(event);
1395
+ _processBindings(refs, current) {
1396
+ const next = [
1397
+ ...current
1398
+ ];
1399
+ const added = [];
1400
+ if (!refs?.length) {
1401
+ return {
1402
+ added,
1403
+ next
1404
+ };
1405
+ }
1406
+ const seen = new Set(current.map((obj) => Obj6.getDXN(obj).toString()));
1407
+ for (const ref of refs) {
1408
+ const dxn = ref.dxn.toString();
1409
+ if (!seen.has(dxn)) {
1410
+ seen.add(dxn);
1411
+ added.push(ref);
1412
+ if (ref.isAvailable) {
1413
+ const target = ref.target;
1414
+ if (target) {
1415
+ next.push(target);
1416
+ }
1417
+ }
938
1418
  }
939
1419
  }
1420
+ return {
1421
+ added,
1422
+ next
1423
+ };
940
1424
  }
941
1425
  /**
942
- * Processes a message event and creates commits for its blocks.
1426
+ * Reduce results into sets of blueprints and objects.
943
1427
  */
944
- _processMessage(message) {
945
- const messageCommits = messageToCommits(message, this._lastBlockId, this._toolCallCommitIds, this._lastCommitByBranch, this._skipBlocks);
946
- this._commits.push(...messageCommits);
947
- messageCommits.forEach((commit) => this._branchNames.add(commit.branch));
948
- this._trackToolCalls(messageCommits);
949
- if (messageCommits.length > 0) {
950
- this._lastBlockId = messageCommits[messageCommits.length - 1].id;
951
- }
1428
+ _reduce(items) {
1429
+ return Function3.pipe(items, EArray.reduce(new Bindings(), (context, { blueprints, objects }) => {
1430
+ blueprints.added.forEach((ref) => context.blueprints.add(ref));
1431
+ blueprints.removed.forEach((ref) => context.blueprints.delete(ref));
1432
+ objects.added.forEach((ref) => context.objects.add(ref));
1433
+ objects.removed.forEach((ref) => {
1434
+ for (const obj of context.objects) {
1435
+ if (DXN2.equalsEchoId(obj.dxn, ref.dxn)) {
1436
+ context.objects.delete(obj);
1437
+ }
1438
+ }
1439
+ });
1440
+ return context;
1441
+ }));
952
1442
  }
953
1443
  /**
954
- * Processes an AgentStatus event.
1444
+ * Merge resolved items into the current set, adding only items not already present (by DXN).
955
1445
  */
956
- _processAgentStatus(event) {
957
- const branch = this._getToolCallBranch(event.parentMessage, event.toolCallId);
958
- this._branchNames.add(branch);
959
- const agentStatusCommit = {
960
- id: event.id,
961
- branch,
962
- message: event.message,
963
- icon: "ph--flag--regular",
964
- parents: this._getAgentStatusParents(event),
965
- timestamp: new Date(event.created)
966
- };
967
- this._commits.push(agentStatusCommit);
968
- this._lastBlockId = event.id;
969
- if (event.toolCallId) {
970
- this._lastCommitByBranch.set(branch, event.id);
971
- this._updatePendingToolResults(event.toolCallId, event.id);
1446
+ _mergeInto(current, resolved) {
1447
+ const seen = new Set(current.map((obj) => Obj6.getDXN(obj).toString()));
1448
+ const merged = [
1449
+ ...current
1450
+ ];
1451
+ for (const obj of resolved) {
1452
+ const dxn = Obj6.getDXN(obj).toString();
1453
+ if (!seen.has(dxn)) {
1454
+ seen.add(dxn);
1455
+ merged.push(obj);
1456
+ }
972
1457
  }
1458
+ return merged;
973
1459
  }
974
1460
  /**
975
- * Stable sort: objects with 'created' field are sorted by it, others remain in place.
1461
+ * Resolve references to objects, loading them first if needed and falling back to existing objects.
976
1462
  */
977
- sortObjectsByCreated(objects) {
978
- const sortedObjects = objects.slice();
979
- const createdObjects = [];
980
- sortedObjects.forEach((obj, idx) => {
981
- if (obj && typeof obj.created === "string") {
982
- createdObjects.push({
983
- idx,
984
- obj
985
- });
1463
+ async _resolve(refs, current) {
1464
+ const refArray = [
1465
+ ...refs
1466
+ ];
1467
+ await Promise.all(refArray.map((ref) => ref.tryLoad()));
1468
+ return refArray.map((ref) => {
1469
+ let target;
1470
+ if (ref.isAvailable) {
1471
+ target = ref.target;
986
1472
  }
1473
+ return target ?? current.find((obj) => Obj6.getDXN(obj).toString() === ref.dxn.toString());
1474
+ }).filter(isNonNullable3);
1475
+ }
1476
+ };
1477
+ var Service = class _Service extends Context3.Tag("@dxos/assistant/AiContextService")() {
1478
+ static bindContext = ({ blueprints, objects }) => Effect7.gen(function* () {
1479
+ const { binder } = yield* _Service;
1480
+ yield* Effect7.promise(() => binder.bind({
1481
+ blueprints,
1482
+ objects
1483
+ }));
1484
+ });
1485
+ static findObjects = (type) => {
1486
+ return Effect7.gen(function* () {
1487
+ const { binder } = yield* _Service;
1488
+ return binder.getObjects().filter(Obj6.instanceOf(type));
987
1489
  });
988
- createdObjects.sort((a, b) => {
989
- const aDate = new Date(a.obj.created).getTime();
990
- const bDate = new Date(b.obj.created).getTime();
991
- return aDate - bDate;
992
- });
993
- let createdIdx = 0;
994
- return sortedObjects.map((obj, _idx) => {
995
- if (obj && typeof obj.created === "string") {
996
- return createdObjects[createdIdx++].obj;
997
- }
998
- return obj;
1490
+ };
1491
+ };
1492
+
1493
+ // src/session/AiSession.ts
1494
+ var AiSession_exports = {};
1495
+ __export(AiSession_exports, {
1496
+ Service: () => Service2,
1497
+ Session: () => Session
1498
+ });
1499
+ import * as Array4 from "effect/Array";
1500
+ import * as Context4 from "effect/Context";
1501
+ import * as Effect8 from "effect/Effect";
1502
+ import * as Either from "effect/Either";
1503
+ import { pipe as pipe4 } from "effect/Function";
1504
+ import * as Layer3 from "effect/Layer";
1505
+ import * as Record2 from "effect/Record";
1506
+ import * as Runtime2 from "effect/Runtime";
1507
+ import { Operation as Operation2, Trace as Trace3 } from "@dxos/compute";
1508
+ import { Resource as Resource2 } from "@dxos/context";
1509
+ import { Database as Database2, Feed as Feed2, Filter, Obj as Obj7 } from "@dxos/echo";
1510
+ import { acquireReleaseResource } from "@dxos/effect";
1511
+ import { invariant as invariant2 } from "@dxos/invariant";
1512
+ import { log as log5 } from "@dxos/log";
1513
+ import { McpToolkit } from "@dxos/mcp-client";
1514
+ import { Message as Message4 } from "@dxos/types";
1515
+ var __dxlog_file6 = "/__w/dxos/dxos/packages/core/compute/assistant/src/session/AiSession.ts";
1516
+ var SUMMARY_THRESHOLD = 8e4;
1517
+ var Session = class extends Resource2 {
1518
+ /**
1519
+ * Blueprints and objects bound to the session.
1520
+ */
1521
+ _binder;
1522
+ _feed;
1523
+ _runtime;
1524
+ constructor(options) {
1525
+ super();
1526
+ this._feed = options.feed;
1527
+ this._runtime = options.runtime;
1528
+ invariant2(this._feed, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 43, S: this, A: ["this._feed", ""] });
1529
+ invariant2(this._runtime, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 44, S: this, A: ["this._runtime", ""] });
1530
+ this._binder = new Binder({
1531
+ feed: this._feed,
1532
+ runtime: this._runtime,
1533
+ registry: options.registry
999
1534
  });
1000
1535
  }
1536
+ async _open() {
1537
+ await this._binder.open(this._ctx);
1538
+ }
1539
+ get feed() {
1540
+ return this._feed;
1541
+ }
1542
+ get context() {
1543
+ return this._binder;
1544
+ }
1545
+ async getHistory() {
1546
+ const queryResult = await Runtime2.runPromise(this._runtime)(Feed2.query(this._feed, Filter.type(Message4.Message)));
1547
+ const items = await queryResult.run();
1548
+ return items.filter(Obj7.instanceOf(Message4.Message));
1549
+ }
1550
+ getTools() {
1551
+ return Effect8.gen(this, function* () {
1552
+ const toolkit = yield* createToolkit({
1553
+ blueprints: this.context.getBlueprints()
1554
+ });
1555
+ return toolkit.toolkit.tools;
1556
+ }).pipe(Effect8.orDie);
1557
+ }
1001
1558
  /**
1002
- * Returns the current state of the graph.
1559
+ * Not provided by default, since users might want to override them.
1003
1560
  */
1004
- getGraph(lastRequest = false) {
1005
- const idx = lastRequest ? this._commits.findLastIndex((c) => c.tags?.includes("user")) : -1;
1006
- const commits = idx === -1 ? this._commits : this._commits.slice(idx);
1007
- return {
1008
- branches: Array.from(this._branchNames),
1009
- commits
1010
- };
1561
+ makeToolExecutionServices() {
1562
+ return ToolExecutionServices.pipe(Layer3.provide(Operation2.withInvocationOptions({
1563
+ conversation: Obj7.getDXN(this._feed).toString()
1564
+ })));
1011
1565
  }
1012
1566
  /**
1013
- * Tracks tool calls and handles pending tool result updates.
1567
+ * Creates a new cancelable request effect.
1014
1568
  */
1015
- _trackToolCalls(commits) {
1016
- commits.forEach((commit) => {
1017
- if (this._isToolCallCommit(commit)) {
1018
- const toolCallId = this._extractToolCallId(commit.id);
1019
- if (toolCallId) {
1020
- this._toolCallCommitIds.set(toolCallId, commit.id);
1021
- }
1022
- }
1023
- if (this._isToolResultCommit(commit)) {
1024
- const toolCallId = this._extractToolCallId(commit.id);
1025
- if (toolCallId) {
1026
- this._pendingToolResults.set(toolCallId, commit.id);
1027
- this._handleLateToolResultUpdate(toolCallId, commit.id);
1028
- }
1029
- }
1030
- this._lastCommitByBranch.set(commit.branch, commit.id);
1031
- });
1032
- }
1033
- /**
1034
- * Updates pending tool results to point to an AgentStatus event.
1035
- */
1036
- _updatePendingToolResults(toolCallId, agentStatusId) {
1037
- const toolResultId = this._pendingToolResults.get(toolCallId);
1038
- if (!toolResultId) return;
1039
- const toolResultCommit = this._commits.find((c) => c.id === toolResultId);
1040
- if (!toolResultCommit) return;
1041
- const toolCallCommitId = this._toolCallCommitIds.get(toolCallId);
1042
- if (!toolCallCommitId) return;
1043
- const toolCallIndex = toolResultCommit.parents.indexOf(toolCallCommitId);
1044
- if (toolCallIndex !== -1) {
1045
- toolResultCommit.parents[toolCallIndex] = agentStatusId;
1046
- }
1047
- this._pendingToolResults.delete(toolCallId);
1048
- }
1049
- /**
1050
- * Handles late updates when AgentStatus is processed before tool result.
1051
- */
1052
- _handleLateToolResultUpdate(toolCallId, toolResultId) {
1053
- const toolCallCommitId = this._toolCallCommitIds.get(toolCallId);
1054
- if (!toolCallCommitId) return;
1055
- const messageId = toolCallCommitId.split("_toolCall_")[0];
1056
- const toolCallBranch = `${messageId}_${toolCallId}`;
1057
- const agentStatusCommitId = this._lastCommitByBranch.get(toolCallBranch);
1058
- if (agentStatusCommitId && agentStatusCommitId !== toolCallCommitId) {
1059
- const toolResultCommit = this._commits.find((c) => c.id === toolResultId);
1060
- if (toolResultCommit) {
1061
- const toolCallIndex = toolResultCommit.parents.indexOf(toolCallCommitId);
1062
- if (toolCallIndex !== -1) {
1063
- toolResultCommit.parents[toolCallIndex] = agentStatusCommitId;
1569
+ createRequest(params) {
1570
+ return Effect8.gen(this, function* () {
1571
+ const history = yield* Effect8.promise(() => this.getHistory());
1572
+ const blueprints = this.context.getBlueprints();
1573
+ const objects = this.context.getObjects();
1574
+ log5("run", {
1575
+ history: history.length,
1576
+ blueprints: blueprints.length,
1577
+ objects: objects.length
1578
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 87, S: this });
1579
+ const request = new AiRequest_exports.Request({
1580
+ summarizationThreshold: SUMMARY_THRESHOLD,
1581
+ observer: params.observer,
1582
+ onOutput: (message) => Effect8.promise(() => Runtime2.runPromise(this._runtime)(Feed2.append(this._feed, [
1583
+ message
1584
+ ])))
1585
+ });
1586
+ yield* request.begin({
1587
+ history,
1588
+ blueprints,
1589
+ objects,
1590
+ prompt: params.prompt,
1591
+ system: params.system
1592
+ });
1593
+ do {
1594
+ yield* Effect8.promise(() => this.context.sync());
1595
+ const currentBlueprints = this.context.getBlueprints();
1596
+ const mcps = yield* connectMcpServers(currentBlueprints, params.mcpServers);
1597
+ const toolkit = yield* createToolkit({
1598
+ toolkit: params.toolkit,
1599
+ blueprints: currentBlueprints,
1600
+ opaqueToolkits: mcps
1601
+ });
1602
+ log5("toolkit", {
1603
+ tools: Record2.keys(toolkit.toolkit.tools)
1604
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 116, S: this });
1605
+ const system = yield* formatSystemPrompt({
1606
+ system: params.system,
1607
+ blueprints: currentBlueprints,
1608
+ objects: this.context.getObjects()
1609
+ }).pipe(Effect8.orDie);
1610
+ const { done } = yield* request.runAgentTurn({
1611
+ system,
1612
+ toolkit
1613
+ });
1614
+ if (done) {
1615
+ break;
1064
1616
  }
1065
- }
1066
- this._pendingToolResults.delete(toolCallId);
1067
- }
1068
- }
1069
- /**
1070
- * Gets the branch name for a tool call.
1071
- */
1072
- _getToolCallBranch(parentMessage, toolCallId) {
1073
- if (toolCallId) {
1074
- return toolCallId;
1075
- }
1076
- return parentMessage || "main";
1077
- }
1078
- /**
1079
- * Gets the parents for an AgentStatus commit.
1080
- */
1081
- _getAgentStatusParents(event) {
1082
- if (event.parentMessage && event.toolCallId) {
1617
+ yield* request.runTools({
1618
+ toolkit
1619
+ });
1620
+ } while (true);
1621
+ log5("result", {
1622
+ messages: request.pending.length,
1623
+ duration: request.duration,
1624
+ toolCalls: request.toolCalls
1625
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 135, S: this });
1083
1626
  return [
1084
- getToolCallId(event.parentMessage, event.toolCallId)
1627
+ ...request.pending
1085
1628
  ];
1086
- }
1087
- return [];
1629
+ }).pipe(Effect8.provide(Layer3.mergeAll(Layer3.succeed(Service, {
1630
+ binder: this.context
1631
+ }), Layer3.succeed(Service2, this), Operation2.withInvocationOptions({
1632
+ conversation: Obj7.getDXN(this._feed).toString()
1633
+ }))), Effect8.withSpan("AiSession.createRequest"));
1088
1634
  }
1635
+ };
1636
+ var Service2 = class _Service extends Context4.Tag("@dxos/assistant/AiSessionService")() {
1089
1637
  /**
1090
- * Checks if a commit is a tool call commit.
1638
+ * Create a new session layer from options.
1091
1639
  */
1092
- _isToolCallCommit(commit) {
1093
- return commit.message.includes("Calling tool");
1094
- }
1640
+ static layer = (options) => aiContextFromSession.pipe(Layer3.provideMerge(Layer3.scoped(_Service, Effect8.gen(function* () {
1641
+ const session = yield* acquireReleaseResource(() => new Session(options));
1642
+ return session;
1643
+ }))));
1095
1644
  /**
1096
- * Checks if a commit is a tool result commit.
1645
+ * Create a new session with a new feed.
1097
1646
  */
1098
- _isToolResultCommit(commit) {
1099
- return commit.message.includes("Tool call succeeded") || commit.message.includes("Tool error:");
1100
- }
1647
+ static layerNewFeed = (options) => Layer3.unwrapScoped(Effect8.gen(function* () {
1648
+ const feed = yield* Database2.add(Feed2.make());
1649
+ const runtime2 = yield* Effect8.runtime();
1650
+ return _Service.layer({
1651
+ ...options,
1652
+ feed,
1653
+ runtime: runtime2
1654
+ });
1655
+ }));
1101
1656
  /**
1102
- * Extracts tool call ID from a commit ID.
1657
+ * Run a prompt in the current session.
1103
1658
  */
1104
- _extractToolCallId(commitId) {
1105
- const toolCallMatch = commitId.match(/_toolCall_(.+)$/);
1106
- const toolResultMatch = commitId.match(/_toolResult_(.+)$/);
1107
- return toolCallMatch?.[1] || toolResultMatch?.[1];
1108
- }
1109
- };
1110
- var messageToCommits = (message, lastBlockId, toolCallIds, lastCommitByBranch, skipBlocks) => {
1111
- let previousBlockId = lastBlockId;
1112
- return message.blocks.map((block, idx) => {
1113
- const branch = getMessageBranch(message);
1114
- const parents = getBlockParents(block, previousBlockId, message, toolCallIds, lastCommitByBranch);
1115
- if (skipBlocks?.includes(block._tag)) {
1116
- return null;
1117
- }
1118
- const commit = createBlockCommit(block, message, branch, parents, idx);
1119
- if (commit) {
1120
- previousBlockId = commit.id;
1121
- }
1122
- return commit;
1123
- }).filter(isNonNullable2);
1124
- };
1125
- var getBlockParents = (block, previousBlockId, message, toolCallIds, lastCommitByBranch) => {
1126
- const parents = [];
1127
- if (block._tag === "toolResult") {
1128
- if (previousBlockId) {
1129
- parents.push(previousBlockId);
1130
- }
1131
- const toolCallCommitId = toolCallIds?.get(block.toolCallId);
1132
- if (toolCallCommitId) {
1133
- const toolCallBranch = getToolCallBranchFromCommitId(toolCallCommitId, block.toolCallId);
1134
- const lastBlockFromToolCallBranch = lastCommitByBranch?.get(toolCallBranch);
1135
- if (lastBlockFromToolCallBranch) {
1136
- parents.push(lastBlockFromToolCallBranch);
1137
- } else {
1138
- parents.push(toolCallCommitId);
1139
- }
1140
- }
1141
- } else {
1142
- if (previousBlockId) {
1143
- parents.push(previousBlockId);
1144
- } else {
1145
- const toolCallParent = getParentId(message);
1146
- if (toolCallParent) {
1147
- parents.push(toolCallParent);
1148
- }
1149
- }
1150
- }
1151
- return parents;
1152
- };
1153
- var createBlockCommit = (block, message, branch, parents, idx) => {
1154
- const timestamp = new Date(message.created);
1155
- switch (block._tag) {
1156
- case "text":
1157
- if (!block.text.trim().length) return null;
1158
- return {
1159
- id: getGenericBlockId(message.id, idx),
1160
- branch,
1161
- parents,
1162
- timestamp,
1163
- ...message.sender.role === "user" ? {
1164
- icon: "ph--user--regular",
1165
- tags: [
1166
- "user"
1167
- ],
1168
- message: "Processing request..."
1169
- } : {
1170
- icon: "ph--robot--regular",
1171
- message: `Response (${block.text.split(" ").length} words)`
1172
- }
1173
- };
1174
- case "toolCall":
1175
- return {
1176
- id: getToolCallId(message.id, block.toolCallId),
1177
- branch,
1178
- parents,
1179
- timestamp,
1180
- icon: "ph--wrench--regular",
1181
- level: LogLevel.INFO,
1182
- message: `Calling tool (${block.name})`
1183
- };
1184
- case "toolResult":
1185
- return {
1186
- id: getToolResultId(message.id, block.toolCallId),
1187
- branch,
1188
- parents,
1189
- timestamp,
1190
- icon: block.error ? "ph--x-circle--regular" : "ph--check-circle--regular",
1191
- level: block.error ? LogLevel.ERROR : LogLevel.INFO,
1192
- message: block.error ? "Tool error: " + block.error : "Tool call succeeded"
1193
- };
1194
- case "stats":
1195
- return {
1196
- id: getGenericBlockId(message.id, idx),
1197
- branch,
1198
- parents,
1199
- timestamp,
1200
- icon: "ph--rocket--regular",
1201
- level: LogLevel.INFO,
1202
- message: ContentBlock.createStatsMessage(block)
1203
- };
1204
- case "status":
1205
- return {
1206
- id: getGenericBlockId(message.id, idx),
1207
- branch,
1208
- parents,
1209
- timestamp,
1210
- message: block.statusText,
1211
- level: LogLevel.INFO,
1212
- icon: "ph--flag--regular"
1213
- };
1214
- case "reasoning":
1215
- return {
1216
- id: getGenericBlockId(message.id, idx),
1217
- branch,
1218
- parents,
1219
- timestamp,
1220
- message: block.reasoningText ?? "Thinking...",
1221
- icon: "ph--brain--regular"
1222
- };
1223
- case "reference":
1224
- return {
1225
- id: getGenericBlockId(message.id, idx),
1226
- branch,
1227
- parents,
1228
- timestamp,
1229
- icon: "ph--link--regular",
1230
- message: stringifyRef(block.reference)
1231
- };
1232
- default:
1233
- return null;
1234
- }
1235
- };
1236
- var getToolCallBranchFromCommitId = (toolCallCommitId, toolCallId) => {
1237
- const messageId = toolCallCommitId.split("_toolCall_")[0];
1238
- return `${messageId}_${toolCallId}`;
1239
- };
1240
- var getToolCallId = (messageId, toolCallId) => `${messageId}_toolCall_${toolCallId}`;
1241
- var getToolResultId = (messageId, toolCallId) => `${messageId}_toolResult_${toolCallId}`;
1242
- var getGenericBlockId = (messageId, idx) => `${messageId}_block_${idx}`;
1243
- var getBranchName = (options) => {
1244
- if (options.toolCallId) {
1245
- return options.toolCallId;
1246
- } else if (options.parentMessage) {
1247
- return options.parentMessage;
1248
- } else {
1249
- return "main";
1250
- }
1251
- };
1252
- var getMessageBranch = (message) => {
1253
- return getBranchName({
1254
- parentMessage: message.parentMessage,
1255
- toolCallId: message.properties?.[MESSAGE_PROPERTY_TOOL_CALL_ID]
1659
+ static run = (params) => Effect8.gen(function* () {
1660
+ const session = yield* _Service;
1661
+ return yield* session.createRequest(params);
1256
1662
  });
1257
1663
  };
1258
- var getParentId = (message) => {
1259
- if (message.parentMessage && message.properties?.[MESSAGE_PROPERTY_TOOL_CALL_ID]) {
1260
- return getToolCallId(message.parentMessage, message.properties[MESSAGE_PROPERTY_TOOL_CALL_ID]);
1261
- } else {
1262
- return void 0;
1263
- }
1264
- };
1265
- var stringifyRef = (ref) => {
1266
- if (ref.target) {
1267
- return stringifyObject(ref.target);
1268
- }
1269
- return ref.dxn.asEchoDXN()?.echoId ?? ref.dxn.asQueueDXN()?.objectId ?? "";
1270
- };
1271
- var stringifyObject = (obj) => {
1272
- return Obj5.getLabel(obj) ?? Obj5.getTypename(obj) ?? obj.id;
1664
+ var aiContextFromSession = Layer3.effect(Service, Effect8.gen(function* () {
1665
+ const session = yield* Service2;
1666
+ return {
1667
+ binder: session.context
1668
+ };
1669
+ }));
1670
+ var connectMcpServers = (blueprints, spaceMcpServers = []) => {
1671
+ const blueprintServers = pipe4(blueprints, Array4.flatMap((_) => _.mcpServers ?? []), Array4.map(({ url, protocol, apiKey }) => ({
1672
+ url,
1673
+ protocol,
1674
+ apiKey
1675
+ })));
1676
+ const spaceServers = spaceMcpServers.map(({ url, protocol, apiKey }) => ({
1677
+ url,
1678
+ protocol,
1679
+ apiKey
1680
+ }));
1681
+ const allServers = [
1682
+ ...blueprintServers,
1683
+ ...spaceServers
1684
+ ];
1685
+ return pipe4(allServers, Effect8.forEach((options) => McpToolkit.make(options).pipe(
1686
+ // NOTE: Type-inference fails here without explicit void return.
1687
+ Effect8.tap((toolkit) => log5.info("Connected to MCP server", {
1688
+ url: options.url,
1689
+ tools: Object.keys(toolkit.toolkit.tools).length
1690
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 199, S: void 0 })),
1691
+ // Surface typed connection failures via ephemeral trace + warn log, then drop the server.
1692
+ Effect8.tapError((error) => Effect8.gen(function* () {
1693
+ log5.warn("Failed to connect to MCP server", {
1694
+ url: error.url,
1695
+ protocol: error.protocol,
1696
+ message: error.message
1697
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 204, S: this });
1698
+ yield* Trace3.write(McpServerError, {
1699
+ url: error.url,
1700
+ protocol: error.protocol,
1701
+ message: error.message
1702
+ });
1703
+ })),
1704
+ // Catch unexpected defects too (e.g. malformed tool schemas) so a single broken
1705
+ // server can never abort the whole turn — surface them through the same channel.
1706
+ Effect8.catchAllDefect((defect) => Effect8.gen(function* () {
1707
+ const message = defect instanceof Error ? defect.message : String(defect);
1708
+ log5.warn("Unexpected MCP defect", {
1709
+ url: options.url,
1710
+ message
1711
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 218, S: this });
1712
+ yield* Trace3.write(McpServerError, {
1713
+ url: options.url,
1714
+ protocol: options.protocol,
1715
+ message: `Unexpected MCP failure: ${message}`
1716
+ });
1717
+ return yield* Effect8.fail(new McpToolkit.McpConnectionError({
1718
+ url: options.url,
1719
+ protocol: options.protocol,
1720
+ message
1721
+ }));
1722
+ })),
1723
+ Effect8.either
1724
+ )), Effect8.map(Array4.filterMap((_) => Either.getRight(_))));
1273
1725
  };
1274
1726
 
1275
1727
  // src/templates/system.ts
1276
1728
  import defaultsDeep from "lodash.defaultsdeep";
1277
- import { Template as Template2 } from "@dxos/blueprints";
1729
+ import { Template as Template2 } from "@dxos/compute";
1278
1730
 
1279
- // raw-loader:/__w/dxos/dxos/packages/core/assistant/src/templates/instructions/format.tpl?raw
1731
+ // raw-loader:/__w/dxos/dxos/packages/core/compute/assistant/src/templates/instructions/format.tpl?raw
1280
1732
  var format_default = "{{! System Core }}\n\n## Input Formats\n\n### Blueprints\n\nYou may be provided with Blueprint definitions that include specific instructions and tools for a given set of tasks.\nBlue print instructions will be enclosed in a <blueprint> tag.\n\n### Context objects\n\nYou may be provided with references to objects that are relevant to the current prompt.\nObjects will be in the form of a <object> tag with a <dxn> element that is the unique key of the object required by many tools.\nThe <typename> element is the type of the object.\n\n<object>\n <dxn>object-id</dxn>\n <typename>example.com/type/TypeName</typename>\n</object>\n\n## Response Formats\n\nIt is very important to respond in the correct format.\nYou response must be valid Markdown.\nIn some cases you should respond with well-formed XML tags along with the content.\nCode snippets should use 2-space indents.\n\n{{#if cot}}\nBefore responding, explain your reasoning and include your detailed chain-of-thought enclosed in a <cot> tag.\nThe <cot> tag should be the first thing in your response.\n{{/if}}\n\n## Block references\n\nYou can reply with a block reference for objects if you have the DXN. For example:\n\n<object>\n <dxn>dxn:queue:data:B6INSIBY3CBEF4M5VZRYBCMAHQMPYK5AJ:01K24XMVHSZHS97SG1VTVQDM5Z:01K24XPK464FSCKVQJAB2H662M</dxn>\n</object>\n<object>\n <dxn>dxn:echo:@:01K24XPK464FSCKVQJAB2H662M</dxn>\n</object>\n\n### Suggestions\n\nEach suggestion must in the form of a proposed action enclosed in a <suggestion> tag.\nExample: <suggestion>Show on a map</suggestion>\nDo not use bullets when listing suggestions.\n\n### Multiple choice questions\n\nYou may ask multiple choice questions by wrapping consise options inside <option> tags inside a well formed <select> tag.\nExample: <select><option>Yes</option><option>No</option></select>\n\n## Context\n\nThe current date and time is {{DATETIME}}.\n";
1281
1733
 
1282
- // raw-loader:/__w/dxos/dxos/packages/core/assistant/src/templates/instructions/system.tpl?raw
1283
- var system_default = "{{! System Prompt }}\n\n- You are a helpful assistant called Kai. \n- Your name is Kai and you are powered by different language models.\n- You were created by DXOS and operate inside of Composer and on the DXOS EDGE network.\n- In your initial greeting state your name and make some suggestions based on the current context objects.\n- You are an advanced AI assistant capable of creating and managing artifacts from provided data and tools.\n- Your task is to process user commands and questions and decide how best to respond.\n- In general be concise and direct.\n- Follow all instructions carefully.\n\n## Planning\n\n- Analyze the structure and type of the content in the user's message.\n- Determine if you complete the task using the available blueprint definitions?\n- If you can't complete the task using the available blueprint definitions, query the list of available blueprint definitions using the appropriate tool.\n- Identify which blueprint definitions are relevant to the user's request.\n- Evaluate the potential benefits of creating an artifact vs. normal processing for each identified element.\n- Make a final decision on whether to create an artifact and explain your reasoning.\n- Are the required blueprint definitions already available?\n- If not, select which blueprint definition(s) will be the most relevant and require them using the require_artifact_definitions tool.\n- The require'd artifact tools will be available for use after require.\n- If creating an artifact, outline how you will structure it within the response.\n- Decide if the artifact needs to be shown to the user.\n- Call the show tool to show the artifact to the user.\n- Your reasoning must include: whether to use artifacts or not, to create one or query, whether to show the artifact to the user, and how to structure the response.\n\n## Blueprints and Artifacts\n\n- Determine if the interaction involves an artifact. Prefer artifacts for tables, lists, spreadsheets, kanbans, games, images, and other structured data.\n- Determine if the user is explicitly talking about creating a new artifact, or wants to use an existing artifact.\n- If it is ambiguous, query for existing artifacts first and then decide.\n- If you decide to create an artifact, call the associated tool to create the artifact.\n- Artifacts are stored in the database. Tools are used to create and query artifacts.\n- If you are unsure about creating an artifact ask the user for clarification.\n- Artifacts are mutable objects that can change over the course of the conversation.\n- Always re-query the artifact using the tool (like query or inspect) to get the latest state of the artifact before answering the user.\n- You must never generate the id of the artifact; only recall the ids that are already in the history.\n- Artifacts are created by requiring the specific artifact using the require_artifact tool and creating it by calling the associated tool.\n- You can add suggested actions at the end of your response.\n- Suggested actions should be very concise and start with a verb and be phrased as a command to an agent -- not a question to the user.\n- Suggested actions must be in the form of a user instruction that you can follow.\n- Suggested actions could include actions that create artifacts.\n- After creating an artifact use a tool to add it to the chat context.\n\n## Context\n\nToday is {{DATETIME}}.\n";
1734
+ // raw-loader:/__w/dxos/dxos/packages/core/compute/assistant/src/templates/instructions/system.tpl?raw
1735
+ var system_default = "{{! System Prompt }}\n\n- You are a helpful assistant called Kai. \n- Your name is Kai and you are powered by different language models.\n- You were created by DXOS and operate inside of Composer and on the DXOS EDGE network.\n- In your initial greeting state your name and make some suggestions based on the current context objects.\n- You are an advanced AI assistant capable of creating and managing artifacts from provided data and tools.\n- Your task is to process user commands and questions and decide how best to respond.\n- In general be concise and direct.\n- Follow all instructions carefully.\n\n## Planning\n\n- Analyze the structure and type of the content in the user's message.\n- Determine if you complete the task using the available blueprint definitions?\n- If you can't complete the task using the available blueprint definitions, query the list of available blueprint definitions using the appropriate tool.\n- Identify which blueprint definitions are relevant to the user's request.\n- Evaluate the potential benefits of creating an artifact vs. normal processing for each identified element.\n- Make a final decision on whether to create an artifact and explain your reasoning.\n- Are the required blueprint definitions already available?\n- If not, select which blueprint definition(s) will be the most relevant and require them using the require_artifact_definitions tool.\n- The require'd artifact tools will be available for use after require.\n- If creating an artifact, outline how you will structure it within the response.\n- Decide if the artifact needs to be shown to the user.\n- Call the show tool to show the artifact to the user.\n- Your reasoning must include: whether to use artifacts or not, to create one or query, whether to show the artifact to the user, and how to structure the response.\n\n## Blueprints and Artifacts\n\n- Determine if the interaction involves an artifact. Prefer artifacts for tables, lists, spreadsheets, kanbans, games, images, and other structured data.\n- Determine if the user is explicitly talking about creating a new artifact, or wants to use an existing artifact.\n- If it is ambiguous, query for existing artifacts first and then decide.\n- If you decide to create an artifact, call the associated tool to create the artifact.\n- Artifacts are stored in the database. Tools are used to create and query artifacts.\n- If you are unsure about creating an artifact ask the user for clarification.\n- Artifacts are mutable objects that can change over the course of the conversation.\n- Always re-query the artifact using the tool (like query or inspect) to get the latest state of the artifact before answering the user.\n- You must never generate the id of the artifact; only recall the ids that are already in the history.\n- Artifacts are created by requiring the specific artifact using the require_artifact tool and creating it by calling the associated tool.\n- You can add suggested actions at the end of your response.\n- Suggested actions should be very concise and start with a verb and be phrased as a command to an agent -- not a question to the user.\n- Suggested actions must be in the form of a user instruction that you can follow.\n- Suggested actions could include actions that create artifacts.\n- After creating an artifact use a tool to add it to the chat context.\n\n## Navigation\n\n- When the user asks to open, go to, navigate to, or show a specific document, page, or object, use the navigation tools instead of loading content inline.\n- First, call the Resolve navigation targets tool to find the navigation path for the requested item.\n - Without arguments, it returns available pages that can be navigated to.\n - With a DXN in the query, it resolves that specific object to a navigation path.\n- If you know the object's DXN (e.g. from a database query or context objects), pass it in the query to get the exact navigation path.\n- Then, call the Open tool with the resolved navigation path(s) in the subject array to navigate the user to that item.\n- Do not respond with the document's content when the user asks to open it \u2014 open it in the main content area instead.\n- If multiple targets match, present the options to the user and let them choose which to open.\n\n## Response format\n\n- NEVER output narration text outside `<status>` tags or the final user-facing answer. Any progress update, transition sentence, or \"let me\u2026\" lead-in MUST be wrapped in `<status>...</status>`. Violating this is an error.\n- When updating the user about the progress of the work you are doing, put the update in a <status> XML tag.\n- Only when you need to show the result to the user or ask a question use a text block without status tags.\n- After a `</status>` tag, the next output must be either another `<status>` tag, a tool call, or the final user-facing answer \u2014 never a transition sentence.\n\n<example>\n// reasoning...\n<status>Looking for emails from Depot</status>\n// tool call..\n<status>Found 2 emails from Depot.</status>\n<status>Loading email details</status>\n// tool call\n<status>Located mailbox</status>\nI found **2 emails from Depot**:\n</example>\n\nDo not do (no status tags):\n\n<incorrect_example>\n// reasoning...\n// tool call..\nI found 2 emails from Depot. Let me load their details:\n// tool call\nI found **2 emails from Depot**:\n</incorrect_example>\n\nDo not do (transition sentence after a status tag, before tool calls):\n\n<incorrect_example>\n<status>Doing X</status>\nLet me start by doing X:\n// tool call\n</incorrect_example>\n\n- NEVER write a lead-in sentence before a tool call. Use a `<status>` tag instead.\n- Avoid using plain text to tell the user what you are about to do; instead, use status blocks.\n- WRONG: Let me get...\n- CORRECT: <status>Getting...</status>";
1284
1736
 
1285
1737
  // src/templates/system.ts
1286
1738
  var createSystemPrompt = (options = {}) => Template2.process(format_default, defaultsDeep(options, {
1287
1739
  DATETIME: (/* @__PURE__ */ new Date()).toISOString()
1288
1740
  }));
1741
+ var SYSTEM_ID = "01K2J2FZWH39F55CS7B26GP1Q3";
1289
1742
  var templates = {
1290
- /** Editable system prompt.*/
1291
1743
  system: Template2.make({
1292
- /**
1293
- * NOTE: We are forbidden from accessing randomness at model level in cloudflare, so we have to have pre-generated ids.
1294
- */
1295
- id: "01K2J2FZWH39F55CS7B26GP1Q3",
1744
+ id: SYSTEM_ID,
1296
1745
  source: system_default
1297
1746
  })
1298
1747
  };
1299
1748
  export {
1749
+ AgentRequestBegin,
1750
+ AgentRequestEnd,
1300
1751
  AiAssistantError,
1301
- AiContextBinder,
1302
- AiContextService,
1303
- AiConversation,
1304
- AiConversationService,
1752
+ AiContext_exports as AiContext,
1305
1753
  AiModelError,
1306
- AiSession,
1307
- ArtifactDiffResolver,
1754
+ AiRequest_exports as AiRequest,
1755
+ AiSession_exports as AiSession,
1308
1756
  ArtifactId,
1309
- Bindings,
1310
- ContextBinding,
1757
+ CompleteBlock,
1311
1758
  ExecutionGraph,
1312
1759
  GenerationObserver,
1760
+ McpServerError,
1761
+ PartialBlock,
1313
1762
  RefFromLLM,
1314
1763
  ToolExecutionServices,
1315
- VersionPin,
1316
1764
  applyDiffs,
1317
1765
  computeDiffsWithCursors,
1318
1766
  createArtifactElement,
@@ -1320,8 +1768,11 @@ export {
1320
1768
  createToolkit,
1321
1769
  formatSystemPrompt,
1322
1770
  formatUserPrompt,
1323
- makeToolExecutionServiceFromFunctions,
1324
- makeToolResolverFromFunctions,
1771
+ getOperationFromTool,
1772
+ makeToolExecutionService,
1773
+ makeToolExecutionServiceFromOperationInvoker,
1774
+ makeToolResolverFromOperations,
1775
+ mapAiError,
1325
1776
  reduceDiffs,
1326
1777
  templates
1327
1778
  };