@dxos/agent-runtime 0.0.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/chunk-AgentService.mjs +789 -0
- package/dist/lib/chunk-AgentService.mjs.map +1 -0
- package/dist/lib/index.mjs +2 -0
- package/dist/lib/testing.mjs +294 -0
- package/dist/lib/testing.mjs.map +1 -0
- package/dist/types/src/agent-service/AgentService.d.ts +4 -4
- package/dist/types/src/agent-service/AgentService.d.ts.map +1 -1
- package/dist/types/src/agent-service/agent-process.d.ts +173 -5
- package/dist/types/src/agent-service/agent-process.d.ts.map +1 -1
- package/dist/types/src/agent-service/delegation-scripted.test.d.ts +2 -0
- package/dist/types/src/agent-service/delegation-scripted.test.d.ts.map +1 -0
- package/dist/types/src/assistant-session-tests/scripted-loop.test.d.ts +2 -0
- package/dist/types/src/assistant-session-tests/scripted-loop.test.d.ts.map +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/testing/assistant-test-layer.d.ts +6 -2
- package/dist/types/src/testing/assistant-test-layer.d.ts.map +1 -1
- package/dist/types/src/testing/index.d.ts +2 -0
- package/dist/types/src/testing/index.d.ts.map +1 -1
- package/dist/types/src/testing/test-agent.d.ts +70 -0
- package/dist/types/src/testing/test-agent.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +17 -16
- package/src/agent-service/AgentService.conversations.json +1 -1
- package/src/agent-service/AgentService.test.ts +115 -58
- package/src/agent-service/AgentService.ts +44 -15
- package/src/agent-service/README.md +4 -4
- package/src/agent-service/agent-process.ts +31 -12
- package/src/agent-service/delegation-scripted.test.ts +123 -0
- package/src/assistant-session-tests/request.conversations.json +1 -1
- package/src/assistant-session-tests/request.test.ts +2 -2
- package/src/assistant-session-tests/scripted-loop.test.ts +119 -0
- package/src/assistant-session-tests/xml-response.conversations.json +1 -1
- package/src/assistant-session-tests/xml-response.test.ts +2 -2
- package/src/functions.conversations.json +1 -1
- package/src/functions.test.ts +2 -2
- package/src/index.ts +0 -2
- package/src/testing/assistant-test-layer.ts +32 -1
- package/src/testing/index.ts +3 -0
- package/src/testing/test-agent.ts +104 -0
- package/dist/lib/neutral/chunk-EZKG2ZGG.mjs +0 -754
- package/dist/lib/neutral/chunk-EZKG2ZGG.mjs.map +0 -7
- package/dist/lib/neutral/index.mjs +0 -9
- package/dist/lib/neutral/index.mjs.map +0 -7
- package/dist/lib/neutral/meta.json +0 -1
- package/dist/lib/neutral/testing/index.mjs +0 -285
- package/dist/lib/neutral/testing/index.mjs.map +0 -7
|
@@ -0,0 +1,789 @@
|
|
|
1
|
+
import * as Cause from "effect/Cause";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Layer from "effect/Layer";
|
|
4
|
+
import * as Option from "effect/Option";
|
|
5
|
+
import { AgentRequestBegin, AgentRequestEnd, AiContext, AiSession, HarnessControl, SkillHooks, getOperationFromTool, makeToolExecutionService, makeToolResolverFromOperations } from "@dxos/assistant";
|
|
6
|
+
import { Credential, Instructions, Operation, Process, Skill, Trace } from "@dxos/compute";
|
|
7
|
+
import { ProcessManager } from "@dxos/compute-runtime";
|
|
8
|
+
import { AgentService as AgentService$1, getSession } from "@dxos/compute/AgentService";
|
|
9
|
+
import { Annotation, Database, Feed, Obj, Ref, Registry } from "@dxos/echo";
|
|
10
|
+
import { EffectEx } from "@dxos/effect";
|
|
11
|
+
import { DXN, EID } from "@dxos/keys";
|
|
12
|
+
import { log } from "@dxos/log";
|
|
13
|
+
import * as Tool from "@effect/ai/Tool";
|
|
14
|
+
import * as Toolkit from "@effect/ai/Toolkit";
|
|
15
|
+
import * as DateTime from "effect/DateTime";
|
|
16
|
+
import * as Duration from "effect/Duration";
|
|
17
|
+
import * as Exit from "effect/Exit";
|
|
18
|
+
import * as Match from "effect/Match";
|
|
19
|
+
import * as Schema from "effect/Schema";
|
|
20
|
+
import { AiService, OpaqueToolkit } from "@dxos/ai";
|
|
21
|
+
import * as StorageService from "@dxos/compute/StorageService";
|
|
22
|
+
import { ContentBlock } from "@dxos/types";
|
|
23
|
+
import { trim } from "@dxos/util";
|
|
24
|
+
//#region \0rolldown/runtime.js
|
|
25
|
+
var __defProp = Object.defineProperty;
|
|
26
|
+
var __exportAll = (all, no_symbols) => {
|
|
27
|
+
let target = {};
|
|
28
|
+
for (var name in all) __defProp(target, name, {
|
|
29
|
+
get: all[name],
|
|
30
|
+
enumerable: true
|
|
31
|
+
});
|
|
32
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
33
|
+
return target;
|
|
34
|
+
};
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/agent-service/agent-process.ts
|
|
37
|
+
var __dxlog_file$1 = "/__w/dxos/dxos/packages/core/compute/agent-runtime/src/agent-service/agent-process.ts";
|
|
38
|
+
var AGENT_PROCESS_KEY = "org.dxos.testing.process.agent";
|
|
39
|
+
/**
|
|
40
|
+
* Hosts a persistent, suspendible AiAgent that can process a number of prompts.
|
|
41
|
+
* The process target is a queue DXN string.
|
|
42
|
+
*/
|
|
43
|
+
var AgentProcess = (options) => Process.make({
|
|
44
|
+
key: AGENT_PROCESS_KEY,
|
|
45
|
+
input: Schema.Union(Schema.String, Schema.Array(ContentBlock.Any)),
|
|
46
|
+
output: Schema.Void,
|
|
47
|
+
services: [
|
|
48
|
+
Database.Service,
|
|
49
|
+
OpaqueToolkit.OpaqueToolkitProvider,
|
|
50
|
+
Operation.Service,
|
|
51
|
+
Registry.Service,
|
|
52
|
+
StorageService.StorageService,
|
|
53
|
+
ProcessManager.ProcessOperationInvoker.Service,
|
|
54
|
+
AiService.AiService,
|
|
55
|
+
Credential.CredentialsService
|
|
56
|
+
],
|
|
57
|
+
rpcs: HarnessControl
|
|
58
|
+
}, (ctx) => Effect.gen(function* () {
|
|
59
|
+
const feedDxn = Annotation.getDictionary(ctx.params.annotations, Process.TargetAnnotation).pipe(Option.getOrUndefined);
|
|
60
|
+
if (feedDxn == null) return yield* Effect.die(/* @__PURE__ */ new Error("Agent executable requires spawn options.target set to a queue DXN."));
|
|
61
|
+
const feed = yield* Database.resolve(feedDxn, Feed.Feed).pipe(Effect.orDie);
|
|
62
|
+
const instructionsDxn = Annotation.getDictionary(ctx.params.annotations, Process.InstructionsAnnotation).pipe(Option.getOrUndefined);
|
|
63
|
+
const instructions = instructionsDxn ? yield* Database.resolve(instructionsDxn, Instructions.Instructions).pipe(Effect.orElseSucceed(() => void 0)) : void 0;
|
|
64
|
+
const runtime = yield* Effect.runtime();
|
|
65
|
+
const session = yield* EffectEx.acquireReleaseResource(() => new AiSession.Session({
|
|
66
|
+
feed,
|
|
67
|
+
runtime,
|
|
68
|
+
instructions: instructions ? [instructions] : []
|
|
69
|
+
}));
|
|
70
|
+
let inputQueue = [...yield* AgentEventsCell.get];
|
|
71
|
+
const storageService = yield* StorageService.StorageService;
|
|
72
|
+
const toolCallManager = new ToolCallManager(storageService);
|
|
73
|
+
yield* toolCallManager.load();
|
|
74
|
+
const clock = yield* Effect.clock;
|
|
75
|
+
const alarmManager = new AlarmManager({
|
|
76
|
+
storageService,
|
|
77
|
+
setAlarm: (timeout) => ctx.setAlarm(timeout),
|
|
78
|
+
now: () => clock.unsafeCurrentTimeMillis()
|
|
79
|
+
});
|
|
80
|
+
yield* alarmManager.load();
|
|
81
|
+
yield* toolCallManager.reconcileWithInputQueue(inputQueue);
|
|
82
|
+
const strategy = Option.fromNullable(options.delegationStrategy);
|
|
83
|
+
let delegations = [...yield* DelegationsCell.get];
|
|
84
|
+
const requestModelLayer = AiService.model(options.model ? DXN.getName(options.model) : "com.anthropic.model.claude-opus-4-8.default", { provider: options.provider });
|
|
85
|
+
const operationInvoker = yield* ProcessManager.ProcessOperationInvoker.Service;
|
|
86
|
+
const runEndRequestHooks = Effect.gen(function* () {
|
|
87
|
+
yield* SkillHooks.runHooks({
|
|
88
|
+
skills: session.context.getSkills(),
|
|
89
|
+
phase: "end-request",
|
|
90
|
+
invoke: (operation, input) => Effect.gen(function* () {
|
|
91
|
+
const exit = yield* (yield* operationInvoker.invokeFiber(operation, input, {
|
|
92
|
+
environment: { conversation: Obj.getURI(feed) },
|
|
93
|
+
traceMeta: { conversation: Ref.make(feed) }
|
|
94
|
+
})).await;
|
|
95
|
+
if (Exit.isFailure(exit)) yield* Effect.failCause(exit.cause);
|
|
96
|
+
}).pipe(Effect.asVoid, Effect.orDie)
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
const maybeComplete = Effect.gen(function* () {
|
|
100
|
+
if (isAgentWorkPending({
|
|
101
|
+
inputQueue,
|
|
102
|
+
alarmManager,
|
|
103
|
+
delegations,
|
|
104
|
+
toolCallManager
|
|
105
|
+
})) return;
|
|
106
|
+
yield* runEndRequestHooks;
|
|
107
|
+
if (isAgentWorkPending({
|
|
108
|
+
inputQueue,
|
|
109
|
+
alarmManager,
|
|
110
|
+
delegations,
|
|
111
|
+
toolCallManager
|
|
112
|
+
})) {
|
|
113
|
+
log("agent work enqueued by end-request hook, continuing", void 0, {
|
|
114
|
+
"~LogMeta": "~LogMeta",
|
|
115
|
+
F: __dxlog_file$1,
|
|
116
|
+
L: 193,
|
|
117
|
+
S: this
|
|
118
|
+
});
|
|
119
|
+
alarmManager.reconcile(true);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
log("agent work complete, succeeding", void 0, {
|
|
123
|
+
"~LogMeta": "~LogMeta",
|
|
124
|
+
F: __dxlog_file$1,
|
|
125
|
+
L: 198,
|
|
126
|
+
S: this
|
|
127
|
+
});
|
|
128
|
+
ctx.succeed();
|
|
129
|
+
});
|
|
130
|
+
return {
|
|
131
|
+
rpcHandlers: yield* HarnessControl.toHandlersContext({
|
|
132
|
+
setAlarm: Effect.fn(function* ({ at, message }) {
|
|
133
|
+
yield* alarmManager.setWakeAt(DateTime.toEpochMillis(at), message);
|
|
134
|
+
alarmManager.reconcile(inputQueue.length > 0);
|
|
135
|
+
}),
|
|
136
|
+
enqueueMessage: Effect.fn(function* ({ content }) {
|
|
137
|
+
inputQueue.push({
|
|
138
|
+
_tag: "prompt",
|
|
139
|
+
content
|
|
140
|
+
});
|
|
141
|
+
yield* AgentEventsCell.set(inputQueue);
|
|
142
|
+
alarmManager.reconcile(true);
|
|
143
|
+
})
|
|
144
|
+
}),
|
|
145
|
+
onInput: Effect.fnUntraced(function* (prompt) {
|
|
146
|
+
log("agent onInput received", { backlog: inputQueue.length }, {
|
|
147
|
+
"~LogMeta": "~LogMeta",
|
|
148
|
+
F: __dxlog_file$1,
|
|
149
|
+
L: 217,
|
|
150
|
+
S: this
|
|
151
|
+
});
|
|
152
|
+
const content = typeof prompt === "string" ? [ContentBlock.Text.make({ text: prompt })] : [...prompt];
|
|
153
|
+
inputQueue.push({
|
|
154
|
+
_tag: "prompt",
|
|
155
|
+
content
|
|
156
|
+
});
|
|
157
|
+
log("agent onInput persisting queue", { depth: inputQueue.length }, {
|
|
158
|
+
"~LogMeta": "~LogMeta",
|
|
159
|
+
F: __dxlog_file$1,
|
|
160
|
+
L: 220,
|
|
161
|
+
S: this
|
|
162
|
+
});
|
|
163
|
+
yield* AgentEventsCell.set(inputQueue);
|
|
164
|
+
log("agent onInput persisted", { depth: inputQueue.length }, {
|
|
165
|
+
"~LogMeta": "~LogMeta",
|
|
166
|
+
F: __dxlog_file$1,
|
|
167
|
+
L: 222,
|
|
168
|
+
S: this
|
|
169
|
+
});
|
|
170
|
+
alarmManager.reconcile(true);
|
|
171
|
+
log("agent onInput alarm scheduled", void 0, {
|
|
172
|
+
"~LogMeta": "~LogMeta",
|
|
173
|
+
F: __dxlog_file$1,
|
|
174
|
+
L: 224,
|
|
175
|
+
S: this
|
|
176
|
+
});
|
|
177
|
+
}),
|
|
178
|
+
onAlarm: Effect.fnUntraced(function* () {
|
|
179
|
+
log("agent onAlarm fired", { pending: inputQueue.length }, {
|
|
180
|
+
"~LogMeta": "~LogMeta",
|
|
181
|
+
F: __dxlog_file$1,
|
|
182
|
+
L: 228,
|
|
183
|
+
S: this
|
|
184
|
+
});
|
|
185
|
+
const fired = yield* alarmManager.takeFiredAlarm();
|
|
186
|
+
if (fired != null) {
|
|
187
|
+
log("agent onAlarm self-wake", { firedAt: fired.firedAt }, {
|
|
188
|
+
"~LogMeta": "~LogMeta",
|
|
189
|
+
F: __dxlog_file$1,
|
|
190
|
+
L: 233,
|
|
191
|
+
S: this
|
|
192
|
+
});
|
|
193
|
+
inputQueue.push({
|
|
194
|
+
_tag: "alarm",
|
|
195
|
+
firedAt: fired.firedAt,
|
|
196
|
+
message: fired.message
|
|
197
|
+
});
|
|
198
|
+
yield* AgentEventsCell.set(inputQueue);
|
|
199
|
+
}
|
|
200
|
+
while (inputQueue.length > 0) {
|
|
201
|
+
const head = inputQueue[0];
|
|
202
|
+
if (head._tag === "tool_result" && toolCallManager.isReported(head.pid)) {
|
|
203
|
+
inputQueue.shift();
|
|
204
|
+
log.info("skip tool result that was reported synchronously", { pid: head.pid }, {
|
|
205
|
+
"~LogMeta": "~LogMeta",
|
|
206
|
+
F: __dxlog_file$1,
|
|
207
|
+
L: 243,
|
|
208
|
+
S: this
|
|
209
|
+
});
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
const item = inputQueue.shift();
|
|
215
|
+
if (!item) {
|
|
216
|
+
log("agent onAlarm empty queue", {}, {
|
|
217
|
+
"~LogMeta": "~LogMeta",
|
|
218
|
+
F: __dxlog_file$1,
|
|
219
|
+
L: 251,
|
|
220
|
+
S: this
|
|
221
|
+
});
|
|
222
|
+
alarmManager.reconcile(false);
|
|
223
|
+
yield* AgentEventsCell.set(inputQueue);
|
|
224
|
+
yield* maybeComplete;
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
log("agent onAlarm handling", { tag: item._tag }, {
|
|
228
|
+
"~LogMeta": "~LogMeta",
|
|
229
|
+
F: __dxlog_file$1,
|
|
230
|
+
L: 258,
|
|
231
|
+
S: this
|
|
232
|
+
});
|
|
233
|
+
const prompt = Match.value(item).pipe(Match.tag("prompt", (item) => [...item.content]), Match.tag("tool_result", (item) => item.isError ? [ContentBlock.Text.make({
|
|
234
|
+
text: toolErrorResponse(item.pid, item.result),
|
|
235
|
+
disposition: "synthetic"
|
|
236
|
+
})] : [ContentBlock.Text.make({
|
|
237
|
+
text: toolResultResponse(item.pid, item.result),
|
|
238
|
+
disposition: "synthetic"
|
|
239
|
+
})]), Match.tag("alarm", (item) => [ContentBlock.Text.make({
|
|
240
|
+
text: wakeUpPrompt(item.firedAt, item.message),
|
|
241
|
+
disposition: "synthetic"
|
|
242
|
+
})]), Match.exhaustive);
|
|
243
|
+
log("begin request", { prompt }, {
|
|
244
|
+
"~LogMeta": "~LogMeta",
|
|
245
|
+
F: __dxlog_file$1,
|
|
246
|
+
L: 283,
|
|
247
|
+
S: this
|
|
248
|
+
});
|
|
249
|
+
log("trace agent request begin", void 0, {
|
|
250
|
+
"~LogMeta": "~LogMeta",
|
|
251
|
+
F: __dxlog_file$1,
|
|
252
|
+
L: 284,
|
|
253
|
+
S: this
|
|
254
|
+
});
|
|
255
|
+
yield* Trace.write(AgentRequestBegin, {});
|
|
256
|
+
yield* session.createRequest({
|
|
257
|
+
prompt,
|
|
258
|
+
system: options.systemPrompt,
|
|
259
|
+
mcpServers: options.getMcpServers?.()
|
|
260
|
+
}).pipe(Effect.onExit((exit) => Trace.write(AgentRequestEnd, {
|
|
261
|
+
status: Exit.isSuccess(exit) ? "success" : Exit.isInterrupted(exit) ? "interrupted" : "error",
|
|
262
|
+
error: Exit.isFailure(exit) ? Cause.pretty(exit.cause) : void 0
|
|
263
|
+
})));
|
|
264
|
+
log("end request", void 0, {
|
|
265
|
+
"~LogMeta": "~LogMeta",
|
|
266
|
+
F: __dxlog_file$1,
|
|
267
|
+
L: 304,
|
|
268
|
+
S: this
|
|
269
|
+
});
|
|
270
|
+
yield* AgentEventsCell.set(inputQueue);
|
|
271
|
+
if (Option.isSome(strategy)) {
|
|
272
|
+
const activeIds = new Set(delegations.map((delegation) => delegation.id));
|
|
273
|
+
const pending = yield* strategy.value.reconcile(feed, activeIds);
|
|
274
|
+
for (const delegation of pending) {
|
|
275
|
+
const pid = yield* delegation.spawn;
|
|
276
|
+
delegations.push({
|
|
277
|
+
pid,
|
|
278
|
+
id: delegation.id
|
|
279
|
+
});
|
|
280
|
+
log("delegated work", {
|
|
281
|
+
pid,
|
|
282
|
+
id: delegation.id
|
|
283
|
+
}, {
|
|
284
|
+
"~LogMeta": "~LogMeta",
|
|
285
|
+
F: __dxlog_file$1,
|
|
286
|
+
L: 315,
|
|
287
|
+
S: this
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
if (pending.length > 0) yield* DelegationsCell.set(delegations);
|
|
291
|
+
}
|
|
292
|
+
alarmManager.reconcile(inputQueue.length > 0);
|
|
293
|
+
yield* maybeComplete;
|
|
294
|
+
}, Effect.orDie, Effect.provide(Layer.mergeAll(makeToolResolverFromOperations(), ToolExecutionService({
|
|
295
|
+
toolCallManager,
|
|
296
|
+
feed,
|
|
297
|
+
enableBackgrounding: options.enableToolBackgrounding ?? false
|
|
298
|
+
}), AsynchronousExectionToolkitLayer, requestModelLayer).pipe(Layer.orDie))),
|
|
299
|
+
onChildEvent: Effect.fnUntraced(function* (event) {
|
|
300
|
+
log("childEvent", { event }, {
|
|
301
|
+
"~LogMeta": "~LogMeta",
|
|
302
|
+
F: __dxlog_file$1,
|
|
303
|
+
L: 341,
|
|
304
|
+
S: this
|
|
305
|
+
});
|
|
306
|
+
if (event._tag === "exited") {
|
|
307
|
+
const delegation = delegations.find((delegation) => delegation.pid === event.pid);
|
|
308
|
+
if (delegation) {
|
|
309
|
+
delegations = delegations.filter((other) => other.pid !== event.pid);
|
|
310
|
+
yield* DelegationsCell.set(delegations);
|
|
311
|
+
const exit = yield* (yield* (yield* ProcessManager.ProcessOperationInvoker.Service).attachFiber(event.pid).pipe(Effect.orDie)).await;
|
|
312
|
+
if (Option.isSome(strategy)) yield* strategy.value.onComplete(feed, delegation.id, exit);
|
|
313
|
+
log("delegated work completed", {
|
|
314
|
+
pid: event.pid,
|
|
315
|
+
id: delegation.id,
|
|
316
|
+
success: Exit.isSuccess(exit)
|
|
317
|
+
}, {
|
|
318
|
+
"~LogMeta": "~LogMeta",
|
|
319
|
+
F: __dxlog_file$1,
|
|
320
|
+
L: 356,
|
|
321
|
+
S: this
|
|
322
|
+
});
|
|
323
|
+
yield* maybeComplete;
|
|
324
|
+
} else if (toolCallManager.isToolCall(event.pid)) {
|
|
325
|
+
const attachExit = yield* (yield* ProcessManager.ProcessOperationInvoker.Service).attachFiber(event.pid).pipe(Effect.exit);
|
|
326
|
+
if (Exit.isFailure(attachExit)) {
|
|
327
|
+
if (toolCallManager.isToolCall(event.pid) || inputQueue.some((item) => item._tag === "tool_result" && item.pid === event.pid) || toolCallManager.isReported(event.pid)) {
|
|
328
|
+
log.verbose("childEvent skipped (process gone, result already handled)", { pid: event.pid }, {
|
|
329
|
+
"~LogMeta": "~LogMeta",
|
|
330
|
+
F: __dxlog_file$1,
|
|
331
|
+
L: 369,
|
|
332
|
+
S: this
|
|
333
|
+
});
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
return yield* Effect.failCause(attachExit.cause).pipe(Effect.orDie);
|
|
337
|
+
}
|
|
338
|
+
const result = yield* attachExit.value.await.pipe(Effect.orDie).pipe(Effect.map(Exit.match({
|
|
339
|
+
onSuccess: (value) => ({
|
|
340
|
+
_tag: "tool_result",
|
|
341
|
+
pid: event.pid,
|
|
342
|
+
result: value,
|
|
343
|
+
isError: false
|
|
344
|
+
}),
|
|
345
|
+
onFailure: (cause) => ({
|
|
346
|
+
_tag: "tool_result",
|
|
347
|
+
pid: event.pid,
|
|
348
|
+
result: Cause.pretty(cause),
|
|
349
|
+
isError: true
|
|
350
|
+
})
|
|
351
|
+
})));
|
|
352
|
+
inputQueue.push(result);
|
|
353
|
+
log("agent onChildEvent persisted tool result", {
|
|
354
|
+
depth: inputQueue.length,
|
|
355
|
+
childPid: event.pid
|
|
356
|
+
}, {
|
|
357
|
+
"~LogMeta": "~LogMeta",
|
|
358
|
+
F: __dxlog_file$1,
|
|
359
|
+
L: 394,
|
|
360
|
+
S: this
|
|
361
|
+
});
|
|
362
|
+
yield* AgentEventsCell.set(inputQueue);
|
|
363
|
+
alarmManager.reconcile(true);
|
|
364
|
+
log("agent onChildEvent alarm scheduled", { depth: inputQueue.length }, {
|
|
365
|
+
"~LogMeta": "~LogMeta",
|
|
366
|
+
F: __dxlog_file$1,
|
|
367
|
+
L: 397,
|
|
368
|
+
S: this
|
|
369
|
+
});
|
|
370
|
+
} else log.verbose("childEvent ignored non-tool call and not a delegation", { pid: event.pid }, {
|
|
371
|
+
"~LogMeta": "~LogMeta",
|
|
372
|
+
F: __dxlog_file$1,
|
|
373
|
+
L: 399,
|
|
374
|
+
S: this
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
})
|
|
378
|
+
};
|
|
379
|
+
}));
|
|
380
|
+
var AgentEvent = Schema.Union(Schema.TaggedStruct("prompt", { content: Schema.Array(ContentBlock.Any) }), Schema.TaggedStruct("tool_result", {
|
|
381
|
+
pid: Process.ID,
|
|
382
|
+
result: Schema.Unknown,
|
|
383
|
+
isError: Schema.Boolean
|
|
384
|
+
}), Schema.TaggedStruct("alarm", {
|
|
385
|
+
firedAt: Schema.Number,
|
|
386
|
+
message: Schema.NullOr(Schema.String)
|
|
387
|
+
}));
|
|
388
|
+
var AgentEventsCell = StorageService.cell(Schema.parseJson(Schema.Array(AgentEvent).pipe(Schema.mutable)), "inputQueue").pipe(StorageService.withDefault(() => []));
|
|
389
|
+
/**
|
|
390
|
+
* Tracks delegated sub-agent child processes (pid -> correlation id) so that, after a hibernation,
|
|
391
|
+
* a delegated child's exit can be matched back to the work it was fulfilling.
|
|
392
|
+
*/
|
|
393
|
+
var Delegation = Schema.Struct({
|
|
394
|
+
pid: Process.ID,
|
|
395
|
+
id: Schema.String
|
|
396
|
+
}).pipe(Schema.mutable);
|
|
397
|
+
var DelegationsCell = StorageService.cell(Schema.parseJson(Schema.Array(Delegation).pipe(Schema.mutable)), "delegations").pipe(StorageService.withDefault(() => []));
|
|
398
|
+
var ToolCallState = Schema.Struct({ activeCalls: Schema.Array(Schema.Struct({
|
|
399
|
+
pid: Process.ID,
|
|
400
|
+
reported: Schema.Boolean
|
|
401
|
+
}).pipe(Schema.mutable)).pipe(Schema.mutable) });
|
|
402
|
+
var ToolCallStateCell = StorageService.cell(Schema.parseJson(ToolCallState.pipe(Schema.mutable)), "toolCallState").pipe(StorageService.withDefault(() => ({ activeCalls: [] })));
|
|
403
|
+
var ToolCallManager = class {
|
|
404
|
+
#storageService;
|
|
405
|
+
#state = { activeCalls: [] };
|
|
406
|
+
constructor(storageService) {
|
|
407
|
+
this.#storageService = storageService;
|
|
408
|
+
}
|
|
409
|
+
load() {
|
|
410
|
+
return Effect.gen(this, function* () {
|
|
411
|
+
this.#state = yield* ToolCallStateCell.get;
|
|
412
|
+
}).pipe(Effect.provideService(StorageService.StorageService, this.#storageService));
|
|
413
|
+
}
|
|
414
|
+
beginCall(pid) {
|
|
415
|
+
return Effect.gen(this, function* () {
|
|
416
|
+
this.#state.activeCalls.push({
|
|
417
|
+
pid,
|
|
418
|
+
reported: false
|
|
419
|
+
});
|
|
420
|
+
yield* ToolCallStateCell.set(this.#state);
|
|
421
|
+
}).pipe(Effect.provideService(StorageService.StorageService, this.#storageService));
|
|
422
|
+
}
|
|
423
|
+
markAsReported(pid) {
|
|
424
|
+
return Effect.gen(this, function* () {
|
|
425
|
+
const call = this.#state.activeCalls.find((call) => call.pid === pid);
|
|
426
|
+
if (!call) return;
|
|
427
|
+
call.reported = true;
|
|
428
|
+
yield* ToolCallStateCell.set(this.#state);
|
|
429
|
+
}).pipe(Effect.provideService(StorageService.StorageService, this.#storageService));
|
|
430
|
+
}
|
|
431
|
+
isToolCall(pid) {
|
|
432
|
+
return this.#state.activeCalls.some((call) => call.pid === pid);
|
|
433
|
+
}
|
|
434
|
+
isReported(pid) {
|
|
435
|
+
return this.#state.activeCalls.some((call) => call.pid === pid && call.reported);
|
|
436
|
+
}
|
|
437
|
+
/** True while a tool-call result has not yet been delivered back to the agent turn. */
|
|
438
|
+
hasPendingToolResults() {
|
|
439
|
+
return this.#state.activeCalls.some((call) => !call.reported);
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Clears reported flags for tool calls that still have a pending queue entry.
|
|
443
|
+
* After reload the in-flight createRequest is gone, so those results must be redelivered via onAlarm.
|
|
444
|
+
*/
|
|
445
|
+
reconcileWithInputQueue(queue) {
|
|
446
|
+
return Effect.gen(this, function* () {
|
|
447
|
+
let changed = false;
|
|
448
|
+
for (const item of queue) {
|
|
449
|
+
if (item._tag !== "tool_result") continue;
|
|
450
|
+
const call = this.#state.activeCalls.find((entry) => entry.pid === item.pid);
|
|
451
|
+
if (call?.reported) {
|
|
452
|
+
call.reported = false;
|
|
453
|
+
changed = true;
|
|
454
|
+
log("reconcile queued tool result", { pid: item.pid }, {
|
|
455
|
+
"~LogMeta": "~LogMeta",
|
|
456
|
+
F: __dxlog_file$1,
|
|
457
|
+
L: 537,
|
|
458
|
+
S: this
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
if (changed) yield* ToolCallStateCell.set(this.#state);
|
|
463
|
+
}).pipe(Effect.provideService(StorageService.StorageService, this.#storageService));
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
/** True while the agent still has queued work, a self-wake, subprocesses, or undelivered tool results. */
|
|
467
|
+
var isAgentWorkPending = ({ inputQueue, alarmManager, delegations, toolCallManager }) => inputQueue.length > 0 || alarmManager.wakeAt != null || delegations.length > 0 || toolCallManager.hasPendingToolResults();
|
|
468
|
+
/**
|
|
469
|
+
* Persisted next agent-scheduled self-wake: the UNIX timestamp (ms) and an optional reminder
|
|
470
|
+
* message delivered when it fires, or `null` when no self-wake is set.
|
|
471
|
+
*/
|
|
472
|
+
var AgentAlarmCell = StorageService.cell(Schema.parseJson(Schema.NullOr(Schema.Struct({
|
|
473
|
+
wakeAt: Schema.Number,
|
|
474
|
+
message: Schema.NullOr(Schema.String)
|
|
475
|
+
}))), "agentAlarm").pipe(StorageService.withDefault(() => null));
|
|
476
|
+
/**
|
|
477
|
+
* Computes the timeout to pass to `ctx.setAlarm`, reconciling pending queue work with the agent's
|
|
478
|
+
* self-wake alarm. Returns `null` when no alarm should be scheduled (process can go idle).
|
|
479
|
+
*/
|
|
480
|
+
var computeAlarmDelay = ({ hasPendingWork, wakeAt, now }) => {
|
|
481
|
+
if (hasPendingWork) return 0;
|
|
482
|
+
if (wakeAt != null) return Math.max(0, wakeAt - now);
|
|
483
|
+
return null;
|
|
484
|
+
};
|
|
485
|
+
/**
|
|
486
|
+
* Tracks the next agent-scheduled self-wake and keeps it in sync with the process alarm
|
|
487
|
+
* (`ctx.setAlarm`). The agent sets alarms via the alarm blueprint, which dispatches to this manager
|
|
488
|
+
* through the `HarnessControl` RPC surface; the process reconciles the underlying single-shot alarm
|
|
489
|
+
* timer to fire at the earliest of pending work or the self-wake.
|
|
490
|
+
*/
|
|
491
|
+
var AlarmManager = class {
|
|
492
|
+
#storageService;
|
|
493
|
+
#setAlarm;
|
|
494
|
+
#now;
|
|
495
|
+
#wakeAt = null;
|
|
496
|
+
#message = null;
|
|
497
|
+
constructor({ storageService, setAlarm, now = () => Date.now() }) {
|
|
498
|
+
this.#storageService = storageService;
|
|
499
|
+
this.#setAlarm = setAlarm;
|
|
500
|
+
this.#now = now;
|
|
501
|
+
}
|
|
502
|
+
/** Currently scheduled self-wake timestamp (ms), or `null` when none is set. */
|
|
503
|
+
get wakeAt() {
|
|
504
|
+
return this.#wakeAt;
|
|
505
|
+
}
|
|
506
|
+
/** Reminder message delivered when the current self-wake fires, or `null` when none is set. */
|
|
507
|
+
get message() {
|
|
508
|
+
return this.#message;
|
|
509
|
+
}
|
|
510
|
+
now() {
|
|
511
|
+
return this.#now();
|
|
512
|
+
}
|
|
513
|
+
/** Restores the persisted alarm state. */
|
|
514
|
+
load() {
|
|
515
|
+
return Effect.gen(this, function* () {
|
|
516
|
+
const persisted = yield* AgentAlarmCell.get;
|
|
517
|
+
this.#wakeAt = persisted?.wakeAt ?? null;
|
|
518
|
+
this.#message = persisted?.message ?? null;
|
|
519
|
+
}).pipe(Effect.provideService(StorageService.StorageService, this.#storageService));
|
|
520
|
+
}
|
|
521
|
+
/** Records a new self-wake target (with an optional reminder message) and persists it. */
|
|
522
|
+
setWakeAt(wakeAt, message = null) {
|
|
523
|
+
return Effect.gen(this, function* () {
|
|
524
|
+
this.#wakeAt = wakeAt;
|
|
525
|
+
this.#message = message;
|
|
526
|
+
yield* AgentAlarmCell.set({
|
|
527
|
+
wakeAt,
|
|
528
|
+
message
|
|
529
|
+
});
|
|
530
|
+
}).pipe(Effect.provideService(StorageService.StorageService, this.#storageService));
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Clears the self-wake alarm if it is due, returning the timestamp it was scheduled for and the
|
|
534
|
+
* reminder message it carried (or `null` if no alarm was due).
|
|
535
|
+
*/
|
|
536
|
+
takeFiredAlarm() {
|
|
537
|
+
return Effect.gen(this, function* () {
|
|
538
|
+
if (this.#wakeAt == null || this.#now() < this.#wakeAt) return null;
|
|
539
|
+
const firedAt = this.#wakeAt;
|
|
540
|
+
const message = this.#message;
|
|
541
|
+
this.#wakeAt = null;
|
|
542
|
+
this.#message = null;
|
|
543
|
+
yield* AgentAlarmCell.set(null);
|
|
544
|
+
return {
|
|
545
|
+
firedAt,
|
|
546
|
+
message
|
|
547
|
+
};
|
|
548
|
+
}).pipe(Effect.provideService(StorageService.StorageService, this.#storageService));
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* Reconciles the process alarm timer with pending work and the tracked self-wake, scheduling the
|
|
552
|
+
* earliest of the two. Does nothing when there is neither pending work nor a self-wake.
|
|
553
|
+
*/
|
|
554
|
+
reconcile(hasPendingWork) {
|
|
555
|
+
const delay = computeAlarmDelay({
|
|
556
|
+
hasPendingWork,
|
|
557
|
+
wakeAt: this.#wakeAt,
|
|
558
|
+
now: this.#now()
|
|
559
|
+
});
|
|
560
|
+
if (delay != null) this.#setAlarm(delay);
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
/**
|
|
564
|
+
* Prompt delivered to the agent when a self-scheduled alarm fires. When the alarm carried a
|
|
565
|
+
* reminder message it is surfaced verbatim, otherwise a generic continuation prompt is used.
|
|
566
|
+
*/
|
|
567
|
+
var wakeUpPrompt = (firedAt, message) => message != null ? trim`
|
|
568
|
+
Your scheduled alarm fired (it was set for ${new Date(firedAt).toISOString()}).
|
|
569
|
+
${message}
|
|
570
|
+
` : trim`
|
|
571
|
+
Your scheduled alarm fired (it was set for ${new Date(firedAt).toISOString()}).
|
|
572
|
+
Continue with whatever you intended to do when you scheduled this wake-up.
|
|
573
|
+
`;
|
|
574
|
+
var ToolExecutionService = ({ enableBackgrounding, backgroundThreshold = Duration.seconds(1), toolCallManager, feed }) => Layer.unwrapEffect(Effect.gen(function* () {
|
|
575
|
+
const operationInvoker = yield* ProcessManager.ProcessOperationInvoker.Service;
|
|
576
|
+
return makeToolExecutionService({ invoke: (tool, input) => Effect.gen(function* () {
|
|
577
|
+
const operationDef = getOperationFromTool(tool).pipe(Option.getOrThrow);
|
|
578
|
+
log("invoking operation", {
|
|
579
|
+
operationDef,
|
|
580
|
+
input
|
|
581
|
+
}, {
|
|
582
|
+
"~LogMeta": "~LogMeta",
|
|
583
|
+
F: __dxlog_file$1,
|
|
584
|
+
L: 723,
|
|
585
|
+
S: this
|
|
586
|
+
});
|
|
587
|
+
const fiber = yield* operationInvoker.invokeFiber(operationDef, input, {
|
|
588
|
+
environment: { conversation: Obj.getURI(feed) },
|
|
589
|
+
traceMeta: { conversation: Ref.make(feed) }
|
|
590
|
+
});
|
|
591
|
+
yield* toolCallManager.beginCall(fiber.pid);
|
|
592
|
+
log("invoked operation", {
|
|
593
|
+
operationDef,
|
|
594
|
+
input,
|
|
595
|
+
fiber
|
|
596
|
+
}, {
|
|
597
|
+
"~LogMeta": "~LogMeta",
|
|
598
|
+
F: __dxlog_file$1,
|
|
599
|
+
L: 733,
|
|
600
|
+
S: this
|
|
601
|
+
});
|
|
602
|
+
const awaitWithReport = fiber.await.pipe(Effect.tap(() => toolCallManager.markAsReported(fiber.pid)));
|
|
603
|
+
const result = enableBackgrounding ? yield* awaitWithReport.pipe(Effect.timeout(backgroundThreshold), Effect.catchTag("TimeoutException", () => Effect.succeed(Exit.succeed(toolIsRunningInBackgroundResponse(fiber.pid))))) : yield* awaitWithReport;
|
|
604
|
+
log("result", { result }, {
|
|
605
|
+
"~LogMeta": "~LogMeta",
|
|
606
|
+
F: __dxlog_file$1,
|
|
607
|
+
L: 744,
|
|
608
|
+
S: this
|
|
609
|
+
});
|
|
610
|
+
return yield* result;
|
|
611
|
+
}) });
|
|
612
|
+
}));
|
|
613
|
+
var AsynchronousExectionToolkit = class extends Toolkit.make(Tool.make("poll-tools", {
|
|
614
|
+
description: trim`
|
|
615
|
+
Poll tool calls running in the background.
|
|
616
|
+
Set wait to true to wait for the tool call to complete before returning.
|
|
617
|
+
Only set wait to true if you dont have other tasks to perform in parallel.
|
|
618
|
+
Set an appropriate timeout to avoid waiting forever.
|
|
619
|
+
You will also be notified about the job completion separatelly, so you do not always need to inspect the job if you dont need the result right now.
|
|
620
|
+
`,
|
|
621
|
+
parameters: {
|
|
622
|
+
ids: Schema.Array(Schema.String).annotations({ description: "The IDs of the jobs to inspect." }),
|
|
623
|
+
wait: Schema.optional(Schema.Boolean).annotations({
|
|
624
|
+
description: "Whether to wait for the tool call to complete before returning.",
|
|
625
|
+
default: false
|
|
626
|
+
}),
|
|
627
|
+
timeout: Schema.optional(Schema.Number).annotations({
|
|
628
|
+
description: "Maximum time to wait for the job to complete. If the job does not complete within the timeout, the current state is returned.",
|
|
629
|
+
default: 1e4
|
|
630
|
+
})
|
|
631
|
+
}
|
|
632
|
+
})) {};
|
|
633
|
+
var AsynchronousExectionToolkitLayer = AsynchronousExectionToolkit.toLayer(Effect.gen(function* () {
|
|
634
|
+
const invoker = yield* ProcessManager.ProcessOperationInvoker.Service;
|
|
635
|
+
return { "poll-tools": ({ ids, wait, timeout = 1e4 }) => Effect.gen(function* () {
|
|
636
|
+
return yield* Effect.forEach(ids, (pid) => invoker.attachFiber(Process.ID.make(pid)).pipe(Effect.flatMap((_) => _.await), Effect.timeout(Duration.millis(timeout)), Effect.flatMap(Exit.match({
|
|
637
|
+
onSuccess: (value) => Effect.succeed(toolResultResponse(pid, value)),
|
|
638
|
+
onFailure: (cause) => Effect.succeed(toolErrorResponse(pid, Cause.pretty(cause)))
|
|
639
|
+
})), Effect.catchTag("ProcessNotFoundError", () => Effect.succeed(`Process not found: ${pid}`)), Effect.catchTag("TimeoutException", () => Effect.succeed(`Process still running: ${pid}`))));
|
|
640
|
+
}) };
|
|
641
|
+
}));
|
|
642
|
+
/**
|
|
643
|
+
* Instructs model that the tool is running in the background.
|
|
644
|
+
*/
|
|
645
|
+
var toolIsRunningInBackgroundResponse = (pid) => `Tool is running in the background (id=${pid}); wait for the completion notification to get the result.`;
|
|
646
|
+
var toolResultResponse = (pid, value) => `<result pid=${pid}>${JSON.stringify(value)}</result>`;
|
|
647
|
+
var toolErrorResponse = (pid, cause) => `<error pid=${pid}>${cause}</error>`;
|
|
648
|
+
//#endregion
|
|
649
|
+
//#region src/agent-service/AgentService.ts
|
|
650
|
+
var AgentService_exports = /* @__PURE__ */ __exportAll({
|
|
651
|
+
createSession: () => createSession,
|
|
652
|
+
layer: () => layer
|
|
653
|
+
});
|
|
654
|
+
var __dxlog_file = "/__w/dxos/dxos/packages/core/compute/agent-runtime/src/agent-service/AgentService.ts";
|
|
655
|
+
var isTerminalProcess = (state) => state === Process.State.SUCCEEDED || state === Process.State.FAILED || state === Process.State.TERMINATED;
|
|
656
|
+
var createSession = Effect.fn("createSession")(function* (opts) {
|
|
657
|
+
const skills = yield* Effect.forEach(opts?.skills ?? [], (skill) => Skill.upsert(Skill.getKey(skill)).pipe(Effect.map(Ref.make)));
|
|
658
|
+
const feed = yield* Database.add(Feed.make());
|
|
659
|
+
const runtime = yield* Effect.runtime();
|
|
660
|
+
const binder = yield* EffectEx.acquireReleaseResource(() => new AiContext.Binder({
|
|
661
|
+
feed,
|
|
662
|
+
runtime
|
|
663
|
+
}));
|
|
664
|
+
yield* Effect.promise(() => binder.bind({
|
|
665
|
+
skills,
|
|
666
|
+
objects: opts?.context ?? []
|
|
667
|
+
}));
|
|
668
|
+
return yield* getSession(feed, {
|
|
669
|
+
model: opts?.model,
|
|
670
|
+
provider: opts?.provider
|
|
671
|
+
});
|
|
672
|
+
}, Effect.scoped);
|
|
673
|
+
var layer = (opts) => Layer.effect(AgentService$1, Effect.gen(function* () {
|
|
674
|
+
const processManager = yield* ProcessManager.Service;
|
|
675
|
+
const sessionCache = /* @__PURE__ */ new Map();
|
|
676
|
+
const makeExecutable = (model, provider) => AgentProcess({
|
|
677
|
+
systemPrompt: opts?.systemPrompt,
|
|
678
|
+
model: model ?? opts?.model,
|
|
679
|
+
provider: provider ?? opts?.provider,
|
|
680
|
+
getMcpServers: opts?.getMcpServers,
|
|
681
|
+
enableToolBackgrounding: opts?.enableToolBackgrounding,
|
|
682
|
+
delegationStrategy: opts?.delegationStrategy
|
|
683
|
+
});
|
|
684
|
+
return {
|
|
685
|
+
getSession: (feed, options) => Effect.gen(function* () {
|
|
686
|
+
const model = options?.model ?? opts?.model;
|
|
687
|
+
const provider = options?.provider ?? opts?.provider;
|
|
688
|
+
const instructions = options?.instructions?.uri;
|
|
689
|
+
const cached = sessionCache.get(feed.id);
|
|
690
|
+
if (cached) {
|
|
691
|
+
if (cached.model === model && cached.provider === provider && cached.instructions === instructions && !isTerminalProcess(cached.handle.status.state)) return cached.session;
|
|
692
|
+
if (!isTerminalProcess(cached.handle.status.state)) yield* cached.handle.terminate();
|
|
693
|
+
sessionCache.delete(feed.id);
|
|
694
|
+
}
|
|
695
|
+
const target = Obj.getURI(feed);
|
|
696
|
+
const parsedEchoUri = EID.tryParse(target);
|
|
697
|
+
const spaceId = parsedEchoUri ? EID.getSpaceId(parsedEchoUri) : void 0;
|
|
698
|
+
const executable = makeExecutable(model, provider);
|
|
699
|
+
let activeProcess = (yield* processManager.list({
|
|
700
|
+
target,
|
|
701
|
+
key: executable.key
|
|
702
|
+
})).find((process) => !isTerminalProcess(process.status.state));
|
|
703
|
+
if (activeProcess) {
|
|
704
|
+
if (Option.getOrUndefined(Annotation.getDictionary(activeProcess.params.annotations, Process.InstructionsAnnotation)) !== instructions) {
|
|
705
|
+
yield* activeProcess.terminate();
|
|
706
|
+
activeProcess = void 0;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
let handle;
|
|
710
|
+
if (activeProcess) {
|
|
711
|
+
yield* activeProcess.hydrate(executable);
|
|
712
|
+
handle = activeProcess;
|
|
713
|
+
} else handle = yield* processManager.spawn(executable, {
|
|
714
|
+
name: "Agent",
|
|
715
|
+
target,
|
|
716
|
+
annotations: Annotation.buildDictionary((dictionary) => {
|
|
717
|
+
Annotation.setDictionary(dictionary, Process.HarnessHostAnnotation, true);
|
|
718
|
+
if (options?.instructions) Annotation.setDictionary(dictionary, Process.InstructionsAnnotation, options.instructions.uri);
|
|
719
|
+
}),
|
|
720
|
+
environment: {
|
|
721
|
+
...spaceId !== void 0 ? { space: spaceId } : {},
|
|
722
|
+
conversation: target
|
|
723
|
+
},
|
|
724
|
+
traceMeta: { conversation: Ref.make(feed) }
|
|
725
|
+
});
|
|
726
|
+
const releaseSession = () => {
|
|
727
|
+
sessionCache.delete(feed.id);
|
|
728
|
+
};
|
|
729
|
+
const session = makeSession(handle, feed, releaseSession);
|
|
730
|
+
sessionCache.set(feed.id, {
|
|
731
|
+
model,
|
|
732
|
+
provider,
|
|
733
|
+
instructions,
|
|
734
|
+
handle,
|
|
735
|
+
session
|
|
736
|
+
});
|
|
737
|
+
return session;
|
|
738
|
+
}),
|
|
739
|
+
hydrate: Effect.fnUntraced(function* () {
|
|
740
|
+
sessionCache.clear();
|
|
741
|
+
const executable = makeExecutable();
|
|
742
|
+
const agents = yield* processManager.list({ key: AGENT_PROCESS_KEY });
|
|
743
|
+
log("agent hydrate", { count: agents.length }, {
|
|
744
|
+
"~LogMeta": "~LogMeta",
|
|
745
|
+
F: __dxlog_file,
|
|
746
|
+
L: 140,
|
|
747
|
+
S: this
|
|
748
|
+
});
|
|
749
|
+
for (const agent of agents) yield* agent.hydrate(executable).pipe(Effect.catchAllCause((cause) => Effect.sync(() => log.warn("agent hydrate skipped", {
|
|
750
|
+
pid: agent.pid,
|
|
751
|
+
cause: Cause.pretty(cause)
|
|
752
|
+
}, {
|
|
753
|
+
"~LogMeta": "~LogMeta",
|
|
754
|
+
F: __dxlog_file,
|
|
755
|
+
L: 146,
|
|
756
|
+
S: this
|
|
757
|
+
}))));
|
|
758
|
+
})
|
|
759
|
+
};
|
|
760
|
+
}));
|
|
761
|
+
var makeSession = (process, feed, releaseSession) => ({
|
|
762
|
+
feed,
|
|
763
|
+
getContext: () => Effect.gen(function* () {
|
|
764
|
+
const runtime = yield* Effect.runtime();
|
|
765
|
+
return (yield* EffectEx.acquireReleaseResource(() => new AiContext.Binder({
|
|
766
|
+
feed,
|
|
767
|
+
runtime
|
|
768
|
+
}))).getObjects().map((object) => Ref.make(object));
|
|
769
|
+
}).pipe(Effect.scoped),
|
|
770
|
+
addContext: (context) => Effect.gen(function* () {
|
|
771
|
+
const runtime = yield* Effect.runtime();
|
|
772
|
+
const binder = yield* EffectEx.acquireReleaseResource(() => new AiContext.Binder({
|
|
773
|
+
feed,
|
|
774
|
+
runtime
|
|
775
|
+
}));
|
|
776
|
+
yield* Effect.promise(() => binder.bind({
|
|
777
|
+
skills: [],
|
|
778
|
+
objects: context
|
|
779
|
+
}));
|
|
780
|
+
}).pipe(Effect.scoped),
|
|
781
|
+
submitPrompt: (prompt) => process.submitInput(prompt),
|
|
782
|
+
waitForCompletion: () => process.runUntilSettled(),
|
|
783
|
+
terminate: () => process.terminate().pipe(Effect.tap(() => Effect.sync(releaseSession))),
|
|
784
|
+
subscribeEphemeral: () => process.subscribeEphemeral()
|
|
785
|
+
});
|
|
786
|
+
//#endregion
|
|
787
|
+
export { __exportAll as i, layer as n, AGENT_PROCESS_KEY as r, AgentService_exports as t };
|
|
788
|
+
|
|
789
|
+
//# sourceMappingURL=chunk-AgentService.mjs.map
|