@dxos/agent-runtime 0.13.0 → 0.14.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 +189 -93
- package/dist/lib/chunk-AgentService.mjs.map +1 -1
- package/dist/lib/testing.mjs +3 -3
- package/dist/lib/testing.mjs.map +1 -1
- package/dist/types/src/agent-service/AgentService.d.ts +9 -0
- package/dist/types/src/agent-service/AgentService.d.ts.map +1 -1
- package/dist/types/src/agent-service/agent-process.d.ts +23 -5
- package/dist/types/src/agent-service/agent-process.d.ts.map +1 -1
- package/dist/types/src/agent-service/delegation-strategy.d.ts +5 -4
- package/dist/types/src/agent-service/delegation-strategy.d.ts.map +1 -1
- package/dist/types/src/agent-service/index.d.ts +1 -1
- package/dist/types/src/agent-service/index.d.ts.map +1 -1
- package/dist/types/src/agent-service/process-selection.test.d.ts +2 -0
- package/dist/types/src/agent-service/process-selection.test.d.ts.map +1 -0
- package/dist/types/src/agent-service/skill-hooks.test.d.ts +2 -0
- package/dist/types/src/agent-service/skill-hooks.test.d.ts.map +1 -0
- package/dist/types/src/testing/assistant-test-layer.d.ts +2 -2
- package/dist/types/src/testing/assistant-test-layer.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +17 -17
- package/src/agent-service/AgentService.test.ts +4 -4
- package/src/agent-service/AgentService.ts +63 -24
- package/src/agent-service/README.md +10 -10
- package/src/agent-service/agent-process.ts +201 -100
- package/src/agent-service/delegation-scripted.test.ts +4 -4
- package/src/agent-service/delegation-strategy.ts +9 -4
- package/src/agent-service/index.ts +1 -0
- package/src/agent-service/process-selection.test.ts +135 -0
- package/src/agent-service/queue-scripted.test.ts +39 -0
- package/src/agent-service/redelivery.test.ts +23 -1
- package/src/agent-service/skill-hooks.test.ts +182 -0
- package/src/testing/assistant-test-layer.ts +3 -2
|
@@ -22,10 +22,11 @@ import * as Option from "effect/Option";
|
|
|
22
22
|
import * as Schema from "effect/Schema";
|
|
23
23
|
import * as Struct from "effect/Struct";
|
|
24
24
|
import { AiService, Model, OpaqueToolkit } from "@dxos/ai";
|
|
25
|
-
import {
|
|
25
|
+
import { OperationProcess } from "@dxos/compute-runtime";
|
|
26
26
|
import * as Credential from "@dxos/compute/Credential";
|
|
27
27
|
import * as McpServer from "@dxos/compute/McpServer";
|
|
28
28
|
import * as Operation from "@dxos/compute/Operation";
|
|
29
|
+
import * as OperationHandlerSet from "@dxos/compute/OperationHandlerSet";
|
|
29
30
|
import * as StorageService from "@dxos/compute/StorageService";
|
|
30
31
|
import * as Trace from "@dxos/compute/Trace";
|
|
31
32
|
import { AccessToken } from "@dxos/link";
|
|
@@ -196,12 +197,13 @@ var AgentProcess = (options) => Operation.makeDurable({
|
|
|
196
197
|
Operation.Service,
|
|
197
198
|
Registry.Service,
|
|
198
199
|
StorageService.StorageService,
|
|
199
|
-
|
|
200
|
+
Process.ManagerService,
|
|
201
|
+
OperationHandlerSet.OperationHandlerProvider,
|
|
200
202
|
AiService.AiService,
|
|
201
203
|
Credential.CredentialsService
|
|
202
204
|
],
|
|
203
205
|
rpcs: HarnessControl
|
|
204
|
-
}
|
|
206
|
+
}).pipe(Operation.withDurableHandler((ctx) => Effect.gen(function* () {
|
|
205
207
|
const chatDxn = Annotation.getDictionary(ctx.params.annotations, Process.TargetAnnotation).pipe(Option.getOrUndefined);
|
|
206
208
|
if (chatDxn == null) return yield* Effect.die(/* @__PURE__ */ new Error("Agent executable requires spawn options.target set to a Chat DXN."));
|
|
207
209
|
const chat = yield* Database.resolve(chatDxn, Chat.Chat).pipe(Effect.orDie);
|
|
@@ -240,23 +242,32 @@ var AgentProcess = (options) => Operation.makeDurable({
|
|
|
240
242
|
yield* reconcileAlarmWith(startupPending);
|
|
241
243
|
const strategy = Option.fromNullishOr(options.delegationStrategy);
|
|
242
244
|
let delegations = [...yield* DelegationsCell.get];
|
|
245
|
+
let asyncHooks = [...yield* AsyncHooksCell.get];
|
|
246
|
+
const untrackedExits = /* @__PURE__ */ new Set();
|
|
243
247
|
const model = chat.session?.model ?? options.defaultModel;
|
|
244
248
|
const requestModelLayer = AiService.languageModel(DXN.getName(model ?? Model.claudeSonnet5.id), { provider: options.provider });
|
|
245
|
-
const
|
|
249
|
+
const childServices = yield* Effect.context();
|
|
246
250
|
const runEndRequestHooks = Effect.gen(function* () {
|
|
247
251
|
yield* SkillHooks.runHooks({
|
|
248
252
|
skills: session.getSkills(),
|
|
249
253
|
phase: "end-request",
|
|
250
|
-
invoke: (operation, input) => Effect.gen(function* () {
|
|
251
|
-
const
|
|
254
|
+
invoke: (operation, input, hook) => Effect.gen(function* () {
|
|
255
|
+
const handle = yield* Process.spawn(OperationProcess.make(operation), input, {
|
|
252
256
|
environment: { conversation: Obj.getURI(feed) },
|
|
253
257
|
traceMeta: { conversation: Ref.make(feed) }
|
|
254
|
-
})
|
|
255
|
-
if (
|
|
256
|
-
|
|
258
|
+
});
|
|
259
|
+
if (hook.async) {
|
|
260
|
+
if (untrackedExits.delete(handle.pid)) return;
|
|
261
|
+
asyncHooks.push(handle.pid);
|
|
262
|
+
yield* AsyncHooksCell.set(asyncHooks);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
yield* Process.awaitOutput(handle);
|
|
266
|
+
}).pipe(Effect.provide(childServices), Effect.asVoid, Effect.orDie)
|
|
257
267
|
});
|
|
258
268
|
});
|
|
259
269
|
let turnRan = false;
|
|
270
|
+
let endHooksFired = asyncHooks.length > 0;
|
|
260
271
|
const unseenWriteIds = /* @__PURE__ */ new Set();
|
|
261
272
|
let unseenWriteWakes = 0;
|
|
262
273
|
const pendingWork = (state) => isAgentWorkPending({
|
|
@@ -271,39 +282,60 @@ var AgentProcess = (options) => Operation.makeDurable({
|
|
|
271
282
|
for (const pid of dropReportedToolResults(toolResults, (pid) => toolCallManager.isReported(pid))) log("drop tool result reported within its turn", { pid }, {
|
|
272
283
|
"~LogMeta": "~LogMeta",
|
|
273
284
|
F: __dxlog_file$1,
|
|
274
|
-
L:
|
|
285
|
+
L: 400,
|
|
275
286
|
S: this
|
|
276
287
|
});
|
|
277
288
|
if (pendingWork(state)) {
|
|
278
289
|
yield* reportSleeping(state);
|
|
279
290
|
return;
|
|
280
291
|
}
|
|
281
|
-
if (!turnRan) {
|
|
292
|
+
if (!turnRan && !endHooksFired) {
|
|
282
293
|
log("agent idle before its first turn, staying resident", void 0, {
|
|
283
294
|
"~LogMeta": "~LogMeta",
|
|
284
295
|
F: __dxlog_file$1,
|
|
285
|
-
L:
|
|
296
|
+
L: 411,
|
|
286
297
|
S: this
|
|
287
298
|
});
|
|
288
299
|
return;
|
|
289
300
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
301
|
+
if (!endHooksFired) {
|
|
302
|
+
endHooksFired = true;
|
|
303
|
+
yield* runEndRequestHooks;
|
|
304
|
+
const after = yield* sessionStore.loadPending(feed);
|
|
305
|
+
if (pendingWork(after)) {
|
|
306
|
+
log("agent work enqueued by end-request hook, continuing", void 0, {
|
|
307
|
+
"~LogMeta": "~LogMeta",
|
|
308
|
+
F: __dxlog_file$1,
|
|
309
|
+
L: 422,
|
|
310
|
+
S: this
|
|
311
|
+
});
|
|
312
|
+
yield* reconcileAlarmWith(after);
|
|
313
|
+
yield* reportSleeping(after);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
if (asyncHooks.length > 0) {
|
|
318
|
+
log("awaiting background end-request hooks", { count: asyncHooks.length }, {
|
|
294
319
|
"~LogMeta": "~LogMeta",
|
|
295
320
|
F: __dxlog_file$1,
|
|
296
|
-
L:
|
|
321
|
+
L: 432,
|
|
322
|
+
S: this
|
|
323
|
+
});
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
if (options.resident) {
|
|
327
|
+
log("agent work complete, staying resident", void 0, {
|
|
328
|
+
"~LogMeta": "~LogMeta",
|
|
329
|
+
F: __dxlog_file$1,
|
|
330
|
+
L: 437,
|
|
297
331
|
S: this
|
|
298
332
|
});
|
|
299
|
-
yield* reconcileAlarmWith(after);
|
|
300
|
-
yield* reportSleeping(after);
|
|
301
333
|
return;
|
|
302
334
|
}
|
|
303
335
|
log("agent work complete, succeeding", void 0, {
|
|
304
336
|
"~LogMeta": "~LogMeta",
|
|
305
337
|
F: __dxlog_file$1,
|
|
306
|
-
L:
|
|
338
|
+
L: 441,
|
|
307
339
|
S: this
|
|
308
340
|
});
|
|
309
341
|
ctx.succeed();
|
|
@@ -315,7 +347,7 @@ var AgentProcess = (options) => Operation.makeDurable({
|
|
|
315
347
|
log.info("discarding queue entries left by a previous process", { count: startupPending.pendingMessages.length }, {
|
|
316
348
|
"~LogMeta": "~LogMeta",
|
|
317
349
|
F: __dxlog_file$1,
|
|
318
|
-
L:
|
|
350
|
+
L: 454,
|
|
319
351
|
S: this
|
|
320
352
|
});
|
|
321
353
|
yield* Effect.forEach(startupPending.pendingMessages, (message) => sessionStore.ack(feed, message), { discard: true });
|
|
@@ -347,7 +379,7 @@ var AgentProcess = (options) => Operation.makeDurable({
|
|
|
347
379
|
log("agent onInput received", { backlog: toolResults.length }, {
|
|
348
380
|
"~LogMeta": "~LogMeta",
|
|
349
381
|
F: __dxlog_file$1,
|
|
350
|
-
L:
|
|
382
|
+
L: 483,
|
|
351
383
|
S: this
|
|
352
384
|
});
|
|
353
385
|
const message = makeInputMessage(input);
|
|
@@ -358,7 +390,7 @@ var AgentProcess = (options) => Operation.makeDurable({
|
|
|
358
390
|
log("agent onInput enqueued to feed", void 0, {
|
|
359
391
|
"~LogMeta": "~LogMeta",
|
|
360
392
|
F: __dxlog_file$1,
|
|
361
|
-
L:
|
|
393
|
+
L: 489,
|
|
362
394
|
S: this
|
|
363
395
|
});
|
|
364
396
|
}),
|
|
@@ -366,15 +398,16 @@ var AgentProcess = (options) => Operation.makeDurable({
|
|
|
366
398
|
log("agent onAlarm fired", { backlog: toolResults.length }, {
|
|
367
399
|
"~LogMeta": "~LogMeta",
|
|
368
400
|
F: __dxlog_file$1,
|
|
369
|
-
L:
|
|
401
|
+
L: 493,
|
|
370
402
|
S: this
|
|
371
403
|
});
|
|
372
404
|
markWork("agent.wake");
|
|
373
|
-
|
|
405
|
+
const announced = toolResults.length > 0 || unseenWriteIds.size > 0;
|
|
406
|
+
if (announced) yield* Trace.emitRequestPhase("preparing");
|
|
374
407
|
for (const pid of dropReportedToolResults(toolResults, (pid) => toolCallManager.isReported(pid))) log.info("skip tool result that was reported synchronously", { pid }, {
|
|
375
408
|
"~LogMeta": "~LogMeta",
|
|
376
409
|
F: __dxlog_file$1,
|
|
377
|
-
L:
|
|
410
|
+
L: 506,
|
|
378
411
|
S: this
|
|
379
412
|
});
|
|
380
413
|
const toolResult = toolResults.shift();
|
|
@@ -384,7 +417,7 @@ var AgentProcess = (options) => Operation.makeDurable({
|
|
|
384
417
|
log("agent onAlarm handling", { tag: toolResult._tag }, {
|
|
385
418
|
"~LogMeta": "~LogMeta",
|
|
386
419
|
F: __dxlog_file$1,
|
|
387
|
-
L:
|
|
420
|
+
L: 514,
|
|
388
421
|
S: this
|
|
389
422
|
});
|
|
390
423
|
prompt = toolResultPrompt(toolResult);
|
|
@@ -411,7 +444,7 @@ var AgentProcess = (options) => Operation.makeDurable({
|
|
|
411
444
|
}, {
|
|
412
445
|
"~LogMeta": "~LogMeta",
|
|
413
446
|
F: __dxlog_file$1,
|
|
414
|
-
L:
|
|
447
|
+
L: 546,
|
|
415
448
|
S: this
|
|
416
449
|
});
|
|
417
450
|
unseenWriteIds.delete(message.id);
|
|
@@ -428,7 +461,7 @@ var AgentProcess = (options) => Operation.makeDurable({
|
|
|
428
461
|
log.warn("agent self-wake budget spent, dropping alarm", { wakes: selfWakes }, {
|
|
429
462
|
"~LogMeta": "~LogMeta",
|
|
430
463
|
F: __dxlog_file$1,
|
|
431
|
-
L:
|
|
464
|
+
L: 559,
|
|
432
465
|
S: this
|
|
433
466
|
});
|
|
434
467
|
yield* sessionStore.ack(feed, dueAlarm);
|
|
@@ -447,7 +480,7 @@ var AgentProcess = (options) => Operation.makeDurable({
|
|
|
447
480
|
}, {
|
|
448
481
|
"~LogMeta": "~LogMeta",
|
|
449
482
|
F: __dxlog_file$1,
|
|
450
|
-
L:
|
|
483
|
+
L: 570,
|
|
451
484
|
S: this
|
|
452
485
|
});
|
|
453
486
|
dequeued = dueAlarm;
|
|
@@ -467,7 +500,7 @@ var AgentProcess = (options) => Operation.makeDurable({
|
|
|
467
500
|
}, {
|
|
468
501
|
"~LogMeta": "~LogMeta",
|
|
469
502
|
F: __dxlog_file$1,
|
|
470
|
-
L:
|
|
503
|
+
L: 588,
|
|
471
504
|
S: this
|
|
472
505
|
});
|
|
473
506
|
yield* ctx.setAlarm(UNSEEN_WRITE_RETRY_MS);
|
|
@@ -479,7 +512,7 @@ var AgentProcess = (options) => Operation.makeDurable({
|
|
|
479
512
|
}, {
|
|
480
513
|
"~LogMeta": "~LogMeta",
|
|
481
514
|
F: __dxlog_file$1,
|
|
482
|
-
L:
|
|
515
|
+
L: 599,
|
|
483
516
|
S: this
|
|
484
517
|
});
|
|
485
518
|
yield* reconcileAlarmWith(state);
|
|
@@ -488,7 +521,7 @@ var AgentProcess = (options) => Operation.makeDurable({
|
|
|
488
521
|
log("agent onAlarm empty queue", {}, {
|
|
489
522
|
"~LogMeta": "~LogMeta",
|
|
490
523
|
F: __dxlog_file$1,
|
|
491
|
-
L:
|
|
524
|
+
L: 606,
|
|
492
525
|
S: this
|
|
493
526
|
});
|
|
494
527
|
yield* reconcileAlarmWith(state);
|
|
@@ -496,18 +529,19 @@ var AgentProcess = (options) => Operation.makeDurable({
|
|
|
496
529
|
return;
|
|
497
530
|
}
|
|
498
531
|
}
|
|
532
|
+
if (!announced) yield* Trace.emitRequestPhase("preparing");
|
|
499
533
|
const [mcpServers] = yield* Effect.all([loadSpaceMcpServers(), Effect.gen(function* () {
|
|
500
534
|
if (dequeued !== void 0) yield* sessionStore.markInFlight(feed, dequeued);
|
|
501
535
|
log("begin request", { prompt }, {
|
|
502
536
|
"~LogMeta": "~LogMeta",
|
|
503
537
|
F: __dxlog_file$1,
|
|
504
|
-
L:
|
|
538
|
+
L: 629,
|
|
505
539
|
S: this
|
|
506
540
|
});
|
|
507
541
|
log("trace agent request begin", void 0, {
|
|
508
542
|
"~LogMeta": "~LogMeta",
|
|
509
543
|
F: __dxlog_file$1,
|
|
510
|
-
L:
|
|
544
|
+
L: 630,
|
|
511
545
|
S: this
|
|
512
546
|
});
|
|
513
547
|
yield* Trace.write(Trace.AgentRequestBegin, {});
|
|
@@ -529,10 +563,11 @@ var AgentProcess = (options) => Operation.makeDurable({
|
|
|
529
563
|
log("end request", void 0, {
|
|
530
564
|
"~LogMeta": "~LogMeta",
|
|
531
565
|
F: __dxlog_file$1,
|
|
532
|
-
L:
|
|
566
|
+
L: 664,
|
|
533
567
|
S: this
|
|
534
568
|
});
|
|
535
569
|
turnRan = true;
|
|
570
|
+
endHooksFired = false;
|
|
536
571
|
yield* ToolResultsCell.set(toolResults);
|
|
537
572
|
if (dequeued !== void 0) {
|
|
538
573
|
yield* sessionStore.ack(feed, dequeued);
|
|
@@ -555,7 +590,7 @@ var AgentProcess = (options) => Operation.makeDurable({
|
|
|
555
590
|
}, {
|
|
556
591
|
"~LogMeta": "~LogMeta",
|
|
557
592
|
F: __dxlog_file$1,
|
|
558
|
-
L:
|
|
593
|
+
L: 686,
|
|
559
594
|
S: this
|
|
560
595
|
});
|
|
561
596
|
yield* Trace.write(Trace.DelegationSpawned, {
|
|
@@ -576,15 +611,29 @@ var AgentProcess = (options) => Operation.makeDurable({
|
|
|
576
611
|
log("childEvent", { event }, {
|
|
577
612
|
"~LogMeta": "~LogMeta",
|
|
578
613
|
F: __dxlog_file$1,
|
|
579
|
-
L:
|
|
614
|
+
L: 713,
|
|
580
615
|
S: this
|
|
581
616
|
});
|
|
617
|
+
if (event._tag === "exited" && asyncHooks.includes(event.pid)) {
|
|
618
|
+
asyncHooks = asyncHooks.filter((pid) => pid !== event.pid);
|
|
619
|
+
yield* AsyncHooksCell.set(asyncHooks);
|
|
620
|
+
const exit = yield* (yield* Process.ManagerService).attach(event.pid).pipe(Effect.flatMap(Process.awaitOutput), Effect.exit);
|
|
621
|
+
if (Exit.isFailure(exit)) log.warn("background end-request hook failed", { pid: event.pid }, {
|
|
622
|
+
"~LogMeta": "~LogMeta",
|
|
623
|
+
F: __dxlog_file$1,
|
|
624
|
+
L: 721,
|
|
625
|
+
S: this
|
|
626
|
+
});
|
|
627
|
+
yield* maybeComplete;
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
582
630
|
if (event._tag === "exited") {
|
|
583
631
|
const delegation = delegations.find((delegation) => delegation.pid === event.pid);
|
|
584
632
|
if (delegation) {
|
|
585
633
|
delegations = delegations.filter((other) => other.pid !== event.pid);
|
|
586
634
|
yield* DelegationsCell.set(delegations);
|
|
587
|
-
const
|
|
635
|
+
const child = yield* (yield* Process.ManagerService).attach(event.pid);
|
|
636
|
+
const exit = yield* Process.awaitOutput(child).pipe(Effect.exit);
|
|
588
637
|
const result = Exit.isSuccess(exit) ? resultPreview(exit.value) : void 0;
|
|
589
638
|
yield* Trace.write(Trace.DelegationCompleted, {
|
|
590
639
|
taskId: delegation.id,
|
|
@@ -608,7 +657,7 @@ var AgentProcess = (options) => Operation.makeDurable({
|
|
|
608
657
|
}, {
|
|
609
658
|
"~LogMeta": "~LogMeta",
|
|
610
659
|
F: __dxlog_file$1,
|
|
611
|
-
L:
|
|
660
|
+
L: 757,
|
|
612
661
|
S: this
|
|
613
662
|
});
|
|
614
663
|
yield* Trace.write(Trace.DelegationSpawned, {
|
|
@@ -625,25 +674,22 @@ var AgentProcess = (options) => Operation.makeDurable({
|
|
|
625
674
|
}, {
|
|
626
675
|
"~LogMeta": "~LogMeta",
|
|
627
676
|
F: __dxlog_file$1,
|
|
628
|
-
L:
|
|
677
|
+
L: 764,
|
|
629
678
|
S: this
|
|
630
679
|
});
|
|
631
680
|
yield* maybeComplete;
|
|
632
681
|
} else if (toolCallManager.isToolCall(event.pid)) {
|
|
633
|
-
const attachExit = yield* (yield*
|
|
634
|
-
if (Exit.isFailure(attachExit)) {
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
return;
|
|
643
|
-
}
|
|
644
|
-
return yield* Effect.failCause(attachExit.cause).pipe(Effect.orDie);
|
|
682
|
+
const attachExit = yield* (yield* Process.ManagerService).attach(event.pid).pipe(Effect.exit);
|
|
683
|
+
if (Exit.isFailure(attachExit) && isToolResultHandled(toolResults, event.pid, (pid) => toolCallManager.isReported(pid))) {
|
|
684
|
+
log.verbose("childEvent skipped (process gone, result already handled)", { pid: event.pid }, {
|
|
685
|
+
"~LogMeta": "~LogMeta",
|
|
686
|
+
F: __dxlog_file$1,
|
|
687
|
+
L: 775,
|
|
688
|
+
S: this
|
|
689
|
+
});
|
|
690
|
+
return;
|
|
645
691
|
}
|
|
646
|
-
const result = yield* attachExit.
|
|
692
|
+
const result = yield* (Exit.isFailure(attachExit) ? Effect.failCause(attachExit.cause) : Process.awaitOutput(attachExit.value)).pipe(Effect.exit, Effect.map(Exit.match({
|
|
647
693
|
onSuccess: (value) => ({
|
|
648
694
|
_tag: "tool_result",
|
|
649
695
|
pid: event.pid,
|
|
@@ -664,7 +710,7 @@ var AgentProcess = (options) => Operation.makeDurable({
|
|
|
664
710
|
}, {
|
|
665
711
|
"~LogMeta": "~LogMeta",
|
|
666
712
|
F: __dxlog_file$1,
|
|
667
|
-
L:
|
|
713
|
+
L: 803,
|
|
668
714
|
S: this
|
|
669
715
|
});
|
|
670
716
|
yield* ToolResultsCell.set(toolResults);
|
|
@@ -672,19 +718,22 @@ var AgentProcess = (options) => Operation.makeDurable({
|
|
|
672
718
|
log("agent onChildEvent alarm scheduled", { depth: toolResults.length }, {
|
|
673
719
|
"~LogMeta": "~LogMeta",
|
|
674
720
|
F: __dxlog_file$1,
|
|
675
|
-
L:
|
|
721
|
+
L: 806,
|
|
676
722
|
S: this
|
|
677
723
|
});
|
|
678
|
-
} else
|
|
679
|
-
"
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
724
|
+
} else {
|
|
725
|
+
log.verbose("childEvent ignored non-tool call and not a delegation", { pid: event.pid }, {
|
|
726
|
+
"~LogMeta": "~LogMeta",
|
|
727
|
+
F: __dxlog_file$1,
|
|
728
|
+
L: 808,
|
|
729
|
+
S: this
|
|
730
|
+
});
|
|
731
|
+
untrackedExits.add(event.pid);
|
|
732
|
+
}
|
|
684
733
|
}
|
|
685
734
|
})
|
|
686
735
|
};
|
|
687
|
-
}));
|
|
736
|
+
})));
|
|
688
737
|
var ToolResultEvent = Schema.TaggedStruct("tool_result", {
|
|
689
738
|
pid: Process.ID,
|
|
690
739
|
result: Schema.Unknown,
|
|
@@ -700,6 +749,8 @@ var Delegation = Schema.Struct({
|
|
|
700
749
|
id: Schema.String
|
|
701
750
|
}).mapFields(Struct.map(Schema.mutableKey));
|
|
702
751
|
var DelegationsCell = StorageService.cell(Schema.fromJsonString(Schema.Array(Delegation).pipe(Schema.mutable)), "delegations").pipe(StorageService.withDefault(() => []));
|
|
752
|
+
/** Pids of background ({@link Skill.Hook.async}) end-request hooks the process is waiting on. */
|
|
753
|
+
var AsyncHooksCell = StorageService.cell(Schema.fromJsonString(Schema.Array(Process.ID).pipe(Schema.mutable)), "asyncHooks").pipe(StorageService.withDefault(() => []));
|
|
703
754
|
/**
|
|
704
755
|
* Ids of queue entries this process has ACKED, held until the ack is visible in the feed read.
|
|
705
756
|
*
|
|
@@ -777,7 +828,7 @@ var ToolCallManager = class {
|
|
|
777
828
|
log("reconcile queued tool result", { pid: item.pid }, {
|
|
778
829
|
"~LogMeta": "~LogMeta",
|
|
779
830
|
F: __dxlog_file$1,
|
|
780
|
-
L:
|
|
831
|
+
L: 971,
|
|
781
832
|
S: this
|
|
782
833
|
});
|
|
783
834
|
}
|
|
@@ -789,6 +840,11 @@ var ToolCallManager = class {
|
|
|
789
840
|
/** True while the agent still has queued work, a pending alarm, subprocesses, or undelivered tool results. */
|
|
790
841
|
var isAgentWorkPending = ({ toolResults, pendingMessages, pendingAlarms, delegations, toolCallManager }) => toolResults.length > 0 || pendingMessages.length > 0 || pendingAlarms.length > 0 || delegations.length > 0 || toolCallManager.hasPendingToolResults();
|
|
791
842
|
/**
|
|
843
|
+
* Whether a tool call's result is already accounted for — queued for the next turn or delivered to the
|
|
844
|
+
* agent — so a finished child that can no longer be reattached needs no answer of its own.
|
|
845
|
+
*/
|
|
846
|
+
var isToolResultHandled = (queue, pid, isReported) => queue.some((item) => item.pid === pid) || isReported(pid);
|
|
847
|
+
/**
|
|
792
848
|
* Discards tool results at the head of the queue whose values already reached the agent.
|
|
793
849
|
*
|
|
794
850
|
* A tool that returned inside its turn is reported synchronously AND left queued; after a reload the
|
|
@@ -838,7 +894,7 @@ var wakeUpPrompt = (firedAt, message, budget) => {
|
|
|
838
894
|
].filter((line) => line != null).join("\n");
|
|
839
895
|
};
|
|
840
896
|
var ToolExecutionService = ({ enableBackgrounding, backgroundThreshold = Duration.seconds(1), toolCallManager, feed }) => Layer.unwrap(Effect.gen(function* () {
|
|
841
|
-
const
|
|
897
|
+
const childServices = yield* Effect.context();
|
|
842
898
|
return makeToolExecutionService({ invoke: (tool, input) => Effect.gen(function* () {
|
|
843
899
|
const operationDef = getOperationFromTool(tool).pipe(Option.getOrThrow);
|
|
844
900
|
log("invoking operation", {
|
|
@@ -847,37 +903,37 @@ var ToolExecutionService = ({ enableBackgrounding, backgroundThreshold = Duratio
|
|
|
847
903
|
}, {
|
|
848
904
|
"~LogMeta": "~LogMeta",
|
|
849
905
|
F: __dxlog_file$1,
|
|
850
|
-
L:
|
|
906
|
+
L: 1117,
|
|
851
907
|
S: this
|
|
852
908
|
});
|
|
853
|
-
const
|
|
909
|
+
const handle = yield* Process.spawn(OperationProcess.make(operationDef), input, {
|
|
854
910
|
environment: { conversation: Obj.getURI(feed) },
|
|
855
911
|
traceMeta: { conversation: Ref.make(feed) }
|
|
856
912
|
});
|
|
857
913
|
markWork("tool.spawned");
|
|
858
|
-
yield* toolCallManager.beginCall(
|
|
914
|
+
yield* toolCallManager.beginCall(handle.pid);
|
|
859
915
|
markWork("tool.call-recorded");
|
|
860
916
|
log("invoked operation", {
|
|
861
917
|
operationDef,
|
|
862
918
|
input,
|
|
863
|
-
|
|
919
|
+
pid: handle.pid
|
|
864
920
|
}, {
|
|
865
921
|
"~LogMeta": "~LogMeta",
|
|
866
922
|
F: __dxlog_file$1,
|
|
867
|
-
L:
|
|
923
|
+
L: 1129,
|
|
868
924
|
S: this
|
|
869
925
|
});
|
|
870
|
-
const awaitWithReport =
|
|
871
|
-
const result = enableBackgrounding ? yield* awaitWithReport.pipe(Effect.timeout(backgroundThreshold), Effect.catchTag("TimeoutError", () => Effect.succeed(Exit.succeed(toolIsRunningInBackgroundResponse(
|
|
926
|
+
const awaitWithReport = Process.awaitOutput(handle).pipe(Effect.exit, Effect.tap(() => toolCallManager.markAsReported(handle.pid)));
|
|
927
|
+
const result = enableBackgrounding ? yield* awaitWithReport.pipe(Effect.timeout(backgroundThreshold), Effect.catchTag("TimeoutError", () => Effect.succeed(Exit.succeed(toolIsRunningInBackgroundResponse(handle.pid))))) : yield* awaitWithReport;
|
|
872
928
|
markWork("tool.settled");
|
|
873
929
|
log("result", { result }, {
|
|
874
930
|
"~LogMeta": "~LogMeta",
|
|
875
931
|
F: __dxlog_file$1,
|
|
876
|
-
L:
|
|
932
|
+
L: 1144,
|
|
877
933
|
S: this
|
|
878
934
|
});
|
|
879
935
|
return yield* result;
|
|
880
|
-
}) });
|
|
936
|
+
}).pipe(Effect.provide(childServices)) });
|
|
881
937
|
}));
|
|
882
938
|
var AsynchronousExectionToolkit = class extends Toolkit.make(Tool.make("poll-tools", {
|
|
883
939
|
description: trim`
|
|
@@ -900,12 +956,16 @@ var AsynchronousExectionToolkit = class extends Toolkit.make(Tool.make("poll-too
|
|
|
900
956
|
})
|
|
901
957
|
})) {};
|
|
902
958
|
var AsynchronousExectionToolkitLayer = AsynchronousExectionToolkit.toLayer(Effect.gen(function* () {
|
|
903
|
-
const
|
|
959
|
+
const manager = yield* Process.ManagerService;
|
|
904
960
|
return { "poll-tools": ({ ids, wait, timeout = 1e4 }) => Effect.gen(function* () {
|
|
905
|
-
return yield* Effect.forEach(ids, (pid) =>
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
961
|
+
return yield* Effect.forEach(ids, (pid) => Effect.gen(function* () {
|
|
962
|
+
const attached = yield* manager.attach(Process.ID.make(pid)).pipe(Effect.exit);
|
|
963
|
+
if (Exit.isFailure(attached)) return `Process not found: ${pid}`;
|
|
964
|
+
return yield* Process.awaitOutput(attached.value).pipe(Effect.exit, Effect.timeout(Duration.millis(timeout)), Effect.map(Exit.match({
|
|
965
|
+
onSuccess: (value) => toolResultResponse(pid, value),
|
|
966
|
+
onFailure: (cause) => toolErrorResponse(pid, Cause.pretty(cause))
|
|
967
|
+
})), Effect.catchTag("TimeoutError", () => Effect.succeed(`Process still running: ${pid}`)));
|
|
968
|
+
}));
|
|
909
969
|
}) };
|
|
910
970
|
}));
|
|
911
971
|
/**
|
|
@@ -981,17 +1041,39 @@ var layer = (opts) => Layer.effect(AgentService.AgentService, Effect.gen(functio
|
|
|
981
1041
|
defaultModel: opts?.defaultModel,
|
|
982
1042
|
provider: provider ?? opts?.provider,
|
|
983
1043
|
enableToolBackgrounding: opts?.enableToolBackgrounding,
|
|
984
|
-
delegationStrategy: opts?.delegationStrategy
|
|
1044
|
+
delegationStrategy: opts?.delegationStrategy,
|
|
1045
|
+
resident: opts?.resident
|
|
985
1046
|
});
|
|
1047
|
+
/**
|
|
1048
|
+
* The process a chat runs on: the one its session names, or ours. A name nothing contributed
|
|
1049
|
+
* (its plugin was removed) falls back to ours rather than leaving the chat unable to run.
|
|
1050
|
+
*/
|
|
1051
|
+
const executableFor = (chat, provider) => {
|
|
1052
|
+
const key = chat.session?.process;
|
|
1053
|
+
if (key === void 0 || key === "org.dxos.testing.process.agent") return makeExecutable(provider);
|
|
1054
|
+
const definition = opts?.processes?.().find((candidate) => candidate.key === key);
|
|
1055
|
+
if (!definition) {
|
|
1056
|
+
log.warn("agent process not contributed; running the default", { key }, {
|
|
1057
|
+
"~LogMeta": "~LogMeta",
|
|
1058
|
+
F: __dxlog_file,
|
|
1059
|
+
L: 201,
|
|
1060
|
+
S: this
|
|
1061
|
+
});
|
|
1062
|
+
return makeExecutable(provider);
|
|
1063
|
+
}
|
|
1064
|
+
return definition;
|
|
1065
|
+
};
|
|
986
1066
|
const service = {
|
|
987
1067
|
getSession: (chat, options) => Effect.suspend(() => lockFor(chat.id).withPermits(1)(Effect.gen(function* () {
|
|
988
1068
|
const provider = options?.provider ?? opts?.provider;
|
|
989
1069
|
const model = chat.session?.model;
|
|
990
1070
|
const instructions = chat.instructions?.uri;
|
|
991
1071
|
const location = options?.location ?? "local";
|
|
1072
|
+
const executable = executableFor(chat, provider);
|
|
1073
|
+
if (location === "edge" && executable.key !== "org.dxos.testing.process.agent") return yield* Effect.die(/* @__PURE__ */ new Error(`Agent process ${executable.key} runs only locally, but the chat asked for edge.`));
|
|
992
1074
|
const cached = sessionCache.get(chat.id);
|
|
993
1075
|
if (cached) {
|
|
994
|
-
if (cached.model === model && cached.provider === provider && cached.instructions === instructions && cached.location === location && !Process.isTerminal(cached.handle.status.state)) return cached.session;
|
|
1076
|
+
if (cached.model === model && cached.provider === provider && cached.instructions === instructions && cached.location === location && cached.process === executable.key && !Process.isTerminal(cached.handle.status.state)) return cached.session;
|
|
995
1077
|
if (!Process.isTerminal(cached.handle.status.state)) yield* cached.handle.terminate();
|
|
996
1078
|
sessionCache.delete(chat.id);
|
|
997
1079
|
}
|
|
@@ -1000,7 +1082,6 @@ var layer = (opts) => Layer.effect(AgentService.AgentService, Effect.gen(functio
|
|
|
1000
1082
|
const parsedEchoUri = EID.tryParse(target);
|
|
1001
1083
|
const spaceId = parsedEchoUri ? EID.getSpaceId(parsedEchoUri) : void 0;
|
|
1002
1084
|
const processLocation = locationFor(options?.location, spaceId);
|
|
1003
|
-
const executable = makeExecutable(provider);
|
|
1004
1085
|
let activeProcess = (yield* processManager.handles({
|
|
1005
1086
|
target,
|
|
1006
1087
|
key: executable.key,
|
|
@@ -1040,6 +1121,7 @@ var layer = (opts) => Layer.effect(AgentService.AgentService, Effect.gen(functio
|
|
|
1040
1121
|
provider,
|
|
1041
1122
|
instructions,
|
|
1042
1123
|
location,
|
|
1124
|
+
process: executable.key,
|
|
1043
1125
|
handle,
|
|
1044
1126
|
session
|
|
1045
1127
|
});
|
|
@@ -1047,27 +1129,41 @@ var layer = (opts) => Layer.effect(AgentService.AgentService, Effect.gen(functio
|
|
|
1047
1129
|
}))),
|
|
1048
1130
|
hydrate: Effect.fnUntraced(function* () {
|
|
1049
1131
|
sessionCache.clear();
|
|
1132
|
+
const contributed = (opts?.processes?.() ?? []).filter(({ key }) => key !== AGENT_PROCESS_KEY);
|
|
1050
1133
|
const executable = makeExecutable();
|
|
1051
|
-
const agents = [
|
|
1052
|
-
key: AGENT_PROCESS_KEY
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1134
|
+
const agents = [
|
|
1135
|
+
...(yield* processManager.handles({ key: AGENT_PROCESS_KEY })).map((agent) => ({
|
|
1136
|
+
agent,
|
|
1137
|
+
executable
|
|
1138
|
+
})),
|
|
1139
|
+
...(yield* Effect.forEach(contributed, (definition) => processManager.handles({ key: definition.key }).pipe(Effect.map((agents) => agents.map((agent) => ({
|
|
1140
|
+
agent,
|
|
1141
|
+
executable: definition
|
|
1142
|
+
})))))).flat(),
|
|
1143
|
+
...(yield* Effect.forEach([...remoteSpaces], (space) => processManager.handles({
|
|
1144
|
+
key: AGENT_PROCESS_KEY,
|
|
1145
|
+
location: {
|
|
1146
|
+
kind: "edge",
|
|
1147
|
+
space
|
|
1148
|
+
}
|
|
1149
|
+
}))).flat().map((agent) => ({
|
|
1150
|
+
agent,
|
|
1151
|
+
executable
|
|
1152
|
+
}))
|
|
1153
|
+
];
|
|
1058
1154
|
log("agent hydrate", { count: agents.length }, {
|
|
1059
1155
|
"~LogMeta": "~LogMeta",
|
|
1060
1156
|
F: __dxlog_file,
|
|
1061
|
-
L:
|
|
1157
|
+
L: 231,
|
|
1062
1158
|
S: this
|
|
1063
1159
|
});
|
|
1064
|
-
for (const agent of agents) yield* agent.hydrate(executable).pipe(Effect.catchCause((cause) => Effect.sync(() => log.warn("agent hydrate skipped", {
|
|
1160
|
+
for (const { agent, executable } of agents) yield* agent.hydrate(executable).pipe(Effect.catchCause((cause) => Effect.sync(() => log.warn("agent hydrate skipped", {
|
|
1065
1161
|
pid: agent.pid,
|
|
1066
1162
|
cause: Cause.pretty(cause)
|
|
1067
1163
|
}, {
|
|
1068
1164
|
"~LogMeta": "~LogMeta",
|
|
1069
1165
|
F: __dxlog_file,
|
|
1070
|
-
L:
|
|
1166
|
+
L: 237,
|
|
1071
1167
|
S: this
|
|
1072
1168
|
}))));
|
|
1073
1169
|
})
|
|
@@ -1102,7 +1198,7 @@ var makeSession = (process, chat, feed, releaseSession, isFinished, resubmit) =>
|
|
|
1102
1198
|
running: Atom.make((get) => get(process.statusAtom).state === Process.State.RUNNING || get(process.statusAtom).state === Process.State.HYBERNATING),
|
|
1103
1199
|
waitForCompletion: () => process.runUntilSettled(),
|
|
1104
1200
|
terminate: () => process.terminate().pipe(Effect.tap(() => Effect.sync(releaseSession))),
|
|
1105
|
-
subscribeEphemeral: () => process.subscribeEphemeral()
|
|
1201
|
+
subscribeEphemeral: (options) => process.subscribeEphemeral(options)
|
|
1106
1202
|
});
|
|
1107
1203
|
//#endregion
|
|
1108
1204
|
export { AgentProcess as a, __exportAll as c, AgentInput as i, layer as n, makeInputMessage as o, AGENT_PROCESS_KEY as r, makeAiSessionTurnProducer as s, AgentService_exports as t };
|