llmist 3.0.0 → 3.1.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/{chunk-67MMSOAT.js → chunk-JCFPJMRQ.js} +540 -51
- package/dist/chunk-JCFPJMRQ.js.map +1 -0
- package/dist/{chunk-NBPKLSXJ.js → chunk-LFI4WQVV.js} +2 -2
- package/dist/cli.cjs +741 -64
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +203 -15
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +539 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +42 -5
- package/dist/index.d.ts +42 -5
- package/dist/index.js +2 -2
- package/dist/{mock-stream-COHw8h9b.d.cts → mock-stream-CTLm00_q.d.cts} +163 -3
- package/dist/{mock-stream-COHw8h9b.d.ts → mock-stream-CTLm00_q.d.ts} +163 -3
- package/dist/testing/index.cjs +539 -50
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.d.cts +2 -2
- package/dist/testing/index.d.ts +2 -2
- package/dist/testing/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-67MMSOAT.js.map +0 -1
- /package/dist/{chunk-NBPKLSXJ.js.map → chunk-LFI4WQVV.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -3562,7 +3562,7 @@ var init_executor = __esm({
|
|
|
3562
3562
|
init_exceptions();
|
|
3563
3563
|
init_parser();
|
|
3564
3564
|
GadgetExecutor = class {
|
|
3565
|
-
constructor(registry, requestHumanInput, logger, defaultGadgetTimeoutMs, errorFormatterOptions, client, mediaStore, agentConfig, subagentConfig) {
|
|
3565
|
+
constructor(registry, requestHumanInput, logger, defaultGadgetTimeoutMs, errorFormatterOptions, client, mediaStore, agentConfig, subagentConfig, onNestedEvent) {
|
|
3566
3566
|
this.registry = registry;
|
|
3567
3567
|
this.requestHumanInput = requestHumanInput;
|
|
3568
3568
|
this.defaultGadgetTimeoutMs = defaultGadgetTimeoutMs;
|
|
@@ -3570,6 +3570,7 @@ var init_executor = __esm({
|
|
|
3570
3570
|
this.mediaStore = mediaStore;
|
|
3571
3571
|
this.agentConfig = agentConfig;
|
|
3572
3572
|
this.subagentConfig = subagentConfig;
|
|
3573
|
+
this.onNestedEvent = onNestedEvent;
|
|
3573
3574
|
this.logger = logger ?? createLogger({ name: "llmist:executor" });
|
|
3574
3575
|
this.errorFormatter = new GadgetExecutionErrorFormatter(errorFormatterOptions);
|
|
3575
3576
|
this.argPrefix = errorFormatterOptions?.argPrefix ?? GADGET_ARG_PREFIX;
|
|
@@ -3715,7 +3716,9 @@ var init_executor = __esm({
|
|
|
3715
3716
|
llmist: this.client ? new CostReportingLLMistWrapper(this.client, reportCost) : void 0,
|
|
3716
3717
|
signal: abortController.signal,
|
|
3717
3718
|
agentConfig: this.agentConfig,
|
|
3718
|
-
subagentConfig: this.subagentConfig
|
|
3719
|
+
subagentConfig: this.subagentConfig,
|
|
3720
|
+
invocationId: call.invocationId,
|
|
3721
|
+
onNestedEvent: this.onNestedEvent
|
|
3719
3722
|
};
|
|
3720
3723
|
let rawResult;
|
|
3721
3724
|
if (timeoutMs && timeoutMs > 0) {
|
|
@@ -3954,14 +3957,21 @@ var init_stream_processor = __esm({
|
|
|
3954
3957
|
options.client,
|
|
3955
3958
|
options.mediaStore,
|
|
3956
3959
|
options.agentConfig,
|
|
3957
|
-
options.subagentConfig
|
|
3960
|
+
options.subagentConfig,
|
|
3961
|
+
options.onNestedEvent
|
|
3958
3962
|
);
|
|
3959
3963
|
}
|
|
3960
3964
|
/**
|
|
3961
|
-
* Process an LLM stream and
|
|
3965
|
+
* Process an LLM stream and yield events in real-time.
|
|
3966
|
+
*
|
|
3967
|
+
* This is an async generator that yields events immediately as they occur:
|
|
3968
|
+
* - Text events are yielded as text is streamed from the LLM
|
|
3969
|
+
* - gadget_call events are yielded immediately when a gadget call is parsed
|
|
3970
|
+
* - gadget_result events are yielded when gadget execution completes
|
|
3971
|
+
*
|
|
3972
|
+
* The final event is always a StreamCompletionEvent containing metadata.
|
|
3962
3973
|
*/
|
|
3963
|
-
async process(stream2) {
|
|
3964
|
-
const outputs = [];
|
|
3974
|
+
async *process(stream2) {
|
|
3965
3975
|
let finishReason = null;
|
|
3966
3976
|
let usage;
|
|
3967
3977
|
let didExecuteGadgets = false;
|
|
@@ -4007,14 +4017,13 @@ var init_stream_processor = __esm({
|
|
|
4007
4017
|
continue;
|
|
4008
4018
|
}
|
|
4009
4019
|
for (const event of this.parser.feed(processedChunk)) {
|
|
4010
|
-
const
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
shouldBreakLoop = true;
|
|
4020
|
+
for await (const processedEvent of this.processEventGenerator(event)) {
|
|
4021
|
+
yield processedEvent;
|
|
4022
|
+
if (processedEvent.type === "gadget_result") {
|
|
4023
|
+
didExecuteGadgets = true;
|
|
4024
|
+
if (processedEvent.result.breaksLoop) {
|
|
4025
|
+
shouldBreakLoop = true;
|
|
4026
|
+
}
|
|
4018
4027
|
}
|
|
4019
4028
|
}
|
|
4020
4029
|
}
|
|
@@ -4025,25 +4034,23 @@ var init_stream_processor = __esm({
|
|
|
4025
4034
|
}
|
|
4026
4035
|
if (!this.executionHalted) {
|
|
4027
4036
|
for (const event of this.parser.finalize()) {
|
|
4028
|
-
const
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
shouldBreakLoop = true;
|
|
4037
|
+
for await (const processedEvent of this.processEventGenerator(event)) {
|
|
4038
|
+
yield processedEvent;
|
|
4039
|
+
if (processedEvent.type === "gadget_result") {
|
|
4040
|
+
didExecuteGadgets = true;
|
|
4041
|
+
if (processedEvent.result.breaksLoop) {
|
|
4042
|
+
shouldBreakLoop = true;
|
|
4043
|
+
}
|
|
4036
4044
|
}
|
|
4037
4045
|
}
|
|
4038
4046
|
}
|
|
4039
|
-
const
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
shouldBreakLoop = true;
|
|
4047
|
+
for await (const evt of this.processPendingGadgetsGenerator()) {
|
|
4048
|
+
yield evt;
|
|
4049
|
+
if (evt.type === "gadget_result") {
|
|
4050
|
+
didExecuteGadgets = true;
|
|
4051
|
+
if (evt.result.breaksLoop) {
|
|
4052
|
+
shouldBreakLoop = true;
|
|
4053
|
+
}
|
|
4047
4054
|
}
|
|
4048
4055
|
}
|
|
4049
4056
|
}
|
|
@@ -4056,8 +4063,8 @@ var init_stream_processor = __esm({
|
|
|
4056
4063
|
};
|
|
4057
4064
|
finalMessage = this.hooks.interceptors.interceptAssistantMessage(finalMessage, context);
|
|
4058
4065
|
}
|
|
4059
|
-
|
|
4060
|
-
|
|
4066
|
+
const completionEvent = {
|
|
4067
|
+
type: "stream_complete",
|
|
4061
4068
|
shouldBreakLoop,
|
|
4062
4069
|
didExecuteGadgets,
|
|
4063
4070
|
finishReason,
|
|
@@ -4065,9 +4072,11 @@ var init_stream_processor = __esm({
|
|
|
4065
4072
|
rawResponse: this.responseText,
|
|
4066
4073
|
finalMessage
|
|
4067
4074
|
};
|
|
4075
|
+
yield completionEvent;
|
|
4068
4076
|
}
|
|
4069
4077
|
/**
|
|
4070
4078
|
* Process a single parsed event (text or gadget call).
|
|
4079
|
+
* @deprecated Use processEventGenerator for real-time streaming
|
|
4071
4080
|
*/
|
|
4072
4081
|
async processEvent(event) {
|
|
4073
4082
|
if (event.type === "text") {
|
|
@@ -4077,6 +4086,23 @@ var init_stream_processor = __esm({
|
|
|
4077
4086
|
}
|
|
4078
4087
|
return [event];
|
|
4079
4088
|
}
|
|
4089
|
+
/**
|
|
4090
|
+
* Process a single parsed event, yielding events in real-time.
|
|
4091
|
+
* Generator version of processEvent for streaming support.
|
|
4092
|
+
*/
|
|
4093
|
+
async *processEventGenerator(event) {
|
|
4094
|
+
if (event.type === "text") {
|
|
4095
|
+
for (const e of await this.processTextEvent(event)) {
|
|
4096
|
+
yield e;
|
|
4097
|
+
}
|
|
4098
|
+
} else if (event.type === "gadget_call") {
|
|
4099
|
+
for await (const e of this.processGadgetCallGenerator(event.call)) {
|
|
4100
|
+
yield e;
|
|
4101
|
+
}
|
|
4102
|
+
} else {
|
|
4103
|
+
yield event;
|
|
4104
|
+
}
|
|
4105
|
+
}
|
|
4080
4106
|
/**
|
|
4081
4107
|
* Process a text event through interceptors.
|
|
4082
4108
|
*/
|
|
@@ -4153,9 +4179,68 @@ var init_stream_processor = __esm({
|
|
|
4153
4179
|
events.push(...triggeredEvents);
|
|
4154
4180
|
return events;
|
|
4155
4181
|
}
|
|
4182
|
+
/**
|
|
4183
|
+
* Process a gadget call, yielding events in real-time.
|
|
4184
|
+
*
|
|
4185
|
+
* Key difference from processGadgetCall: yields gadget_call event IMMEDIATELY
|
|
4186
|
+
* when parsed (before execution), enabling real-time UI feedback.
|
|
4187
|
+
*/
|
|
4188
|
+
async *processGadgetCallGenerator(call) {
|
|
4189
|
+
if (this.executionHalted) {
|
|
4190
|
+
this.logger.debug("Skipping gadget execution due to previous error", {
|
|
4191
|
+
gadgetName: call.gadgetName
|
|
4192
|
+
});
|
|
4193
|
+
return;
|
|
4194
|
+
}
|
|
4195
|
+
yield { type: "gadget_call", call };
|
|
4196
|
+
if (call.dependencies.length > 0) {
|
|
4197
|
+
if (call.dependencies.includes(call.invocationId)) {
|
|
4198
|
+
this.logger.warn("Gadget has self-referential dependency (depends on itself)", {
|
|
4199
|
+
gadgetName: call.gadgetName,
|
|
4200
|
+
invocationId: call.invocationId
|
|
4201
|
+
});
|
|
4202
|
+
this.failedInvocations.add(call.invocationId);
|
|
4203
|
+
const skipEvent = {
|
|
4204
|
+
type: "gadget_skipped",
|
|
4205
|
+
gadgetName: call.gadgetName,
|
|
4206
|
+
invocationId: call.invocationId,
|
|
4207
|
+
parameters: call.parameters ?? {},
|
|
4208
|
+
failedDependency: call.invocationId,
|
|
4209
|
+
failedDependencyError: `Gadget "${call.invocationId}" cannot depend on itself (self-referential dependency)`
|
|
4210
|
+
};
|
|
4211
|
+
yield skipEvent;
|
|
4212
|
+
return;
|
|
4213
|
+
}
|
|
4214
|
+
const failedDep = call.dependencies.find((dep) => this.failedInvocations.has(dep));
|
|
4215
|
+
if (failedDep) {
|
|
4216
|
+
const skipEvents = await this.handleFailedDependency(call, failedDep);
|
|
4217
|
+
for (const evt of skipEvents) {
|
|
4218
|
+
yield evt;
|
|
4219
|
+
}
|
|
4220
|
+
return;
|
|
4221
|
+
}
|
|
4222
|
+
const unsatisfied = call.dependencies.filter((dep) => !this.completedResults.has(dep));
|
|
4223
|
+
if (unsatisfied.length > 0) {
|
|
4224
|
+
this.logger.debug("Queueing gadget for later - waiting on dependencies", {
|
|
4225
|
+
gadgetName: call.gadgetName,
|
|
4226
|
+
invocationId: call.invocationId,
|
|
4227
|
+
waitingOn: unsatisfied
|
|
4228
|
+
});
|
|
4229
|
+
this.gadgetsAwaitingDependencies.set(call.invocationId, call);
|
|
4230
|
+
return;
|
|
4231
|
+
}
|
|
4232
|
+
}
|
|
4233
|
+
for await (const evt of this.executeGadgetGenerator(call)) {
|
|
4234
|
+
yield evt;
|
|
4235
|
+
}
|
|
4236
|
+
for await (const evt of this.processPendingGadgetsGenerator()) {
|
|
4237
|
+
yield evt;
|
|
4238
|
+
}
|
|
4239
|
+
}
|
|
4156
4240
|
/**
|
|
4157
4241
|
* Execute a gadget through the full hook lifecycle.
|
|
4158
4242
|
* This is the core execution logic, extracted from processGadgetCall.
|
|
4243
|
+
* @deprecated Use executeGadgetGenerator for real-time streaming
|
|
4159
4244
|
*/
|
|
4160
4245
|
async executeGadgetWithHooks(call) {
|
|
4161
4246
|
const events = [];
|
|
@@ -4308,6 +4393,159 @@ var init_stream_processor = __esm({
|
|
|
4308
4393
|
}
|
|
4309
4394
|
return events;
|
|
4310
4395
|
}
|
|
4396
|
+
/**
|
|
4397
|
+
* Execute a gadget and yield the result event.
|
|
4398
|
+
* Generator version that yields gadget_result immediately when execution completes.
|
|
4399
|
+
*/
|
|
4400
|
+
async *executeGadgetGenerator(call) {
|
|
4401
|
+
if (call.parseError) {
|
|
4402
|
+
this.logger.warn("Gadget has parse error", {
|
|
4403
|
+
gadgetName: call.gadgetName,
|
|
4404
|
+
error: call.parseError,
|
|
4405
|
+
rawParameters: call.parametersRaw
|
|
4406
|
+
});
|
|
4407
|
+
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4408
|
+
call.parseError,
|
|
4409
|
+
call.gadgetName,
|
|
4410
|
+
"parse",
|
|
4411
|
+
call.parameters
|
|
4412
|
+
);
|
|
4413
|
+
if (!shouldContinue) {
|
|
4414
|
+
this.executionHalted = true;
|
|
4415
|
+
}
|
|
4416
|
+
}
|
|
4417
|
+
let parameters = call.parameters ?? {};
|
|
4418
|
+
if (this.hooks.interceptors?.interceptGadgetParameters) {
|
|
4419
|
+
const context = {
|
|
4420
|
+
iteration: this.iteration,
|
|
4421
|
+
gadgetName: call.gadgetName,
|
|
4422
|
+
invocationId: call.invocationId,
|
|
4423
|
+
logger: this.logger
|
|
4424
|
+
};
|
|
4425
|
+
parameters = this.hooks.interceptors.interceptGadgetParameters(parameters, context);
|
|
4426
|
+
}
|
|
4427
|
+
call.parameters = parameters;
|
|
4428
|
+
let shouldSkip = false;
|
|
4429
|
+
let syntheticResult;
|
|
4430
|
+
if (this.hooks.controllers?.beforeGadgetExecution) {
|
|
4431
|
+
const context = {
|
|
4432
|
+
iteration: this.iteration,
|
|
4433
|
+
gadgetName: call.gadgetName,
|
|
4434
|
+
invocationId: call.invocationId,
|
|
4435
|
+
parameters,
|
|
4436
|
+
logger: this.logger
|
|
4437
|
+
};
|
|
4438
|
+
const action = await this.hooks.controllers.beforeGadgetExecution(context);
|
|
4439
|
+
validateBeforeGadgetExecutionAction(action);
|
|
4440
|
+
if (action.action === "skip") {
|
|
4441
|
+
shouldSkip = true;
|
|
4442
|
+
syntheticResult = action.syntheticResult;
|
|
4443
|
+
this.logger.info("Controller skipped gadget execution", {
|
|
4444
|
+
gadgetName: call.gadgetName
|
|
4445
|
+
});
|
|
4446
|
+
}
|
|
4447
|
+
}
|
|
4448
|
+
const startObservers = [];
|
|
4449
|
+
if (this.hooks.observers?.onGadgetExecutionStart) {
|
|
4450
|
+
startObservers.push(async () => {
|
|
4451
|
+
const context = {
|
|
4452
|
+
iteration: this.iteration,
|
|
4453
|
+
gadgetName: call.gadgetName,
|
|
4454
|
+
invocationId: call.invocationId,
|
|
4455
|
+
parameters,
|
|
4456
|
+
logger: this.logger
|
|
4457
|
+
};
|
|
4458
|
+
await this.hooks.observers.onGadgetExecutionStart(context);
|
|
4459
|
+
});
|
|
4460
|
+
}
|
|
4461
|
+
await this.runObserversInParallel(startObservers);
|
|
4462
|
+
let result;
|
|
4463
|
+
if (shouldSkip) {
|
|
4464
|
+
result = {
|
|
4465
|
+
gadgetName: call.gadgetName,
|
|
4466
|
+
invocationId: call.invocationId,
|
|
4467
|
+
parameters,
|
|
4468
|
+
result: syntheticResult ?? "Execution skipped",
|
|
4469
|
+
executionTimeMs: 0
|
|
4470
|
+
};
|
|
4471
|
+
} else {
|
|
4472
|
+
result = await this.executor.execute(call);
|
|
4473
|
+
}
|
|
4474
|
+
const originalResult = result.result;
|
|
4475
|
+
if (result.result && this.hooks.interceptors?.interceptGadgetResult) {
|
|
4476
|
+
const context = {
|
|
4477
|
+
iteration: this.iteration,
|
|
4478
|
+
gadgetName: result.gadgetName,
|
|
4479
|
+
invocationId: result.invocationId,
|
|
4480
|
+
parameters,
|
|
4481
|
+
executionTimeMs: result.executionTimeMs,
|
|
4482
|
+
logger: this.logger
|
|
4483
|
+
};
|
|
4484
|
+
result.result = this.hooks.interceptors.interceptGadgetResult(result.result, context);
|
|
4485
|
+
}
|
|
4486
|
+
if (this.hooks.controllers?.afterGadgetExecution) {
|
|
4487
|
+
const context = {
|
|
4488
|
+
iteration: this.iteration,
|
|
4489
|
+
gadgetName: result.gadgetName,
|
|
4490
|
+
invocationId: result.invocationId,
|
|
4491
|
+
parameters,
|
|
4492
|
+
result: result.result,
|
|
4493
|
+
error: result.error,
|
|
4494
|
+
executionTimeMs: result.executionTimeMs,
|
|
4495
|
+
logger: this.logger
|
|
4496
|
+
};
|
|
4497
|
+
const action = await this.hooks.controllers.afterGadgetExecution(context);
|
|
4498
|
+
validateAfterGadgetExecutionAction(action);
|
|
4499
|
+
if (action.action === "recover" && result.error) {
|
|
4500
|
+
this.logger.info("Controller recovered from gadget error", {
|
|
4501
|
+
gadgetName: result.gadgetName,
|
|
4502
|
+
originalError: result.error
|
|
4503
|
+
});
|
|
4504
|
+
result = {
|
|
4505
|
+
...result,
|
|
4506
|
+
error: void 0,
|
|
4507
|
+
result: action.fallbackResult
|
|
4508
|
+
};
|
|
4509
|
+
}
|
|
4510
|
+
}
|
|
4511
|
+
const completeObservers = [];
|
|
4512
|
+
if (this.hooks.observers?.onGadgetExecutionComplete) {
|
|
4513
|
+
completeObservers.push(async () => {
|
|
4514
|
+
const context = {
|
|
4515
|
+
iteration: this.iteration,
|
|
4516
|
+
gadgetName: result.gadgetName,
|
|
4517
|
+
invocationId: result.invocationId,
|
|
4518
|
+
parameters,
|
|
4519
|
+
originalResult,
|
|
4520
|
+
finalResult: result.result,
|
|
4521
|
+
error: result.error,
|
|
4522
|
+
executionTimeMs: result.executionTimeMs,
|
|
4523
|
+
breaksLoop: result.breaksLoop,
|
|
4524
|
+
cost: result.cost,
|
|
4525
|
+
logger: this.logger
|
|
4526
|
+
};
|
|
4527
|
+
await this.hooks.observers.onGadgetExecutionComplete(context);
|
|
4528
|
+
});
|
|
4529
|
+
}
|
|
4530
|
+
await this.runObserversInParallel(completeObservers);
|
|
4531
|
+
this.completedResults.set(result.invocationId, result);
|
|
4532
|
+
if (result.error) {
|
|
4533
|
+
this.failedInvocations.add(result.invocationId);
|
|
4534
|
+
}
|
|
4535
|
+
yield { type: "gadget_result", result };
|
|
4536
|
+
if (result.error) {
|
|
4537
|
+
const errorType = this.determineErrorType(call, result);
|
|
4538
|
+
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4539
|
+
result.error,
|
|
4540
|
+
result.gadgetName,
|
|
4541
|
+
errorType,
|
|
4542
|
+
result.parameters
|
|
4543
|
+
);
|
|
4544
|
+
if (!shouldContinue) {
|
|
4545
|
+
this.executionHalted = true;
|
|
4546
|
+
}
|
|
4547
|
+
}
|
|
4548
|
+
}
|
|
4311
4549
|
/**
|
|
4312
4550
|
* Handle a gadget that cannot execute because a dependency failed.
|
|
4313
4551
|
* Calls the onDependencySkipped controller to allow customization.
|
|
@@ -4464,6 +4702,99 @@ var init_stream_processor = __esm({
|
|
|
4464
4702
|
}
|
|
4465
4703
|
return events;
|
|
4466
4704
|
}
|
|
4705
|
+
/**
|
|
4706
|
+
* Process pending gadgets, yielding events in real-time.
|
|
4707
|
+
* Generator version that yields events as gadgets complete.
|
|
4708
|
+
*
|
|
4709
|
+
* Note: Gadgets are still executed in parallel for efficiency,
|
|
4710
|
+
* but results are yielded as they become available.
|
|
4711
|
+
*/
|
|
4712
|
+
async *processPendingGadgetsGenerator() {
|
|
4713
|
+
let progress = true;
|
|
4714
|
+
while (progress && this.gadgetsAwaitingDependencies.size > 0) {
|
|
4715
|
+
progress = false;
|
|
4716
|
+
const readyToExecute = [];
|
|
4717
|
+
const readyToSkip = [];
|
|
4718
|
+
for (const [_invocationId, call] of this.gadgetsAwaitingDependencies) {
|
|
4719
|
+
const failedDep = call.dependencies.find((dep) => this.failedInvocations.has(dep));
|
|
4720
|
+
if (failedDep) {
|
|
4721
|
+
readyToSkip.push({ call, failedDep });
|
|
4722
|
+
continue;
|
|
4723
|
+
}
|
|
4724
|
+
const allSatisfied = call.dependencies.every((dep) => this.completedResults.has(dep));
|
|
4725
|
+
if (allSatisfied) {
|
|
4726
|
+
readyToExecute.push(call);
|
|
4727
|
+
}
|
|
4728
|
+
}
|
|
4729
|
+
for (const { call, failedDep } of readyToSkip) {
|
|
4730
|
+
this.gadgetsAwaitingDependencies.delete(call.invocationId);
|
|
4731
|
+
const skipEvents = await this.handleFailedDependency(call, failedDep);
|
|
4732
|
+
for (const evt of skipEvents) {
|
|
4733
|
+
yield evt;
|
|
4734
|
+
}
|
|
4735
|
+
progress = true;
|
|
4736
|
+
}
|
|
4737
|
+
if (readyToExecute.length > 0) {
|
|
4738
|
+
this.logger.debug("Executing ready gadgets in parallel", {
|
|
4739
|
+
count: readyToExecute.length,
|
|
4740
|
+
invocationIds: readyToExecute.map((c) => c.invocationId)
|
|
4741
|
+
});
|
|
4742
|
+
for (const call of readyToExecute) {
|
|
4743
|
+
this.gadgetsAwaitingDependencies.delete(call.invocationId);
|
|
4744
|
+
}
|
|
4745
|
+
const eventSets = await Promise.all(
|
|
4746
|
+
readyToExecute.map(async (call) => {
|
|
4747
|
+
const events = [];
|
|
4748
|
+
for await (const evt of this.executeGadgetGenerator(call)) {
|
|
4749
|
+
events.push(evt);
|
|
4750
|
+
}
|
|
4751
|
+
return events;
|
|
4752
|
+
})
|
|
4753
|
+
);
|
|
4754
|
+
for (const events of eventSets) {
|
|
4755
|
+
for (const evt of events) {
|
|
4756
|
+
yield evt;
|
|
4757
|
+
}
|
|
4758
|
+
}
|
|
4759
|
+
progress = true;
|
|
4760
|
+
}
|
|
4761
|
+
}
|
|
4762
|
+
if (this.gadgetsAwaitingDependencies.size > 0) {
|
|
4763
|
+
const pendingIds = new Set(this.gadgetsAwaitingDependencies.keys());
|
|
4764
|
+
for (const [invocationId, call] of this.gadgetsAwaitingDependencies) {
|
|
4765
|
+
const missingDeps = call.dependencies.filter((dep) => !this.completedResults.has(dep));
|
|
4766
|
+
const circularDeps = missingDeps.filter((dep) => pendingIds.has(dep));
|
|
4767
|
+
const trulyMissingDeps = missingDeps.filter((dep) => !pendingIds.has(dep));
|
|
4768
|
+
let errorMessage;
|
|
4769
|
+
let logLevel = "warn";
|
|
4770
|
+
if (circularDeps.length > 0 && trulyMissingDeps.length > 0) {
|
|
4771
|
+
errorMessage = `Dependencies unresolvable: circular=[${circularDeps.join(", ")}], missing=[${trulyMissingDeps.join(", ")}]`;
|
|
4772
|
+
logLevel = "error";
|
|
4773
|
+
} else if (circularDeps.length > 0) {
|
|
4774
|
+
errorMessage = `Circular dependency detected: "${invocationId}" depends on "${circularDeps[0]}" which also depends on "${invocationId}" (directly or indirectly)`;
|
|
4775
|
+
} else {
|
|
4776
|
+
errorMessage = `Dependency "${missingDeps[0]}" was never executed - check that the invocation ID exists and is spelled correctly`;
|
|
4777
|
+
}
|
|
4778
|
+
this.logger[logLevel]("Gadget has unresolvable dependencies", {
|
|
4779
|
+
gadgetName: call.gadgetName,
|
|
4780
|
+
invocationId,
|
|
4781
|
+
circularDependencies: circularDeps,
|
|
4782
|
+
missingDependencies: trulyMissingDeps
|
|
4783
|
+
});
|
|
4784
|
+
this.failedInvocations.add(invocationId);
|
|
4785
|
+
const skipEvent = {
|
|
4786
|
+
type: "gadget_skipped",
|
|
4787
|
+
gadgetName: call.gadgetName,
|
|
4788
|
+
invocationId,
|
|
4789
|
+
parameters: call.parameters ?? {},
|
|
4790
|
+
failedDependency: missingDeps[0],
|
|
4791
|
+
failedDependencyError: errorMessage
|
|
4792
|
+
};
|
|
4793
|
+
yield skipEvent;
|
|
4794
|
+
}
|
|
4795
|
+
this.gadgetsAwaitingDependencies.clear();
|
|
4796
|
+
}
|
|
4797
|
+
}
|
|
4467
4798
|
/**
|
|
4468
4799
|
* Safely execute an observer, catching and logging any errors.
|
|
4469
4800
|
* Observers are non-critical, so errors are logged but don't crash the system.
|
|
@@ -4586,6 +4917,8 @@ var init_agent = __esm({
|
|
|
4586
4917
|
// Subagent configuration
|
|
4587
4918
|
agentContextConfig;
|
|
4588
4919
|
subagentConfig;
|
|
4920
|
+
// Nested event callback for subagent gadgets
|
|
4921
|
+
onNestedEvent;
|
|
4589
4922
|
/**
|
|
4590
4923
|
* Creates a new Agent instance.
|
|
4591
4924
|
* @internal This constructor is private. Use LLMist.createAgent() or AgentBuilder instead.
|
|
@@ -4663,6 +4996,7 @@ var init_agent = __esm({
|
|
|
4663
4996
|
temperature: this.temperature
|
|
4664
4997
|
};
|
|
4665
4998
|
this.subagentConfig = options.subagentConfig;
|
|
4999
|
+
this.onNestedEvent = options.onNestedEvent;
|
|
4666
5000
|
}
|
|
4667
5001
|
/**
|
|
4668
5002
|
* Get the gadget registry for this agent.
|
|
@@ -4893,12 +5227,30 @@ var init_agent = __esm({
|
|
|
4893
5227
|
client: this.client,
|
|
4894
5228
|
mediaStore: this.mediaStore,
|
|
4895
5229
|
agentConfig: this.agentContextConfig,
|
|
4896
|
-
subagentConfig: this.subagentConfig
|
|
5230
|
+
subagentConfig: this.subagentConfig,
|
|
5231
|
+
onNestedEvent: this.onNestedEvent
|
|
4897
5232
|
});
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
5233
|
+
let streamMetadata = null;
|
|
5234
|
+
let gadgetCallCount = 0;
|
|
5235
|
+
const textOutputs = [];
|
|
5236
|
+
const gadgetResults = [];
|
|
5237
|
+
for await (const event of processor.process(stream2)) {
|
|
5238
|
+
if (event.type === "stream_complete") {
|
|
5239
|
+
streamMetadata = event;
|
|
5240
|
+
continue;
|
|
5241
|
+
}
|
|
5242
|
+
if (event.type === "text") {
|
|
5243
|
+
textOutputs.push(event.content);
|
|
5244
|
+
} else if (event.type === "gadget_result") {
|
|
5245
|
+
gadgetCallCount++;
|
|
5246
|
+
gadgetResults.push(event);
|
|
5247
|
+
}
|
|
5248
|
+
yield event;
|
|
4901
5249
|
}
|
|
5250
|
+
if (!streamMetadata) {
|
|
5251
|
+
throw new Error("Stream processing completed without metadata event");
|
|
5252
|
+
}
|
|
5253
|
+
const result = streamMetadata;
|
|
4902
5254
|
this.logger.info("LLM response completed", {
|
|
4903
5255
|
finishReason: result.finishReason,
|
|
4904
5256
|
usage: result.usage,
|
|
@@ -4923,9 +5275,6 @@ var init_agent = __esm({
|
|
|
4923
5275
|
});
|
|
4924
5276
|
let finalMessage = result.finalMessage;
|
|
4925
5277
|
if (this.hooks.controllers?.afterLLMCall) {
|
|
4926
|
-
const gadgetCallCount = result.outputs.filter(
|
|
4927
|
-
(output) => output.type === "gadget_result"
|
|
4928
|
-
).length;
|
|
4929
5278
|
const context = {
|
|
4930
5279
|
iteration: currentIteration,
|
|
4931
5280
|
maxIterations: this.maxIterations,
|
|
@@ -4955,9 +5304,7 @@ var init_agent = __esm({
|
|
|
4955
5304
|
}
|
|
4956
5305
|
if (result.didExecuteGadgets) {
|
|
4957
5306
|
if (this.textWithGadgetsHandler) {
|
|
4958
|
-
const textContent =
|
|
4959
|
-
(output) => output.type === "text"
|
|
4960
|
-
).map((output) => output.content).join("");
|
|
5307
|
+
const textContent = textOutputs.join("");
|
|
4961
5308
|
if (textContent.trim()) {
|
|
4962
5309
|
const { gadgetName, parameterMapping, resultMapping } = this.textWithGadgetsHandler;
|
|
4963
5310
|
this.conversation.addGadgetCallResult(
|
|
@@ -4967,7 +5314,7 @@ var init_agent = __esm({
|
|
|
4967
5314
|
);
|
|
4968
5315
|
}
|
|
4969
5316
|
}
|
|
4970
|
-
for (const output of
|
|
5317
|
+
for (const output of gadgetResults) {
|
|
4971
5318
|
if (output.type === "gadget_result") {
|
|
4972
5319
|
const gadgetResult = output.result;
|
|
4973
5320
|
this.conversation.addGadgetCallResult(
|
|
@@ -8475,6 +8822,8 @@ var init_builder = __esm({
|
|
|
8475
8822
|
signal;
|
|
8476
8823
|
trailingMessage;
|
|
8477
8824
|
subagentConfig;
|
|
8825
|
+
nestedEventCallback;
|
|
8826
|
+
parentContext;
|
|
8478
8827
|
constructor(client) {
|
|
8479
8828
|
this.client = client;
|
|
8480
8829
|
}
|
|
@@ -8971,6 +9320,74 @@ var init_builder = __esm({
|
|
|
8971
9320
|
this.subagentConfig = config;
|
|
8972
9321
|
return this;
|
|
8973
9322
|
}
|
|
9323
|
+
/**
|
|
9324
|
+
* Set the callback for nested subagent events.
|
|
9325
|
+
*
|
|
9326
|
+
* Subagent gadgets (like BrowseWeb) can use ExecutionContext.onNestedEvent
|
|
9327
|
+
* to report their internal LLM calls and gadget executions in real-time.
|
|
9328
|
+
* This callback receives those events, enabling hierarchical progress display.
|
|
9329
|
+
*
|
|
9330
|
+
* @param callback - Function to handle nested agent events
|
|
9331
|
+
* @returns This builder for chaining
|
|
9332
|
+
*
|
|
9333
|
+
* @example
|
|
9334
|
+
* ```typescript
|
|
9335
|
+
* .withNestedEventCallback((event) => {
|
|
9336
|
+
* if (event.type === "llm_call_start") {
|
|
9337
|
+
* console.log(` Nested LLM #${event.event.iteration} starting...`);
|
|
9338
|
+
* } else if (event.type === "gadget_call") {
|
|
9339
|
+
* console.log(` ⏵ ${event.event.call.gadgetName}...`);
|
|
9340
|
+
* }
|
|
9341
|
+
* })
|
|
9342
|
+
* ```
|
|
9343
|
+
*/
|
|
9344
|
+
withNestedEventCallback(callback) {
|
|
9345
|
+
this.nestedEventCallback = callback;
|
|
9346
|
+
return this;
|
|
9347
|
+
}
|
|
9348
|
+
/**
|
|
9349
|
+
* Enable automatic nested event forwarding to parent agent.
|
|
9350
|
+
*
|
|
9351
|
+
* When building a subagent inside a gadget, call this method to automatically
|
|
9352
|
+
* forward all LLM calls and gadget events to the parent agent. This enables
|
|
9353
|
+
* hierarchical progress display without any manual event handling.
|
|
9354
|
+
*
|
|
9355
|
+
* The method extracts `invocationId` and `onNestedEvent` from the execution
|
|
9356
|
+
* context and sets up automatic forwarding via hooks and event wrapping.
|
|
9357
|
+
*
|
|
9358
|
+
* @param ctx - ExecutionContext passed to the gadget's execute() method
|
|
9359
|
+
* @param depth - Nesting depth (default: 1 for direct child)
|
|
9360
|
+
* @returns This builder for chaining
|
|
9361
|
+
*
|
|
9362
|
+
* @example
|
|
9363
|
+
* ```typescript
|
|
9364
|
+
* // In a subagent gadget like BrowseWeb - ONE LINE enables auto-forwarding:
|
|
9365
|
+
* execute: async (params, ctx) => {
|
|
9366
|
+
* const agent = new AgentBuilder(client)
|
|
9367
|
+
* .withModel(model)
|
|
9368
|
+
* .withGadgets(Navigate, Click, Screenshot)
|
|
9369
|
+
* .withParentContext(ctx) // <-- This is all you need!
|
|
9370
|
+
* .ask(params.task);
|
|
9371
|
+
*
|
|
9372
|
+
* for await (const event of agent.run()) {
|
|
9373
|
+
* // Events automatically forwarded - just process normally
|
|
9374
|
+
* if (event.type === "text") {
|
|
9375
|
+
* result = event.content;
|
|
9376
|
+
* }
|
|
9377
|
+
* }
|
|
9378
|
+
* }
|
|
9379
|
+
* ```
|
|
9380
|
+
*/
|
|
9381
|
+
withParentContext(ctx, depth = 1) {
|
|
9382
|
+
if (ctx.onNestedEvent && ctx.invocationId) {
|
|
9383
|
+
this.parentContext = {
|
|
9384
|
+
invocationId: ctx.invocationId,
|
|
9385
|
+
onNestedEvent: ctx.onNestedEvent,
|
|
9386
|
+
depth
|
|
9387
|
+
};
|
|
9388
|
+
}
|
|
9389
|
+
return this;
|
|
9390
|
+
}
|
|
8974
9391
|
/**
|
|
8975
9392
|
* Add an ephemeral trailing message that appears at the end of each LLM request.
|
|
8976
9393
|
*
|
|
@@ -9038,14 +9455,58 @@ ${endPrefix}`
|
|
|
9038
9455
|
return this;
|
|
9039
9456
|
}
|
|
9040
9457
|
/**
|
|
9041
|
-
* Compose the final hooks, including
|
|
9458
|
+
* Compose the final hooks, including:
|
|
9459
|
+
* - Trailing message injection (if configured)
|
|
9460
|
+
* - Nested event forwarding for LLM calls (if parentContext is set)
|
|
9042
9461
|
*/
|
|
9043
9462
|
composeHooks() {
|
|
9463
|
+
let hooks = this.hooks;
|
|
9464
|
+
if (this.parentContext) {
|
|
9465
|
+
const { invocationId, onNestedEvent, depth } = this.parentContext;
|
|
9466
|
+
const existingOnLLMCallStart = hooks?.observers?.onLLMCallStart;
|
|
9467
|
+
const existingOnLLMCallComplete = hooks?.observers?.onLLMCallComplete;
|
|
9468
|
+
hooks = {
|
|
9469
|
+
...hooks,
|
|
9470
|
+
observers: {
|
|
9471
|
+
...hooks?.observers,
|
|
9472
|
+
onLLMCallStart: async (context) => {
|
|
9473
|
+
onNestedEvent({
|
|
9474
|
+
type: "llm_call_start",
|
|
9475
|
+
gadgetInvocationId: invocationId,
|
|
9476
|
+
depth,
|
|
9477
|
+
event: {
|
|
9478
|
+
iteration: context.iteration,
|
|
9479
|
+
model: context.options.model
|
|
9480
|
+
}
|
|
9481
|
+
});
|
|
9482
|
+
if (existingOnLLMCallStart) {
|
|
9483
|
+
await existingOnLLMCallStart(context);
|
|
9484
|
+
}
|
|
9485
|
+
},
|
|
9486
|
+
onLLMCallComplete: async (context) => {
|
|
9487
|
+
onNestedEvent({
|
|
9488
|
+
type: "llm_call_end",
|
|
9489
|
+
gadgetInvocationId: invocationId,
|
|
9490
|
+
depth,
|
|
9491
|
+
event: {
|
|
9492
|
+
iteration: context.iteration,
|
|
9493
|
+
model: context.options.model,
|
|
9494
|
+
outputTokens: context.usage?.outputTokens,
|
|
9495
|
+
finishReason: context.finishReason
|
|
9496
|
+
}
|
|
9497
|
+
});
|
|
9498
|
+
if (existingOnLLMCallComplete) {
|
|
9499
|
+
await existingOnLLMCallComplete(context);
|
|
9500
|
+
}
|
|
9501
|
+
}
|
|
9502
|
+
}
|
|
9503
|
+
};
|
|
9504
|
+
}
|
|
9044
9505
|
if (!this.trailingMessage) {
|
|
9045
|
-
return
|
|
9506
|
+
return hooks;
|
|
9046
9507
|
}
|
|
9047
9508
|
const trailingMsg = this.trailingMessage;
|
|
9048
|
-
const existingBeforeLLMCall =
|
|
9509
|
+
const existingBeforeLLMCall = hooks?.controllers?.beforeLLMCall;
|
|
9049
9510
|
const trailingMessageController = async (ctx) => {
|
|
9050
9511
|
const result = existingBeforeLLMCall ? await existingBeforeLLMCall(ctx) : { action: "proceed" };
|
|
9051
9512
|
if (result.action === "skip") {
|
|
@@ -9060,9 +9521,9 @@ ${endPrefix}`
|
|
|
9060
9521
|
};
|
|
9061
9522
|
};
|
|
9062
9523
|
return {
|
|
9063
|
-
...
|
|
9524
|
+
...hooks,
|
|
9064
9525
|
controllers: {
|
|
9065
|
-
...
|
|
9526
|
+
...hooks?.controllers,
|
|
9066
9527
|
beforeLLMCall: trailingMessageController
|
|
9067
9528
|
}
|
|
9068
9529
|
};
|
|
@@ -9123,6 +9584,19 @@ ${endPrefix}`
|
|
|
9123
9584
|
this.client = new LLMistClass();
|
|
9124
9585
|
}
|
|
9125
9586
|
const registry = GadgetRegistry.from(this.gadgets);
|
|
9587
|
+
let onNestedEvent = this.nestedEventCallback;
|
|
9588
|
+
if (this.parentContext) {
|
|
9589
|
+
const { invocationId, onNestedEvent: parentCallback, depth } = this.parentContext;
|
|
9590
|
+
const existingCallback = this.nestedEventCallback;
|
|
9591
|
+
onNestedEvent = (event) => {
|
|
9592
|
+
parentCallback({
|
|
9593
|
+
...event,
|
|
9594
|
+
gadgetInvocationId: invocationId,
|
|
9595
|
+
depth: event.depth + depth
|
|
9596
|
+
});
|
|
9597
|
+
existingCallback?.(event);
|
|
9598
|
+
};
|
|
9599
|
+
}
|
|
9126
9600
|
return {
|
|
9127
9601
|
client: this.client,
|
|
9128
9602
|
model: this.model ?? "openai:gpt-5-nano",
|
|
@@ -9148,7 +9622,8 @@ ${endPrefix}`
|
|
|
9148
9622
|
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
9149
9623
|
compactionConfig: this.compactionConfig,
|
|
9150
9624
|
signal: this.signal,
|
|
9151
|
-
subagentConfig: this.subagentConfig
|
|
9625
|
+
subagentConfig: this.subagentConfig,
|
|
9626
|
+
onNestedEvent
|
|
9152
9627
|
};
|
|
9153
9628
|
}
|
|
9154
9629
|
ask(userPrompt) {
|
|
@@ -9305,6 +9780,19 @@ ${endPrefix}`
|
|
|
9305
9780
|
this.client = new LLMistClass();
|
|
9306
9781
|
}
|
|
9307
9782
|
const registry = GadgetRegistry.from(this.gadgets);
|
|
9783
|
+
let onNestedEvent = this.nestedEventCallback;
|
|
9784
|
+
if (this.parentContext) {
|
|
9785
|
+
const { invocationId, onNestedEvent: parentCallback, depth } = this.parentContext;
|
|
9786
|
+
const existingCallback = this.nestedEventCallback;
|
|
9787
|
+
onNestedEvent = (event) => {
|
|
9788
|
+
parentCallback({
|
|
9789
|
+
...event,
|
|
9790
|
+
gadgetInvocationId: invocationId,
|
|
9791
|
+
depth: event.depth + depth
|
|
9792
|
+
});
|
|
9793
|
+
existingCallback?.(event);
|
|
9794
|
+
};
|
|
9795
|
+
}
|
|
9308
9796
|
const options = {
|
|
9309
9797
|
client: this.client,
|
|
9310
9798
|
model: this.model ?? "openai:gpt-5-nano",
|
|
@@ -9330,7 +9818,8 @@ ${endPrefix}`
|
|
|
9330
9818
|
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
9331
9819
|
compactionConfig: this.compactionConfig,
|
|
9332
9820
|
signal: this.signal,
|
|
9333
|
-
subagentConfig: this.subagentConfig
|
|
9821
|
+
subagentConfig: this.subagentConfig,
|
|
9822
|
+
onNestedEvent
|
|
9334
9823
|
};
|
|
9335
9824
|
return new Agent(AGENT_INTERNAL_KEY, options);
|
|
9336
9825
|
}
|