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
|
@@ -3549,7 +3549,7 @@ var init_executor = __esm({
|
|
|
3549
3549
|
init_exceptions();
|
|
3550
3550
|
init_parser();
|
|
3551
3551
|
GadgetExecutor = class {
|
|
3552
|
-
constructor(registry, requestHumanInput, logger, defaultGadgetTimeoutMs, errorFormatterOptions, client, mediaStore, agentConfig, subagentConfig) {
|
|
3552
|
+
constructor(registry, requestHumanInput, logger, defaultGadgetTimeoutMs, errorFormatterOptions, client, mediaStore, agentConfig, subagentConfig, onNestedEvent) {
|
|
3553
3553
|
this.registry = registry;
|
|
3554
3554
|
this.requestHumanInput = requestHumanInput;
|
|
3555
3555
|
this.defaultGadgetTimeoutMs = defaultGadgetTimeoutMs;
|
|
@@ -3557,6 +3557,7 @@ var init_executor = __esm({
|
|
|
3557
3557
|
this.mediaStore = mediaStore;
|
|
3558
3558
|
this.agentConfig = agentConfig;
|
|
3559
3559
|
this.subagentConfig = subagentConfig;
|
|
3560
|
+
this.onNestedEvent = onNestedEvent;
|
|
3560
3561
|
this.logger = logger ?? createLogger({ name: "llmist:executor" });
|
|
3561
3562
|
this.errorFormatter = new GadgetExecutionErrorFormatter(errorFormatterOptions);
|
|
3562
3563
|
this.argPrefix = errorFormatterOptions?.argPrefix ?? GADGET_ARG_PREFIX;
|
|
@@ -3702,7 +3703,9 @@ var init_executor = __esm({
|
|
|
3702
3703
|
llmist: this.client ? new CostReportingLLMistWrapper(this.client, reportCost) : void 0,
|
|
3703
3704
|
signal: abortController.signal,
|
|
3704
3705
|
agentConfig: this.agentConfig,
|
|
3705
|
-
subagentConfig: this.subagentConfig
|
|
3706
|
+
subagentConfig: this.subagentConfig,
|
|
3707
|
+
invocationId: call.invocationId,
|
|
3708
|
+
onNestedEvent: this.onNestedEvent
|
|
3706
3709
|
};
|
|
3707
3710
|
let rawResult;
|
|
3708
3711
|
if (timeoutMs && timeoutMs > 0) {
|
|
@@ -3941,14 +3944,21 @@ var init_stream_processor = __esm({
|
|
|
3941
3944
|
options.client,
|
|
3942
3945
|
options.mediaStore,
|
|
3943
3946
|
options.agentConfig,
|
|
3944
|
-
options.subagentConfig
|
|
3947
|
+
options.subagentConfig,
|
|
3948
|
+
options.onNestedEvent
|
|
3945
3949
|
);
|
|
3946
3950
|
}
|
|
3947
3951
|
/**
|
|
3948
|
-
* Process an LLM stream and
|
|
3952
|
+
* Process an LLM stream and yield events in real-time.
|
|
3953
|
+
*
|
|
3954
|
+
* This is an async generator that yields events immediately as they occur:
|
|
3955
|
+
* - Text events are yielded as text is streamed from the LLM
|
|
3956
|
+
* - gadget_call events are yielded immediately when a gadget call is parsed
|
|
3957
|
+
* - gadget_result events are yielded when gadget execution completes
|
|
3958
|
+
*
|
|
3959
|
+
* The final event is always a StreamCompletionEvent containing metadata.
|
|
3949
3960
|
*/
|
|
3950
|
-
async process(stream2) {
|
|
3951
|
-
const outputs = [];
|
|
3961
|
+
async *process(stream2) {
|
|
3952
3962
|
let finishReason = null;
|
|
3953
3963
|
let usage;
|
|
3954
3964
|
let didExecuteGadgets = false;
|
|
@@ -3994,14 +4004,13 @@ var init_stream_processor = __esm({
|
|
|
3994
4004
|
continue;
|
|
3995
4005
|
}
|
|
3996
4006
|
for (const event of this.parser.feed(processedChunk)) {
|
|
3997
|
-
const
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
shouldBreakLoop = true;
|
|
4007
|
+
for await (const processedEvent of this.processEventGenerator(event)) {
|
|
4008
|
+
yield processedEvent;
|
|
4009
|
+
if (processedEvent.type === "gadget_result") {
|
|
4010
|
+
didExecuteGadgets = true;
|
|
4011
|
+
if (processedEvent.result.breaksLoop) {
|
|
4012
|
+
shouldBreakLoop = true;
|
|
4013
|
+
}
|
|
4005
4014
|
}
|
|
4006
4015
|
}
|
|
4007
4016
|
}
|
|
@@ -4012,25 +4021,23 @@ var init_stream_processor = __esm({
|
|
|
4012
4021
|
}
|
|
4013
4022
|
if (!this.executionHalted) {
|
|
4014
4023
|
for (const event of this.parser.finalize()) {
|
|
4015
|
-
const
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
shouldBreakLoop = true;
|
|
4024
|
+
for await (const processedEvent of this.processEventGenerator(event)) {
|
|
4025
|
+
yield processedEvent;
|
|
4026
|
+
if (processedEvent.type === "gadget_result") {
|
|
4027
|
+
didExecuteGadgets = true;
|
|
4028
|
+
if (processedEvent.result.breaksLoop) {
|
|
4029
|
+
shouldBreakLoop = true;
|
|
4030
|
+
}
|
|
4023
4031
|
}
|
|
4024
4032
|
}
|
|
4025
4033
|
}
|
|
4026
|
-
const
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
shouldBreakLoop = true;
|
|
4034
|
+
for await (const evt of this.processPendingGadgetsGenerator()) {
|
|
4035
|
+
yield evt;
|
|
4036
|
+
if (evt.type === "gadget_result") {
|
|
4037
|
+
didExecuteGadgets = true;
|
|
4038
|
+
if (evt.result.breaksLoop) {
|
|
4039
|
+
shouldBreakLoop = true;
|
|
4040
|
+
}
|
|
4034
4041
|
}
|
|
4035
4042
|
}
|
|
4036
4043
|
}
|
|
@@ -4043,8 +4050,8 @@ var init_stream_processor = __esm({
|
|
|
4043
4050
|
};
|
|
4044
4051
|
finalMessage = this.hooks.interceptors.interceptAssistantMessage(finalMessage, context);
|
|
4045
4052
|
}
|
|
4046
|
-
|
|
4047
|
-
|
|
4053
|
+
const completionEvent = {
|
|
4054
|
+
type: "stream_complete",
|
|
4048
4055
|
shouldBreakLoop,
|
|
4049
4056
|
didExecuteGadgets,
|
|
4050
4057
|
finishReason,
|
|
@@ -4052,9 +4059,11 @@ var init_stream_processor = __esm({
|
|
|
4052
4059
|
rawResponse: this.responseText,
|
|
4053
4060
|
finalMessage
|
|
4054
4061
|
};
|
|
4062
|
+
yield completionEvent;
|
|
4055
4063
|
}
|
|
4056
4064
|
/**
|
|
4057
4065
|
* Process a single parsed event (text or gadget call).
|
|
4066
|
+
* @deprecated Use processEventGenerator for real-time streaming
|
|
4058
4067
|
*/
|
|
4059
4068
|
async processEvent(event) {
|
|
4060
4069
|
if (event.type === "text") {
|
|
@@ -4064,6 +4073,23 @@ var init_stream_processor = __esm({
|
|
|
4064
4073
|
}
|
|
4065
4074
|
return [event];
|
|
4066
4075
|
}
|
|
4076
|
+
/**
|
|
4077
|
+
* Process a single parsed event, yielding events in real-time.
|
|
4078
|
+
* Generator version of processEvent for streaming support.
|
|
4079
|
+
*/
|
|
4080
|
+
async *processEventGenerator(event) {
|
|
4081
|
+
if (event.type === "text") {
|
|
4082
|
+
for (const e of await this.processTextEvent(event)) {
|
|
4083
|
+
yield e;
|
|
4084
|
+
}
|
|
4085
|
+
} else if (event.type === "gadget_call") {
|
|
4086
|
+
for await (const e of this.processGadgetCallGenerator(event.call)) {
|
|
4087
|
+
yield e;
|
|
4088
|
+
}
|
|
4089
|
+
} else {
|
|
4090
|
+
yield event;
|
|
4091
|
+
}
|
|
4092
|
+
}
|
|
4067
4093
|
/**
|
|
4068
4094
|
* Process a text event through interceptors.
|
|
4069
4095
|
*/
|
|
@@ -4140,9 +4166,68 @@ var init_stream_processor = __esm({
|
|
|
4140
4166
|
events.push(...triggeredEvents);
|
|
4141
4167
|
return events;
|
|
4142
4168
|
}
|
|
4169
|
+
/**
|
|
4170
|
+
* Process a gadget call, yielding events in real-time.
|
|
4171
|
+
*
|
|
4172
|
+
* Key difference from processGadgetCall: yields gadget_call event IMMEDIATELY
|
|
4173
|
+
* when parsed (before execution), enabling real-time UI feedback.
|
|
4174
|
+
*/
|
|
4175
|
+
async *processGadgetCallGenerator(call) {
|
|
4176
|
+
if (this.executionHalted) {
|
|
4177
|
+
this.logger.debug("Skipping gadget execution due to previous error", {
|
|
4178
|
+
gadgetName: call.gadgetName
|
|
4179
|
+
});
|
|
4180
|
+
return;
|
|
4181
|
+
}
|
|
4182
|
+
yield { type: "gadget_call", call };
|
|
4183
|
+
if (call.dependencies.length > 0) {
|
|
4184
|
+
if (call.dependencies.includes(call.invocationId)) {
|
|
4185
|
+
this.logger.warn("Gadget has self-referential dependency (depends on itself)", {
|
|
4186
|
+
gadgetName: call.gadgetName,
|
|
4187
|
+
invocationId: call.invocationId
|
|
4188
|
+
});
|
|
4189
|
+
this.failedInvocations.add(call.invocationId);
|
|
4190
|
+
const skipEvent = {
|
|
4191
|
+
type: "gadget_skipped",
|
|
4192
|
+
gadgetName: call.gadgetName,
|
|
4193
|
+
invocationId: call.invocationId,
|
|
4194
|
+
parameters: call.parameters ?? {},
|
|
4195
|
+
failedDependency: call.invocationId,
|
|
4196
|
+
failedDependencyError: `Gadget "${call.invocationId}" cannot depend on itself (self-referential dependency)`
|
|
4197
|
+
};
|
|
4198
|
+
yield skipEvent;
|
|
4199
|
+
return;
|
|
4200
|
+
}
|
|
4201
|
+
const failedDep = call.dependencies.find((dep) => this.failedInvocations.has(dep));
|
|
4202
|
+
if (failedDep) {
|
|
4203
|
+
const skipEvents = await this.handleFailedDependency(call, failedDep);
|
|
4204
|
+
for (const evt of skipEvents) {
|
|
4205
|
+
yield evt;
|
|
4206
|
+
}
|
|
4207
|
+
return;
|
|
4208
|
+
}
|
|
4209
|
+
const unsatisfied = call.dependencies.filter((dep) => !this.completedResults.has(dep));
|
|
4210
|
+
if (unsatisfied.length > 0) {
|
|
4211
|
+
this.logger.debug("Queueing gadget for later - waiting on dependencies", {
|
|
4212
|
+
gadgetName: call.gadgetName,
|
|
4213
|
+
invocationId: call.invocationId,
|
|
4214
|
+
waitingOn: unsatisfied
|
|
4215
|
+
});
|
|
4216
|
+
this.gadgetsAwaitingDependencies.set(call.invocationId, call);
|
|
4217
|
+
return;
|
|
4218
|
+
}
|
|
4219
|
+
}
|
|
4220
|
+
for await (const evt of this.executeGadgetGenerator(call)) {
|
|
4221
|
+
yield evt;
|
|
4222
|
+
}
|
|
4223
|
+
for await (const evt of this.processPendingGadgetsGenerator()) {
|
|
4224
|
+
yield evt;
|
|
4225
|
+
}
|
|
4226
|
+
}
|
|
4143
4227
|
/**
|
|
4144
4228
|
* Execute a gadget through the full hook lifecycle.
|
|
4145
4229
|
* This is the core execution logic, extracted from processGadgetCall.
|
|
4230
|
+
* @deprecated Use executeGadgetGenerator for real-time streaming
|
|
4146
4231
|
*/
|
|
4147
4232
|
async executeGadgetWithHooks(call) {
|
|
4148
4233
|
const events = [];
|
|
@@ -4295,6 +4380,159 @@ var init_stream_processor = __esm({
|
|
|
4295
4380
|
}
|
|
4296
4381
|
return events;
|
|
4297
4382
|
}
|
|
4383
|
+
/**
|
|
4384
|
+
* Execute a gadget and yield the result event.
|
|
4385
|
+
* Generator version that yields gadget_result immediately when execution completes.
|
|
4386
|
+
*/
|
|
4387
|
+
async *executeGadgetGenerator(call) {
|
|
4388
|
+
if (call.parseError) {
|
|
4389
|
+
this.logger.warn("Gadget has parse error", {
|
|
4390
|
+
gadgetName: call.gadgetName,
|
|
4391
|
+
error: call.parseError,
|
|
4392
|
+
rawParameters: call.parametersRaw
|
|
4393
|
+
});
|
|
4394
|
+
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4395
|
+
call.parseError,
|
|
4396
|
+
call.gadgetName,
|
|
4397
|
+
"parse",
|
|
4398
|
+
call.parameters
|
|
4399
|
+
);
|
|
4400
|
+
if (!shouldContinue) {
|
|
4401
|
+
this.executionHalted = true;
|
|
4402
|
+
}
|
|
4403
|
+
}
|
|
4404
|
+
let parameters = call.parameters ?? {};
|
|
4405
|
+
if (this.hooks.interceptors?.interceptGadgetParameters) {
|
|
4406
|
+
const context = {
|
|
4407
|
+
iteration: this.iteration,
|
|
4408
|
+
gadgetName: call.gadgetName,
|
|
4409
|
+
invocationId: call.invocationId,
|
|
4410
|
+
logger: this.logger
|
|
4411
|
+
};
|
|
4412
|
+
parameters = this.hooks.interceptors.interceptGadgetParameters(parameters, context);
|
|
4413
|
+
}
|
|
4414
|
+
call.parameters = parameters;
|
|
4415
|
+
let shouldSkip = false;
|
|
4416
|
+
let syntheticResult;
|
|
4417
|
+
if (this.hooks.controllers?.beforeGadgetExecution) {
|
|
4418
|
+
const context = {
|
|
4419
|
+
iteration: this.iteration,
|
|
4420
|
+
gadgetName: call.gadgetName,
|
|
4421
|
+
invocationId: call.invocationId,
|
|
4422
|
+
parameters,
|
|
4423
|
+
logger: this.logger
|
|
4424
|
+
};
|
|
4425
|
+
const action = await this.hooks.controllers.beforeGadgetExecution(context);
|
|
4426
|
+
validateBeforeGadgetExecutionAction(action);
|
|
4427
|
+
if (action.action === "skip") {
|
|
4428
|
+
shouldSkip = true;
|
|
4429
|
+
syntheticResult = action.syntheticResult;
|
|
4430
|
+
this.logger.info("Controller skipped gadget execution", {
|
|
4431
|
+
gadgetName: call.gadgetName
|
|
4432
|
+
});
|
|
4433
|
+
}
|
|
4434
|
+
}
|
|
4435
|
+
const startObservers = [];
|
|
4436
|
+
if (this.hooks.observers?.onGadgetExecutionStart) {
|
|
4437
|
+
startObservers.push(async () => {
|
|
4438
|
+
const context = {
|
|
4439
|
+
iteration: this.iteration,
|
|
4440
|
+
gadgetName: call.gadgetName,
|
|
4441
|
+
invocationId: call.invocationId,
|
|
4442
|
+
parameters,
|
|
4443
|
+
logger: this.logger
|
|
4444
|
+
};
|
|
4445
|
+
await this.hooks.observers.onGadgetExecutionStart(context);
|
|
4446
|
+
});
|
|
4447
|
+
}
|
|
4448
|
+
await this.runObserversInParallel(startObservers);
|
|
4449
|
+
let result;
|
|
4450
|
+
if (shouldSkip) {
|
|
4451
|
+
result = {
|
|
4452
|
+
gadgetName: call.gadgetName,
|
|
4453
|
+
invocationId: call.invocationId,
|
|
4454
|
+
parameters,
|
|
4455
|
+
result: syntheticResult ?? "Execution skipped",
|
|
4456
|
+
executionTimeMs: 0
|
|
4457
|
+
};
|
|
4458
|
+
} else {
|
|
4459
|
+
result = await this.executor.execute(call);
|
|
4460
|
+
}
|
|
4461
|
+
const originalResult = result.result;
|
|
4462
|
+
if (result.result && this.hooks.interceptors?.interceptGadgetResult) {
|
|
4463
|
+
const context = {
|
|
4464
|
+
iteration: this.iteration,
|
|
4465
|
+
gadgetName: result.gadgetName,
|
|
4466
|
+
invocationId: result.invocationId,
|
|
4467
|
+
parameters,
|
|
4468
|
+
executionTimeMs: result.executionTimeMs,
|
|
4469
|
+
logger: this.logger
|
|
4470
|
+
};
|
|
4471
|
+
result.result = this.hooks.interceptors.interceptGadgetResult(result.result, context);
|
|
4472
|
+
}
|
|
4473
|
+
if (this.hooks.controllers?.afterGadgetExecution) {
|
|
4474
|
+
const context = {
|
|
4475
|
+
iteration: this.iteration,
|
|
4476
|
+
gadgetName: result.gadgetName,
|
|
4477
|
+
invocationId: result.invocationId,
|
|
4478
|
+
parameters,
|
|
4479
|
+
result: result.result,
|
|
4480
|
+
error: result.error,
|
|
4481
|
+
executionTimeMs: result.executionTimeMs,
|
|
4482
|
+
logger: this.logger
|
|
4483
|
+
};
|
|
4484
|
+
const action = await this.hooks.controllers.afterGadgetExecution(context);
|
|
4485
|
+
validateAfterGadgetExecutionAction(action);
|
|
4486
|
+
if (action.action === "recover" && result.error) {
|
|
4487
|
+
this.logger.info("Controller recovered from gadget error", {
|
|
4488
|
+
gadgetName: result.gadgetName,
|
|
4489
|
+
originalError: result.error
|
|
4490
|
+
});
|
|
4491
|
+
result = {
|
|
4492
|
+
...result,
|
|
4493
|
+
error: void 0,
|
|
4494
|
+
result: action.fallbackResult
|
|
4495
|
+
};
|
|
4496
|
+
}
|
|
4497
|
+
}
|
|
4498
|
+
const completeObservers = [];
|
|
4499
|
+
if (this.hooks.observers?.onGadgetExecutionComplete) {
|
|
4500
|
+
completeObservers.push(async () => {
|
|
4501
|
+
const context = {
|
|
4502
|
+
iteration: this.iteration,
|
|
4503
|
+
gadgetName: result.gadgetName,
|
|
4504
|
+
invocationId: result.invocationId,
|
|
4505
|
+
parameters,
|
|
4506
|
+
originalResult,
|
|
4507
|
+
finalResult: result.result,
|
|
4508
|
+
error: result.error,
|
|
4509
|
+
executionTimeMs: result.executionTimeMs,
|
|
4510
|
+
breaksLoop: result.breaksLoop,
|
|
4511
|
+
cost: result.cost,
|
|
4512
|
+
logger: this.logger
|
|
4513
|
+
};
|
|
4514
|
+
await this.hooks.observers.onGadgetExecutionComplete(context);
|
|
4515
|
+
});
|
|
4516
|
+
}
|
|
4517
|
+
await this.runObserversInParallel(completeObservers);
|
|
4518
|
+
this.completedResults.set(result.invocationId, result);
|
|
4519
|
+
if (result.error) {
|
|
4520
|
+
this.failedInvocations.add(result.invocationId);
|
|
4521
|
+
}
|
|
4522
|
+
yield { type: "gadget_result", result };
|
|
4523
|
+
if (result.error) {
|
|
4524
|
+
const errorType = this.determineErrorType(call, result);
|
|
4525
|
+
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4526
|
+
result.error,
|
|
4527
|
+
result.gadgetName,
|
|
4528
|
+
errorType,
|
|
4529
|
+
result.parameters
|
|
4530
|
+
);
|
|
4531
|
+
if (!shouldContinue) {
|
|
4532
|
+
this.executionHalted = true;
|
|
4533
|
+
}
|
|
4534
|
+
}
|
|
4535
|
+
}
|
|
4298
4536
|
/**
|
|
4299
4537
|
* Handle a gadget that cannot execute because a dependency failed.
|
|
4300
4538
|
* Calls the onDependencySkipped controller to allow customization.
|
|
@@ -4451,6 +4689,99 @@ var init_stream_processor = __esm({
|
|
|
4451
4689
|
}
|
|
4452
4690
|
return events;
|
|
4453
4691
|
}
|
|
4692
|
+
/**
|
|
4693
|
+
* Process pending gadgets, yielding events in real-time.
|
|
4694
|
+
* Generator version that yields events as gadgets complete.
|
|
4695
|
+
*
|
|
4696
|
+
* Note: Gadgets are still executed in parallel for efficiency,
|
|
4697
|
+
* but results are yielded as they become available.
|
|
4698
|
+
*/
|
|
4699
|
+
async *processPendingGadgetsGenerator() {
|
|
4700
|
+
let progress = true;
|
|
4701
|
+
while (progress && this.gadgetsAwaitingDependencies.size > 0) {
|
|
4702
|
+
progress = false;
|
|
4703
|
+
const readyToExecute = [];
|
|
4704
|
+
const readyToSkip = [];
|
|
4705
|
+
for (const [_invocationId, call] of this.gadgetsAwaitingDependencies) {
|
|
4706
|
+
const failedDep = call.dependencies.find((dep) => this.failedInvocations.has(dep));
|
|
4707
|
+
if (failedDep) {
|
|
4708
|
+
readyToSkip.push({ call, failedDep });
|
|
4709
|
+
continue;
|
|
4710
|
+
}
|
|
4711
|
+
const allSatisfied = call.dependencies.every((dep) => this.completedResults.has(dep));
|
|
4712
|
+
if (allSatisfied) {
|
|
4713
|
+
readyToExecute.push(call);
|
|
4714
|
+
}
|
|
4715
|
+
}
|
|
4716
|
+
for (const { call, failedDep } of readyToSkip) {
|
|
4717
|
+
this.gadgetsAwaitingDependencies.delete(call.invocationId);
|
|
4718
|
+
const skipEvents = await this.handleFailedDependency(call, failedDep);
|
|
4719
|
+
for (const evt of skipEvents) {
|
|
4720
|
+
yield evt;
|
|
4721
|
+
}
|
|
4722
|
+
progress = true;
|
|
4723
|
+
}
|
|
4724
|
+
if (readyToExecute.length > 0) {
|
|
4725
|
+
this.logger.debug("Executing ready gadgets in parallel", {
|
|
4726
|
+
count: readyToExecute.length,
|
|
4727
|
+
invocationIds: readyToExecute.map((c) => c.invocationId)
|
|
4728
|
+
});
|
|
4729
|
+
for (const call of readyToExecute) {
|
|
4730
|
+
this.gadgetsAwaitingDependencies.delete(call.invocationId);
|
|
4731
|
+
}
|
|
4732
|
+
const eventSets = await Promise.all(
|
|
4733
|
+
readyToExecute.map(async (call) => {
|
|
4734
|
+
const events = [];
|
|
4735
|
+
for await (const evt of this.executeGadgetGenerator(call)) {
|
|
4736
|
+
events.push(evt);
|
|
4737
|
+
}
|
|
4738
|
+
return events;
|
|
4739
|
+
})
|
|
4740
|
+
);
|
|
4741
|
+
for (const events of eventSets) {
|
|
4742
|
+
for (const evt of events) {
|
|
4743
|
+
yield evt;
|
|
4744
|
+
}
|
|
4745
|
+
}
|
|
4746
|
+
progress = true;
|
|
4747
|
+
}
|
|
4748
|
+
}
|
|
4749
|
+
if (this.gadgetsAwaitingDependencies.size > 0) {
|
|
4750
|
+
const pendingIds = new Set(this.gadgetsAwaitingDependencies.keys());
|
|
4751
|
+
for (const [invocationId, call] of this.gadgetsAwaitingDependencies) {
|
|
4752
|
+
const missingDeps = call.dependencies.filter((dep) => !this.completedResults.has(dep));
|
|
4753
|
+
const circularDeps = missingDeps.filter((dep) => pendingIds.has(dep));
|
|
4754
|
+
const trulyMissingDeps = missingDeps.filter((dep) => !pendingIds.has(dep));
|
|
4755
|
+
let errorMessage;
|
|
4756
|
+
let logLevel = "warn";
|
|
4757
|
+
if (circularDeps.length > 0 && trulyMissingDeps.length > 0) {
|
|
4758
|
+
errorMessage = `Dependencies unresolvable: circular=[${circularDeps.join(", ")}], missing=[${trulyMissingDeps.join(", ")}]`;
|
|
4759
|
+
logLevel = "error";
|
|
4760
|
+
} else if (circularDeps.length > 0) {
|
|
4761
|
+
errorMessage = `Circular dependency detected: "${invocationId}" depends on "${circularDeps[0]}" which also depends on "${invocationId}" (directly or indirectly)`;
|
|
4762
|
+
} else {
|
|
4763
|
+
errorMessage = `Dependency "${missingDeps[0]}" was never executed - check that the invocation ID exists and is spelled correctly`;
|
|
4764
|
+
}
|
|
4765
|
+
this.logger[logLevel]("Gadget has unresolvable dependencies", {
|
|
4766
|
+
gadgetName: call.gadgetName,
|
|
4767
|
+
invocationId,
|
|
4768
|
+
circularDependencies: circularDeps,
|
|
4769
|
+
missingDependencies: trulyMissingDeps
|
|
4770
|
+
});
|
|
4771
|
+
this.failedInvocations.add(invocationId);
|
|
4772
|
+
const skipEvent = {
|
|
4773
|
+
type: "gadget_skipped",
|
|
4774
|
+
gadgetName: call.gadgetName,
|
|
4775
|
+
invocationId,
|
|
4776
|
+
parameters: call.parameters ?? {},
|
|
4777
|
+
failedDependency: missingDeps[0],
|
|
4778
|
+
failedDependencyError: errorMessage
|
|
4779
|
+
};
|
|
4780
|
+
yield skipEvent;
|
|
4781
|
+
}
|
|
4782
|
+
this.gadgetsAwaitingDependencies.clear();
|
|
4783
|
+
}
|
|
4784
|
+
}
|
|
4454
4785
|
/**
|
|
4455
4786
|
* Safely execute an observer, catching and logging any errors.
|
|
4456
4787
|
* Observers are non-critical, so errors are logged but don't crash the system.
|
|
@@ -4573,6 +4904,8 @@ var init_agent = __esm({
|
|
|
4573
4904
|
// Subagent configuration
|
|
4574
4905
|
agentContextConfig;
|
|
4575
4906
|
subagentConfig;
|
|
4907
|
+
// Nested event callback for subagent gadgets
|
|
4908
|
+
onNestedEvent;
|
|
4576
4909
|
/**
|
|
4577
4910
|
* Creates a new Agent instance.
|
|
4578
4911
|
* @internal This constructor is private. Use LLMist.createAgent() or AgentBuilder instead.
|
|
@@ -4650,6 +4983,7 @@ var init_agent = __esm({
|
|
|
4650
4983
|
temperature: this.temperature
|
|
4651
4984
|
};
|
|
4652
4985
|
this.subagentConfig = options.subagentConfig;
|
|
4986
|
+
this.onNestedEvent = options.onNestedEvent;
|
|
4653
4987
|
}
|
|
4654
4988
|
/**
|
|
4655
4989
|
* Get the gadget registry for this agent.
|
|
@@ -4880,12 +5214,30 @@ var init_agent = __esm({
|
|
|
4880
5214
|
client: this.client,
|
|
4881
5215
|
mediaStore: this.mediaStore,
|
|
4882
5216
|
agentConfig: this.agentContextConfig,
|
|
4883
|
-
subagentConfig: this.subagentConfig
|
|
5217
|
+
subagentConfig: this.subagentConfig,
|
|
5218
|
+
onNestedEvent: this.onNestedEvent
|
|
4884
5219
|
});
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
|
|
5220
|
+
let streamMetadata = null;
|
|
5221
|
+
let gadgetCallCount = 0;
|
|
5222
|
+
const textOutputs = [];
|
|
5223
|
+
const gadgetResults = [];
|
|
5224
|
+
for await (const event of processor.process(stream2)) {
|
|
5225
|
+
if (event.type === "stream_complete") {
|
|
5226
|
+
streamMetadata = event;
|
|
5227
|
+
continue;
|
|
5228
|
+
}
|
|
5229
|
+
if (event.type === "text") {
|
|
5230
|
+
textOutputs.push(event.content);
|
|
5231
|
+
} else if (event.type === "gadget_result") {
|
|
5232
|
+
gadgetCallCount++;
|
|
5233
|
+
gadgetResults.push(event);
|
|
5234
|
+
}
|
|
5235
|
+
yield event;
|
|
4888
5236
|
}
|
|
5237
|
+
if (!streamMetadata) {
|
|
5238
|
+
throw new Error("Stream processing completed without metadata event");
|
|
5239
|
+
}
|
|
5240
|
+
const result = streamMetadata;
|
|
4889
5241
|
this.logger.info("LLM response completed", {
|
|
4890
5242
|
finishReason: result.finishReason,
|
|
4891
5243
|
usage: result.usage,
|
|
@@ -4910,9 +5262,6 @@ var init_agent = __esm({
|
|
|
4910
5262
|
});
|
|
4911
5263
|
let finalMessage = result.finalMessage;
|
|
4912
5264
|
if (this.hooks.controllers?.afterLLMCall) {
|
|
4913
|
-
const gadgetCallCount = result.outputs.filter(
|
|
4914
|
-
(output) => output.type === "gadget_result"
|
|
4915
|
-
).length;
|
|
4916
5265
|
const context = {
|
|
4917
5266
|
iteration: currentIteration,
|
|
4918
5267
|
maxIterations: this.maxIterations,
|
|
@@ -4942,9 +5291,7 @@ var init_agent = __esm({
|
|
|
4942
5291
|
}
|
|
4943
5292
|
if (result.didExecuteGadgets) {
|
|
4944
5293
|
if (this.textWithGadgetsHandler) {
|
|
4945
|
-
const textContent =
|
|
4946
|
-
(output) => output.type === "text"
|
|
4947
|
-
).map((output) => output.content).join("");
|
|
5294
|
+
const textContent = textOutputs.join("");
|
|
4948
5295
|
if (textContent.trim()) {
|
|
4949
5296
|
const { gadgetName, parameterMapping, resultMapping } = this.textWithGadgetsHandler;
|
|
4950
5297
|
this.conversation.addGadgetCallResult(
|
|
@@ -4954,7 +5301,7 @@ var init_agent = __esm({
|
|
|
4954
5301
|
);
|
|
4955
5302
|
}
|
|
4956
5303
|
}
|
|
4957
|
-
for (const output of
|
|
5304
|
+
for (const output of gadgetResults) {
|
|
4958
5305
|
if (output.type === "gadget_result") {
|
|
4959
5306
|
const gadgetResult = output.result;
|
|
4960
5307
|
this.conversation.addGadgetCallResult(
|
|
@@ -5191,6 +5538,8 @@ var init_builder = __esm({
|
|
|
5191
5538
|
signal;
|
|
5192
5539
|
trailingMessage;
|
|
5193
5540
|
subagentConfig;
|
|
5541
|
+
nestedEventCallback;
|
|
5542
|
+
parentContext;
|
|
5194
5543
|
constructor(client) {
|
|
5195
5544
|
this.client = client;
|
|
5196
5545
|
}
|
|
@@ -5687,6 +6036,74 @@ var init_builder = __esm({
|
|
|
5687
6036
|
this.subagentConfig = config;
|
|
5688
6037
|
return this;
|
|
5689
6038
|
}
|
|
6039
|
+
/**
|
|
6040
|
+
* Set the callback for nested subagent events.
|
|
6041
|
+
*
|
|
6042
|
+
* Subagent gadgets (like BrowseWeb) can use ExecutionContext.onNestedEvent
|
|
6043
|
+
* to report their internal LLM calls and gadget executions in real-time.
|
|
6044
|
+
* This callback receives those events, enabling hierarchical progress display.
|
|
6045
|
+
*
|
|
6046
|
+
* @param callback - Function to handle nested agent events
|
|
6047
|
+
* @returns This builder for chaining
|
|
6048
|
+
*
|
|
6049
|
+
* @example
|
|
6050
|
+
* ```typescript
|
|
6051
|
+
* .withNestedEventCallback((event) => {
|
|
6052
|
+
* if (event.type === "llm_call_start") {
|
|
6053
|
+
* console.log(` Nested LLM #${event.event.iteration} starting...`);
|
|
6054
|
+
* } else if (event.type === "gadget_call") {
|
|
6055
|
+
* console.log(` ⏵ ${event.event.call.gadgetName}...`);
|
|
6056
|
+
* }
|
|
6057
|
+
* })
|
|
6058
|
+
* ```
|
|
6059
|
+
*/
|
|
6060
|
+
withNestedEventCallback(callback) {
|
|
6061
|
+
this.nestedEventCallback = callback;
|
|
6062
|
+
return this;
|
|
6063
|
+
}
|
|
6064
|
+
/**
|
|
6065
|
+
* Enable automatic nested event forwarding to parent agent.
|
|
6066
|
+
*
|
|
6067
|
+
* When building a subagent inside a gadget, call this method to automatically
|
|
6068
|
+
* forward all LLM calls and gadget events to the parent agent. This enables
|
|
6069
|
+
* hierarchical progress display without any manual event handling.
|
|
6070
|
+
*
|
|
6071
|
+
* The method extracts `invocationId` and `onNestedEvent` from the execution
|
|
6072
|
+
* context and sets up automatic forwarding via hooks and event wrapping.
|
|
6073
|
+
*
|
|
6074
|
+
* @param ctx - ExecutionContext passed to the gadget's execute() method
|
|
6075
|
+
* @param depth - Nesting depth (default: 1 for direct child)
|
|
6076
|
+
* @returns This builder for chaining
|
|
6077
|
+
*
|
|
6078
|
+
* @example
|
|
6079
|
+
* ```typescript
|
|
6080
|
+
* // In a subagent gadget like BrowseWeb - ONE LINE enables auto-forwarding:
|
|
6081
|
+
* execute: async (params, ctx) => {
|
|
6082
|
+
* const agent = new AgentBuilder(client)
|
|
6083
|
+
* .withModel(model)
|
|
6084
|
+
* .withGadgets(Navigate, Click, Screenshot)
|
|
6085
|
+
* .withParentContext(ctx) // <-- This is all you need!
|
|
6086
|
+
* .ask(params.task);
|
|
6087
|
+
*
|
|
6088
|
+
* for await (const event of agent.run()) {
|
|
6089
|
+
* // Events automatically forwarded - just process normally
|
|
6090
|
+
* if (event.type === "text") {
|
|
6091
|
+
* result = event.content;
|
|
6092
|
+
* }
|
|
6093
|
+
* }
|
|
6094
|
+
* }
|
|
6095
|
+
* ```
|
|
6096
|
+
*/
|
|
6097
|
+
withParentContext(ctx, depth = 1) {
|
|
6098
|
+
if (ctx.onNestedEvent && ctx.invocationId) {
|
|
6099
|
+
this.parentContext = {
|
|
6100
|
+
invocationId: ctx.invocationId,
|
|
6101
|
+
onNestedEvent: ctx.onNestedEvent,
|
|
6102
|
+
depth
|
|
6103
|
+
};
|
|
6104
|
+
}
|
|
6105
|
+
return this;
|
|
6106
|
+
}
|
|
5690
6107
|
/**
|
|
5691
6108
|
* Add an ephemeral trailing message that appears at the end of each LLM request.
|
|
5692
6109
|
*
|
|
@@ -5754,14 +6171,58 @@ ${endPrefix}`
|
|
|
5754
6171
|
return this;
|
|
5755
6172
|
}
|
|
5756
6173
|
/**
|
|
5757
|
-
* Compose the final hooks, including
|
|
6174
|
+
* Compose the final hooks, including:
|
|
6175
|
+
* - Trailing message injection (if configured)
|
|
6176
|
+
* - Nested event forwarding for LLM calls (if parentContext is set)
|
|
5758
6177
|
*/
|
|
5759
6178
|
composeHooks() {
|
|
6179
|
+
let hooks = this.hooks;
|
|
6180
|
+
if (this.parentContext) {
|
|
6181
|
+
const { invocationId, onNestedEvent, depth } = this.parentContext;
|
|
6182
|
+
const existingOnLLMCallStart = hooks?.observers?.onLLMCallStart;
|
|
6183
|
+
const existingOnLLMCallComplete = hooks?.observers?.onLLMCallComplete;
|
|
6184
|
+
hooks = {
|
|
6185
|
+
...hooks,
|
|
6186
|
+
observers: {
|
|
6187
|
+
...hooks?.observers,
|
|
6188
|
+
onLLMCallStart: async (context) => {
|
|
6189
|
+
onNestedEvent({
|
|
6190
|
+
type: "llm_call_start",
|
|
6191
|
+
gadgetInvocationId: invocationId,
|
|
6192
|
+
depth,
|
|
6193
|
+
event: {
|
|
6194
|
+
iteration: context.iteration,
|
|
6195
|
+
model: context.options.model
|
|
6196
|
+
}
|
|
6197
|
+
});
|
|
6198
|
+
if (existingOnLLMCallStart) {
|
|
6199
|
+
await existingOnLLMCallStart(context);
|
|
6200
|
+
}
|
|
6201
|
+
},
|
|
6202
|
+
onLLMCallComplete: async (context) => {
|
|
6203
|
+
onNestedEvent({
|
|
6204
|
+
type: "llm_call_end",
|
|
6205
|
+
gadgetInvocationId: invocationId,
|
|
6206
|
+
depth,
|
|
6207
|
+
event: {
|
|
6208
|
+
iteration: context.iteration,
|
|
6209
|
+
model: context.options.model,
|
|
6210
|
+
outputTokens: context.usage?.outputTokens,
|
|
6211
|
+
finishReason: context.finishReason
|
|
6212
|
+
}
|
|
6213
|
+
});
|
|
6214
|
+
if (existingOnLLMCallComplete) {
|
|
6215
|
+
await existingOnLLMCallComplete(context);
|
|
6216
|
+
}
|
|
6217
|
+
}
|
|
6218
|
+
}
|
|
6219
|
+
};
|
|
6220
|
+
}
|
|
5760
6221
|
if (!this.trailingMessage) {
|
|
5761
|
-
return
|
|
6222
|
+
return hooks;
|
|
5762
6223
|
}
|
|
5763
6224
|
const trailingMsg = this.trailingMessage;
|
|
5764
|
-
const existingBeforeLLMCall =
|
|
6225
|
+
const existingBeforeLLMCall = hooks?.controllers?.beforeLLMCall;
|
|
5765
6226
|
const trailingMessageController = async (ctx) => {
|
|
5766
6227
|
const result = existingBeforeLLMCall ? await existingBeforeLLMCall(ctx) : { action: "proceed" };
|
|
5767
6228
|
if (result.action === "skip") {
|
|
@@ -5776,9 +6237,9 @@ ${endPrefix}`
|
|
|
5776
6237
|
};
|
|
5777
6238
|
};
|
|
5778
6239
|
return {
|
|
5779
|
-
...
|
|
6240
|
+
...hooks,
|
|
5780
6241
|
controllers: {
|
|
5781
|
-
...
|
|
6242
|
+
...hooks?.controllers,
|
|
5782
6243
|
beforeLLMCall: trailingMessageController
|
|
5783
6244
|
}
|
|
5784
6245
|
};
|
|
@@ -5839,6 +6300,19 @@ ${endPrefix}`
|
|
|
5839
6300
|
this.client = new LLMistClass();
|
|
5840
6301
|
}
|
|
5841
6302
|
const registry = GadgetRegistry.from(this.gadgets);
|
|
6303
|
+
let onNestedEvent = this.nestedEventCallback;
|
|
6304
|
+
if (this.parentContext) {
|
|
6305
|
+
const { invocationId, onNestedEvent: parentCallback, depth } = this.parentContext;
|
|
6306
|
+
const existingCallback = this.nestedEventCallback;
|
|
6307
|
+
onNestedEvent = (event) => {
|
|
6308
|
+
parentCallback({
|
|
6309
|
+
...event,
|
|
6310
|
+
gadgetInvocationId: invocationId,
|
|
6311
|
+
depth: event.depth + depth
|
|
6312
|
+
});
|
|
6313
|
+
existingCallback?.(event);
|
|
6314
|
+
};
|
|
6315
|
+
}
|
|
5842
6316
|
return {
|
|
5843
6317
|
client: this.client,
|
|
5844
6318
|
model: this.model ?? "openai:gpt-5-nano",
|
|
@@ -5864,7 +6338,8 @@ ${endPrefix}`
|
|
|
5864
6338
|
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
5865
6339
|
compactionConfig: this.compactionConfig,
|
|
5866
6340
|
signal: this.signal,
|
|
5867
|
-
subagentConfig: this.subagentConfig
|
|
6341
|
+
subagentConfig: this.subagentConfig,
|
|
6342
|
+
onNestedEvent
|
|
5868
6343
|
};
|
|
5869
6344
|
}
|
|
5870
6345
|
ask(userPrompt) {
|
|
@@ -6021,6 +6496,19 @@ ${endPrefix}`
|
|
|
6021
6496
|
this.client = new LLMistClass();
|
|
6022
6497
|
}
|
|
6023
6498
|
const registry = GadgetRegistry.from(this.gadgets);
|
|
6499
|
+
let onNestedEvent = this.nestedEventCallback;
|
|
6500
|
+
if (this.parentContext) {
|
|
6501
|
+
const { invocationId, onNestedEvent: parentCallback, depth } = this.parentContext;
|
|
6502
|
+
const existingCallback = this.nestedEventCallback;
|
|
6503
|
+
onNestedEvent = (event) => {
|
|
6504
|
+
parentCallback({
|
|
6505
|
+
...event,
|
|
6506
|
+
gadgetInvocationId: invocationId,
|
|
6507
|
+
depth: event.depth + depth
|
|
6508
|
+
});
|
|
6509
|
+
existingCallback?.(event);
|
|
6510
|
+
};
|
|
6511
|
+
}
|
|
6024
6512
|
const options = {
|
|
6025
6513
|
client: this.client,
|
|
6026
6514
|
model: this.model ?? "openai:gpt-5-nano",
|
|
@@ -6046,7 +6534,8 @@ ${endPrefix}`
|
|
|
6046
6534
|
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
6047
6535
|
compactionConfig: this.compactionConfig,
|
|
6048
6536
|
signal: this.signal,
|
|
6049
|
-
subagentConfig: this.subagentConfig
|
|
6537
|
+
subagentConfig: this.subagentConfig,
|
|
6538
|
+
onNestedEvent
|
|
6050
6539
|
};
|
|
6051
6540
|
return new Agent(AGENT_INTERNAL_KEY, options);
|
|
6052
6541
|
}
|
|
@@ -11106,4 +11595,4 @@ export {
|
|
|
11106
11595
|
createEmptyStream,
|
|
11107
11596
|
createErrorStream
|
|
11108
11597
|
};
|
|
11109
|
-
//# sourceMappingURL=chunk-
|
|
11598
|
+
//# sourceMappingURL=chunk-JCFPJMRQ.js.map
|