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